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:
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