Automating the build with MSBuild

Automating the build with MSBuild

One of the less well known tools that comes with the .NET framework is MSBuild. This command line utility runs XML scripts which can automate the build of a software project.

The question is, “why would I want to automate my build?”. Well, all but the most trivial of projects are likely to contain a number of build steps, for example

  • Incrementing the version number
  • Compiling source code
  • Choosing the correct settings for the deployment environment (e.g. test, live)
  • Including third party dependencies in the install package

As such, producing the build by hand can be a time-consuming and error-prone process. The initial effort invested in creating a build script will be more than offset by that gained from automation. This is especially true if the manual process often went wrong and either had to be repeated or fixed.

OK, at this point hopefully you are sold on the benefits of build automation and are champing at the bit to write your first script. Let’s take a look at our sample project, which is an ASP .NET web site.

Project structure

Notice that, in addition to the web.config file, there are live.config and test.config files. The idea here is that whilst web.config contains all settings for the web site, live.config and test.config contain only those settings that are different for that deployment environment. This avoids repeating settings in each file, which can turn into a maintenance headache. However it does present a challenge in that we will need to merge the environment-specific settings into the web.config when performing the build. More on that later.

Before we start, here are the steps that constitute the build for this project

  • Clean the bin directory
  • Compile the project
  • Copy all files required for deployment into a new build output directory
  • Merge config settings
  • Deploy the build output to the correct environment

Each project will have its own build process, however for this purposes of this demo ours is fairly basic.

Right then, let’s get started with the tutorial. If you would like to follow along, the sample project is available from CodePlex. The first step is to add a new XML file to the root of the site, called Build.xml. All MSBuild scripts should contain a root Project node at the top. Having added that, our next job is to clean the bin directory, ready for another compilation to be performed. That leaves our script looking like this

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

  <Target Name="Clean">
    <ItemGroup>
      <BinFiles Include="bin\*.*" />
    </ItemGroup>
    <Delete Files="@(BinFiles)" />
  </Target>

</Project>

There are a number of points to make here

  • Including a namespace allows Visual Studio to provide IntelliSense for all MSBuild features
  • MSBuild scripts are largely made up of targets, which contain one or more tasks. In this case our target is called Clean and it uses the Delete task
  • ItemGroups, surprisingly, are used to define a group of items. In our case this is a list of all files in the bin folder
  • All paths, e.g. bin\*.*, are relative to the build script itself. It is a good idea to use relative paths in case you try to run the script from a different location in the future

I have to admit that I was initially unaware of the need for ItemGroup, instead expecting to simply pass “bin\*.*” to the Delete task. A post on the MSBuild Team Blog explains why.

OK, having coded our first target, let’s run it. Open a command prompt and switch directories into the folder containing your build script. Then execute this statement

msbuild Build.xml /t:Clean

If you receive an error stating that MSBuild could not be found, you will have to either fully qualify the path to it (in my case C:\WINDOWS\Microsoft.NET\Framework\v3.5), or add its location to your PATH environment variable. I recommend the latter, instructions on which can be found here.

Having run the command the bin folder should be empty. Make sure something was actually in there in the first place before giving yourself a pat on the back! As you can see, the syntax for running MSBuild is to provide the name of the build script, then the t switch with the name of the target to run.

Moving swiftly on, our next step is to compile the project. To do so we create a target called Compile, and use the MSBuild task to perform the compilation, passing it the web site’s project filename, as follows

<Target Name="Compile" DependsOnTargets="Clean">
  <MSBuild Projects="BuildDemoSite.csproj" />
</Target>

It seems a little confusing that Microsoft decided to have a task called “MSBuild”, but there you go. I’ve introduced the DependsOnTargets attribute here, which forces Clean to run before Compile does. This ensures we can’t forget to to clean the bin folder, and in fact removes the need to run Clean separately. Instead we can just call Compile, as follows

msbuild Build.xml /t:Compile

Reviewing the messages output to the command window confirms that Clean ran before Compile did.

OK, that’s it for this post. So far we have

  • Established what a build consists of
  • Decided to automate it
  • Created targets for cleaning the bin folder and compiling the project

You can download the script in its current state from CodePlex.

In part 2 we’ll create the remaining targets and refactor our script. Stay tuned and, in the meantime, please leave feedback in the comments.

Technorati tags:

Tags: ,

