Welcome!

.NET Authors: Liz McMillan, Peter Silva, Yakov Werde, Matthew Pollicove , Corey Roth

Related Topics: .NET

.NET: Article

Refactoring Your MSBuild Scripts

How to make maintainable processes

Getting and Setting Properties
Once you split out your MSBuild script into separate files and can call targets in those files, you'll soon find yourself wanting to pass data to those targets. For example, you may write a task that drops a database, but you'll want to be able to specify which database to drop. Any external data source, such as database connection strings, ftp addresses, file paths, output product names, etc., should each be abstracted to a single property. The last thing you want to do is waste time searching your script to change every instance of a file path from "C:\something" to "D:\something." You may also consider abstracting strategy decisions to properties - should this check local or network resources, should this copy the output products to destination X or not, etc,? You could then reference these properties in the conditional attribute available to all targets and tasks, giving your script immediate flexibility. (See Table 3)

MSBuild offers at least five ways to get and set properties.

1.  PropertyGroup: The easiest way to set properties is with a PropertyGroup, as we discussed earlier. Any property specified in this group is global for the entire project script.

   <PropertyGroup>
      <ApplicationPath>c:\myproj\stuff.exe</ApplicationPath>
      <UseLocalOptions>Value3</UseLocalOptions>
   </PropertyGroup>

2.  MSBuild.exe: You can also pass in values through the command line using MSBuild.exe (the executable engine, not the task) and the "/p" switch. You can set multiple properties by separating them with semicolons. Any property set here overrides what may be in the propertyGroup section.

MSBuild.exe CommandLine.msbuild /p:Var1=AAA;Var2=BBB

You can combine these two techniques to make your scripts much more maintainable. For example, you could first abstract all directory information to their own properties, and then have different bat files pass in different directories (as property values) to the same script. This would let you use the same MSBuild script for multiple directories.

3.  MSBuild Task: We've already mentioned our third way to pass properties - using the MSBuild task. This is similar to the command line in that you also pass values through a semicolon-separated list, and those values have a global scope in the target being called. However, the task and command=line calls are different, as best explained by the MSDN Help: "Unlike using the Exec Task to invoke MSBuild.exe, this task uses the same MSBuild process to build the child projects. The list of already-built targets that can be skipped is shared between the parent and child builds. This task is also faster because no new MSBuild process is created."

<MSBuild Projects="CommonTasks.proj" Targets="TestOutput" Properties="Var1=xyz;Var2=abc" />

4.  Output: Tasks can have output values. For example, an executable has a return code. You can get that value and store it in a local property via the task's <Output> inner property. In the snippet below we run an exe that creates a random number and saves it to the property "NumberA." This property need not exist in a global PropertyGroup list, but if it does then the local value will overwrite the global one for the scope of the target. Once control leaves the target the newly created property is lost.

<Target Name="EndPoint">
   <Exec IgnoreExitCode="true" Command="GetRandomNumber.exe">
       <Output TaskParameter="ExitCode" PropertyName="NumberA"/>
      </Exec>
      <Message Text="Output value is '$(NumberA)'" />
</Target>

5.  CreateProperty Task: Sometimes you just want to create a temporary local variable. You can use the CreatePropertyTask and its Output value to do just that. The following snippet shows how to create a local variable "MyLocalProp."

<PropertyGroup>
    <Var1>Value1</Var1>
</PropertyGroup>

<Target Name="EndPoint">
   <CreateProperty Value="Prop_$(Var1)_val">
     <Output
       TaskParameter="Value" PropertyName="MyLocalProp" />
   </CreateProperty>

   <Message Text="New value: $(MyLocalProp)" />
</Target>

These five different ways to set properties offer much flexibility.

Conclusion
MSBuild scripts can automate many of the most tedious and boring parts of your project, so it's a good investment to learn how they work. Knowing how to split your scripts into separate files and then call any target with any properties lets you refactor those scripts, making it even better.

More Stories By Timothy Stall

Tim Stall is a software developer at Paylocity, an independent provider of payroll and human resource solutions. He can be contacted at tims@paylocity.com.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.