Zach Burlingame
Programming, Computers, and Other Notes on Technology

Posts Tagged ‘C’

Integrating the Subversion Revision into the Version Automatically with Native C/C++

Monday, March 7th, 2011

We’ve covered how to automatically include the mercurial revision into the version. Now how about doing this for Subversion?

The basics of how to accomplish this are much the same as in the mercurial example. The major difference is that I don’t use WSH to generate the version file. Unlike TortoiseHg, which includes the CLI tools for Mercurial, TortoiseSVN does not. Therefore you have two choices:

  1. Download a CLI SVN implementation like SlikSVN and implement a script similar to the one that we did for Hg.
  2. Use the SubWCRev.exe utility that comes with TortoiseSVN to generate your version file from a template

Since I already use TortoiseSVN and SubWCRev has already done the heavy-lifting for me, I’ve decided to go that route. As usual, I’ll be doing my example using Visual Studio 2010 Professional although these techniques should work on other versions of VS as well.

The source code for this post is available here.

Step 1: Setup Basic Versioning

First we need to have the basic building blocks in place, as discussed in Versioning a Native C/C++ Binary with Visual Studio

Step 2: Add an SVN Version Template

Next we create a file named svn_version.h.tmpl in our project. I generally keep it under the Resources filter. This file serves as the basis of the SVN version header that we ultimately want to include in our project. The tokens (e.g. $WCREV$) will be automatically replaced by the SubWCRev utility later.

#ifndef _SVN_VERSION_H_
#define _SVN_VERSION_H_

#define SVN_LOCAL_MODIFICATIONS $WCMODS?1:0$  // 1 if there are modifications to the local working copy, 0 otherwise
#define SVN_REVISION            $WCREV$       // Highest committed revision number in the working copy
#define SVN_TIME_NOW            $WCNOW$       // Current system date & time

#endif

Step 3: Add the Pre-build Step

Finally we need to add a pre-build step which will execute the SubWCRev tool, thus generating the svn_version.h file prior to the binary being built.

  1. Right-click on your project
  2. Select Properties
  3. Click Build Events
  4. Click Pre-Build Event
  5. In the Configuration drop-down, select All Configurations
  6. In the Command Line field, enter:
    SubWCRev.exe $(SolutionDir) $(ProjectDir)\svn_version.h.tmpl $(ProjectDir)\svn_version.h
  7. In the Description field, add a comment such as:
    Generate the svn_version.h file with the necessary repo identify info for versioning

NOTE:If you have multiple projects in the same solution that all need to use the same information from Subversion you have a few choices. One is to put the template and pre-build event in one project which all the other projects depend on and add a link to the svn_version.h file that gets generated. Another option is to create a specific version project that all it does is generate the svn_version.h file and define common version information and then have every project in the solution depends on it so it’s executed first in the build order.

Step 4: Update Version.h

The version.h file we created in the basic versioning post needs to be updated to include the generated svn_version.h file and to make use of its defines.

#include "svn_version.h"

#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)

#define VERSION_MAJOR               1
#define VERSION_MINOR               0
#define VERSION_REVISION            0
#define VERSION_BUILD               SVN_REVISION

#if SVN_LOCAL_MODIFICATIONS
  #define VERSION_MODIFIER "M"
#else
  #define VERSION_MODIFIER
#endif

#define VER_FILE_DESCRIPTION_STR    "Built " STRINGIZE(SVN_TIME_NOW) " from r" STRINGIZE(SVN_REVISION)
#define VER_FILE_VERSION            VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
#define VER_FILE_VERSION_STR        STRINGIZE(VERSION_MAJOR)        \
                                    "." STRINGIZE(VERSION_MINOR)    \
                                    "." STRINGIZE(VERSION_REVISION) \
                                    "." STRINGIZE(VERSION_BUILD)    \
                                    VERSION_MODIFIER

#define VER_PRODUCTNAME_STR         "c_svn_autoversion"
#define VER_PRODUCT_VERSION         VER_FILE_VERSION
#define VER_PRODUCT_VERSION_STR     VER_FILE_VERSION_STR

#if LIBRARY_EXPORTS
  #define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".dll"
#else
  #define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".exe"
#endif
#define VER_INTERNAL_NAME_STR       VER_ORIGINAL_FILENAME_STR

#define VER_COPYRIGHT_STR           "Copyright (C) 2011"

#ifdef _DEBUG
  #define VER_VER_DEBUG             VS_FF_DEBUG
#else
  #define VER_VER_DEBUG             0
#endif

#define VER_FILEOS                  VOS_NT_WINDOWS32
#define VER_FILEFLAGS               VER_VER_DEBUG

#if LIBRARY_EXPORTS
  #define VER_FILETYPE              VFT_DLL
#else
  #define VER_FILETYPE              VFT_APP
#endif

Step 6: Add SVN Ignore on svn_version.h

Finally, we need to go ahead and add an svn:ignore on svn_version.h because we don’t want its difference checking in every commit and we don’t want it constantly marking the working copy as modified if it’s tracked.

Results

Now when each time you build your projects, the latest Subversion information of the working copy is automatically included in the file version information.

Version info with Subversion

Version info with Subversion

Final Thoughts

So there you have it, you can now automatically include all the necessary information from your Subversion working copy in your build to map them one-to-one with the source code that was used.

Other Posts in this Series

  1. Mapping Binaries in the Field to Source Code in the Repository
  2. Versioning a Native C/C++ Binary with Visual Studio
  3. Versioning a .NET Assembly with Visual Studio
  4. Integrating the Mercurial Revision into the Version Automatically with Native C/C++
  5. Integrating the Mercurial Revision into the Version Automatically with .NET
  6. Integrating the Subversion Revision into the Version Automatically with Native C/C++
  7. Integrating the Subversion Revision into the Version Automatically with .NET

Integrating the Mercurial Revision into the Version Automatically with Native C/C++

Tuesday, February 15th, 2011

Now that we’ve covered why we should include version information in our binaries and how to do that for native C/C++ and managed .NET projects, it’s time to up our game. One of the major shortfalls of the previous solutions is that the version information on the binary didn’t map one-to-one to revisions from source control. As a result, while you may be able to determine the version of a particular file, you can’t easily get to the corresponding source code, if at all. One solution to this is to include the source control revision information in the version so you know exactly what was used to build it. Here I’ll be building on the previous posts and discussing how to do this with Visual Studio 2010 Professional and Mercurial using TortoiseHg for a native C/C++ application. Other versions of Visual Studio should work similarly and other Mercurial packages will work as well, as long as they provide command-line tools that are in the path of your IDE. The steps can be easily modified for use with a .NET project based on the previous post.

Update 2011/02/17: Doing this for a .NET assembly is actually a bit trickier than I originally thought because you can’t use static variables or class data in the assembly attributes – you have to use constant literals or expressions. I’ll do a separate post on how to do this with a .NET project.

The basic concept of how this works is explained in the here. The source code for this post is available here.

Step 1: Using WSH to Generate a Version Header

The first step is to add a file to your project named hg_version.jse. Personally, I add this file under the Resource filter of my project. Copy the code below in to the script file. This code does two things:

  1. Creates hg_version.h
  2. Extracts the desired Mercurial version info from the working copy and places it in the header

The extracted version info includes the full node identity, the short node identity, the revision number, and if there are any local modifications to the working copy.

var fso   = new ActiveXObject("Scripting.FileSystemObject");
var shell = new ActiveXObject("WScript.Shell");

var outFile = fso.CreateTextFile("hg_version.h", true);

var hgRevNum               = shell.Exec("hg identify --num");
var rev                    = hgRevNum.StdOut.ReadAll();
var hg_revision            = String(rev).replace(/\n/g,"").replace(/\+/g,"");
var hg_local_modifications = 0
if( String(rev).replace(/\n/g, "").indexOf("+") != -1 )
{
   hg_local_modifications = 1;
}
outFile.WriteLine( "#define HG_REVISION               " + hg_revision );
outFile.WriteLine( "#define HG_LOCAL_MODIFICATIONS    " + hg_local_modifications );

var hgChangeset  = shell.Exec("hg parents --template \"{node}\"");
var changeset    = hgChangeset.StdOut.ReadAll();
var hg_changeset = String(changeset).replace(/\n/g,"");
outFile.WriteLine( "#define HG_CHANGESET              \"" + hg_changeset +"\"" );

var hgChangesetShort    = shell.Exec("hg parents --template \"{node|short}\"");
var changeset_short     = hgChangesetShort.StdOut.ReadAll();
var hg_changeset_short  = "#define HG_CHANGESET_SHORT        \"" + String(changeset_short).replace(/\n/g,"") + "\"";
outFile.WriteLine( hg_changeset_short );

