A scheme for automatic build numbers in C/C++ projects
May 22, 2010 Leave a comment
Many times it’s desirable on medium and large scale projects to generate some form of build number in a semiautomatic or automatic way to differentiate builds in order to track different release code bases, even to generate different but easily distinguishable private and public builds, on single or multiplatform projects.
Unfortunately, none of the C/C++ compilers I know (VC/GCC) support build versioning, altough this task is better suited to build systems. However, none of the systems I use (UNIX make, QMake, Microsoft NMAKE) can generate build version information automatically.
In the build script for the project I’m developing at a private company, I’ve developed an automatic build numbering system, running under Windows command line. This approach is working for Win32 projects compiled with QMake (QT’s build systems), altough I imagine it can be adapted to different toolchains on either Windows or Unix/Linux platforms.
The script generates build numbers and modifies Win32 resource files (RC) accordingly, so each target executable and DLL built gets the build number on their VERSION_INFO resource.
On the directory where my build script resides, I created the following three important folders:
- verstamp where the version stamping actually occurs.
- tools where the needed command line tools reside (actually, only gawk for Windows is there).
- rc where the RC files for each subproject are located. Those are processed by the RC.EXE tool when NMAKE processes the Makefiles generated by QMake
The flow is as follows.
On each release build, my verstamp utility reads a VERSIONINFO file. This file contains the current build number (major version, minor version, SVN commit rev, and build). For example:
1,0,10234,230
I also generate the latest revision number from Tortoise SVN SUBWCREV utility and store it in %REV% environment var. The SUBWCREV utility substitutes $WCREV$ string to the latest commit rev. So a simple file named __SVN__.REV contains the template:
$WCREV$
To replace the string with the commit number in a new SVN.REV file, copying result to %REV%:
subwcrev %SOURCETREE% __SVN__.REV SVN.REV > NUL
set /p REV= < SVN.REV
Where %SOURCETREE% is the root of my SVN-managed source tree.
Using AWK, build number is increased and SVN rev patched up, with this line:
awk -F, “{ printf(\”%%d,%%d,%%d,%%d\”,$1,$2,%REV%,$4+1); }” VERSIONINFO.OLD > VERSIONINFO
Now VERSIONINFO contains the updated build number and source revision.
Version resource update
To update the version information resource on my executables, first I store the new build number on proper environment variables (on Unix shells this would be far better — NT command line is lackluster):
awk -F, "{ print $1; }" VERSIONINFO > MAJORVER
awk -F, "{ print $2; }" VERSIONINFO > MINORVER
awk -F, "{ print $4; }" VERSIONINFO > BUILD
set /p MAJOR= < MAJORVER
set /p MINOR= < MINORVER
set /p BUILD= < BUILD
The task now is to patch a header that is included by my RC files, where product name, product version, file version are defined. I work with the following ‘template’, named version_info.hdr:
#define PRODUCTNAME "Your product for Windows\0" #define PRODUCTVER $$MAJOR$$,$$MINOR$$,$$REV$$,$$BUILD$$ #define FILEVER PRODUCTVER #define STRPRODUCTVER "$$MAJOR$$.$$MINOR$$.$$REV$$.$$BUILD$$" #define STRFILEVER STRPRODUCTVER
Each field is updated by AWK using the gsub command with the MAJOR,MINOR,REV and BUILD variables setup above:
awk “{ gsub(/\$\$MAJOR\$\$/, %MAJOR%); gsub(/\$\$MINOR\$\$/, %MINOR%); gsub(/\$\$REV\$\$/, %REV%); gsub(/\$\$BUILD\$\$/, %BUILD%); print }” version_info.hdr > version_info.h
Now version_info.h is ready for inclusion:
#define PRODUCTNAME "Your product for Windows\0" #define PRODUCTVER 1, 0, 10260, 231 #define FILEVER PRODUCTVER #define STRPRODUCTVER "1.0.10260.231" #define STRFILEVER STRPRODUCTVER
Finally, a typical RC using our generated build numbers in my project is as:
#include "version_info.h" // Generated above
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
1 VERSIONINFO
FILEVERSION FILEVER
PRODUCTVERSION PRODUCTVER
FILEFLAGSMASK 0x0L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "my company name\0"
VALUE "FileDescription", "file desc\0"
VALUE "FileVersion", STRFILEVER
VALUE "InternalName", "binname.exe\0"
VALUE "LegalCopyright", "(c) copyright\0"
VALUE "OriginalFilename", "binname.exe\0"
VALUE "ProductName", PRODUCTNAME
VALUE "ProductVersion", STRPRODUCTVER
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END
And that’s all!

Powered by ScribeFire.