OrthoCoders

You can code it, I can help!

MSBuild Scripting Part II, Versioning and Deployment

MsBuild Introduction

MsBuild is a language to create build scripts using predefined tasks. Please check my previous blog for a full description

Versioning

Before talking about deployment we have to consider how to version our assemblies before copying them to any environment. Having the correct version number is very important once you start a development iteration. The QA team needs to know which version to test and report, the user needs to know which version they are seeing, you need to know which version is causing problems, etc... .NET uses the Properties/AssemblyInfo.cs file to store the version info. Inside each file you will find the following code:
// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
Here is a brief description of the recommended usage of each number:
  • Major: This number indicates a big difference between versions. When it changes the user will expect "Major" changes in functionality.
  • Minor: This number indicates fixes, bugs, etc. Still same functionality but with some modifications.
  • Build: This number indicates the amount of times you built the app so far. (not always used)
  • Revision: This number indicates the revision number from your source control. This number is very important because it would give you the information needed in order to check the revision you need and be able to reproduce bugs, errors, etc.
So far, so good. We just need to change all the files in each project and put the same version... and when it changes... again... easy, right?

MsBuild to the rescue

Not to fret my friend, luckily we have msbuild on our side. First of all we will define the version we want in or msbuild file, using a property group.
	<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

	<PropertyGroup>
		<ProjectName>MediaLibrary</ProjectName>
		<ExportFolder>\temp\Export\$(ProjectName)</ExportFolder>
		<BuildFolder>\temp\Builds\$(ProjectName)\Latest</BuildFolder>
		<Major>0</Major>
		<Minor>1</Minor>
		<Build>0</Build>
	</PropertyGroup>
As you can see the revision number is not included because we would like to use the source control tool to get the revision number every time we run the script to make sure we always have the right version. Tigris has a set of tasks that will help us to achieve our goal. You can download it for free and install it. Now that we have the version we want (Major.Minor.Build) we need to get the revision number from the repository and store it into a property. Because I use SVN I'll use the SvnVersion task from Tigris to get the version number and store it into the property Revision. If you also use SVN you will need a command line tool in order to run svnversion. Collabnet has a free SVN client you can download and use. After finding the version number we need to find all the files that we would like to change. For that purpose we will define another ItemGroup including all the AssemblyInfo.cs files in our hierarchy and store them into the variable AssemblyFiles. The final step is to replace each declaration with the version in the AssemblyInfo.cs file with the version we defined in our build file using the FileUpdate task for each cs file.
<Target Name="UpdateAssemblyVersion">
		<SvnVersion LocalPath="$(MSBuildProjectDirectory)">
	      <Output TaskParameter="Revision" PropertyName="Revision" />
		</SvnVersion>

		<ItemGroup>
			<AssemblyFiles Include="**\AssemblyInfo.cs"/>
		</ItemGroup>
		
 	    <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)"/>

		<FileUpdate Files="@(AssemblyFiles)"
                Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
                ReplacementText="$(Major).$(Minor).$(Build).$(Revision)" />
		
</Target>
The FileUpdate task uses a regular expression to find the version number. In this case we are looking for four digits separated by dots and as replacement we use the variables we defined previously.

Deployment

Alright. We have all our assemblies (executable, etc) with the right version and we are ready to roll. Now, we need to build again to include the right version in release mode and then copy the files to the destination of our preference. A common practice is to copy the files to a folder where all the team knows the latest version will reside. Using a continuous integration tool like Cruise Control .NET or TeamCity makes easier to have always the latest version available as soon a new change is committed to the repository. I'll call this task DeployBin and will do the following:
  • Delete the destination folder
  • Call the UpdateAssemblyVersion task to update to the latest version of all the Assembly.cs files.
  • Build again (Release mode) to have the latest binaries with the version updated.
  • Create the destination folder.
  • Define a variable called SourceFiles using an ItemGroup to include all the assemblies I would like to copy and excluding the ones I don't need (testing assemblies).
  • Copy all the files listed in the SourceFiles variable to the chosen destination.
Ready? Here is the magic:
	<Target Name="DeployBin">
		<RemoveDir Directories="$(BuildFolder);$(ExportFolder)" ContinueOnError="true" />
		<CallTarget Targets="UpdateAssemblyVersion"/>
		<CallTarget Targets="Build"/>
		<MakeDir Directories ="$(BuildFolder)"/>
		<ItemGroup>
			<SourceFiles Include="**\bin\Release\*.dll" Exclude="**\*.Tests\bin\release\*.dll"/>
		</ItemGroup>
		<Copy SourceFiles="@(SourceFiles)" 
				DestinationFolder="$(BuildFolder)">
			<Output TaskParameter="CopiedFiles" PropertyName="Copied"/>
		</Copy>
	</Target>
