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.