18 Responses to “Automating the build with MSBuild”

  1. Brent says:

    Great article topic. I do hope you will make a comment about how this can or cannot relate to Team Explorer “Team Builds”.

    Also, I’m looking forward to how you handle merging config files (Is it a MS tool? Is it a 3rd party merge tool?). Indeed, while you are proposing a config merge to handle build targets, there are still other important uses for that feature. For example, suppose a solution has three DLL assemblies…each possibly needing its own unique configuration information. It would be best to keep each unique config piece together in the DLL project that needs it. That way, if I have two executable assemblies that seperately rely on those 3 DLL assemblies, a “configuration merge” step in the build can locate each config file piece / segment from each DLL, and compile into the final app config file that either of the two executables needed (along with whatever general config information each executable was declaring for itself).

    It is interesting that the notion of merging config files – is something that can actually be done (recursively) at run-time if we’re talking about an asp.net application. But if not asp.net, then the merging must be done at build time. When is your next installment to be posted?

  2. codingcockerel says:

    Thanks for your comment Brent. You have the (dubious) honour of being my first ever commenter!

    You raise some interesting points. Team Builds are not something I am familiar with yet. We use TFS at my current employer and I hope to learn about it soon.

    With regards to the multiple assemblies, I believe my technique will work for a solution containing more than one project. However it is my intention to keep things simple, for now at least.

    The next post should be available towards the end of next week. Once it is published perhaps you could comment on whether it would work in the scenario you described.

    Thanks again for the feedback – I’m a little surprised you found me as I’ve only just appeared on Google.

  3. Eugenez says:

    Several notes – there is good tutorial by Sayed Hashimi (http://en.csharp-online.net/MSBuild:_By_Example); and there is tool (which am personally involved with, so you can discard it as an ad :) for authoring MSBuild scripts in UI, called MSBuild Sidekick (http://www.attrice.info/mbsuild)

    Just thought that some useful resources wont go amiss …

  4. @Eugenez,

    Thanks for the links. I intend on providing a list of resources at the end of part three, which should be done soon. I’ll be sure to check yours out before I finish the post.

  5. Mike Clark says:

    With respect, I am not finding that things work as you are saying. If I run the “Clean” target as you show above, I get an error: “The element beneath element is unrecognized.” The VS2005 product proclaims this when it is coded, in fact. Oddly enough, if I move the ItemGroup to directly under the Project node, it runs successfully, even though VS2005 it says “BinFiles” is an invalid element.

    Hmmm.

  6. Mike,

    This is strange, I’ve not encountered any issues like this before.

    Could you clarify what you mean by

    “The VS2005 product proclaims this when it is coded, in fact.”

    Ross

  7. @Mike,

    I just tried running the build script at work and reproduced your problem. You need to use Visual Studio 2008 and the .NET framework 3.5′s version of MSBuild.

  8. Shawn Carroll says:

    I had the same problem with the ItemGroup node as well. I was able to work around it by pulling the ItemGroup out and placing it above the Target group:

  9. Shawn Carroll says:

    Alright, my XML didn’t post so here goes plain text :)

    Project
    ItemGroup
    BinFiles
    Target

    I used v2.0 and Visual Studio 2005 for this.

  10. hungster says:

    Thanks. I finally get this MSBuild stuff.

  11. Nam Gi VU says:

    Nice tutorial on MSBuild for newbie like me ^_^.

  12. Shaw says:

    Thanks! This is great.

  13. Khanh says:

    Hi, thanks for the tutorial. When I ran

    msbuild Build.xml /t:Compile

    it said

    The project file “BuildDemoSite.csproj”
    was not found.
    Done Building Project “C:\Documents and Settings\Do_K\My Documents\Visual Studi
    o 2008\WebSites\BuildDemoSite\Build.xml” (Compile target(s)) — FAILED.

    Build FAILED.

    can you help me? I am a beginner. Thanks

  14. Andy says:

    Is it possible to use Visual Studio to be LaTeX editor and use MSBuild to invoke batch file compiling the LaTeX input file, converting *.dvi to *.ps and finally *.ps to *.pdf?

    Thank you in advance. Your article sounds very interesting. It might be what I am looking for.

  15. Andy, I’m not familiar with LaTeX but if you just want to call programs from the command line then MSBuild can do that with the Exec command. In theory what you want to do should be achievable.

  16. Patrick says:

    Excellent article, thank you! I used the above code in the csproj file by just changing the BinFiles node to:

    And adding the Target as a Dependency in the following Target:

    It works really well.

    Cheers,
    Patrick.

  17. Patrick says:

    Sorry, posting again because it didn’t allowed me to post tags.

    Excellent article, thank you! I used the above code in the csproj file by just changing the BinFiles node to:

    BinFiles Include=”$(OutputPath)\*.*”

    And adding the Target as a Dependency in the following Target:

    Target Name=”BeforeBuild” DependsOnTargets=”Clean”

    It works really well.

    Cheers,
    Patrick.

  18. Hema says:

    Very useful for the beginners.
    I gained a confidence that i can automate the builds.

Leave a Reply