Chapter 3. Automating the build process – Continuous Integration in .NET

Chapter 3. Automating the build process

This chapter covers

  • Choosing a build-automation tool for the CI process
  • Using MSBuild
  • Extending MSBuild

Having a single repository that contains everything you need for building your software is the first step on the path to a good CI process. The second, which is also important, is to have the software build. To do this, you need a kind of metaphorical lever that will help you jack up your source code from transcription of ideas into working software. You’ll use this lever in your day-to-day work as well as in the CI process you’re building. Your build lever must be designed in a way that’ll let you build your application in one step.

The first thing that may come to mind as a lever in the .NET Framework world is Visual Studio. It seems to have everything that makes for a good lever. When you press the F6 button, you start a build process that leads to working software. But is it enough? Does it make a good lever? We’re afraid not. It’s able to compile and start a program, but nothing else. We need more to incorporate CI: something that’ll let you test everything, analyze the code, generate documentation, deploy, and create installation routines. We’re looking for something powerful, customizable, and extensible. Visual Studio is a great development environment, but a poor software-automation tool—and we want to automate the entire software build process.

If your build process doesn’t take care of everything in addition to compiling, those elements will most likely be neglected. You don’t click one button to compile, another to test, and another to deploy. The key is automation—and that means you have to get rid of the human factor.

We want to create a build process that can work autonomously, without supervision. The way to achieve this goal is through an automation platform.

In this chapter, we’ll browse through various build-management systems and determine which ones are suitable for the .NET integration process. We’ll look at NAnt, but in the end we’ll choose MSBuild as the best build tool. You’ll use built-in and community-owned MSBuild features to create a build-and-deploy process. Finally, you’ll extend MSBuild with your own functionality.

3.1. Build automation

In the CI context, an automation platform is a tool or a set of tools that helps automate the entire software build process, including doing the following:

  • Compiling the source code
  • Preparing the database
  • Performing tests
  • Analyzing the code
  • Creating installation routines and deploying
  • Creating documentation

What we’re looking for should be easily maintainable. And it should be stored in the source control system like everything else that takes part in the CI process.

The obvious way to automate the build process is to script it using human-readable text. You should avoid everything that doesn’t use text as a description of a build process. Compile managers are bad, bad things. You should ban from your mind any automation tool that keeps the build description in binary format and requires you to manually click to make it run. Text form is easier to create, read, edit, and keep track of (using version control) than binary form.

In chapter 1, you saw a simple example of automation using ordinary command-line commands organized in a batch file; software automation was done this way at the dawn of time. It makes the process faster in comparison to manually issuing commands, it reduces redundant tasks because you don’t have to be involved in every build, and it lets others maintain the build. Now, let’s walk through some real automation tools and search for the best one.

3.1.1. Make and its children

Software-automation platforms are older than most active software developers. The great-grandfather of almost all current tools is the UNIX make utility, which was created at the end of the 1970s and has been used mostly in the *ix world. It has a Windows version called nmake and a fairly good clone called Opus Make. All the make systems use a text file called a make file to describe the software build process.

Later-generation tools like Jam and Cook changed this. They used more sophisticated statements to hide some of the lower-level aspects of software automation. With time, the automation platforms became bigger and more complex and began to be called automation systems. One of them is GNU Automake with the GNU Build System (GBS—a set of small tools that comes in handy when you’re building software on *ix systems).

Finally, we have automation tools that use a specific programming language to describe the build process. For example, SCons uses Python, and rake uses Ruby.

All the tools we’ve mentioned can be used to set up a CI process. But we’ll look at the vanguard of build automation: the XML-based build systems Ant (NAnt) and MSBuild. The XML-based systems are a step away from tools that use fairly complicated commands or a programming language to describe the build process. Using them, you can declare the steps in an XML build script, and the steps are easy to extend and adapt.

NAnt and MSBuild are two of the tools you should choose from if you’re creating a build process in a .NET environment. Both do the same job using similar techniques. NAnt is an open source tool maintained by the community, and MSBuild comes from Microsoft. Table 3.1 shows the most significant differences between them.

Table 3.1. NAnt vs. MSBuild: significant differences

Feature

NAnt

MSBuild

Actively developed no yes
Built-in features yes some
Open source yes no
Cross-platform (Linux, Mono) yes no
Good if you already know Ant yes no
Built in to .NET Framework yes yes
Integrated with Visual Studio no yes

