Sep 022012
 

It brings me great joy that the title of this blog post is both technically accurate and evokes the type of comic-book language I use when trying to figure out MSBuild variable syntax. Every time I try to dive in and do something non-trivial with MSBuild, I get tripped up over PropertyGroups, ItemGroups, Item Metadata, and the differences in syntax between $(This), @(This), and %(This). I figure I’m probably not alone, so in this post I will try to help my future self, and everyone else that can’t keep this stuff straight.

Property Groups and the Dollar Sign

Property groups are used to capture one or more simple, scalar values. The XML nodes within the property group can be named whatever you would like. Properties can reference the value of other properties in their own values by using the $(PropertName) syntax. This same syntax can be used in other tasks to extract the value of a property. A fairly standard example would be something like this:

<PropertyGroup>
  <BaseFolder>c:\base\folder</BaseFolder>
  <SettingsFile>$(BaseFolder)\settings\settings.xml</SettingsFile>
</PropertyGroup>
<Message Text="Using settings file found at $(SettingsFile)"/>

This gives the following output:

Using settings file found at c:\base\folder\settings\settings.xml

Item Groups and the At Symbol

Item groups are used to capture a single variable that has multiple values, akin to arrays or dictionaries in other languages. An ItemGroup is declared thusly:

<ItemGroup>
  <MyItems Include="First" />
  <MyItems Include="Second;Third;" />
  <MyItems Include=";;;;Fourth;;" />
</ItemGroup>
<Message Text="My items using dollar: $(MyItems)"/>
<Message Text="My items using at symbol: @(MyItems)"/>

Which gives this output:

My items using dollar:
My items using at symbol: First;Second;Third;Fourth

As you can see you cannot use the $(MyItems) syntax for ItemGroups; you just get an empty string. Instead, you can use the @(MyItems) to expand the ItemGroup into a semi-colon delimited list. MSBuild is also happy to take the input of an ItemGroup as a semi-colon delimited list to add multiple values to the group in one shot. This comes in handy when passing values between scripts or from the command line. Note that I included some extra semi-colons to prove that MSBuild isn’t just concatenating strings, but is assembling the items into a collection.

Item Metadata and the Percent Sign

Item groups can be used not just as collections of scalar values, but also as collections of key/value dictionaries. These key/value pairs are called Item Metadata in MSBuild. Here is an example:

<ItemGroup>
  <People Include="Joe">
    <Email>joe@example.com</Email>
  </People>
  <People Include="Bill">
    <Email>bill@example.com</Email>
  </People>
  <People Include="Oscar">
    <Email>oscar@example.com</Email>
  </People>
</ItemGroup>
<Message Text="Processing person %(People.Identity) with email %(People.Email)"/>

This gives the output:

Processing person Joe with email joe@example.com
Processing person Bill with email bill@example.com
Processing person Oscar with email oscar@example.com

Note the use of the %(ItemGroup.MetadataKey) syntax. The special key “Identity” refers to the value used in the Include attribute of the item node. Also note that with a single <Message/> task I get output for all three items in the group. This is because of the percent sign syntax which expands the task it is used in and executes that task for every item in a group. This is one of the key features of MSBuild that make it extremely powerful for processing batches of items.

Item Groups and Well-known Metadata

MSBuild is primarily designed for building code from source files. Because of this, it is heavily optimized for processing groups of files. Item groups can easily be created from the file system, and groups of files will automatically contain well-known metadata keys that contain additional information about each file. An example:

<ItemGroup>
  <FilesToProcess Include=".\theFiles\*.*"/>
</ItemGroup>
<Message Text="Processing file %(FilesToProcess.Filename)%(FilesToProcess.Extension), created %(FilesToProcess.CreatedTime)"/>

Which gives the output:

Processing file File1.txt, created 2012-08-27 15:46:19.3351955
Processing file File2.txt, created 2012-08-27 15:46:19.3351955
Processing file File3.txt, created 2012-08-27 15:46:19.3351955

Clear as mud?

Hopefully that helps clear things up a bit. MSBuild is a very powerful scripting language, but it definitely requires a bit of paradigm shifting from “normal” languages such as C# in order to use it efficiently. Usually it only takes me a few minutes to re-figure out all of this syntax, but this post should help in the future.

  4 Responses to “MSBuild: PropertyGroup, ItemGroup, Item Metadata, and crazy $@% syntax”

  1. So $!@ing awesome, thanks so much!

  2. [...] sign in front of the array, if you would like to know more about the difference between @,%,$ read this article (also listed at the bottom of this [...]

  3. This is one of the best written articles I have come across on the topic of ItemGroups in MSBuild. This thing has always confused me in the past but now as you say, it is “clear as mud” in my head!

    I am planning on a series of advanced articles on MSbuild myself. I might borrow some of your content from here. Of course I would give full credit where credit is due and link back to your blog and relevant articles. I hope that is OK?

  4. Excellent post, i’ll use it as my personal msbuild reference whenever the syntax howls at me again ;-)

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>