outFile.Close();

Step 2: Update Version.h

The version.h file we created in a previous post needs to be updated to use the information from the generated header. The mercurial revision number is a human friendly integer and can be used directly in the file version. However, due to the nature of a DVCS it is not guaranteed to be globally unique (and it often won’t be on projects with multiple developers using common workflow patterns). The node identity however, does uniquely identify the changeset globally. We include this information in the file description field which maps the binaries one-to-one with the source code they were built with. It’s important to note that the identity field is not a 16-bit integer and thus cannot be used in the file version field directly. Finally, we want to know if the binary was built with local modifications to the working copy, which would complicate reproducing the build. As a result we append an ‘M’ to the end of the file version string if local modifications are present.

#include "hg_version.h"

#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)

#define VERSION_MAJOR               1
#define VERSION_MINOR               0
#define VERSION_REVISION            0
#define VERSION_BUILD               HG_REVISION

#if HG_LOCAL_MODIFICATIONS
  #define VERSION_MODIFIER "M"
#else
  #define VERSION_MODIFIER
#endif

#define VER_FILE_DESCRIPTION_STR    HG_CHANGESET
#define VER_FILE_VERSION            VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
#define VER_FILE_VERSION_STR        STRINGIZE(VERSION_MAJOR)        \
                                    "." STRINGIZE(VERSION_MINOR)    \
                                    "." STRINGIZE(VERSION_REVISION) \
                                    "." STRINGIZE(VERSION_BUILD)    \
                                    VERSION_MODIFIER

#define VER_PRODUCTNAME_STR         "c_hg_autoversion"
#define VER_PRODUCT_VERSION         VER_FILE_VERSION
#define VER_PRODUCT_VERSION_STR     VER_FILE_VERSION_STR

#if LIBRARY_EXPORTS
  #define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".dll"
#else
  #define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".exe"
#endif
#define VER_INTERNAL_NAME_STR       VER_ORIGINAL_FILENAME_STR

#define VER_COPYRIGHT_STR           "Copyright (C) 2011"

#ifdef _DEBUG
  #define VER_VER_DEBUG             VS_FF_DEBUG
#else
  #define VER_VER_DEBUG             0
#endif

#define VER_FILEOS                  VOS_NT_WINDOWS32
#define VER_FILEFLAGS               VER_VER_DEBUG

#if LIBRARY_EXPORTS
  #define VER_FILETYPE              VFT_DLL
#else
  #define VER_FILETYPE              VFT_APP
#endif

Step 3: Add the Pre-build Step

Finally we need to add a pre-build step which will execute the hg_version.jse script, thus generating the hg_version.h file prior to the binary being built.

  1. Right-click on your project
  2. Select Properties
  3. Click Build Events
  4. Click Pre-Build Event
  5. In the Configuration drop-down, select All Configurations
  6. In the Command Line field, enter:
    cscript.exe hg_version.jse
  7. In the Description field, add a comment such as:
    Generate the hg_version.h file with the necessary repo identify info for versioning

NOTE:If you have multiple projects in the same solution that all need to use the same information from Mercurial you have a few choices. One is to put the hg_version.jse script in one project which all the other projects depend on and add a link to the hg_version.h file. Another option is to create a specific version project that all it does is generate the hg_version.h file and define common version information and then have every project in the solution depends on it so it’s executed first in the build order.

Results

Now when each time you build your projects, the latest Mercurial information of the working copy is automatically included in the file version information.

Version Info with Mercurial

Version Info with Mercurial

Final Thoughts

So there you have it, you can now automatically include all the necessary information from your Mercurial working copy in your build to map them one-to-one with the source code that was used. In an upcoming post I’ll discuss how to do this using Subversion as your VCS.

Other Posts in this Series

  1. Mapping Binaries in the Field to Source Code in the Repository
  2. Versioning a Native C/C++ Binary with Visual Studio
  3. Versioning a .NET Assembly with Visual Studio
  4. Integrating the Mercurial Revision into the Version Automatically with Native C/C++
  5. Integrating the Mercurial Revision into the Version Automatically with .NET
  6. Integrating the Subversion Revision into the Version Automatically with Native C/C++
  7. Integrating the Subversion Revision into the Version Automatically with .NET

Versioning a Native C/C++ Binary with Visual Studio

Monday, February 7th, 2011

