⬆️ ⬇️

Simple automation of versioning and building a C / C ++ project in Ruby

Suppose that, as in my case, you first encountered the need

minimizing the movement on the way from the SVN source to the NSIS installer with associated auto-increment version of the project. In manual mode, it looks like this:



The vulnerability of this practice is found during rush jobs, when one or several of these items are missing, as a result of which the user is left with unmatched components.



Without pretending to be new, the scripts offered below automate the assembly of C / C ++ Visual Studio projects with almost one click and will be useful, first of all, in a single development.



It assumes the use of the ABCD format, where A and B rarely and manually change, and the following two parameters are updated with each release compilation:



The first script updates the version of the file through a special header, which, connecting to the resource file .rc, changes the attributes of the compiled project file. The second script uses them to generate the NSIS installer, the name of which will reflect all the information about the current version, for example, MyApp-1.0.837.1.exe. This guarantees the automation of the above assembly steps.

')

Autoincrementing implementation



To get the SVN revision number, we will use the base utility SubWCRev.exe, and for the autoincrement of the build number we cannot do without the Pre-Build Event, as it was done, for example, here or here .



Create a VersionInfo.h file:

#ifndef __VERSION_INFO_H #define __VERSION_INFO_H #define APP_NAME "MyApp" #define APP_VERSION 1,0,,0 #define APP_VERSION_S "" #define APP_DATE "" #endif 


and connect it to the .rc file (indicating the path where it was saved):

 #include "../src/VersionInfo.h" 


Change the version of the final binaries, which will be needed later for .nsi:

  FILEVERSION APP_VERSION PRODUCTVERSION APP_VERSION 


In the task of the script VersionBuild.rb after each execution

 C:\Ruby193\bin\ruby VersionBuild.rb VersionInfo.h 


includes modifying VersionInfo.h by inserting special keywords $ WCREV $ and $ WCNOW = $ from SubWCRev.exe:

 #define APP_VERSION 1,0,$WCREV$,1 #define APP_VERSION_S "1.0.$WCREV$.1" #define APP_DATE "$WCNOW=%d.%m.%Y %H:%M$" 


 #define APP_VERSION 1,0,$WCREV$,2 #define APP_VERSION_S "1.0.$WCREV$.2" #define APP_DATE "$WCNOW=%d.%m.%Y %H:%M$" 


 ... 


Subsequent call

 SubWCRev.exe .. VersionInfo.h VersionInfo.h 


substitute actual values ​​of the SVN revision number and date, for example

 #define APP_VERSION 1,0,29,2 #define APP_VERSION_S "1.0.29.2" #define APP_DATE "01.11.2012 20:09" 


When implementing VersionBuild.rb, regular expressions are used so that formatting is preserved (spaces / tabs):

 FNAME = ARGV[0] file = File::read(FNAME) ANY_IN_QUOTES = Regexp.new('"[^"]*"') def replaceVersion(file) #   APP_VERSION matcher = Regexp.new('APP_VERSION\s*[^\r\n]*') line = file.match(matcher)[0] old_ver = /(\d+),(\d+),(.*),(\d+)/ line.match(old_ver) new_ver = [$1, $2, "$WCREV$", $4.to_i + 1] line.sub!(old_ver, new_ver.join(",")) file.sub!(matcher, line) #   APP_VERSION_S matcher = Regexp.new('APP_VERSION_S\s*' + ANY_IN_QUOTES.source) line = file.match(matcher)[0] line.sub!(ANY_IN_QUOTES, '"' + new_ver.join(".") + '"') file.sub!(matcher, line) end def replaceDate(file) #   APP_DATE matcher = Regexp.new('APP_DATE\s*' + ANY_IN_QUOTES.source) date = file.match(matcher)[0] date.sub!(ANY_IN_QUOTES, '"$WCNOW=%d.%m.%Y %H:%M$"') file.sub!(matcher, date) end replaceVersion(file) replaceDate(file) File::write(FNAME, file) 


To avoid an increase in compile time in a large project, it is recommended not to include VersionInfo.h in the project headers, but to add additional proxy files:

 #include "Version.h" #include "VersionInfo.h" const char *Version() { return APP_VERSION_S; } const char *BuildDate() { return APP_DATE; } 




Setup implementation



Below is a standard NSIS file for installing one exe (located in the same folder) with the creation of shortcuts on the desktop and in Start.

 !include "MUI.nsh" ;     ;!define APP_NAME "MyApp" ;!define MAJOR_VERSION "1" ;!define MINOR_VERSION "0" ;!define SVN_REVISION "29" ;!define BUILD_NUMBER "2" ;!define APP_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${SVN_REVISION}.${BUILD_NUMBER}" ;!define SETUP_NAME "${APP_NAME}-${APP_VERSION}.exe" Name ${SETUP_NAME} OutFile ${SETUP_NAME} InstallDir $PROGRAMFILES\${APP_NAME} VIProductVersion ${APP_VERSION} VIAddVersionKey "ProductName" ${APP_NAME} VIAddVersionKey "Comments" "" VIAddVersionKey "CompanyName" "" VIAddVersionKey "LegalTrademarks" "" VIAddVersionKey "LegalCopyright" "© " VIAddVersionKey "FileDescription" ${APP_NAME} VIAddVersionKey "FileVersion" ${APP_VERSION} !define MUI_ABORTWARNING !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH !insertmacro MUI_LANGUAGE "Russian" Section "${APP_NAME} (required)" SetOutPath $INSTDIR File "${APP_NAME}.exe" WriteUninstaller "uninstall.exe" SectionEnd Section "Uninstall" Delete "$INSTDIR\*.*" Delete "$SMPROGRAMS\${APP_NAME}\*.*" Delete "$DESKTOP\${APP_NAME}.lnk" RMDir "$SMPROGRAMS\${APP_NAME}" RMDir "$INSTDIR" SectionEnd Section "Start Menu and Desktop Shortcuts" CreateDirectory "$SMPROGRAMS\${APP_NAME}" CreateShortCut "$SMPROGRAMS\${APP_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_NAME}.exe" SectionEnd 


And instead of manually setting the constants! Define, we use the transfer of the constants APP_NAME and APP_VERSION to the makensis.exe utility. This is done by the SetupBuild.rb script, which takes the name of the compiled binary file as input:

 C:\Ruby193\bin\ruby SetupBuild.rb MyApp.exe 


For example, if after compiling MyApp.exe it had APP_VERSION_S = "1.0.29.2", then the script will request the string version from the file attributes and, if successful, will create the installation file MyApp-1.0.29.2.exe, otherwise the log file from NSIS will be opened notepad.



SetupBuild.rb source code:

 NSIS_MAKE = '"C:\Program Files (x86)\NSIS\makensis.exe"' require 'win32ole' def versionOf(fname) fso = WIN32OLE.new("Scripting.FileSystemObject") return fso.GetFileVersion(fname) end def alert(msg) fso = WIN32OLE.new("WScript.Shell") fso.Popup msg end SRC_FNAME = File::expand_path(ARGV[0]) APP_NAME = File::basename(SRC_FNAME, '.*') params = Hash.new params['APP_NAME'] = APP_NAME params['APP_VERSION'] = versionOf(SRC_FNAME) params['SETUP_NAME'] = "#{APP_NAME}-#{params['APP_VERSION']}.exe" # /D        NSIS ( !define) cmd = params.map{|key, val| "/D#{key}=#{val}"}.join(" ") LOG_FNAME = "#{APP_NAME}_#{$0}.log" r = system("#{NSIS_MAKE} #{cmd} #{APP_NAME}.nsi > #{LOG_FNAME}") if r File::delete(LOG_FNAME) else system('notepad', LOG_FNAME) end 




Using



The test SVN project at Visual Studio 2008 is here .



To install required:



For release in Project-> Properties-> Build Events-> Pre-Build Event-> Command Line is set

 C:\Ruby193\bin\ruby "..\src\VersionBuild.rb" "..\src\VersionInfo.h" SubWCRev.exe .. "..\src\VersionInfo.h" "..\src\VersionInfo.h" 


When running setup \ SetupBuild.bat with contents

 C:\Ruby193\bin\ruby SetupBuild.rb MyApp.exe 


The setup file MyApp-1.0.xyexe will be generated, where x is the SVN version number, y is the sequential number of the compilation.



If you place SetupBuild.bat in the Post-Build Event, the installer will be built at each recompilation.

Source: https://habr.com/ru/post/157197/



All Articles