Extending a .NET Development Tree – Versioning

The next area we’re going to look at is adding support for versioning. The .NET documentation has some fairly clear guidelines about versioning which you can read here.

Versioned assemblies can be generated on 2 types of occasion – during ‘interactive’ development and also when the automated build is used to generate distributions. For the first of these, you don’t care too much (at least initially) exactly what the name of the version is, as long as it doesn’t conflict with your distribution versions. These distribution versions though will have more structure around them, specifically:

  • The format of the version should consist of the 4 period-separated integers specified by .NET conventions
  • Version names should be unique for each distribution you publish
  • Version names should fit a specified standard for your project and organisation
  • All assemblies for one project for one distribution instance should have the same version name

These are all fine ideas, but how does it fit in with our project tree? Let’s take a look.

At the moment each Visual Studio project in our Sycamore solution has an AssemblyInfo.cs file with a bunch of default content that Visual Studio generates for us. We’re going to get rid of most of this. First of all, edit each project’s AssemblyInfo.cs file, and set the content to be something like the following:

[assembly : AssemblyTitle("My Assembly Name")]

[assembly : AssemblyDescription("")]

[assembly : AssemblyConfiguration("")]

These 3 assembly attributes are all optional, but I tend to leave them in as a hint to be filled in later.

We said above that all the assemblies for a given project should have the same version for a given distribution. To help us implement this, we are going to store all the versioning information for the entire solution in one file, called CommonAssemblyInfo.cs and saved in the src folder of our project tree. Its best to actually create this file outside of Visual Studio, and then include it as a link for every project in the solution. To do this, choose ‘Add Existing Item…’ from the project context menu, select the CommonAssemblyInfo.cs file you just created and then choose ‘Link File’ from the ‘Open’ button drop down menu.

By now you should have this file linked in every project, and the next step is to add some content to it. I tend to use the following format:

[assembly: AssemblyVersionAttribute("0")]

[assembly: AssemblyCopyrightAttribute("Copyright  2005 Sherwood Forest Inc.")]

[assembly: AssemblyCompanyAttribute("Sherwood Forest")]

[assembly: AssemblyProductAttribute("Sycamore")]

If you compile the project now and look at some of the output DLLs in Windows Explorer you should see this metadata. The AssemblyVersionAttribute value here is fine for initial interactive development, but you may need to customise it for your own environmental standards.

That’s enough for interactive builds, but what about automated builds, specifically when we want to publish distributions? An initial thought may be just to save our ‘real’ version number in the Source Control version of the CommonAssemblyInfo.cs file, but the problem with that is then our interactive development will produce assemblies with version numbers identical to our published distributions, which is far from ideal.

The next idea is that we edit the CommonAssemblyInfo.cs file every time we want to publish a distribution, but that’s manual work and as such doomed to failure. It would be much better to automate this, so that’s what we’re going to do. Specifcally, at build time, we are going to generate a new CommonAssemblyInfo.cs source file (this technique is known as code gen.) NAnt has a useful task for code genning AssemblyInfo type files called <asminfo>. To generate the same type of file we have stored in Source Control, we use the following target:

<target name="set-version">

<ifnot test="${property::exists('version')}">

<property name="version" value="${default.version}" />

</ifnot>

<asminfo output="srcCommonAssemblyInfo.cs" language="CSharp">

<imports>

<import namespace="System.Reflection" />

</imports>

<attributes>

<attribute type="AssemblyVersionAttribute" value="${version}" />

<attribute type="AssemblyCopyrightAttribute" value="Copyright  2005 Sherwood Forest Inc." />

<attribute type="AssemblyCompanyAttribute" value="Sherwood Forest" />

<attribute type="AssemblyProductAttribute" value="Sycamore" />

</attributes>

</asminfo>

<echo message="Version set to ${version}" />

</target>

You’ll notice here that version is a property which you can set externally, but defaults to a property called default.version. We need to add this default property to the build script, and also update our full target:

<property name="default.version" value="0" />

<target name="full" depends="clean, set-version, test, dist"

description="Compiles, tests, and produces versioned distributions" />

We can now build a versioned distribution as follows:

c:develSycamore>go -D:version=1.1.0.0 full

We can now build .NET-version standardized applications – great! But there’s still a manual process here – wouldn’t it be better if these distributions were generated automatically for us somewhere? The good news is they can be, but we’ll be looking at that next time.

To summarise this part:

  • Make your published distributions contain properly versioned assemblies
  • Define versioning information in a project-common source file
  • Code-gen your versioning file at automated-build time
  • Add the version name as a parameter to your automated build process

Extending a .NET Development Tree – Basic Distributions

If you read my thoughts on setting up a .NET development tree you might be interested to know how we can extend these ideas to gain even more value from automation and configuration on our .NET projects. This series of entries should help you with these investigations.

In this first entry I’m going to talk about Distributions. Distributions are one kind of artifact produced by running the automated build of your project tree. They are typically a bundling of compiled code, dependencies, configuration and documentation that enables someone to get started using your application, or update an existing copy of your application. Distributions tend to take the form of file archives (such as Zip files) or installers (such as MSI files.)

Console applications tend to allow the simplest kind of distribution. Our Sycamore example project that we created in the original article has a console application so we’re going to use this to generate an example distribution.

First of all, we’re going to add a full target to our build script. A ‘full’ build is a concept that means ‘run an end-to-end development build, and produce all artifacts’. For Sycamore’s build it looks like this:

