A scheme for automatic build numbers in C/C++ projects

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.

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: