7

I am attempting to remove one more step in my application's release process by automatically retrieving versioning info from my executable (in this case, a .NET application).

Up to this point, I have been able to get by with a limited knowledge of NSIS, but I am quickly learning that this is not enough.

Step 1: Declare version info in executable

In AssemblyInfo.cs, I declare [assembly: AssemblyVersion("1.0.0.1")]. This successfully makes the version info appear in the compiled executable (under "File version" and "Product version").

Step 2: Retrieve version info from executable

According to this article on "GetFileVersion", importing "FileFunc.nsh" allows you to retrieve version info from an executable.

Code used:

Section
    Var /GLOBAL version
    ${GetFileVersion} "C:\test.exe" $version
    ...
SectionEnd

Step 3: Verify contents of function call

Based on section 5.1.7 of the documentation, I should be able to print to the command line during compile time using the "!echo" command. The difference between printing the contents of a variable (or a constant, etc) still confuses me, so I have tried all four of these options:

!echo $version
!echo "$version"
!echo "${version}"
!echo ${version}

This results in:

$version (InstallScript.nsi:15)
$version (InstallScript.nsi:16)
${version} (InstallScript.nsi:17)
${version} (InstallScript.nsi:18)

Step 4: Declare the installer metadata

Based on section 4.8.3, I should be able to add installer metadata via VIProductVersion and VIAddVersionKey.

VIProductVersion $version 
VIAddVersionKey "FileVersion" "$version"

In the built installer, this adds the string "$version" into the specified fields.


Is there a ToString() equivalent in NSIS? How can I access a variable's contents? Does the print of the variable name mean that it has no contents? How can I verify that GetFileVersion is called correctly, executes correctly, and returns a value?