Voila! That's it! You can make the deployment as complicated as you want. You can separate the folders, rename the previous version, etc, etc. Here is the complete file in case you would like to look at it. I hope you will find it useful. In my next blog about MSBuild we will talk about creating custom tasks. Any feedback is most welcome. Enjoy.

How to Use MSbuild to Build and Test

What is a build script?

A build script is a small program written in the language of your choice that usually has more than one task used by the developer to build, test, deploy, etc...

Why do I need a build script?

A build script can give you the ability to compile, run tests, and build libraries, etc, without using the IDE. This is most useful before committing code to the repository and also, after the source is committed before being run by the continuous integration service.

Doing all these tasks manually can be tedious and error prone.  If you are also suffering from lack of documentation the knowledge of the process will leave with the developer in charge of compiling, building, etc.

Thus, instead of spending time writing documentation why not write a script to do all the steps for us and achieve the same results?

Enters MS Build

MS provides a script building language based on XML. Very similar to Ant and NMake this XML based script has many predefined tasks that allow us to compile, build, etc, etc...

How do I Start?

Here is a minimal ms build file called algorithm.msbuild used to compile our solution in release mode:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<ItemGroup>
		<Solutions Include="**\*.sln"/>
	</ItemGroup>

	<Target Name="Build">
		<MSBuild Projects="@(Solutions)" Properties="Configuration=Release"/>
	</Target>
</Project>
The project tasks are identified by the tag Target . The code above defines one called Build. Inside the task you can see a call to the MSBuild task used to compile all the solutions defined in the variable @Solutions. This variable is declared inside an ItemGroup which allow us to use the wildcards to match all the solutions in our current folder and subfolders. To run this file, open a command window and run:
msbuild algorithm.msbuild  /t:Build

Clean and rebuild

In order to rebuild our assemblies we need to be able to clean all the projects and then build them again. In order to do that we are going to use the internal predefined task Clean that comes with the default target definitions when you install vs.
<Target Name="Clean">
     <MSBuild Targets="Clean" Projects="@(Solutions)" Properties="Configuration=Release"/>
</Target>

<Target Name="Rebuild">
     <CallTarget Targets="Clean;Build"/>
</Target>
The Rebuild target uses the previous defined targets Build and Clean to achieve our goal.

Testing

Testing is an integral part of development and for each library or executable we build we should have our test counterpart.

In this case I'm using MBunit and when you install Gallio (test runner) it will also install an assembly with MSBuild related tasks to be used in our scripts.

First we have to include the Gallio task library to be used in our script file and then we can call the test runner to load all our assemblies that contain tests.

<UsingTask TaskName="Gallio.MSBuildTasks.Gallio" AssemblyFile="c:\program files\gallio\bin\Gallio.MSBuildTasks.dll"/>
<Target Name="Test">
     <ItemGroup>
          <TestAssemblies Include="**\bin\release\*.Tests.dll"/>
     </ItemGroup>
     <Gallio Assemblies="@(TestAssemblies)"   
          WorkingDirectory=""
          ApplicationBaseDirectory="$(SolutionFolder)\src\"
          IgnoreFailures="true"
          ReportDirectory="$(CCNetLastBuildFolder)"
          reportNameFormat="Test-Report"
          ReportTypes="Xml"
          ShowReports="false"
          >
          <Output TaskParameter="ExitCode" PropertyName="ExitCode" />
     </Gallio>
     <Error Text="Testing failed" Condition="$(ExitCode) == 1"/>
</Target>

As you can see I'm using another ItemGroup to define the assemblies that should be included in the testing. By convention I name all my test assemblies with the postfix Tests in order to be able to identify them easily.

I won't describe in detail all the attributes in here but you have to be careful with the test exit code.

The last line of the task contains an Output parameter. That is a variable that is going to be defined after the task runs. In this case the exit code is most important because it will indicate if the test has passed or not. Because the Gallio task does not update this value we need to check for it after running all the tests to be able to make the build fail if the tests fail.

The last line of the Test task uses the built in Error task to generate an error only when the ExitCode equals one indicating that the testing has failed.

Please follow the links to read more about MSBuild and MBUnit. Part II shows how to version our files using the version number from the repository and copy them to a different location. Go to MSBuild Part II. Enjoy.

Algorithm.NET Released!

The first release of the Algorithm.NET library is out. The library provides common algorithms useful in every day coding tasks. Why to use them? Instead of write again and again your own, test them, etc, you can benefit from using common algorithm like Find, ForEach and Collect to be more descriptive in your code and use predicates, actions and functions to express your intentions. If you want to read more or donwload the library and code please checkout the page.

Welcome

Welcome to ORTHOcoders! Yet another site about coding, and doing the right thing.