<target name="full" depends="clean, test, dist"	description="Compiles, tests, and produces distributions" />

So far, so good. Now we need to actually create our dist target. Here it is, and I’ll explain it afterwards:

<target name="dist">

<copy todir="${build.dir}dist">

<fileset basedir="${build.dir}DebugSycamoreConsole">

<include name="***"/>

<exclude name="***.pdb" />

</fileset>

</copy>

<zip zipfile="${build.dir}Sycamore.zip">

<fileset basedir="${build.dir}dist">

<include name="***" />

</fileset>

</zip>

</target>

There are 2 clear sections to this target. The first is to setup the file tree of our distribution. In our case, we just want the Sycamore application. We specifically don’t want the .pdb debugging file. (Note to those thinking Why didn’t you just use a release build? – I’m just using the .pdb as an example of a file produced by the compile step that we don’t want in our distribution.)

One of the nice side-effects of using the Visual Studio solution/project files for defining our automated build is that all required dependencies will already be copied to the build output folder, ready for us to copy to the dist folder. In fact you can even use the pre- and post- build scripting enabled for projects in Visual Studio (but make sure that your exclude list filters out any build event batch files!) In this first section you could also add extra <copy /> sections to include documentation, license files, etc.

Once the file tree is complete, we can create the actual distribution file. In our case, we use a simple Zip file, which is appropriate for XCopy Deployment. Since all the hard work has been done in the first part of the target, the <zip /> call is very simple.

Distributions typically get much more complicated than this. Deciding exactly what files to include is something you’ll need to think carefully about. Also, you need to decide to what extent your distribution should be environment specific. For example, you may want to produce a distribution of a web application, the configuration of which has already been setup to for your production database. The benefit of this is less work for your deployment team. The drawback is that you won’t be able to deploy this distribution somewhere else. Typically, your development distributions will be environment generic, and you may introduce post-development builds to deploy and configure your application for specific environments, but that really is subject for a much bigger discussion!

Distributions are also tied in to a versioning strategy, and stored on a server somewhere, but that is also out of scope for this particular entry.

To summarise this part:

  • Produce distributions to enable your project to be used elsewhere.
  • Your distributions should be generated by a full development build.
  • Distributions should include a combination of code, dependencies, configuration and documentation.
  • Separate out the work of deciding what should be in your distribution, and creating the distribution file itself.

CruiseControl.NET 0.9 Released

A bit of a late night blog this, but its worth it. We’ve just finished off CruiseControl.NET 0.9!

Big changes in this release:

– An installer! Not just a file distribution, it also sets up your windows service and sets up your IIS settings for the Dashboard.

– Arbitrary task workflow.

– Lots of updates to the Dashboard (more plugins, more configurable, completely re-skinnable.)

The Dashboard updates were my favourite. I’ve already mentioned on this blog the fun I had with NVelocity and removing all the Web Forms code. I haven’t even mentioned yet the other fun I had combining Dependency Injection ideas with de-serialization. 🙂

Read the release notes here and download it here.

Scaling Continuous Integration talk in London

Next week Steve Freeman and I will be presenting a session in London on Scaling Continuous Integration. This is basically about how you can get the benefits of CI on larger projects.

If you’re in London and are interested in this, it will be taking place next Wednesday evening (6th April). Its totally free to come along, you just need to be able to make it to the ThoughtWorks office on High Holburn. For more details on logistics, please look here.

We’ll also be presenting this as a tutorial at SPA 2005 in a couple of week’s time.

Leaving New Zealand

I have moved back from New Zealand to the UK. Its been a very hard decision to make for a whole bunch of reasons, most of which I don’t want to go into here, but I did want to write a little about my experiences there.

New Zealand is a fantastic country. The most obvious thing is that it is beautiful. I hardly made it down to the South Island, where all the famous bits are, but even the North Island offers some great countryside. 20 miles from the centre of Auckland you can be walking through national parks of native bush and hardly see another soul.

New Zealand also has a culture that I like. It offers a healthy mix of honesty and compassion, and I think this has lead to a tolerance that I haven’t seen in other places. Sure, the fact that there are only 4 million people in a country only slightly smaller geographically than the UK helps, but I’d like to think that everyone could learn a lot from such a culture.

New Zealanders are also an intriguing bunch of people. They are resourceful beyond brits or americans, probably again because of their country’s relatively small size, but I guess also because of their isolation. Using the IT industry as an example, you are much more likely to find ‘jack of all trade’ developers than in the UK, where specialisation tends to be the norm.

Its not a perfect country though. The distance to anywhere else, especially beyond Australia, is hard. Apart from anything else I think that New Zealand could look more beyond itself, but maybe that’s just plain impractical. It also suffers hugely from emigration. Something like 15% of kiwis live outside of their own country, and of course this creates a huge ‘brain drain’.

New Zealanders are also terrible drivers 😉 .

I’m back in London now, at least for a few months, at which point I might move on somewhere else. Of course, there are many things available from a London life that I missed in New Zealand, but I will always look back fondly at my time there.

Tree Surgeon 1.0

This week I released ‘version 1’ of Tree Surgeon, my application that will generate a .NET development tree for you.

The main updates since 0.1 are a GUI, an installer, and an update of the included version of NAnt.

Over time, I plan on adding some more patterns, including versioning, continuous integration support and distributable generation.