MSBuild is a really interesting beast! He who has not used it haven’t encountered its quirks!

Should I care about MSBuild?

If you are working on a .NET stuff, you definitely should know how it works, because the whole .NET ecosystem is built around it. From your project files (e.g. csproj) all the way down to all Microsoft targets. Varying from what should go to the intermediate (obj) dir and what to the output (bin), copying references (if needed) or even publishing a self-contained executable (if using .NET Core).

Basically, there is not way around it! Love it or hate it - you are going to use it.

MSBuild 101

Before continuing, you should familiarize yourself with 2 core concepts:

Those are the 3 building blocks of MSBuild.

If you don’t want to dig in, just remember this:

  • Properties are used for a single value (e.g. path to a config file).
  • Items (or items groups) are used for multiple values (e.g. list of all project files).
  • Tasks do the actual work (executing commands, copying stuff, etc).

Execute an external command

Executing an external command in MSBuild is pretty easy, we need to just use the exec task like so:

<Exec Command="echo foobar" />

Pretty simple and of course - pretty useless!

Check the exit code

In a lot of scenarios you want to check the exit code:

<Exec Command="myecho foobar"
      ContinueOnError="true">
    <Output TaskParameter="ExitCode" PropertyName="MyEchoExitCode"/>
</Exec>

This will set the MyEchoExitCode property to 127 if running on MacOS and 9009 if running on Windows for command not found (unless you have myecho executable in your PATH).

Note the ContinueOnError part - if omitted the script will fail and the execution will stop. You can think of it as a try/catch block!

* You can check more options for the exec task.

Act accordingly

Having captured the exit code was the hard part! Now comes the easy part - doing stuff with the result you’ve got.

Having repeatable build process is a MUST, but you can spice it up with the following snippet:

<PropertyGroup>
    <IsBlueMoon Condition=" '$([System.DateTime]::Now.ToString(ss))' == '59' " >true</IsBlueMoon>
</PropertyGroup>

<Exec Command="build my.project"
      ContinueOnError="true">
    <Output TaskParameter="ExitCode" PropertyName="BuildExitCode"/>
</Exec>

<Error Message="BUILD FAILED!" Condition=" '$(BuildExitCode)' != '0' AND '$(IsBlueMoon)' != 'true' ">

This way your build won’t fail when there is a blue moon outside!

Final words

That was a basic example of how you can use MSBuild to execute a custom command and check the exit code.

Next time, we’re going to leverage this to actually do some useful stuff! ;)