Pages

Sunday 16 October 2011

TeamCity, msbuild and Version Numbers

How to automatically set all your solution’s projects to the same version number with msbuild and TeamCity?

We have a number of solutions mixing native and managed projects.

The mechanism we use to ensure all DLLs and EXEs have the same version number relies on modifying 2 files:

  • A VersionInfo.h file for native projects
  • A VersionInfo.cs file for managed projects

The .h file contains something like this:

#define PROJECT_VERSION 3,9,0,1234
#define PROJECT_VERSION_STR “3.9.0.1234”

All native projects in the solution have a resource file (.rc) that includes this unique VersionInfo.h.  This is how the exe/dll obtain their version info.

On the .NET side, the VersionInfo.cs file contains:

[assembly: Assemblyversion(“3.9.0.1234”)]
[assembly: AssemblyFileversion(“3.9.0.1234”)]

All managed projects in the solution compile this VersionInfo.cs file (using a soft link, no duplication necessary).

So all we have to do in order to ensure that the 30+ binaries have exactly the same version number is to get our build script to modify the .h and the .cs just before building the solution.

Sounds easy!

Then there is the question of where to store the “3.9.0.1234” version. It should be in one single place of course. An obvious choice seems to be the TeamCity build number.

From Major.Minor.Build.Revision, we can set Major.Minor.Build by hand in TeamCity and have the Revision set automatically to the current SVN revision number, which is a handy way to relate builds to SVN revisions.

In the TeamCity project settings, the build number format field looks like this:

3.9.0.%build.vcs.number.MyProject_SVN_Root%

ScreenHunter_01 Oct. 15 18.37

Then within our msbuild XML file we refer to the build number set in TeamCity like this:

$(BUILD_NUMBER)

The following msbuild lines update the version number stored in the VersionInfo.cs.

    <FileUpdateFiles Files="VersionInfo.cs"
      Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
      ReplacementText="$(BUILD_NUMBER)"/>

This regex does wonders to replace “1.2.3.4” with “3.9.0.1234”. But now, more tricky: when doing the string replace in VersionInfo.h (it’s always the native projects that cause trouble Winking smile), how do you replace

#define PROJECT_VERSION 1,2,3,4

with

#define PROJECT_VERSION 3,9,0,1234

given the string in $(BUILD_NUMBER) “3.9.0.1234”.

Can’t do anything a bit subtle with msbuild without having to download and install third-party tasks… So I’m currently looking into the TextString task from the MsBuild Extension Pack to tokenise the version string and then concatenate it back to the appropriate format. It’s all a bit complicated, there must be an easier way to do this…

Update (16/10/2011):

It’s actually not that bad, here is the code that formats $(BUILD_NUMBER) and updates the .h /.cs VersionInfo files.

    <TextString TaskAction="Replace"
                OldString="$(BUILD_NUMBER)"
                OldValue="."
                NewValue=",">
      <Output PropertyName="FormattedBuildNumber"
              TaskParameter="NewString"/>
    </TextString>
    <Message Text="Replacing version in VersionInfo.h with $(FormattedBuildNumber) and $(BUILD_NUMBER)">
    </Message>
    <FileUpdateFiles Files="VersionInfo.h"
      Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
      ReplacementText="$(BUILD_NUMBER)"/>
    <FileUpdateFiles Files="VersionInfo.h"
      Regex="(\d+)\,(\d+)\,(\d+)\,(\d+)"
      ReplacementText="$(FormattedBuildNumber)"/>
    <Message Text="Replacing version in VersionInfo.cs with $(BUILD_NUMBER)">
    </Message>
    <FileUpdateFiles Files="VersionInfo.cs"
      Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
      ReplacementText="$(BUILD_NUMBER)"/>

No comments: