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

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

Comments

15 responses to “Integrating the Subversion Revision into the Version Automatically with Native C/C++”

  1. Mr T Avatar
    Mr T

    svn_version.h(8): fatal error RC1004: unexpected end of file found

    Is what I get. I set up your example now 3 times, I don’t think I make an mistake. Which VS Version are you using?

    1. ZachB Avatar

      I’m using Visual Studio 2010, although I’ve also used this solution with Visual Studio 2008 SP1 as well. Do you have a newline at the end of your svn_version.h and resource.rc files? Some versions of the resource compiler get hung up when a file doesn’t have a newline at the end of the file.

  2. Mr T Avatar
    Mr T

    Hey, thanks for the fast response.
    I’m using VS2010 as well and I have new lines at the end of the files. I also checked the headers content all over, I can’t find any mistake that would lead to this error. πŸ™ And I’m pretty sure if there were any errors in the header file which would prevent it from getting pre-processed than someone else would have complained bout it all ready.

    1. Mr T Avatar
      Mr T

      The tutorial (http://www.zachburlingame.com/2011/02/versioning-a-native-cc-binary-with-visual-studio/) is working fine, but as soon as I modify the version.h and include the svn_version.h I get this error message. :S

      1. ZachB Avatar

        What is on line 8 of svn_version.h?

      2. ZachB Avatar

        Also, did you copy and paste the headers from this webpage or from the bitbucket repository? I noticed that my last upgrade of WordPress messed up a bunch of source code blocks in my posts, including this one, replacing true ” characters with "

  3. David Avatar
    David

    What the diffrence between
    VER_FILE_VERSION (used in the header of the rc file)
    and
    VER_FILE_VERSION_STR (used more below in the rc file)

    Whatever I put in the VER_FILE_VERSION_STR, it can not see it when displaying the properties of the file.

    My idea is to put a “d” after the version whenever it is a debug version.

    1.0.0.9d (for debug version)
    1.0.0.9 (for release version)

    1. ZachB Avatar

      Literally the difference is that the VER_FILE_VERSION is of the format <short>,<short>,<short>,<short> while VER_FILE_VERSION_STR is a string form of that same information plus the modifiers such as ‘d’

      The reason for the difference is in how they are used. If you look in the .rc file you can see where I use VER_FILE_VERSION:

      VS_VERSION_INFO VERSIONINFO
       FILEVERSION        VER_FILE_VERSION
       PRODUCTVERSION     VER_PRODUCT_VERSION
      

      Microsoft requires those two fields from the fixed-info portion of VERSIONINFO to be in a specific format (see: VERSIONINFO resource)
      There is another section of VERSIONINFO which takes that information in string format, which is where I use the _STR versions and since they are in string format, I can include modifiers such as ‘d’.

      1. David Avatar
        David

        Thanks for the answer.

        But what I mean is whatever I put in the _STR version I never see it displayed in the properties screen.

        1. ZachB Avatar

          Ahh yes. Unfortunately, the file version field that is displayed by Explorer isn’t the string-name FileVersion (which is VER_FILE_VERSION_STR) but rather the fixed-info FILEVERSION (which is VER_FILE_VERSION). The “Product Version” shown in Explorer should be the VER_PRODUCT_VERSION_STR however, which is why I also place the modifiers in that field as well.

  4. T Smailus Avatar
    T Smailus

    Given the version is limited to 16 bit numbers per section xx.yy.zz.qq, if we use the SVN revision, once we get past 65535 as a repository revision, the svn version will overflow the FILEVERSION (and PRODUCTVERSION) numerical constraints.

    Any ideas how to solve this?

    1. ZachB Avatar

      Wow, that’s a lot of revisions! My initial thoughts are either to split the SVN revision number across the zz.qq fields or to truncate, for example placing only the least 16 bits of the SVN revision in the qq field. The thought being that there would be a significant amount of time between wraps of the revision number and thus, it would be easy to determine which SVN revision it corresponds to. For example, if 1.0.0.5015 => SVN r5015 and 10.0.0.5015 => SVN r70550, then given a binary with 10.0.0.5015, it would be easy to conclude that it must have come from an SVN revision that has wrapped. If you are willing to put a bit more scripting work into it, you could also truncate it on 10,000 decimal so the wrapped numbers would always be multiples of 10,000 making it easier to translate it. You would just inspect the 4 least significant digits of the SVN revision for a match. Finally, you could expand this “binary coded decimal” style to the zz field to get 1.0.0.5015 and 10.0.70.5015. Hope this helps!

  5. David AMAR Avatar
    David AMAR

    Thank you a lot for this article. I just wish to share a little variant with you.

    The issue I met was SubWCRev.exe that did not work outside a SVN working copy (after a SVN export),

    To address this, I made these little variations to your process:
    1) svn_version.h is set with default values and is not in ignore files. This file is not modified by developpers in a SVN trunk.
    2) I do not use Pre build compilation but a simple batch file that embed SubWCRev command.
    3) As a integrator, I call manually the batch file in a delivery branch in order to modify svn_version.h and after I commit it.

    So I can safely export a version and build the VC solution outside the working copy to produce the final release with the version number.

    1. ZachB Avatar

      Thank you David!

  6. MATT Avatar
    MATT

    Worked perfectly. Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.