user664939
  • 1,977
  • 2
  • 20
  • 35
  • 1
    The reason this won't work is that the ${GetFileVersion} gets executed at runtime. You can't use the contents of a variable in a thing that gets fixed at compile time. You could do it with a preprocessing executable. – Chris Morgan Nov 18 '11 at 22:09
  • @ChrisMorgan: Can you expand this comment and your alternative into an answer? – user664939 Nov 19 '11 at 06:00
  • Possible duplicate of [NSIS - put EXE version into name of installer](https://stackoverflow.com/questions/3039024/nsis-put-exe-version-into-name-of-installer) – default locale Feb 28 '19 at 05:13

2 Answers2

13

Edit: NSIS v3 now includes a !getdllversion preprocessor instruction, you only need the GetVersionLocal workaround if you are still using NSIS v2.

There are plans for a !getdllversionlocal in NSIS 2.47, for now you have to use this workaround:

outfile test.exe
requestexecutionlevel user

!macro GetVersionLocal file basedef
!verbose push
!verbose 1
!tempfile _GetVersionLocal_nsi
!tempfile _GetVersionLocal_exe
!appendfile "${_GetVersionLocal_nsi}" 'Outfile "${_GetVersionLocal_exe}"$\nRequestexecutionlevel user$\n'
!appendfile "${_GetVersionLocal_nsi}" 'Section$\n!define D "$"$\n!define N "${D}\n"$\n'
!appendfile "${_GetVersionLocal_nsi}" 'GetDLLVersion "${file}" $2 $4$\n'
!appendfile "${_GetVersionLocal_nsi}" 'IntOp $1 $2 / 0x00010000$\nIntOp $2 $2 & 0x0000FFFF$\n'
!appendfile "${_GetVersionLocal_nsi}" 'IntOp $3 $4 / 0x00010000$\nIntOp $4 $4 & 0x0000FFFF$\n'
!appendfile "${_GetVersionLocal_nsi}" 'FileOpen $0 "${_GetVersionLocal_nsi}" w$\nStrCpy $9 "${N}"$\n'
!appendfile "${_GetVersionLocal_nsi}" 'FileWrite $0 "!define ${basedef}1 $1$9"$\nFileWrite $0 "!define ${basedef}2 $2$9"$\n'
!appendfile "${_GetVersionLocal_nsi}" 'FileWrite $0 "!define ${basedef}3 $3$9"$\nFileWrite $0 "!define ${basedef}4 $4$9"$\n'
!appendfile "${_GetVersionLocal_nsi}" 'FileClose $0$\nSectionend$\n'
!system '"${NSISDIR}\makensis" -NOCD -NOCONFIG "${_GetVersionLocal_nsi}"' = 0
!system '"${_GetVersionLocal_exe}" /S' = 0
!delfile "${_GetVersionLocal_exe}"
!undef _GetVersionLocal_exe
!include "${_GetVersionLocal_nsi}"
!delfile "${_GetVersionLocal_nsi}"
!undef _GetVersionLocal_nsi
!verbose pop
!macroend

!insertmacro GetVersionLocal "$%windir%\Explorer.exe" MyVer_
VIProductVersion "${MyVer_1}.${MyVer_2}.${MyVer_3}.${MyVer_4}"
VIAddVersionKey "FileVersion" "${MyVer_1}.${MyVer_2}.${MyVer_3}.${MyVer_4}"

page instfiles
section
sectionend

This macro:

  1. Creates a temporary .nsi file
  2. Compiles the temporary .nsi to a temporary executable
  3. Runs the temporary .exe
  4. Deletes the two files (.nsi and .exe)
  5. Returns an array containing the version info of the specified executable.
Anders
  • 97,548
  • 12
  • 110
  • 164
  • There, I knew you'd do better, @Anders. That's why I didn't bother filling my comment out into an answer :-) – Chris Morgan Nov 19 '11 at 13:47
  • @Anders: Thank you for the answer. I was unable to work on this problem in the interim due to other priorities, but now I'm working on it again. From what I can tell, this macro creates a temporary nsi file, compiles it, runs it, and deletes the files. Is that correct? – user664939 Nov 29 '11 at 14:37
  • @user664939 It also includes the result but yes that is correct – Anders Nov 29 '11 at 15:57
  • @Anders: Tested and works. I edited in a version of my comment into your post. Please remove or change it if you feel it is inaccurate. – user664939 Nov 29 '11 at 17:30
  • Can someone explain how this is used please? – MoonKnight Oct 04 '16 at 11:10
  • @MoonKnight What do you mean "how this is used"? The example uses it. But NSIS v3 has a command for it now so there is no need for this workaround in the first place anymore... – Anders Oct 04 '16 at 22:02
  • I don't see where a custom .exe path is used in the above. Can you provide a reference to the new way of doing this please? – MoonKnight Oct 04 '16 at 22:04
  • @MoonKnight Look at the top of my answer for NSIS 3. For the macro when using NSIS 2 it is right there: !insertmacro GetVersionLocal "$%windir%\Explorer.exe" MyVer_ , just replace $%windir%\Explorer.exe with whatever path you want. You can change MyVer_ if you want as well... – Anders Oct 04 '16 at 22:10
  • Sorry about this. It's been a hell of a long day... Thanks very much for your time. – MoonKnight Oct 04 '16 at 22:11
10

Updated information:

NSIS now includes !getdllversion command which does the same thing. Usage:

!getdllversion "$%windir%\explorer.exe" expv_
!echo "Explorer.exe version is ${expv_1}.${expv_2}.${expv_3}.${expv_4}"

UPDATE: !getdllversion command is a "Compile Time Command", it's different from the GetDLLVersion instruction. IMO it should have been named as !getdllversionlocal to prevent confusion, since it's similar to GetDLLVersionLocal instruction, not the GetDLLVersion

In the user manual included in NSIS 3.0a1 it says:

5.1.14 !getdllversion

localfilename define_basename

This is similar to GetDLLVersionLocal, only it stores the version number in defines and can therefore be used anywhere, not just inside functions and sections.

!getdllversion "$%windir%\explorer.exe" expv_
!echo "Explorer.exe version is ${expv_1}.${expv_2}.${expv_3}.${expv_4}"
yclkvnc
  • 913
  • 8
  • 15
  • `GetDllVersion` runs on the *target machine* running the installer. He really wants `GetDLLVersionLocal`, which is evaluated on the building system. – EricLaw Sep 16 '13 at 15:15
  • 2
    @EricLaw: No, it's evaulated on the _building system_; from the NSIS User Manual: This is similar to GetDLLVersionLocal, only it stores the version number in defines and can therefore be used anywhere, not just inside functions and sections. Not: This section does't exist in online manual currently, only in user manual coming with the NSIS installation. – yclkvnc Sep 16 '13 at 15:22
  • 1
    You're right-- but that's really odd. The NSIS.chm file included in NSIS3.0a has no such information, and says: `GetDLLVersionLocal - This is similar to GetDLLVersion, only it acts on the system building the installer.` It doesn't really make any sense that `GetDllVersion` runs on the building system, but testing suggests that it seems to be true. – EricLaw Sep 16 '13 at 15:29
  • GetDLLVersion instruction runs on the target machine, but !getdllversion command runs on the building system. Command should have been named as !getdllversionlocal – yclkvnc Sep 16 '13 at 15:42
  • @EricLaw, I'm confused after seeing that you opened a ticket about this 4 years ago and thanked after !getdllversion is added in June. http://sourceforge.net/p/nsis/bugs/913/ . Am I missing something ? – yclkvnc Sep 16 '13 at 16:28
  • :-) I simply keep forgetting that `!getdllversion` exists and does something different than `getdllversion` – EricLaw Sep 16 '13 at 19:38
  • I think it's named just `!getdllversion` because as a compile-time command it's impossible to run it in a non-local environment (the target machine). Maybe that was the reason... – Andre Soares Mar 28 '16 at 11:15