Daniel Doubrovkine bio photo

Daniel Doubrovkine

aka dB., CTO at artsy.net, fun at playplay.io, NYC

Email Twitter LinkedIn Github

In a previous post I’ve described how to do product/build versioning with MSBuild. Another common build aspect is building different flavors of code: Debug vs. Release.

All the projects I work on now have a very simple MSBuild script that allows you to build either Debug or Release, defaulting to Debug for developers. Our CruiseControl configurations build Release. Comes a shared libraries project, which needs to do Debug, Release or both. How can I accomplish this with MSBuild?

Default Configuration

Let’s define a property for the default configuration, ie. when no Configuration is specified on the command line.

<PropertyGroup Condition="'$(Configuration)'==''">
 <Configuration>Debug</Configuration>
</PropertyGroup>

Multiple Configurations

What if multiple configurations were specified? For example, Debug;Release. We want to transform a single property into an array of task parameters. There’s a brain-twisting way of doing this with MSBuild.

<Target Name="configurations">
 <CreateItem Include="$(Configuration)">
  <Output TaskParameter="Include" ItemName="Configuration" />
 </CreateItem>
</Target>

It seems that the above code does nothing, but it creates an item called Configuration that can be specified as input to another target.

<Target Name="showconfigurations" DependsOnTargets="configurations" Inputs="@(Configuration)" Outputs="target\%(Configuration.FileName)">
 <Message Importance="high" Text="Building project, %(Configuration.Identity) ..." />
</Target>

Try it.

> msbuild test.proj /t:showconfigurations
Building project, Debug

> msbuild test.proj /t:showconfigurations /p:Configuration=Release
Building project, Release ...

> msbuild test.proj /t:showconfigurations /p:Configuration="Debug;Release"
Building project, Debug ...
Building project, Release ...

Target Inputs

Finally, change all the targets that depend on the configuration name accordingly and use %(Configuration.Identity) rather than $(Configuration) in those tasks.

<Target Name="version" DependsOnTargets="configurations" Inputs="@(Configuration)" Outputs="target\%(Configuration.FileName)">
 <Version Major="$(MajorVersion)" Minor="$(MinorVersion)">
 <Output TaskParameter="Major" PropertyName="Major" />
 <Output TaskParameter="Minor" PropertyName="Minor" />
 <Output TaskParameter="Revision" PropertyName="Revision" />
 </Version>
 <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision) (%(Configuration.Identity))"/>
 ...
</Target>