18 February 2008

So, what's the deal with $, @, and % anyway?

So, you've decided that the default build script that's built by the "New Build Type" wizard doesn't quite do everything that you need it to do. So, you click on over to the Version Control Explorer, check out the TFSBuild.proj file, and dive in!

At first glance, everything looks simple enough, you have various combinations of Targets, Tasks, and Properties. Things start to get a little interesting as you explore the concept of Item Groups. However, you start to notice a subtle difference with how various properties (or at least what look like properties) are accessed - e.g. some begin with '$', some with '@', and yet others with '%'. What's going on here? What's the difference? Before we can answer this question, we need to take a look at Properties and Items.

You can think of properties as MSBuild's version of a variable (as you might use it in some programming language such as C#). Once you declare a property, you can use it throughout your build script to reference whatever value it may hold. You create a property by defining an element within a PropertyGroup element. For example:


In the above example, the property is named "WebBinSource" and can be referenced in your build script as $(WebBinSource). You can create as many properties within the PropertyGroup element as you like. You can also create as many PropertyGroup elements as you like.

Aside from the obvious advantages of using properties within your build script (i.e. so you don't have to hard-code values in multiple places) another nice feature is that you can pass in custom property values on the MSBuild command line. For example:

MSBuild.exe MyProject.proj /p:WebBinSource=Bin

These properties can also be specified in the Team Explorer "Queue Build" dialog:

Queue Build Dialog

You can also reference environment variables using the same syntax. For example, you can reference the environment variable PATH using the syntax $(Path). If you declare a custom property with the same name as an existing environment variable, the custom property will override the environment variable and its value will be used when referenced.

MSBuild defines several reserved properties that you can utilize in your build scripts. There are also several customizable properties available within Team Foundation Build for use within your TFS build scripts.

Items allow you to create user-defined collections which can then be used an input arguments for MSBuild tasks. The task can then act on the individual items within the collection as needed. You create an item collection by defining elements within an ItemGroup element. For example:

<SourceFiles Include = "default.aspx"/>
<SourceFiles Include = "register.aspx"/>

NOTE: You can also include multiple items within a single element by separating each item with a semicolon. For example:

<SourceFiles Include = "default.aspx;register.aspx"/>

In this example, the item list is named "SourceFiles" and can be referenced in the build script as @(SourceFiles). For example, you may have a custom task that deploys a collection of files to a web server:

<Target Name="AfterBuild">
<DeployWeb Sources="@(SourceFiles)" />

Depending upon the task using the item collection, it may support the use of wildcards in the item definition. You can also change the item delimiter to be something other than the default semicolon for custom processing.

Although items are a useful feature, they're even more useful if you can associate metadata with them.

Each individual item contained within an item collection may also contain metadata information. For example, a collection of files to be copied may contain the attributes of each file (e.g. read only, archive bit, etc.) or possibly the creation and last updated date/time.

You can associate metadata with individual items by adding child elements to the individual item elements. For example:

<SourceFiles Include = "default.aspx">

<SourceFiles Include = "Web.config">


Using this example, the following task will copy all web files that have a metadata item of Copy with a value of true:

<Target Name="AfterBuild">
<DeployWeb Sources="@(SourceFiles)" Condition=" '%(Copy)' == 'true' />

Also, any time a new item is created, there is a set of "well-known" metadata created and assigned to each item.

Putting it All Together
So, now when you see items prefixed with a '$', you'll know that it's referencing a property whereas items prefixed with a '@' are referencing a collection of items to be processed. If a task (or condition) needs access to the metadata for an individual item (or collection of items) then you will see the metadata items being prefixed with the '%'.

Hopefully this has cleared up the differences between the different types of "variables" in MSBuild and your Team Foundation Build scripts.