Skip to content

Easily add Pre and Post build tasks to a Visual Studio Solution

One dirty little secret about Visual Studio 2008 and even Visual Studio 2010 is that while MSBuild governs the solution build process, the .sln file is not an MSBuild file. The .*proj files are, but solution isn't. So trying to customize the build on the solution level seemed really annoying.

As I dug around trying to find the Solution level equivalent of the Build Events dialog from Visual Studio, Sayed Ibrahim pointed out that in Visual Studio 2010 there is now a hook to let you inject some before and after tasks, but unfortunately the problem I was trying to solve was the build process for MindTouch DReAM, which is still in Visual Studio 2008.

Approach 1: Generating the solution msbuild .proj

Digging around further, I found out that you could get the MSBuild file that the solution was turned into. By setting the environment variable MSBuildEmitSolution=1 and running MSBuild will write out the generated .proj file.

While this enables you to edit it and add new tasks, it means that your build script will drift out of sync with the solution as it is modified. I initially went down this path, since the build i wanted was very specialized to the distribution build. That let me eliminate 90% of the .proj file and I felt confident that the smaller the .proj, the simpler it would be to keep it in sync with the solution.

Approach 2: Calling the solution from a different build script

But wait, all the solution .proj did was call MSBuild on each of its projects. So if one MSBuild script can call another, why do i even need to use a generated version of the solution? Turns out you don't. You can write a very simple MSbuild script, that in turn calls the .sln, letting MSBuild perform the conversion magic, and you still get your pre and post conditions.

<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Build">
        <CallTarget Targets="PreBuild"/>
        <CallTarget Targets="Dream"/>
        <CallTarget Targets="PostBuild"/>
    </Target>
    <Target Name="PreBuild">
        <Message Text="Pre Build" />
        ...
    </Target>
    <Target Name="PostBuild">
        <Message Text="Post Build" />
        ...
    </Target>
    <Target Name="Dream" Outputs="@(DreamBuildOutput)">
        <Message Text="Building DReAM" />
        <MSBuild Targets="Rebuild"
                 Projects="src\\MindTouchDream.sln"
                 Properties="Configuration=Signed Release; Platform=Any CPU; BuildingSolutionFile=true;">
            <Output TaskParameter="TargetOutputs" ItemName="DreamBuildOutput" />
        </MSBuild>
        <Message Text="Done building DReAM" />
    </Target>
</Project>

Now that I've implemented this, I am surprised that when I looked for a solution, this didn't come up in google and I hope that this post helps the next person that runs into this issue. The only drawback (which it shares with the first approach) is that this script is only for manual execution. Building from within Visual Studio can't take advantage of it.