A continuation of the previous post. Today we’re going to take a look how we can execute multiple commands with MSBuild.

Hopefully, you took the time to familiarize yourself with the MSBuild concepts listed in the previous post, if not - go check it out.

Executing multiple external commands

Last time we wanted to execute a single command and check its error code, although useful, it’s kinda limited. Usually, we want to execute a set of commands instead of a single one.

The copy/paste way

Remember - your first job is to get the everything working, refactoring and optimization come after that!

Here is the simplest way to do it:

<Exec Command="echo foobar" />
<Exec Command="echo zaz" />

More of “MSBuild” way

Although we can define a single exec task, we can use it to iterate through a list of commands, like so:

<ItemGroup>
    <Commands Include="echo foobar;
                       echo zaz" />
</ItemGroup>

<Exec Command="%(Commands.Identity)" />

Now that’s more lines, however, we only need to touch the commands item group, no need to copy/paste the exec task!

Of course, you can get away when you know all your items (or commands), however that may not always be the case. Then you can’t go with the copy/paste way. :(

Check the exit codes

Now that we’ve executed multiple commands we may want to check their exit codes. In my previous post we checked the exit code of a single command. That’s pretty easy, we write it to a property, but we don’t have a single command now, we have multiple commands. That means a property won’t suffice.We can leverage an item group for this use case!

Here how this can be done:

<ItemGroup>
    <Commands Include="echo foobar;
                       echo zaz" />
</ItemGroup>

<Exec Command="%(Commands.Identity)"
      ContinueOnError="true">
    <Output TaskParameter="ExitCode" ItemName="ExitCodes"/>
</Exec>

<Message Text="ExitCodes: @(ExitCodes)" />

If you run the snippet above, you’re going to get the following output in the console:

foobar
zaz
ExitCodes: 0;0

* Do note the ContinueOnError attribute, if you omit this, only the first command will be executed in case of an error!

Rule of thumb

If you need more than 1 property - use an item group!

Bubble-up the error

If you want to bubble-up the error, you can add an Error task as follows:

<Error Text="Command execution failed!" Condition=" '%(ExitCodes.Identity)' != '0' "/>

This will go through every item (0;0) in the item group (remember, items printed with a semi-colon delimiter) and check if it’s different than 0, if it is - Command execution failed! error will be raised.

Final words

This is you can execute multiple commands and fail after each one of the commands is executed.

Next time we’re going to use the knowledge to actually do something useful!