- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Thursday, 08 May 2008

MSBuild: Building the same project multiple times

After some discussion at the MSBuild MSDN Forum I started investigating building the same project with different Compiler constants defined. I was actually pretty surprised by what I found. I created a simple Windows Form Application with a lone button one it. The image below show the simple app.

Here is the entire code behind file.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

 

namespace WindowsFormsApplication1

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

 

private void buttonExample_Click(object sender, EventArgs e)

{

string message = "default message";

#if CONSTANT_ONE

message = "CONSTANT_ONE";

#endif

#if CONSTANT_TWO

message = "CONSTANT_TWO";

#endif

 

MessageBox.Show(message);

}

}

}

Ddd

The parts to take note are the regions highlighted in yellow. So if the constant CONSTANT_ONE is defined the message results to CONSTANT_ONE and if CONSTANT_TWO is defined it will show the message CONSTANT_TWO.

I then created the simple build file.

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

<Project DefaultTargets="BuildAll" ToolsVersion="3.5"

xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

 

<PropertyGroup>

<ConstOne>CONSTANT_ONE</ConstOne>

<ConstTwo>CONSTANT_TWO</ConstTwo>

</PropertyGroup>

 

<ItemGroup>

<Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj">

</Projects>

</ItemGroup>

 

<!--

Cleans the project before we start the build process.

The only reason I am calling this is to remove artifacts of

previous builds, to clearly demonstrate what's going on.

-->

<Target Name="CleanAll">

<MSBuild Projects="@(Projects)" Targets="Clean" />

</Target>

 

<Target Name="BuildAll" DependsOnTargets="CleanAll;BuildConstOne;BuildConstTwo" />

 

<Target Name="BuildConstOne">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstOne);OutputPath=binOne\;"/>

</Target>

<Target Name="BuildConstTwo">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstTwo);OutputPath=binTwo\;"

 

/>

</Target>

 

<!-- ===========================================================================

This region rebuilds the projects, so after it is built once it is then cleaned

before building it again.

This produces the assemblies with the desired behavior

================================================================================-->

 

<Target Name="ReBuildAll" DependsOnTargets="CleanAll;ReBuildConstOne;ReBuildConstTwo" />

<Target Name="ReBuildConstOne">

<MSBuild Projects="@(Projects)" Targets="Rebuild"

Properties="DefineConstants=$(ConstOne);OutputPath=binOne\;"/>

</Target>

<Target Name="ReBuildConstTwo">

<MSBuild Projects="@(Projects)" Targets="Rebuild"

Properties="DefineConstants=$(ConstTwo);OutputPath=binTwo\;"/>

</Target>

 

<!--==========================================================================

This region calls the default target (Build) but also specifies the

BaseIntermediateOutputPath so it works as well because the projects

are building to different locations on disk.

==============================================================================-->

<Target Name="BuildAllBetter" DependsOnTargets="CleanAll;BuildConstOneBetter;BuildConstTwoBetter" />

<Target Name="BuildConstOneBetter">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstOne);OutputPath=binOne\BaseIntermediateOutputPath=objOne\"/>

</Target>

<Target Name="BuildConstTwoBetter">

<MSBuild Projects="@(Projects)"

Properties="DefineConstants=$(ConstTwo);OutputPath=binTwo\;BaseIntermediateOutputPath=objTwo\;"

 

/>

</Target>

 

</Project>

Ddddd

Let's take a look at what's going on here. The BuildConstOne target passes the CONSTANT_ONE value as a property when invoking the MSBuild task, and builds to the binOne folder. The BuildConstTwo target passes the CONSTANT_TWO value and builds to binTwo. If you execute the target BuildAll, you would expect that the exe in binOne would show the message CONSTANT_ONE and the exe in the binTwo folder has CONSTANT_TWO message showing. What you actually get is that both actually show CONSTANT_ONE. This is because MSBuild doesn't entirely rebuild the project when building it for the second time in BuildConstTwo. The real reason behind this would be because both projects are building to the same BaseIntermediateOutputPath, obj\. There are two ways to of getting around this, one is to call Rebuild instead of Build, and the other is to specify the BaseIntermediateOutputPath as well. In the above I have demonstrated both approaches. The ReBuildAll calls Rebuild, and the BuildAllBetter specifies the BaseIntermediateOutputPath as well as OutputPath.

Questions?!

You can download all the files at:

DefineConst.zip

Sayed Ibrahim Hashimi

msbuild | Visual Studio Thursday, 08 May 2008 00:52:14 (GMT Daylight Time, UTC+01:00)  #     |