Wednesday, July 06, 2005

Versioning Using NAnt

I've posted already on how much I'm getting out of Marc Holmes' Expert .NET Delivery using NAnt and CruiseControl .NET. (Marc's no relation to me, at least none that I know of.) One of the topics within is versioning. Marc uses the version task from nant-contrib to increment build numbers held in a Build.Number file. This gets used in the same work task to create the assembly information via the asminfo task. I found one mistake in his example code, plus made one enhancement to it. Changes are below. (I should say I think I found a mistake. I haven't heard back from him yet.) The mistake seems to be that the incremented version number isn't used when generating the AssemblyVersionAttribute. The enhancement I did was wrapping the Build.Number file into Source Safe, checking it out before incrementing the version, then checking it back in afterwards. Below is the updated XML for the task. I'm not going to fill in all the NAnt properties created upstream from this task-- they're pretty obvious. Note that Marc's strategy uses the debug property to control whether the build number's incremented or not. You don't have to increment build numbers if you're just testing additions to the NAnt scripts. I may rename this property in the future since "debug" means different things to me. Marc also uses a CommonAssemblyInfo file which he links to individual projects within a solution, a great trick for cutting duplication of redundant information. ("Duplication of redundant..." Clever, no?) <target name="version1" description="Increments version number in CommonAssemblyInfo file.">    <property name="sys.version" value=""/>    <ifnot test="${debug}">       <!-- Enhancement: check out Build.Number file from source control -->       <vsscheckout             username="${vss.username}"             password="${vss.password}"             localpath="${core.source}\"             recursive="false"             dbpath="${vss.dbpath}"             path="${vss.path}\${}.Build.Number"       />       <version buildtype="Increment"             revisiontype="Increment"             path="${core.source}\${}.Build.Number"/>       <!-- Bugfix: store version task's output back to sys.version property if we're incrementing -->       <property name="sys.version" value="${buildnumber.version}" />       <!-- Enhancement: check Build.Number file back into source control -->       <vsscheckin             username="${vss.username}"             password="${vss.password}"             localpath="${core.source}\${}.Build.Number"             recursive="false"             writable="false"             dbpath="${vss.dbpath}"             path="${vss.path}\${}.Build.Number"             comment="Incrementing for version ${sys.version}"          />       </ifnot>       <attrib file="${core.source}\CommonAssemblyInfo.vb" readonly="false" />       <asminfo output="${core.source}\CommonAssemblyInfo.vb" language="VB">          <imports>             <import name="System" />             <import name="System.Reflection"/>          </imports>          <attributes>             <attribute type="AssemblyVersionAttribute" value="${sys.version}" />             <attribute type="AssemblyProductAttribute" value="${}" />             <attribute type="AssemblyCopyrightAttribute" value="Copyright (c) 2005, ${}"/>             <attribute type="AssemblyCompanyAttribute" value="${}" />             <attribute type="AssemblyTrademarkAttribute" value="Trademark by ${}" />          </attributes>       </asminfo>       <attrib file="${core.source}\CommonAssemblyInfo.vb" readonly="true" /> </target> This approach may not work for everyone. In particular, the linked CommonAssemblyInfo file won't work with Web projects because of their pain-in-the-ass broken up directory structure. It also doesn't give granular control down to each assembly. That's OK, because Marc says frequently in his book that his topics aren't solutions for everyone, they're guides to get you started. So there you have it. UPDATE: Marc pointed out that sys.version is the property set by nant-contrib's <version> task -- in earlier versions of nant-contrib. I'm using 0.85RC3 which defaults to buildnumber.version. I should have checked that from the get-go!


Anonymous said...

I have been spinning my wheels all day changing a build file from version 84 to 85 and learning all of this from the start. sys.version has been laughing at me all dayl.....

Thanks for the post, it was very helpful!

Ajayshanker said...

I was using the code from Marc's book and the sys.version was never getting incremented. Did a google search, found your a link to your blog ..and discovered that buildnumber.version is what incremented ..


I was wondering if you could answer my question on the versioning part of the build process. My question is why are we incrementing the version number before compiling. Its quite possible that the compilation may fail and why would one increment the version for failed builds.

Thanks again

Jim Holmes said...

Glad my post was helpful, Ajayshanker!

You need to have a new version number for passing to the assembly. Otherwise how will the new assembly have the proper version number?

You do raise a good point about not incrementing for failed builds. You could set the "debug" property which doesn't increment the version number. However, then what do you do when you need to version the assembly? Rerun the build?

I suppose you could wrap in some sort of failover condition where the build number gets decremented if the build fails, but that's quite a bit of hassle. Frankly, I'd just let the version number increment and run with it.

Ajayshanker said...

Jim, Thanks for taking time and clearing my doubt.

You are right in pointing that the build script will be much simpler when the version is incremented everytime the build script runs.

Thanks again.

VC said...

Hi Jim,
Thanks for this info. I have one question.

My Env:
1) 3 .sln files each of which are referring to 10-12 .prj files (each of which are referring around 70-80 .cs files and other related proj. files)
2) one web site project folder(ref- not having a prj file at all.

My need :
I would like to increment the version numbers in both the cases whenever I create a new full-build or patch-build with the only difference being the patch build should contain only the changed files and not all of the files, just because a new version number has been stamped on to all the files in a single project referring to one common assembly.cs file.

What I tried :
I couldn't succeed because my patch build gets either all the files or none of them. But never just the changed files alone.

Appreciate if you could share some light on how to configure Nant for the above need. Even some pointers would help.

Thanks in advance,


VC said...

Just subscribing to the comments..I forgot to Check the option for "Email Followup Comments to my email" earlier. Sorry for the spam.

Jim Holmes said...

@VC: Sorry, but this post is seven years old and I haven't worked with NAnt in a very, very long time. I can't even really point you in a good direction for that. Sorry!

VC said...

Not a Problem, Jim. An idea I have is to get a manifest-like file generated having file-hashes of all files during the product-build and get the patch build contain only the changes files based on the changed-hash data. Though it sounds like a work around, will try it to see if it works until I figure out an easier way.

But your article triggered new ways. Thanks for posting it.

Subscribe (RSS)

The Leadership Journey