There are a lot of new and improved features in the upcoming version of Visual Studio (2005) and one of those is a completely refurbished build system called MSBuild. Most developers will be able to go on with their lives without knowing anything about MSBuild, but if you are a technical leader or build/release manager you are probably going to need to get to know it.

Before I dive in and talk about MSBuild we should probably have an abridged and slightly inaccurate build system/tool history lesson (cue tinkle tinkle sound and wobbly wobbly screen).

The problem of compiling code and associated “bits” in a structured manner has plagued hacker kind from the first time we had to compile two or more source files and compilers had switches. Over the aeons we have devised many solutions to this problem including trusty shell scripts and batch files to more convential “makefiles” which are executed by “make” and its derivitves such as “nmake” and “gmake”.

A makefile was a simple text file that listed a set of rules that were named according to the file output that they produced. Dependencies could be established between these rules which would ensure that the steps listed out under each rule were executed in the right order. This makes sense for C/C++ code where you have distinct phases such compilation and linking.

Makefile

When I first got my hands on .NET five or so years ago I actually used a hierarchy of makefiles to keep track of all the little sample programs I wrote. This was useful when I transitioned from alpha to beta to release candidate because I could just compile it all up and see what broke. Of course the other reason was that at the time VS.NET was so flakey that I was coding in Emacs and make files were I could do to stop me going back to Ant.

Did I say Ant? Thats right .NETophiles, our beloved open source build automation tool – NAnt is nothing but a cheap knock off – well not quite, but Ant is an important piece of software and definately rates a mention in this history lesson (there was also this other thing called jam, but let us never speak of it again).

Ant was important because it combined two fads () of the time together – Java and XML to produce a very powerful and extensible build system. The idea was that you would describe what you wanted done in an XML file, and then the Ant program would run along, read that XML file and map the elements and attributes to lumps of Java code that knew how to interpret them – consequently the principal extensibility mechanism for Ant was writing Java code.

Although the technology had moved on quite a bit from makefiles the underlying philosophy was the same – you define a number of things you want to do (rules in makefiles, targets in Ant scripts) and how to do them (simple command-lines in makefiles, tasks mapped to Java code in Ant scripts). Over the years, as Ant got more popular the beautiful target/depedency model got poluted by stinking proceduralists and so when you read Ant build scripts they read a little bit like sad script programs with a lot of extra punctuation (XML).

Of course, for .NET developers, the heavy Java dependencies of Ant didn’t necessarily sit too well so the tool was ported to to .NET and the NAnt project was born and is still going strong today thanks to these cheerful chaps. Actually – I contributed code to the NAnt project in the form of two tasks – <xmlpeak /> and <xmlpoke />. There are still there although they have been rewritted several times since I submitted them (it was better that way – trust me).

Now, even though NAnt is arguably the best build tool for .NET code today, it is very much open source and for some organisations that means (rightly or wrongly) that they can’t play. The NAnt project has been living in Microsoft’s shadow for quite some time now, due in part to its own success but also because of a number of painful deficiencies in the Visual Studio .NET 2003 project/build environment.

With Visual Studio 2005 Microsoft chose to tackle the issue head on and they invested resources into rebuilding the project/build system from scratch – MSBuild is the result. While I don’t think NAnt is going away, I don’t think that it can hope to win out over MSBuild in virgin .NET projects, and projects that migrate for .NET 1.x to .NET 2.0 will most probably migrate their established build system at some point too.

MSBuild, like NAnt is an application of XML and has .NET extensibility points, but despite the crys of “ripoff” the MSBuild team has done something interesting here – instead of making just another XML scripting language, they’ve built a transform engine – they’ve gone back to their “makefile” roots (take that you proceduralists!).

Like any good build system, MSBuild project files model a production line with a series of distinct phases with named inputs and outputs. The parts of the production line are called “targets”, just like in NAnt and the work that each target performs is defined by an arrangement of tasks. Your typical production line for a program written in C# might look like the following.

BuildProcess

In the real world MSBuild is a little bit more convoluted than that, but luckily it is pretty well abstracted away from us. Here is an example of what a minimal MSBuild *.proj file might look like.

MSBuildProjFile1

If I then drop into the command-line (with the Framework directory in the path and issue the MSBuild command I will get the following output.

MSBuildSuccess1

Look at the pretty colours  So … what has happened here? All we did in the project file is define this thing called an “ItemGroup” and include Program.cs as a compilable (Compile), there are several other built-in items which MSBuild supports (ripped right out of the documentation):

  • Reference
  • Compile
  • EmbeddedResource
  • None
  • AppDesigner
  • Folder
  • ProjectReference

If some of those look familar to you go and look under the “Build Action” property for a file in Visual Studio – I’m not sure if its a one-to-one mapping but it does tweak these settings in project files if you set it to say “Embedded Resource”.

BuildAction

One thing we didn’t do in the *.proj file however is tell MSBuild how to compile it – thats because MSBuild already knows. If you look at the *.proj file a little more closely you can see that on the Project element I have a DefaultTargets attribute set with a value of Build. There is no “Build” target in this file but if you look at the second bottom line you can see an <Import /> element which points to “$(MSBuildBinPath)\Microsoft.CSharp.targets”.

The *.targets files contain heaps pre-written targets which get enlisted to build certain types of projects. In this case, “Microsoft.CSharp.targets” actually imports “Microsoft.Common.targets” which is where the Build target is defined. The complete list of *.targets files that I could find is as follows:

  • Microsoft.Common.targets
  • Microsoft.CompactFramework.Common.targets
  • Microsoft.CompactFramework.CSharp.targets
  • Microsoft.CompactFramework.CSharp.v1.targets
  • Microsoft.CompactFramework.VisualBasic.targets
  • Microsoft.CompactFramework.VisualBasic.v1.targets
  • Microsoft.CSharp.targets

These files are located in the runtime directory, so on BETA 2 that is %SYSTEMDRIVE%\WINDOWS\Microsoft.NET\Framework\v2.0.50215”. I encourage you to look through these files to get a feel for how it hangs together but be careful not to modify them or you could potentially stop your code compiling in Visual Studio!

Actually – the location of these files gives us a subtle hint – MSBuild ships as part of the runtime! Why is this important? Well it means that if you have source you can compile it anywhere, provided you have the .NET Framework installed. Even if those source files are hooked together with a *.csproj or *.vbproj file created in Visual Studio 2005.

If we look at a *.csproj file from Visual Studio 2005 we can see that its just a more complex example of the project file above with build configuration settings like project references and assembly references.

VSProjectFile

Pretty neat eh?