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
Advertisements