
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.

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: msbuild msbuild tutorial build script .NET
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?
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.
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 …
@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.
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.
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
@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.
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:
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.
Thanks. I finally get this MSBuild stuff.
Nice tutorial on MSBuild for newbie like me ^_^.
Thanks! This is great.
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
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.
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.
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.
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.
Very useful for the beginners.
I gained a confidence that i can automate the builds.