Last time in  Mapping Binaries in the Field to Source Code in the Repository we talked about the value of including version information in your binaries. Today I’m going to explain how to accomplish this in Visual Studio for a native C/C++ binary. I’m using 2010 Professional, but it should work on other versions as well.

The source code is available here.

Step 1: Add a Version Resource

  1. Right-click on your project
  2. Select Add->Resource
  3. Select Version
  4. Click New

This will give you two files: resource.h and <project_name>.rc. I generally rename the .rc file to be version.rc

Step 2: Updating Version.rc

Out of the box the version.rc file will have you define the values right there. I recommend that you instead define the values in a version.h file and use those defines in the version.rc file.

Edit the resource file in a text editor:

  1. In the Solution Explorer, Right-click on version.rc
  2. Select Open With
  3. Select C++ Source Code Editor
  4. Scroll down to the Version section

Here is a template Version section that I frequently use (and will build on below):

/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
 FILEVERSION        VER_FILE_VERSION
 PRODUCTVERSION     VER_PRODUCT_VERSION
 FILEFLAGSMASK      0x3fL
 FILEFLAGS          VER_FILEFLAGS
 FILEOS             VER_FILEOS
 FILETYPE           VER_FILETYPE
 FILESUBTYPE        0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "FileDescription",  VER_FILE_DESCRIPTION_STR "\0"
            VALUE "FileVersion",      VER_FILE_VERSION_STR "\0"
            VALUE "InternalName",     VER_INTERNAL_NAME_STR "\0"
            VALUE "LegalCopyright",   VER_COPYRIGHT_STR "\0"
            VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR "\0"
            VALUE "ProductName",      VER_PRODUCTNAME_STR
            VALUE "ProductVersion",   VER_PRODUCT_VERSION_STR "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

Step 3: Adding a Version Header

Next we create a file named version.h to provide a more convenient location to set the various version information. This is especially useful if you are sharing version information across multiple projects in a single solution. Here’s the information I generally start with mine:


#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)

#define VERSION_MAJOR               1
#define VERSION_MINOR               0
#define VERSION_REVISION            0
#define VERSION_BUILD               0

#define VER_FILE_DESCRIPTION_STR    "Description"
#define VER_FILE_VERSION            VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
#define VER_FILE_VERSION_STR        STRINGIZE(VERSION_MAJOR)        \
                                    "." STRINGIZE(VERSION_MINOR)    \
                                    "." STRINGIZE(VERSION_REVISION) \
                                    "." STRINGIZE(VERSION_BUILD)    \

#define VER_PRODUCTNAME_STR         "c_version_binary"
#define VER_PRODUCT_VERSION         VER_FILE_VERSION
#define VER_PRODUCT_VERSION_STR     VER_FILE_VERSION_STR
#define VER_ORIGINAL_FILENAME_STR   VER_PRODUCTNAME_STR ".exe"
#define VER_INTERNAL_NAME_STR       VER_ORIGINAL_FILENAME_STR
#define VER_COPYRIGHT_STR           "Copyright (C) 2011"

#ifdef _DEBUG
  #define VER_VER_DEBUG             VS_FF_DEBUG
#else
  #define VER_VER_DEBUG             0
#endif

#define VER_FILEOS                  VOS_NT_WINDOWS32
#define VER_FILEFLAGS               VER_VER_DEBUG
#define VER_FILETYPE                VFT_APP

Step 4: Add the Necessary Include

The final step is to add the necessary include line to the version.rc file for the version.h file:

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#include "version.h"

#define APSTUDIO_READONLY_SYMBOLS

Results

Now when you build your application, all the version info above will be defined in the binary itself. Much of this information is available directly from the Details pane of the Properties window in Explorer.

Version Info

Version Info

Final Thoughts

Note that this information is static from build to build. You must change the version numbering yourself or use a script to auto-increment values. I’ll be discussing how to make the BUILD number automatically correspond to the revision info from the working copy of the source code using Mercurial or Subversion in an upcoming post.

Other Posts in this Series

  1. Mapping Binaries in the Field to Source Code in the Repository
  2. Versioning a Native C/C++ Binary with Visual Studio
  3. Versioning a .NET Assembly with Visual Studio
  4. Integrating the Mercurial Revision into the Version Automatically with Native C/C++
  5. Integrating the Mercurial Revision into the Version Automatically with .NET
  6. Integrating the Subversion Revision into the Version Automatically with Native C/C++
  7. Integrating the Subversion Revision into the Version Automatically with .NET