A very popular NAnt topic is that of versioning one’s assemblies. Over the years, I’ve read many different posts on this topic, and each has its own method of performing versioning. Regardless, all share the common goal: generate an assembly version as part of the build process.
A couple of years ago I developed my owner versioning process. It’s mostly self-contained as a set of custom NAnt tasks, called from my build scripts. There’s an additional step of synchronizing the version with the repository, but that step is outside the scope of this post.
At a high level, this is how I manage the version information for a build:
1 – Read the current version number from an external resource/file
2 – Increment the version number
3 – Write the version information back to the external file
4 – Generate a c# source file that contains the version number
And here is an example script that demonstrates these steps:
<loadtasks assembly="CustomNAntTasks.Versioning.dll" />
<property name="version.offset" value="0.0.1.0" />
<!-- get the version -->
<getautoversion filename="version.txt" property="version" />
<!-- increment the version -->
<echo>Incrementing version ${version} by offset ${version.offset}</echo>
<property name="version" value="${version::offset-version(version,version.offset)}" />
<echo>Version is now ${version}</echo>
<!-- write the version back out -->
<setautoversion filename="version.txt" property="version" />
<!-- now generate a new file -->
<generateassemblyinfoversionfile filename="AssemblyInfoVersion.cs" property="version"
set-file-version="true" set-asm-version="true" overwrite="true" />
Custom Task: getautoversion
After the script loads the custom task dll, it defines a property named “property.offset”. This is the value by which the current version will be offset. For example, if the current version is 1.0.0.0, and the offset is 0.0.1.0, then the combined version number is 1.0.1.0.
Next, the script calls the first custom task: getautoversion. This task simply reads the “version.txt” file and places the contents into a property named “version”. The value in the version.txt file must follow the Major.Minor.Build.Revision format used by dotnet. In fact, the string read from the file is actually passed to the constructor of a System.Version object at runtime; so if the version format is not correct, we’ll get a runtime exception.
Custom Functions: Incrementing the version
The next step is to somehow augment/offset/increment the current version number. This is implemented as a single function called “offset-version” (implemented in the VersionFunctions class). This function takes the current version number, as a string, in version form, and an offset to apply the version. The second argument is also a string, in version form. So a call to this function takes the form:
<property name="version" value="${version::offset-version(version,version.offset)}" />
If the first argument is “1.0.0.0″, and the offset argument is “0.0.1.0″, then the result, placed in the version property, will be “1.0.1.0″.
Custom Task: saving the version
Now that we have a new version number, its time to save the version back out to disk so that the version sequencing is maintained for the next build. This is accomplished via the “setautoversion” custom task. It simply takes the name of the version property, and the name of the file to write.
The attributes of this custom task are the same as the “getautoversion” task. That’s because both custom tasks derive from the same class: AutoVersionFileTaskBase. This is an abstract base class that simply declares two instance properties to map to the two attributes in the xml. The derived classes override the ExecuteTask method to read or write the version information to/from the file.
This behavior could have been implemented as a single task. But I preferred to break it out into two separate tasks to keep the reading behavior distinct from the writing behavior.
Custom Task: versioning the assembly
And finally here is where all of the versioning behavior comes together: versioning the assembly. Reading/writing the version information from a text file, and offsetting the version number is pointless if we don’t inject the version number into the assembly at build time.
Traditionally, the version number for an assembly lives in the AssemblyInfo.cs file, and is expressed via two attributes: AssemblyVersion and AssemblyFileVersion. The last custom task, “generateassemblyinfoversionfile”, generates a new .CS file that contains these two attributes. As a result, you must remove these two attribute declarations from your current AssemblyInfo.cs file.
This task defines five properties: filename, property, set-file-version, set-asm-version, and overwrite. The filename property defines the name of the file to be generated. The “property” property, like the getautoversion and setautoversion custom tasks, defines the name of the property where the version number is located. The set-file-version property, a boolean, emits the AssemblyFileVersion attribute if true. The set-asm-version property, also a boolean, emits the AssemblyVersion attribute if true. The overwrite property simply tells the task to overwrite the file if it already exists.
When this task is executed, as configured in the first code listing, the resulting file looks like this:
using System.Reflection; [assembly: AssemblyFileVersion( "2.0.3.0" )] [assembly: AssemblyVersion( "2.0.3.0" )]
Finally, this file must be included in your source code files when you build the application.
The full source code for all of these custom tasks can be found here: Download
Hi,
I have integrated StyleCopCmd into nAnt. It works well, yet I wish get FAILED in case of there are some violations in StyleCopCmd section. How can I achieve this?
You will be appreciated if you email me your resolution.
Thanks in advance
Comment by Kambiz — November 12, 2008 @ 8:23 pm
Great article, I’ll look at implementing it in our build.
@Kambiz it was a bit of a pain, but I used an “exec” then did some xml poking to check values. Details here:
http://www.robertbeal.com/archives/47
Comment by Robert — March 29, 2009 @ 1:03 pm