Let’s take a quick look at NAnt and see why we’ll go with MSBuild instead.

3.1.2. It’s not an Ant

Once upon a time, there was Ant. It was a good, established tool used to build applications in Java shops. It was ported to work in the .NET world and called NAnt (Not an Ant). From its Java ancestor, it inherited the XML declarative automation description language.

Let’s try to use NAnt with this full-blown, single-line C# program:

class c{static void Main(){System.Console.Write("Hello NAnt");}}

Place this program in a file called HelloNAnt.cs. Now write the following NAnt script to build an application. Call it HelloNAnt.build.

Listing 3.1. NAnt build script to clean and compile a Windows application
<?xml version="1.0"?>
<project name="Hello NAnt" default="build" basedir=".">
<property name="debug" value="true" overwrite="false" />
<target name="clean">
<delete file="HelloNAnt.exe" failonerror="false" />
<delete file="HelloNAnt.pdb" failonerror="false" />
</target>
<target name="build" depends="clean">
<csc target="exe"
output="HelloNAnt.exe"
debug="${debug}">
<sources>
<include name="HelloNAnt.cs" />
</sources>
</csc>
</target>
</project>

An NAnt script is an ordinary XML document. First you declare the project, specifying the name (Hello NAnt), the default target (Build), and the working directory (dot [.] for the current directory). Next, NAnt gives you the ability to define properties. A property is a kind of named variable to which you can assign a value. The overwrite attribute lets you set the variable from the command line. The debug variable is used by the C# compiler task in a moment. The Clean target uses two delete tasks to erase unnecessary files. Setting the failonerror attribute tells NAnt to ignore possible errors—for example, if there’s nothing to delete. The second target, Build, first runs the Clean target because of the depends attribute, and then runs the csc target to compile the source file.

