PS for .NET devs part 3: List Targets in your MSBuild file

Posted on Nov 19, 2011

The project I’m currently working on, started more than five years ago and has quite a large MSBuild file to perform all the build automation for the project. A while back I found myself struggling to remember the name of a specific build Target that I don’t use very often. Since such a large XML file is not as readable as one would hope I couldn’t find what I was looking for fast enough. Since I know PowerShell has good XML support I opened the PowerShell Integrated Scripting environment and experimented a bit to see how hard it would be to list all the Targets in the MSBuild file using PowerShell.

To give you an idea what an MSBuild file looks like here is a little snippet from the OpenWrap source:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="OpenWrap-Package" ToolsVersion="3.5">
  <PropertyGroup>
    <Root>$(MSBuildProjectDirectory)\..</Root>
    <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
    <PackageName Condition="'$(PackageName)' == ''">OpenWrap</PackageName>
    <OpenWrap-DescriptorPath Condition="'$(OpenWrap-DescriptorPath)' == ''">$(Root)\$(PackageName).wrapdesc</OpenWrap-DescriptorPath>
    <OpenWrap-BuildTasksDirectory Condition="'$(OpenWrap-BuildTasksDirectory)' == ''">$(Root)\wraps\openwrap\build</OpenWrap-BuildTasksDirectory>
  </PropertyGroup>
  
  <ItemGroup> 
    <WrapBinary Include="$(Root)\src\**\*.csproj" />
  </ItemGroup>
  <Target Name="Clean">
    <Delete Files="$(Root)\scratch\build\**\*.*" ContinueOnError="true" />
    <Delete Files="$(Root)\scratch\package\**\*.*" ContinueOnError="true" />
    <RemoveDir Directories="$(Root)\scratch\build" ContinueOnError="true" />
    <RemoveDir Directories="$(Root)\scratch\package" ContinueOnError="true" />
  </Target>
  <!-- lots of lines deleted-->
</Project>

What’s so great about PowerShell’s XML support? No fiddling around with namespaces, the element tree can be walked using the . (aka the property dereferencing operator). So to get all Target elements from the MSBuild file you can use:

1
2
$build = [xml](Get-Content openwrap.proj)
$build.Project.Target

Using Get-Member you see that the Target objects have a Name property.

Viewing the Name property

One obvious thing to try would be

1
2
$build = [xml](Get-Content openwrap.proj)
$build.Project.Target.Name

But that gives no output, this is caused by the fact that Name is an attribute. Using Select-Object however works perfectly. The total script becomes:

1
2
$build = [xml](Get-Content openwrap.proj)
$build.Project.Target| Select-Object Name | Sort-Object Name

This could be put on 1 line to uphold the slogan, “Automating the world one-liner at a time…” but I find this to be a little more readable.

This script took me 5 minutes to write and has already payed itself back dozens of times when I need to find the name of the target that I wish to run. That’s all for now, next time will take a look at how you can get an overview of your code-base using a few simple PowerShell commands.

References