Tuesday, December 24, 2013

Versioning in NSIS installer

Versioning

Versioning is very important part of software development.
In today's world, when digital downloading is the most popular way to distribute software
products, installer version must be easily recognizable. Users should know what version of the application they are using now and what they can download for update.
Server software also need version information to properly display downloading options.
NSIS installers are very convenient for web distribution - all application files are compressed and packed in one executable. The only thing programmer need to do is add version information to installer file.

Hello world (version information in NSIS installer)

The simplest way is to use defines in NSIS script as in following example:
!define PRODUCT_MAJOR "1"
!define PRODUCT_MINOR "0"
!define PRODUCT_TIMESTAMP "13293"
!define PRODUCT_BUILD "1"
!define PRODUCT_VERSION "${PRODUCT_MAJOR}.${PRODUCT_MINOR}.${PRODUCT_TIMESTAMP}.${PRODUCT_BUILD}"
!define PRODUCT_NAME "AwesomeProgram"
!define OUT_FILE_NAME "${PPRODUCT_NAME}_${PRODUCT_VERSION}_setup.exe"
;set output file name for installer
OutFile "${OUT_FILE_NAME}"
After successful compilation of this nsi script file named "AwesomeProgram_1.0.13293.1_setup.exe" will be created.

Notice, that versioning model used in this example (and all other examples in this post too) combines four numbers:
  • Major version number
  • Minor version number
  • Time stamp in  %YEAR%%DAY_OF_YEAR% format
  • Build number

Automatic version increment

Main disadvantage of previous example is that version is absolutely static - all variables are defined in script file and you must change them manually every time you need to rebuild your installer.
It's good for a start, but eventually it will become very annoying. Also, static file name makes this code not very useful in any automatic build pipeline.
Hopefully, there are plenty of ways to automate version generation.
For example, you can get system time to produce unique timestamp:
!define PRODUCT_MAJOR "1"
!define PRODUCT_MINOR "0"
!define /date PRODUCT_TIMESTAMP "%y%j"
!define /date PRODUCT_BUILD "%H%M"
...
In this example !define statement is used with /date parameter to produce unique version number (current date and time). Every time script is compiled, unique names will be generated (well, if you will compile it twice within one minute, names will be the same, obviously).

More complex scripts

With proper usage of NSIS almost everything is possible. Lets assume that we want to update PRODUCT_TIMESTAMP constant every day and increment PRODUCT_BUILD each time NSIS script is compilled.
The following code example demonstrates how to use text file to save version information:
!define PRODUCT_MAJOR "1"
!define PRODUCT_MINOR "0"
!define /date PRODUCT_TIMESTAMP "%y%j"
!define BUILDFILE "LastBuild.txt"
!include "${BUILDFILE}"
!if ${LAST_TIMESTAMP} != ${PRODUCT_TIMESTAMP}
!define NEW_BUILD_NUMBER 0
!else
!define /math NEW_BUILD_NUMBER ${LAST_BUILD_NUMBER} + 1
!endif
!delfile "${BUILDFILE}"
!appendfile "${BUILDFILE}" "!define LAST_BUILD_NUMBER ${NEW_BUILD_NUMBER}$\n"
!appendfile "${BUILDFILE}" "!define LAST_TIMESTAMP ${PRODUCT_TIMESTAMP}$\n"
!define PRODUCT_BUILD "${NEW_BUILD_NUMBER}"
!undef BUILDFILE
!undef NEW_BUILD_NUMBER
!undef LAST_BUILD_NUMBER

File LastBuild.txt must contain two strings:
!define LAST_TIMESTAMP 13212
!define LAST_BUILD_NUMBER 0

Script will take last timestamp and build number from file, update them, and save in the same file again.

Using existing version file

Very often installer is created for application that already have its own version.
In this case installer version must be the same as program this installer is created for.
Lets assume that application version is stored in some source file of your program. The following code can be used to retrieve it when compiling NSIS script:
!define PATH_TO_SOURCE "d:\path\to\source"
!searchparse /file ${PATH_TO_SOURCE}\version.h `#define APP_VERSION ` MAJOR `,` MINOR `,` TIMESTAMP `,` BUILD

File vesion.h must contain following string:
#define APP_VERSION 1,0,13317,1437

For Microsoft Net applications, one can use the following searchparse:
!searchparse /file ${PATH_TO_SOURCE}\Properties\AssemblyInfo.cs `[assembly: AssemblyFileVersion("` PRODUCT_VERSION `")]`
You got the idea - any text file can be parsed for version constants.

File properties

NSIS have built in commands ( VIProductVersion and VIAddVersionKey) that can be used to set properties of the installer file on compile-time. Defined constants and variables can be used as parameters:
!define /date BUILD_YEAR "%Y"
!define PRODUCT_VERSION "${PRODUCT_MAJOR}.${PRODUCT_MINOR}.${PRODUCT_TIMESTAMP}.${PRODUCT_BUILD}"
!define PRODUCT_NAME "AwesomeProgram"
!define OUT_FILE_NAME "${PPRODUCT_NAME}_${PRODUCT_VERSION}_setup.exe"
;version info
VIProductVersion "${PRODUCT_VERSION}"
VIAddVersionKey "ProductName" "Awesome Program Installer"
VIAddVersionKey "OriginalFilename" "${OUT_FILE_NAME}"
VIAddVersionKey "CompanyName" "Awesomness Inc"
VIAddVersionKey "LegalCopyright" "Copyright Awesomness Inc ${BUILD_YEAR}"
VIAddVersionKey "FileDescription" "Awesome Program - simply awesome"
VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "InternalName" "AwesomeProgramBeta"

No comments:

Post a Comment