Article Index

Machine specific tasks with MSBuild

Jeremy D. Miller recently wrote about how to make a NAnt script branch based on the machine it's runnig on. Since I've been working almost exclusively with MSBuild, I figured it might be educational to post the equivalent script.

First, in Jeremy's example, he is really only changing the values of a property based on the machine name. If that is all you need to do, the MSBuild solution is fairly straightforward. I'll include the entire file contents so that you can easily copy/paste and test, and verify their are no hidden tricks.

<?xml version="1.0" encoding="utf-8"?>

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

  <Database>invoice_dev</Database>

  <Database Condition="'$(Computername)'=='TEST-SERVER'">invoice_test</Database>

  <Database Condition="'$(Computername)'=='PRODUCTION-SERVER'">invoice_prod</Database>

  </PropertyGroup>

 

  <Target Name="Build" >

  <Message Text="Connect to database $(Database)" />

  </Target>

</Project>

This takes advantage of the fact that all environment variables are immediately available as properties in an MSBuild script; and that all Windows machines (that I've worked on recently) have the COMPUTERNAME environment variable set.

However, if you actually needed to run different tasks (as opposed to just setting properties), things would look a little different. One way I might approach it would be:

<?xml version="1.0" encoding="utf-8"?>

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

 

  <Target Name="Build" DependsOnTargets="MachineSpecificTasks">

  <Message Text="Start Build" />

  </Target>

 

  <Target Name="MachineSpecificTasks"

  DependsOnTargets="TestServers;

                      ProductionServers" />

 

  <Target Name="TestServers" Condition="'$(Computername)'=='TEST-SERVER1'">

  <Message Text="Building on test server." />

  </Target>

 

  <Target Name="ProductionServers" Condition="'$(Computername)'=='PRODUCTION-SERVER1'

                                    OR '$(Computername)'=='PRODUCTION-SERVER2'">

  <Message Text="Building on production server." />

  </Target>

</Project>

I created a separate dummy target called MachineSpecificTasks. It is just used to define the different targets that you make available for each environment. I make all of the machine specific targets as dependencies on this target, so that the script will attempt to execute each of them. I could have just as easily skipped the MachineSpecificTasks target and put the dependencies on my Build target, but I felt that list might get large and get confusing. Better to hide it away. I then set a condition on each target so that it is only run if the computername environment variable matches the given computer name. I went a little further and showed how you could associate multiple computer names (PRODUCTION-SERVER1 and PRODUCTION-SERVER2) with the same set of tasks (collectively called ProductionServers).

There is probably a way to use the CallTarget task to more closely match the look of the NAnt solution, but I'm not sure if it would buy you anything.

Hope that helps.