Technically Blogging

Tuesday, November 21, 2006

Customize MSBUILD without modifying your project file

In this post I’ll explain how you can use "project.user" file along with custom target file to
customize MSBuild project behavior.

Little background

Like *.suo (Visual Studio Solution User Options), which stores user specific settings for the solution(including breakpoints information), MSBUILD also supports the notion of user specific settings on per project basis. This is done using a file with the same name as your project plus the “.user” tag appended at the end and is called “Visual Studio Project User Options file”.

So if you project file is named “Common.csproj”, your user option will be named Common.csproj.user”.MSBuild checks for this file in the same directory as your project and imports it, if it’s available.

This file can be used to define user specific project properties. As this file is imported in “Microsoft.Common.targetsfile, so it can be used to override all the properties defined in yourProject.csproj and Microsoft.CSharp.targets or Microsoft.VisualBasic.targets.

Why will you customize using .user file?

A typical use case is that you have customized your project settings (e.g referring to assemblies in a different way than build machine). You can keep all your customization in .user file allowing your project file to remain pure so it can be used by multiple people while checked into the source control. I usually don’t check-in .user file in source control.

This gives you best of both worlds, project on other machines work in default/standard way, however when it gets built on your machine, it uses your custom settings from .user file.

Another scenario is you want to add your custom task in the build process but you don’t want to modify your project file.

How to do this

You cannot do much with user files alone. As most of the target dependencies are defined after this file is imported. So if you have inserted your target in some dependency chain, it will be overridden by default dependencies. E.g If you have added your task as the first dependencies of core build tasks using following property.

<PropertyGroup>

<CoreBuildDependsOn>YourTargetName;$(CoreBuildDependsOn)</CoreBuildDependsOn>

</PropertyGroup>

It will be overwritten because of following property definition in Microsoft.Common.targets.

<PropertyGroup>

<CoreBuildDependsOn>

BuildOnlySettings;

PrepareForBuild;

PreBuildEvent;

UnmanagedUnregistration;

ResolveReferences;

PrepareResources;

ResolveKeySource;

Compile;

GenerateSerializationAssemblies;

CreateSatelliteAssemblies;

GenerateManifests;

GetTargetPath;

PrepareForRun;

UnmanagedRegistration;

IncrementalClean;

PostBuildEvent

</CoreBuildDependsOn>

</PropertyGroup>

There is another interception point in MSBuild which we can utilize to achieve above scenarios. MSBuild looks for custom Targets before and after default targets. Specifically, MSBuild looks for following two properties, if anyone of them is defined, MSBuild imports it and use it.

  • CustomBeforeMicrosoftCommonTargets
  • CuCustomAfterMicrosoftCommonTargets

We will use CustomeAfterMicrosoftCommonTargets property to define our target(s) and run it based on some other target.

So, create a file in your project root with name custom.target


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

<UsingTask TaskName="BuildInspector" AssemblyFile="C:\zahmed\Temp\building\CustomTasks\bin\Debug\CustomTasks.dll" />

<PropertyGroup>

<CoreBuildDependsOn>MyCustomTarget;$(CoreBuildDependsOn)</CoreBuildDependsOn>

</PropertyGroup>


<Target Name="MyCustomTarget">

<Message Text="Running custom task ..."/>

<!--Run my custom task. -->


<BuildInspector/>

</Target>

</Project>


Now create the project.user file with following contents.


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

<PropertyGroup>

<CustomAfterMicrosoftCommonTargets>

C:\zahmed\Temp\building\conApp\custom.target

</CustomAfterMicrosoftCommonTargets>


</PropertyGroup>

</Project>


Now build your project to run your custom task.

Note: You need to reload the project to load project.user file.



0 Comments:

Post a Comment

<< Home