As I was surprised about the ugliness of various file versioning implementations I’ve seen over the years in various build scripts I decided to write a small guide on how you should do it.

Do not, ever, do find-and-replace inside the source-code in order to replace version numbers. The version should be stored in a single place and the revision number should be generated automatically.

If after you run the build scripts you do have source files modified on disk, you can be sure that you are doing something deeply wrong. If you are using git just run git status, which should return:

nothing to commit, working directory clean

Version Components

First we need to establish a common language for naming each component of versioning. As Microsoft decided that versions are supposed to be formed only from 4 integers that can have values between 0-65500 you should not use the build number in the last integer. You will be surprised how often people are faced with serious problems because their build number passed 65535.

As we cannot use the sha1 form the source control we will have to be a little bit creative and generate a numeric revision number.

At this moment I am using this piece of code to generate an incremental numeric revision:

REVISION=$(git rev-list --count HEAD)
re='^[0-9]+$'
if ! [[ $REVISION =~ $re ]] ; then
  echo "WARN: Unable to generated revision number from git log, falling back to revision = 0" >&2
  REVISION="0"
fi
  • MAJOR - 1
  • MIJOR - 0
  • PATCH - 0
  • REVISION - generated from the SCM or build system, defaults to 0 for dev builds. In many cases this can be the build number.
  • VERSION would be ``$MAJOR.$MINOR.$PATCH like 1.0.1`
  • FULLVERSION would be $MAJOR.$MINOR.$PATCH.$REVISION as 1.0.2.8343

MSBUILD

Convincing msbuild to put the correct version numbers on builded files can be obtained only by tunning a little bit the project files in order to generate the assemblies files, instead of modifying them at each build.

<PropertyGroup>
  <Version>1.0.0.0</Version>
  <AssemblyVersion>1.0</AssemblyVersion>
  <AssemblyFileVersion>$(AssemblyVersion).0.0</AssemblyFileVersion>
</PropertyGroup>


<Target Name="BeforeBuild">
  <ItemGroup>
    <VersionInfo Include="[assembly: System.Reflection.AssemblyVersion(&quot;$(AssemblyVersion)&quot;)] // Generated by build" />
    <VersionInfo Include="[assembly: System.Reflection.AssemblyFileVersion(&quot;$(AssemblyFileVersion)&quot;)] // Generated by build" />
    <VersionInfo Include="[assembly: System.Reflection.AssemblyInformationalVersion(&quot;$(Version)&quot;)] // Generated by build" />
    <VersionInfo Include="[assembly: System.Reflection.AssemblyConfiguration(&quot;$(Configuration)&quot;)] // Generated by build" />
  </ItemGroup>
  <MakeDir Directories="$(IntermediateOutputPath)" />
  <WriteLinesToFile File="$(IntermediateOutputPath)VersionInfo.cs" Overwrite="True" Lines="@(VersionInfo)" />
  <ItemGroup>
    <Compile Include="$(IntermediateOutputPath)VersionInfo.cs" />
  </ItemGroup>
</Target>

Once you implemented these changes the only thing you need to add is a parameter to msbuild calls, like:

 msbuild.exe /nologo /maxcpucount /verbosity:minimal project.sln /p:Configuration=Release /p:VERSION=1.0.0.123

NSIS Installers

In order to use the same version as you used on msbuild for your installer, just do makensis.exe /V1 /DVERSION=%VERSION% installer.nsi and inside your .nsi` file you will want to have to do something like:

!ifndef VERSION
!define VERSION "1.0.0.00000"
!endif

References