MAKEMSI quickly and reliably creates MSI files in a non-programmatic way
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
[Bottom][Contents][Prev]: VER_FILENAME.VER[Next]: Processing Mode - Developer or Production Build?
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
\->Source Code->Configuration / Options->MSI Build Numbers

MSI Build Numbers

This code demonstates how a build number processing can be used to alter the version details supplied in the version file. If you are looking at this you are probably also interested in the "Unattended MSI Builds" section.

For maximum flexability I have created the following generic component which doesn't need to know where build numbers come from or how they get added to version numbers):

;----------------------------------------------------------------------------
;--- Validate correct insertion of build number code (Version 07.156) -------
;----------------------------------------------------------------------------
#ifdef MAKEMSI_AFTER_VerFileGetProductInfo
    #error ^Sorry but this build number code needs to be inserted earlier (before MAKEMSI loaded)^
#endif


;----------------------------------------------------------------------------
;--- Insert build number generation code -----------------------------------
;----------------------------------------------------------------------------
#(  ''
    #define   MAKEMSI_AFTER_VerFileGetProductInfo   ;;Version 07.163

    #DependsOn INPUT "*Expires=NOW"             ;;Must always get new build number (probably don't really need...)
    #ifndef BUILD_NUMBER_READ
           #info ^Macro "BUILD_NUMBER_READ" is not defined^
    #elseif
           #info ^What is next build number (for version <$ProductVersion>)?^
           <$BUILD_NUMBER_READ BuildVerVar="@@BuildVer">
    #endif
    #ifndef BUILD_NUMBER_UPDATE_VERSION_NUMBER
           #info ^Macro "BUILD_NUMBER_UPDATE_VERSION_NUMBER" is not defined^
    #elseif
           #info ^Updating the version number with build <??@@BuildVer>^
           <$BUILD_NUMBER_UPDATE_VERSION_NUMBER BuildVerVar="@@BuildVer">
           #info ^Version number is now <$ProductVersion>^
    #endif
#)


;----------------------------------------------------------------------------
;--- When do we want to record the use of this build number -----------------
;----------------------------------------------------------------------------
#(
    #define   ONEXIT_AFTER_MSI_BUILT_AND_VALIDATED

    #ifndef BUILD_NUMBER_UPDATE
           #info ^Macro "BUILD_NUMBER_UPDATE" is not defined^
    #elseif
           #info ^Recording the build number <??@@BuildVer>^
           <$BUILD_NUMBER_UPDATE BuildVerVar="@@BuildVer">
    #endif
#)

The above code does nothing on its own and expects to find these macros:

  1. BUILD_NUMBER_READ
    This macro does whatever is required to build a formatted build number this build will use. It could read files, databases or produce a random one.

  2. BUILD_NUMBER_UPDATE
    This macro is invoked at the end of a successful build. It need not be defined if the build number is maintained external to MAKEMSI (and you simply "read" it). If MAKEMSI maintains it then this is your opportunity to record the value so you can "increment" the number for the next build.

  3. BUILD_NUMBER_UPDATE_VERSION_NUMBER
    This macro contains the code that determines how the build number (whole value has already been determined and formatted) is to be added to the version number. The example below simply appends it.

When you are happy with the code you'd generally add it to your personal MAKEMSI configuration file.

Example 1 - Store build number in a file

For a quick test, add the following code to the start of "tryme.mm" and after the MSI builds, the summary should contain the build number information and the version number in the msi will have a build number (4th) part.

This example reads a file if it exists. If we got a build number from the file for the same version of the product then we will increment this number else we will initialize the value to the first valid one, we will then format the value (pad to 4 digits):

#define? BuildFileDir             OUT
#define? NameOfBuildFile          <$BuildFileDir>\<$ProdInfo.ProductName>.B#
#define? BuildRange               1-9999
#define? BuildNumberMinDigits     4
#(
    ;--- Determine the build number we will use -----------------------------
    #define BUILD_NUMBER_READ           ;;A "hook" (Version 07.163)

    #evaluate "" "<$@@BUILD_NUMBER_READ {$?}>"
#)
#DefineRexx '@@BUILD_NUMBER_READ'
    ;--- Init ---------------------------------------------------------------
    parse value '<$BuildRange>' with @@BuildMin '-' @@BuildMax;
    @@BuildFile   = '<$NameOfBuildFile>';
    @@ProductName = '<$ProdInfo.ProductName>'
    @@MinDigits   = <$BuildNumberMinDigits>;
    @@OrigVer     = '<$ProductVersion>';
    parse var @@OrigVer @@VersionMaj '.' @@VersionMin '.' @@3 '.' @@NotExpected;
    if  @@3 = '' | @@NotExpected <> '' then
        error('The version number "' || @@OrigVer || '" is not correctly formatted.', 'It should be in 3 parts (Maj.Min.Whatever)')

    ;--- Do we have a build number recorded? --------------------------------
    if  FileQueryExists(@@BuildFile) = "" then
    do
        ;--- No file so start with the first number -------------------------
        {$BuildVerVar} = @@BuildMin;
    end;
    else
    do
        ;--- Read the first 2 lines of the file -----------------------------
        call Reading @@BuildFile;
        call stream @@BuildFile, 'c', 'close';
        @@BldFileVer = linein(@@BuildFile);
        {$BuildVerVar}   = linein(@@BuildFile)
        call stream @@BuildFile, 'c', 'close';

        ;--- Update the build number ----------------------------------------
        if  @@OrigVer <> @@BldFileVer then
        do
            ;--- Product version changed so start from 1st build number -----
            {$BuildVerVar} = @@BuildMin;
        end;
        else
        do
            ;--- The version hasn't changed Increment the build number ------
            {$BuildVerVar} = {$BuildVerVar} + 1;
            if  {$BuildVerVar} > @@BuildMax then
                error('The build number "' || {$BuildVerVar} || '" is not in the range <$BuildRange>!', 'Change version numbers to reset build number');
        end;
    end;

    ;--- Format the number --------------------------------------------------
    if  length({$BuildVerVar}) < @@MinDigits then
        {$BuildVerVar} = right({$BuildVerVar}, @@MinDigits, '0')