One of the rules of CI that we keep mentioning is placing everything you need to fully build a project inside the project directory/repository. To use the script you just wrote, you need NAnt executables (available from http://nant.sourceforge.net). Place the NAnt executables in the tools/nant folder. NAnt is now ready to use.

Open a command window, navigate to the project folder, and type tools/nant/ bin/nant.exe to launch NAnt (see figure 3.1). Run the script, and build your one-line program. Now that the script is working, you can extend it, declare more steps, and integrate more actions.

Figure 3.1. Starting a Hello World–style NAnt script. The build performs a clean followed by a build task. As an artifact, you get a compiled executable.

At the time we started writing this book, the open source NAnt project seemed to be dead. But in mid-2010, just as we were finishing writing, a new version of NAnt emerged. We felt that delaying publication didn’t merit reworking examples and text to include NAnt. From a technical point of view, it isn’t a big deal. NAnt is a good alternative for software developers with a Java background who are familiar with its ancestor, Ant. Many developers use MSBuild only to compile the source code and use NAnt to integrate all other tools into the CI process.

3.2. The Microsoft worker: MSBuild

Microsoft first shipped its own build tool for the .NET platform with the second version of the .NET Framework. Updated versions were shipped with .NET Frameworks 3.0, 3.5, and 4.0. If you check C:\Windows\Microsoft.NET\Framework\, you’ll see that the subfolders for v2.0, v3.5, and v4.0 contain MSBuild.exe.

Using MSBuild means less work. You don’t need to worry about third-party tools and how to integrate them with your environment. You don’t have to worry about whether your favorite build tool is installed on the integration machine, because if you have .NET Framework installed, the tool will be there. Pragmatic people will find MSBuild appealing. Who knows how your business will grow? You may hit the scaling wall with the free software and have to think about something bigger. The entire Microsoft Team Foundation Server Build (more about it in chapter 4) is set on top of MSBuild. Keep this in mind, and you’ll feel prepared.

MSBuild is freely distributed with the .NET platform. It has Microsoft’s machinery behind it, so you don’t need to worry about wide adoption and popularity. It won’t die suddenly, leaving you without support. MSBuild is extensible and well-documented. It uses XML syntax similar to NAnt to perform build tasks. And it’s closely integrated with Visual Studio: it understands Visual Studio solution files and makes it possible to compile Visual Studio solutions and projects without Visual Studio. It seems to be the build tool for .NET developers who want to set up a CI assembly line. But let’s start small with a simple script that compiles a simple Hello World application.

3.2.1. First encounters with MSBuild

To use MSBuild from the command line, you have to write the full path for the executable or add it to your System Paths variable. But if you have Visual Studio installed, you can use the Visual Studio Command Prompt, which knows the path to MSBuild. You launch the Visual Studio Command Prompt from the Windows Start menu (see figure 3.2).

Figure 3.2. The Visual Studio Command Prompt knows the paths to various handy .NET tools. One of them is MSBuild.

As a small workout in MSBuild, you’ll perform the same tasks as you did previously with NAnt. Write another captivating one-liner:

class c{static void Main(){System.Console.Write("Hello MSBuild");}}

Compile it using the MSBuild script build.proj, shown next. The .proj file should go in the same folder as the source file for the program.

Listing 3.2. Simple MSBuild script
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Debug Condition="'$(Delete)'==''">true</Debug>
</PropertyGroup>
<ItemGroup>
<CompileFiles Include="HelloMSBuild.cs" />
<DeleteFiles Include="HelloMSBuild.exe;HelloMSBuild.pdb" />
</ItemGroup>
<Target Name="Clean">
<Delete Files="@(DeleteFiles)" />
</Target>
<Target Name="Build" DependsOnTargets="Clean">
<Csc Sources="@(CompileFiles)"
OutputAssembly="HelloMSBuild.exe"
EmitDebugInformation="$(Debug)" />
</Target>
</Project>

This script should look familiar. Just as with NAnt, an MSBuild script is an XML document, and it uses a similar set of ideas including properties, targets, and tasks. The main element of an MSBuild script is a Project, which defines the entire build process. It must be equipped with an xmlns attribute that defines the namespace. Optionally, you can define default targets. In this case, you use the Build target.

A Target is a logical part that declares a set of tasks. The target can be organized hierarchically, so that one target depends on another. In this case, the Build target depends on the Clean target, so MSBuild first runs the Clean target and then the Build target.

The Build target has only one task: Csc. This task calls the C# compiler with parameters. In the first parameter, specified by the Sources attribute, you provide an item containing a list of documents to be compiled. In the second parameter, Output-Assembly, you provide a name for the output file. In the last parameter, EmitDebug-Information, you specify whether you’re interested in debug information for your program. The value for it is defined in the Debug property.

An ItemGroup contains a list of items. In the example, items contain one or more references to a file. You have two of them: the first defines the files to be compiled (in this case, one file, HelloMSBuild.cs) and the second contains the list of files that the Clean target should delete.

MSBuild properties are containers for values. Every property has a name and is defined in a PropertyGroup. You define only one property here: Debug. It contains a Boolean value and is used in the Csc target to determine whether the compiler should create a .pdb debug symbols file.

If the path to MSBuild exists in system variables or you’re using the Visual Studio Command Prompt, the only thing you have to do is start MSBuild from the command line. Type msbuild to launch and run the build process (see figure 3.3).

Figure 3.3. MSBuild is less verbose than NAnt. If you have only one file with the *.proj extension in the directory where you start MSBuild, it’s automatically executed.

Now that you’ve written your first MSBuild script, let’s extend it a little.

 

Passing parameters to MSBuild scripts

Another neat thing you’ll use often when creating a CI process using MSBuild is the ability to pass parameters from the command line to the script. Your Debug property has one attribute, Condition, which you can set from the command line like this: msbuild /property:Debug=false. This attribute helps set the default value if you call the script without setting the value explicitly.

After you do this, the condition '$(Delete)'=='' isn’t fulfilled. That is, the value is false and not empty. MSBuild uses what’s defined at the command line. In the end, you get the compilation without the debug files.

 

3.2.2. Using predefined tasks to extend an MSBuild script

MSBuild comes with a set of predefined tasks. You’ve already used two of them: the C# compiler task Csc and the Delete task. Other useful MSBuild tasks include the following:

  • Copy copies a file.
  • MakeDir creates a folder.
  • RemoveDir removes a folder.
  • Message prints a message on the screen.
  • Exec runs any program.

You’ll use these tasks to extend your MSBuild script. Using the code from listing 3.3, you can create something like a mini-CI iteration step (without the loop). Using MSBuild, you clean up the building site, compile and archive the software, and then copy the output to a folder and start the program to test whether it works.

Listing 3.3. Extending the build script

The first thing that catches your eye is probably the extended DefaultTargets list . You define three new tasks, divide by semicolons. They’re executed in the same order that they appear in the list. Note that the Build task still depends on Clean.

The Build target is the same as in the previous example. The Deploy target creates the output folder (MakeDir task) and copies the executable file (Copy task) to the folder defined in the property $(OutputFile).

The Execute target first uses the Message task to write text to the screen. The message contains information about what will be executed and where. The message uses one of many predefined properties, $(MSBuildProjectDirectory), which contains the path to the MSBuild project. The Message task has one more parameter, Importance, which defines the verbosity of the MSBuild execution. In a minute, you’ll learn what this means and how to start MSBuild with different verbosity settings.

After the Message task, you use the Exec task to start the program. The Exec task uses two parameters: Command to define the program that needs to be started and WorkingDirectory to define where it needs to be started.

The Clean target is then extended with additional functionality to remove old folders (RemoveDir) and files (Delete).

Let’s start the automated build process. For the sake of cleanliness, delete all the artifacts that remained in the project directory. You don’t have to do this manually! You have all you need in your MSBuild script. You can start it with the /target command-line parameter. Using this parameter, you can start any target defined in your MSBuild project, disregarding the DefaultTargets project attribute.

Go to the command prompt, and type msbuild /target:Clean. You should see something similar to figure 3.4.

Figure 3.4. You can pass MSBuild a specific target—for example, one to clean folders and files—on the command line.

You’ve cleaned everything, and you’re ready to start the actual build. Enter msbuild in the command window to build and run the program (see figure 3.5).

Figure 3.5. MSBuild can build and run a program.

But what happened to the Message task in the Execute target? It’s nowhere to be seen in the output. It was omitted because of MSBuild’s default verbosity level. The verbosity level defines how much information the MSBuild process writes on the screen. The higher the level, the more information you see on screen. To see the messages with Importance set to Low, you must start MSBuild with high verbosity. It may sound trivial, but it’s an art to set the correct verbosity level in the CI process. You have to set verbosity this way to be able to quickly browse through and know what’s going on. You’ll do this often. You don’t want to be flooded with information you don’t need; instead, you want to be able to quickly and precisely locate the cause of a problem. Only with the correct verbosity level can you do this.

Let’s run MSBuild with a nonstandard verbosity level. Go back to the command window, and type msbuild /verbosity:detailed. This time, the Message task is executed (see figure 3.6).

Figure 3.6. MSBuild is more verbose if you start it with a /verbosity:detailed switch.

We’ve shown you how to use MSBuild with an essential set of tasks. These tasks are built in to MSBuild. But sooner or later, you’ll need something more. MSBuild Community Tasks are a great set of additional tasks.

3.2.3. MSBuild Community Tasks

Using MSBuild, you aren’t limited to the tasks that are delivered inside the program from Microsoft. The set of tasks can easily be extended. You can do this by writing a task yourself, or you can use tasks others have written. A useful set of free tasks called MSBuild Community Tasks is distributed as open source and contains a lot of ready-touse functionality, such as using FTP servers, sending email, manipulating XML, managing SVN, getting the date and time, and much more. For the complete list, refer to http://msbuildtasks.tigris.org/.

The easiest way to start using the MSBuild Community Tasks is to download the MSI package and install it on the system. But this isn’t the best way if you intend to set up a CI process. By installing the package, you get all the system variables set, and the Community Tasks are instantly ready to use. But if you do this, you must install the software on the build server as well. You’ll encounter a similar problem if you want to use it on various machines for your team. Think about what it means to install the new version on every machine—that’s one of the reasons to keep everything you need in the project directory.

Create the folder tools/MSBuildCommunityTasks under your project directory. Download the Community Tasks zip archive, decompress it, and copy the content of the bin directory into your tools directory (see figure 3.7). This way, everyone can use the Community Tasks after they get the latest version of the project from your source control system.

Figure 3.7. The directory structure for a project should include the MSBuild Community Tasks.

Now, let’s put the Community Tasks to work. Listing 3.4 uses three of the many tasks that are available. These tasks let you archive your software, give the archive a unique name, and send it using email. To keep the script brief, it omits some parts that are duplicates from listing 3.3.

Listing 3.4. Build script using MSBuild Community Tasks

First, you must inform MSBuild that you’re about to use an additional task . You do this in the UsingTask tag, giving it an attribute with the path to the MSBuild Community Tasks library and specifying what task you’ll be using. Here, you use the Zip, Mail, and Time tasks in the Deploy target.

You use the Time task to set a new property with the current date and time. This property is named $(BuildDate) and is used in the next task, Zip. This task creates an archive with the name defined in the attribute ZipFileName, which contains the files defined in the Files attribute. The last step is to send the archived file to a given email address using the Mail task . The Mail task needs to be configured: you must provide the SMTP server name, the username and password if necessary, and the mail recipient. In a development environment, you might think about using a fake SMTP server to test the functionality. We like Antix SMTP Imposter (www.antix.co.uk/Projects/SMTPImpostor)—it has everything a normal SMTP server has, but it keeps the messages unsent and ready for review.

Run MSBuild as before, and you’ll see that the MSBuild Community Tasks are run just like the native MSBuild tasks (see figure 3.8).

Figure 3.8. The extended MSBuild script in action. Using MSBuild Community Tasks, you can archive the output and send it as an email attachment.

Additional Community Tasks are handy when you write your own build script. Another important feature of MSBuild is its integration with Visual Studio.

3.3. The happy couple: Visual Studio and MSBuild

MSBuild is used mostly in conjunction with Visual Studio, because they understand each other so well. MSBuild has tasks that can read and compile entire Visual Studio projects or solutions. And project files since Visual Studio version 2005 are nothing other than MSBuild scripts, which means you can extend your project file directly. IntelliSense and validation for MSBuild scripts are present in Visual Studio.

3.3.1. Examining a project file

In chapter 1, you created some Visual Studio projects. This set contains one shared mathematical library and two clients for a leasing calculator. Open one of the project files: for C# projects, the name is *.csproj; and for VB, it’s *.vbproj.

To open the project file in text form in Visual Studio 2010, unload the project (by choosing Unload Project from the project context menu in Solution Explorer) and edit it (also using the context menu). Don’t forget to reload the project afterward. You can do the same thing using the PowerCommands plug-in (available from http://visualstudiogallery.msdn.microsoft.com). It’ll let you open the project file by right-clicking in the Solution Explorer and choosing Edit Project File from the context menu.

The following listing shows part of a project file. To save space, we cut out the PropertyGroups responsible for project configuration and the ItemGroups that define references, includes, and files.

Listing 3.5. Visual Studio project file, which is an MSBuild script

This project file should look familiar, because it’s an MSBuild script. It has a default target named Build, a PropertyGroup, and so on. But wait! Where’s the definition of the Build target? It’s nowhere to be seen. To solve this riddle, you have to look in the imported Microsoft.CSharp.targets project . It’s an import of the standard C# targets file. You can check it by opening the CalcCore project you created in chapter 1; the project file name is CalcCore.csproj. The property $(MSBuildToolsPath) points to the default MSBuild installation folder. Effectively, you’re inserting the contents of the file C:\Windows\Microsoft.NET\Framework\[version number]\Microsoft.CSharp.targets into your project file. This file defines the standard targets in the compilation processes of C# projects. A similar file for Visual Basic resides in the same directory. Both of them import Microsoft.Common.targets that defines the common tasks for various project types.

The project files are ordinary MSBuild scripts, and it’s possible to override and redefine the targets. You have to remember one rule: the target definition that’s closer to your MSBuild script counts. So if you override the BeforeBuild or After-Build target in your file, MSBuild will take this definition and not the definition with the same name from an imported target file. BeforeBuild and AfterBuild are visible in every project file . They’re commented out, and all you have to do is to uncomment and define them to extend your build process.

Let’s implement one of them to start the executable after the build. You can easily do so like this:

<Target Name="AfterBuild">
<Exec Command="bin\$(Configuration)\WinCalc.exe"></Exec>
</Target>

Similar functionality is offered with the pre-build and post-build events. These are legacy events from pre–Visual Studio times. They’re simple command-line commands that are executed line by line. You can use macros with them; these so-called macros are nothing more than MSBuild properties translated to strings by execution. These events are available in project properties in Visual Studio and are saved in the project file as PreBuildEvent and PostBuildEvent targets. To see the windows shown in figure 3.9, right-click the project file in Solution Explorer, choose Project Properties, click the Build Events tab, click the Pre- or Post-Build button, and click the Macros.

Figure 3.9. Using pre- and post-build events, you can add simple command-line commands enriched with MSBuild variables.

MSBuild integrates with Visual Studio solution files, but the integration looks different than it does with project files. MSBuild knows how to execute the solution files, as you’ll see next.

3.3.2. Examining the solution file

Unfortunately, Visual Studio solution files (*.sln) aren’t MSBuild projects. But MSBuild knows how to talk to them. Using a task called MSBuild, you can execute an MSBuild project from another MSBuild script. The MSBuild task has one neat feature: it can execute the Visual Studio solution file, which is the same thing Visual Studio does. Let’s try it.

You can use the leasing calculator from chapter 1 as a test field. Your goal is to compile the solution without using Visual Studio. To do that, you’ll need an MSBuild script. The easiest approach is to place it in the same folder as the solution file and make it a solution item (see figure 3.10). The Solution Items folder in Solution Explorer is created if you add any file directly to the solution. Create a text file, and name it build.proj. It helps to name the build scripts the same way in every solution (you’ll learn why in the next section).

Figure 3.10. Custom build script as a solution item in Solution Explorer

To perform clean and rebuild operations on the solution, you must provide the Targets attribute to the MSBuild task. The targets are analogous to the action that Visual Studio performs when you choose Clean Solution and Rebuild Solution from the solution’s context menu in Solution Explorer. The other attribute is the name of the solution file on which the MSBuild project file is to perform the targets. Here’s the code:

<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build">
<MSBuild Targets="Clean;Rebuild" Projects="WindowsCalculator.sln" />
</Target>
</Project>

Save this file and reopen it to make Visual Studio realize that it’s an MSBuild script and turn on IntelliSense and code coloring.

3.3.3. Starting an MSBuild script with Visual Studio

During the course of setting up a CI process, you’ll work extensively with MSBuild. So it’s a good idea to integrate it more closely with Visual Studio. It would be handy to be able to execute the script directly from Visual Studio. To do so, you can set MSBuild as an external tool. In Visual Studio, choose Tools > External Tools, click Add, and name the tool MSBuild. Complete the definition as shown in figure 3.11 and outlined in table 3.2.

Figure 3.11. Setting a new external tool in Visual Studio. The name will appear in the Tools menu. The command will be executed using the arguments provided in the Initial Directory field, and the output will be sent to the Output window.

Table 3.2. MSBuild external tool definition in Visual Studio

Setting name (as in figure 3.11)

Value

Title MSBuild
Command C:\Windows\Microsoft.NET\Framework\[version number]\ MSBuild.exe
Arguments $(SolutionDir)build.proj
Initial directory $(SolutionDir)
Use Output window Checked
Treat output as Unicode Unchecked
Prompt for arguments Unchecked

After you define the new external tool, an MSBuild item appears on the Tools menu. Click it, and look at the Output window. The script build.proj is executed. As you can see, the convention of always naming build projects the same way is necessary here: otherwise, you have to define the external tool for every project file name. The output of the MSBuild script is visible in Visual Studio, as shown in figure 3.12.

Figure 3.12. Custom build script output in the Output window in Visual studio

Visual Studio and MSBuild integration are a great productivity boost. A similar situation exists with MSBuild extensibility; it’s easy to write your own custom tasks.

3.4. Extending MSBuild by writing and using custom tasks

Extending MSBuild is easy. To write your own tasks, you need to implement the Micro-soft.Build.Framework.ITask interface or inherit from Microsoft.Build.Utilities.Task. The second solution is easier: all you have to do is override the Execute method. Let’s use it to do something useful.

Let’s assume you want to associate the assembly version number with the Subversion (SVN) revision number. For some compelling reason, you decide that the revision part of the assembly version should be the current SVN revision number. For example, you want your CI process to update the version number every time it builds.

The assembly version is kept in the assembly: AssemblyVersion attribute in AssemblyInfo.cs. This file resides in the Properties folder in every project. Figure 3.13 shows how .NET Framework versioning works.

Figure 3.13. Assembly versioning in .NET. You can use * to let the system auto-manage your build and revision numbers.

The revision number is easily readable with SvnInfo, a new MSBuild Community Task. It uses the SVN command-line client to read information about a given SVN path. So in addition to the MSBuild Community Tasks in your tools folder, you need the SVN client executable (available from http://subversion.tigris.org/).

Another MSBuild Community Task can help you easily update the AssemblyInfo.cs file with the new version number, including the revision number. Keep in mind that the version numbers have a maximum value of 65535.

One additional thing you want to do is archive the output in a zip file named after the version number. You can write your own MSBuild task to read this number directly from AssemblyInfo.cs.

3.4.1. Implementing a custom task

It’s time to implement your custom task. Follow these steps:

1.  Create a new solution named CustomBuildExtensions.

2.  Add a new class library project named BuildTasks.MSBuildTasks. If you can go without the newest C# features, it’s best to create the task in .NET Framework 2.0; this way, you can use it in every MSBuild version.

3.  Add references to Microsoft.Build.Framework.dll and Microsoft.Build.Utilities.dll.

4.  Add a new class to the BuildTasks.MSBuildTasks project, and name it Assembly-InfoReader.cs. Here’s the code.

Listing 3.6. MSBuild task to read the assembly version from AssemblyInfo file

To implement your own functionality and be able to use it in an MSBuild script, you must inherit the Microsoft.Build.Utilities.Task class . It has everything you need. The only thing you have to do is to override the Execute() method . This method does the actual job and returns true if it succeeds or false if it fails. It uses custom properties that you can define, for example, to pass data in to the task. In the required property Path , you must set the path to the AssemblyInfo file. The other required property is Property , which contains the attribute to be read. The output is set in the Value property. The reading in the Execute() method is done with a mix of regular expressions and hack-and-slash string manipulation.

Another thing you can do here is synchronize the version number among all the assemblies in the project. Doing so may be a good idea if you always release the files simultaneously and you want to have the version synced over the release. You do this by creating a common assembly info file and adding it as a link in each project of the solution. The assembly info file must still be updated, but you can add that as a task in the build script. We leave the implementation of this as an exercise for you to complete.

Now, let’s use your new task to do something useful.

3.4.2. Putting it all together

Your custom task is ready, so let’s implement versioning using MSBuild and Subversion. To use your task, you have to put the compiled version in the tools directory of your framework. This is the project you intend to share with other projects. This way, the new MSBuild task is available in all the projects that are using it.

Do the same with the SVN client you downloaded. You should end up with the directory structure shown in figure 3.14.

Figure 3.14. The project directory structure with a reusable Framework/tools folder that contains MSBuild Community Tasks, your own tasks, and the SVN client.

You’ll version your leasing calculator. Go to the build.proj script you created in section 3.3.2, and extend it as follows.

Listing 3.7. Project versioning with Subversion and a custom MSBuild task

You extend the default build for the Windows calculator project and add the deploy target. You import the MSBuild Community Tasks using the predefined targets file. And you tell MSBuild that the task AssemblyInfoReference defined in TaskName that you intend to use is implemented in the assembly defined in the AssemblyFile attribute . Then you extend the build target. You read the SVN revision number first, using the SvnInfo Community Task. The revision number is saved in the $(Revision-Number) property. Next, you use the FileUpdate Community Task to update AssemblyInfo.cs with the new version number. After that, you perform the build on the solution. When the build target is ready, MSBuild fires the Deploy target. Using your custom task, you read the version number into the $(Version) property . Using the property metadata %(Version.Identity), you create a zip file. The archive is copied into the release directory and deleted.

You’ve put everything together. The script you wrote is ready to be used as a part of a CI process.

3.5. Summary

Build automation is an essential part of the CI process, because CI occurs behind the scenes. You need an automated process that will perform the entire build every time it’s needed.

Ideally, the automation process is scriptable. Changes can be made manually, or automatically with a tool like Visual Studio. You should be able to define various execution paths. Using conditions and parameterization, you should be able to perform various types of builds according to the situation. And your build process should be easily extensible.

Many tools deal with the build automation. Right now, MSBuild seems to be the best choice for .NET developers using Windows and Visual Studio; it’s integrated with .NET Framework and used in the UI. But there are alternatives, such as NAnt. The choice is yours.

In the next chapter, we’ll look at ways to bend the build process a little. You’ll connect the end with the beginning and add some continuity to this process. To do so, you need a CI server.