In the previous post we started our journey in our quest to learn more about MSBuild and to unravel the mystery about Ctrl + Shift + B in Visual Studio. This time we will dig deeper and learn more about Targets and how we can use them.

This post is part of the following series:

Get to know your Targets

It is possible to have multiple Targets in a MSBuild Script. By default only the first target in a script is executed. You can however specify the order which targets are executed in several ways. Before we go through how we can achieve that, lets add some more targets to our build script.

Open the build script located at c:\MSBuildDemo\construction.msbuild an change the content to the following:

<?xml version="1.0" encoding="utf-8"?>  
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Foundation">
        <Message Text="Foundation for construction done" />
    </Target>

    <Target Name="Walls">
        <Message Text="Walls for construction done" />
    </Target>

    <Target Name="Roof">
        <Message Text="Roof for construction done" />
    </Target>
</Project> 

Then open Visual Studio Command Prompt and change the location to the demo folder by typing the following command:

>cd c:\MSBuildDemo

Then run your script by typing:

>msbuild construction.msbuild

This will only execute the first target specified in our build script, which is the default behavior (You can try to rearrange the targets and run your script again to verify this behavior).
MSBuild Default Target Order

Run a specific target

What if you want to run a specific target then? You can either change the Project node to the following to run the Walls Target as default:

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

Or you can specify the target to run directly in the Command Line as follows:

>msbuild construction.msbuild /target:Walls

Or you can use the short form:

>msbuild construction.msbuild /t:Walls

MSBuild Run specific target
If you have specified a target in the DefaultTargets attribute and you also specify one in the Command Line, the one specified in the Command Line will override the one in your script file.

Run multiple targets

As you may have noticed in the previous example the attribute DefaultTargets we added indicates that we can specify multiple targets. To do so we separate the targets with ; or , in the attribute:

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

And it also works in the Command Line:

>msbuild construction.msbuild /target:Foundation;Walls

MSBuild Run multiple targets

The Life Cycle of Targets

Now that we know how to run a specific target and multiple targets would it not be nice to be able to control the order the targets are executed? We can do so by defining attributes on our target nodes or by using the CallTargets action. Lets go through how we can use them one by one with some simple examples.

DependsOnTargets

Let say you have a target that depends on that other targets have been invoked before it gets executed. Then you can use the DependsOnTargets attribute. As an example let say that we want to make sure that if we invoke the Roof Target that the Walls Target is executed before, you can achieve this by changing the Roof Target to the following:

<Target Name="Roof" DependsOnTargets="Walls">

And run your script invoking the Roof Target with the command:

>msbuild construction.msbuild /target:Roof

Which should result in the Walls Target running before the Roof target.
MSBuild DependsOnTargets

CallTarget

It is also possible to call another target inside a target using the CallTarget action. If you for example want to invoke the Roof Target from the Foundation Target we can use the CallTarget action as follows:

<Target Name="Foundation">
    <Message Text="Foundation for construction done" />
    <CallTarget Targets="Roof" />
</Target>

And run your script invoking the Foundation Target with the command:

>msbuild construction.msbuild /target:Foundation

MSBuild CallTarget

BeforeTargets & AfterTargets

Another way to specify the order of targets, is by using the attributes BeforeTargets and AfterTargets. As an example how you can use this, let say that you want to make sure that a target is invoked before another target you use the BeforTargets attribute. If you instead want to make sure that a target is invoked after another target you use the AfterTargets attribute. The attributes are very similar to the DependsOnTargets attribute but they more or less work the other way around. If you don´t understand, don't worry, you will probably do that after this example:

<?xml version="1.0" encoding="utf-8"?>  
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <Target Name="Foundation" BeforeTargets="Walls">
        <Message Text="Foundation for construction done" />
    </Target>

    <Target Name="Walls">

        <Message Text="Walls for construction done" />

    </Target>
    
    <Target Name="Roof" AfterTargets="Walls">

        <Message Text="Roof for construction done" />

    </Target>

</Project>  

Finally execute your new script:

>msbuild construction.msbuild /target:Walls

And as expected your result should be:
MSBuild BeforeTargets and AfterTargets

Conditional Targets

With the Condition attribute you are able to specify a condition that will ensure that the target is only executed if the condition is met. To easier understand conditional targets we need to introduce Properties. A property is like a variable that you can use to store a value. You define your properties in the PropertyGroup node. Add the following lines of code before your Foundation Target inside the Project node.

<PropertyGroup>
    <FoundationIsReady>true<FoundationIsReady> 
    <WallsStatus>Pending</WallsStatus>
</PropertyGroup>

As you can see you define your properties as a new xml node with the value inside. To reference your property you use the following syntax $(PropertyName). Now that we have our properties set up, we can look at an example. We want to only run the Walls Target if the property FoundationIsReady is true. We also only want to run the Roof Target if the property WallsStatus is equal to Ready.

<?xml version="1.0" encoding="utf-8"?>  
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
        <FoundationIsReady>true</FoundationIsReady>
        <WallsStatus>Pending</WallsStatus>
    </PropertyGroup>

    <Target Name="Foundation" BeforeTargets="Walls">
        <Message Text="Foundation for construction done" />
    </Target>

    <Target Name="Walls" Condition="$(FoundationIsReady)">

        <Message Text="Walls for construction done" />

    </Target>
    
    <Target Name="Roof" AfterTargets="Walls" Condition="$(WallsStatus) == 'Ready'">

        <Message Text="Roof for construction done" />

    </Target>

</Project>  

Lets invoke our script:

>msbuild construction.msbuild /target:Walls

MSBuild Conditional Targets

Phew...that was a long runner but we reached our target :)

Next time will we continue with learning more about properties and some more convenient features that can help us with our build script.

See you next time!