#DefineRexx

At the end of the build the following code will be called to record the build number we used so it can be incremented by the next invokation of the "BUILD_NUMBER_READ" code:

#(
    ;--- At end of build we wish to record build number ---------------------
    #define BUILD_NUMBER_UPDATE         ;;A "hook" (Version 07.163)

    #evaluate "" "<$@@BUILD_NUMBER_UPDATE {$?}>"
#)
#DefineRexx '@@BUILD_NUMBER_UPDATE'  ;;Call when you feel it is OK to update number
    call Making @@BuildFile;
    call FileDelete @@BuildFile;
    call lineout @@BuildFile, @@OrigVer;        ;;Product version (first 3 bits)
    call lineout @@BuildFile, {$BuildVerVar};    ;;Its build #
    call FileClose @@BuildFile
#DefineRexx

The following code says that we update the version number by appending a "." followed by the already formatted 4 digit build number.

#(
    ;--- At end of build we wish to record build number ---------------------
    #define BUILD_NUMBER_UPDATE_VERSION_NUMBER   ;;A "hook" (Version 07.163)

    #evaluate "" "<$@@BUILD_NUMBER_UPDATE_VERSION_NUMBER {$?}>"
#)
#DefineRexx '@@BUILD_NUMBER_UPDATE_VERSION_NUMBER'
    ;--- Get current version number, validate (in our scheme build number is 3rd/4th part) ---
    @@OrigVer = '<$ProductVersion>';
    parse var @@OrigVer @@VersionMaj '.' @@VersionMin '.' @@3 '.' @@NotExpected;
    if  @@NotExpected <> '' then
        error('The version number "' || @@OrigVer || '" is not correctly formatted.', 'It should be in 2 or 3 parts (Maj.Min[.Whatever])')

    ;--- Now add the build number to the end --------------------------------
    @@Ver  = @@OrigVer || '.' || {$BuildVerVar};
    call MacroSet 'ProductVersion', @@Ver, 'Y';

    ;--- Add to PPWIZARD summary at end of build ----------------------------
    call Summary "Build #", {$BuildVerVar};
#DefineRexx

Example 2 - Read build number from CruiseControl project file

The "CruiseControl" build program uses serialized object files (.ser) to store its configuration. As I don't know how to extract the build number "cleanly" (say via a tool to dump it as text), we will use a "dodgy brothers" technique (let me know if you know a better way!).

The contents of "BUILD_NUMBER_UPDATE_VERSION_NUMBER" is as per example 1. As "CruiseControl" maintains the correct build number we don't need to record anything so "BUILD_NUMBER_UPDATE" should not be defined. That leaves the reading (looks for "build." followed by build number then "sr") and formatting of the build number to this code:

#define? CruiseControl.Ser      <$ProductName>.ser    ;;Assume by default that CruiseControl project name matches the MSI name (and in same directory)
#define? CruiseControl.BEFORE   build.
#define? CruiseControl.AFTER    sr
#(
    ;--- Determine the build number we will use -----------------------------
    #define BUILD_NUMBER_READ           ;;A "hook" (Version 07.163)

    #evaluate "" "<$@@BUILD_NUMBER_READ {$?}>"
#)
#DefineRexx '@@BUILD_NUMBER_READ'
    ;--- Sorry don't know of a better way (help...) -------------------------
    @@SerFile = '<$CruiseControl.Ser>';
    call Reading @@SerFile
    call FileClose @@SerFile, "N";
    @@WholeFile = charin(@@SerFile, 1, 999999);
    call FileClose @@SerFile;
    parse value @@WholeFile with '<$CruiseControl.BEFORE>' @@SerBuildNumber '<$CruiseControl.AFTER>' @@ExpectSomethingHere;
    if  @@ExpectSomethingHere = '' then
        error('We did not manage to extract the build number from "' || @@SerFile || '"...');
    {$BuildVerVar} = @@SerBuildNumber
    if  length({$BuildVerVar}) < 4 then                 ;;Want minimum of 4 digits.
        {$BuildVerVar} = right({$BuildVerVar}, 4, '0')
#DefineRexx


Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.Please email me any feedback, additional information or corrections.
See this page online (look for updates)

[Top][Contents][Prev]: VER_FILENAME.VER[Next]: Processing Mode - Developer or Production Build?


MAKEMSI© is (C)opyright Dennis Bareis 2003-2008 (All rights reserved).
Saturday May 28 2022 at 3:11pm
Visit MAKEMSI's Home Page
Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.