I use maven, I have a maven-project with version X.Y.Z-SNAPSHOT, and I use maven-nsis-plugin. Because the Version is in format X.Y.Z-SNAPSHOT, I have to remove this suffix and repace it with a 0.
The maven plugin maven-nsis-plugin generates a project.nsh:
!define PROJECT_VERSION "4.23.9-SNAPSHOT"
which is used in my setup.nsi:
!include target\project.nsh
Section VersionReplace
Push "${PROJECT_VERSION}"
Push "-SNAPSHOT"
Push "0"
Call StrRep
Pop $0
!define VERSION_SHORT $0
SectionEnd
Name "Installer ${VERSION_SHORT}"
(...)
VIProductVersion ${VERSION_SHORT}
Problem: In the Console i can see:
Name: "Installer $0"
(...)
VIAddVersionKey: "ProductVersion" "$0"
so the $0 is not replaced. What am I doing wrong?
Replacement function used: StrRep
This can be done using the !searchreplace command, which runs at compile time
!searchreplace PROJECT_VERSION_SHORT ${PROJECT_VERSION} "-SNAPSHOT" ".0"
The Name and VIProductVersion are installer attributes that are taken into account at compile time while creating the .exe
The StrRep function will be called at runtime when passing into the section VersionReplace, and it will be too late to change Name or VIProductVersion.
BTW: if you want to define some value at runtime like in your !define VERSION_SHORT $0 statement, create a variable with the Var statement and modify the variable (with StrCpy). A !define is a string replacement defined during compilation that cannot change. What you have actually written is that VERSION_SHORT is an alias for $0.
Related
I would like to dynamically name my EXE files based on the folder in which they're being built. So, in other words, if my NSIS script is located in C:\[blah]\MyProjectDir\Nullsoft\ and my project is located in C:\[blah]\MyProjectDir\MyProject\ the output NSIS executable should be MyProjectDir.exe. I'm obviously obfuscating the names for simplicity, the structure of the folders has actual meaning to our team.
Basically the our (already written, ages ago) NSIS scripts look something like this:
!define APP_NAME "OurGUI"
!define PROJ_NAME "OurGUI"
!define PATH_TO_FILES ..\${PROJ_NAME}\bin\Debug
!define EXEC_NAME "OurGUI.exe"
OutFile "GUI_Setup.exe"
There's a whole bunch more that handle things like registry keys, installation, etc... this is the important part as it pertains to my question (because I don't want to modify the rest of the script).
I'd like to change it to something like this
!define M_NUMBER !system "for %I in (..) do echo %~nxI"
!define APP_NAME "${M_NUMBER}/ Demo GUI"
!define PROJ_NAME "OurGUI"
!define PATH_TO_FILES ..\${PROJ_NAME}\bin\Debug
!define EXEC_NAME "M_Number.exe"
OutFile "${M_NUMBER}_GUI_Setup.exe"
I know my syntax is totally off, but that's the idea.
Running the command for %I in (..) do echo %~nxI returns to me the name of the grandparent directory, which is exactly what I want. Now I need to figure out how to store it in some sort of variable so that it can be used in the !define's. I need to somehow capture the output to the console (not the return value of for, which is always zero, within my NSIS script. Ideally I wouldn't have to use external plugins because that'll be a nightmare to manage across the team.
As a nice bonus, I'd also love to be able to manipulate the string (again, at compile time so it can be used in the !define's.
The strings follow the format: SWD1234-567, but I would like to store it as D1234567 in another variable for other renaming purposes. So, drop the "SW" and the "-" from the original string.
I'm open to other suggestions, but as a newbie to NSIS, this is what I've come up with.
You need to use the !system+!include idiom to read the output of external commands:
!tempfile MYINCFILE
!system 'for %I in (..) do echo !define MYDIR "%~nxI" > "${MYINCFILE}"'
!include "${MYINCFILE}"
!delfile "${MYINCFILE}"
!echo "${MYDIR}"
The pre-processor has limited string handling but removing known sub-strings is possible:
!define MYDIR "SWD1234-567"
!searchreplace MYDIR2 "${MYDIR}" "SW" ""
!searchreplace MYDIR2 "${MYDIR2}" "-" ""
!echo "${MYDIR2}" ; D1234567
I have .ini file that contains a file version (like X.X.X.X).
So, I used ReadINIStr to get the version in 'var' format.
ReadINIStr $0 "C:\Users\a\Downloads\Info.ini" "Version" "VersionNumber"
StrCpy $varVersion $0
The above code results in the value of 'varVersion' being stored in the Version in the ini file.
So far, the values I want are well stored.
However, the value to be used for VIProductVersion must be a value using !define.
Can not use a variable other than !define?
If variable is not available, can I store the value of a particular entry in the ini file in !define?
I know that the contents of the file can be read using the !define /file command. However it is my intent to read only the entry value of the ini file.
VIProductVersion is an attribute stored in your installer .exe and can only be set at compile-time by makensis.
You can use any of the pre-processor instructions like !define and !searchparse to read/store the version but there is no instruction you can use to read .ini files. !searchparse can be used in a pinch if the .ini value name is only used once in the entire file because it does not understand .ini sections.
Here is my simple code:
!include "EnvVarUpdate.nsh"
Outfile "text.exe"
Section
${EnvVarUpdate} $0 "PATH" "A" "HKLM" "C:\Program Files\something"
SectionEnd
I understand that the "A" argument means this should APPEND the last argument to system path. However, testing this revealed that it overwrote my Path variable. Further tests reveal this is because Path was too long (>1024 chars, per the tutorial).
Is there a "safe" way to append to Path then? I am looking for a function that will append if Path is short enough, otherwise do nothing and report an error, something of that sort. I'm wondering if a standard method of doing this already exists. Thanks!
We have encountered some problems with path modifications from NSIS installers due to the fact that the default string management is limited to 1024 bytes and that string manipulations involved in path modification is truncating strings to 1024 and that is sometimes braking workstation environment (especially in development hosts where many tools are installed). BTW, There are many nsis built setups in the wild that are suffering from this problem.
We are using some different code derived from the AddToPath function from Path manipulation but the problem stays similar.
The best workaround that we are using until now is by using the special build of NSIS that provides the large string support (of 8kB instead of 1 kB). That build is available in the special builds page of the NSIS wiki.
Can you try this?
Section
ReadEnvStr $0 PATH
StrCpy $0 "$0;C:\Program Files\something"
StrLen $1 $0
${if} $1 < 1024
${EnvVarUpdate} $0 "PATH" "A" "HKLM" "C:\Program Files\something"
${else}
messagebox mb_ok "error writing environment variable"
${endIf}
SectionEnd
I have a file 'releaseVersionNumber.txt' which I read during my build process; currently its read for my Mac build but I want to read it in my Windows NSIS build to reduce the number of edit locations (duplication being evil)...
So I'm trying to replace:
!define VERSION 1.2.3
with something like
FileOpen $4 "..\releaseVersionNumber.txt" r
FileRead $4 $1
FileClose $4
!define VERSION ${1}
But I get an error command FileOpen not valid outside Section or Function. Wrapping it in a function I produces command call not valid outside Section or Function so I can't seem to do this in the installer setup, only at runtime.
Is there a way to achieve what I'm after?!
All commands begining with ! are compile time commands, so they are processed at compile time, much before your program runs.
You can try declaring VERSION as a Variable instead of a define:
Var VERSION
FileOpen $4 "..\releaseVersionNumber.txt" r
FileRead $4 $VERSION
FileClose $4
If you need VERSION to be a define, then you can try the /file parameter in !define.
!define /file VERSION "..\releaseVersionNumber.txt"
I like to have a version.nsh file with just the define:
!define VERSION "2013-03-25:16:23:50"
And then, I include it:
!include /NONFATAL version.nsh
# Default value in case no version.nsh is present
!ifndef VERSION
!define /date VERSION "%Y-%m-%d %H:%M:%S"
!endif
I have an executable on my disk-on-key in dir\program\prog.exe
I'd like to have a shortcut to the executable on the DoK's root directory, that is, prog.lnk would refer to dir\program\prog.exe.
However, it seems that prog.lnk can't have a relative target. This is a problem when the DoK will have different drive letters assigned to it, depending on which PC it's connected to.
Any suggestions, aside from the obvious one of putting prog.exe in the root dir?
(ultimately, I'd like to do this at install time using nsis)
Thanks,
Rony
If we assume cmd.exe would be on the same absolute path for all windows installations (probable, but not fool-proof) you can make out the .lnk file to start cmd like this
cmd.exe /c start /d. your command here
/d sets the directory to the directory of the .lnk file
There might be other useful options for the start command (e.g. /b)
While it is possible for shortcuts to contain a relative path to the target (.lnk files have a flag called SLDF_HAS_RELPATH) NSIS does not support creating anything other than "normal" shortcuts so you need to write the binary data directly (The .lnk format is pretty stable and has been documented by MS)
!macro FileWriteHexBytes h b
push ${h}
push ${b}
call FileWriteHexBytes
!macroend
Function FileWriteHexBytes
exch $9
exch
exch $0
push $1
push $2
loop:
StrCpy $2 $9 2
StrLen $1 $2
IntCmp $1 2 0 end
FileWriteByte $0 "0x$2"
StrCpy $9 $9 "" 2
goto loop
end:
pop $2
pop $1
pop $0
pop $9
FunctionEnd
Function CreateRelativeLnk
exch $9
exch
exch $0
push $1
FileOpen $0 "$0" w
StrCmp $0 "" clean
!insertmacro FileWriteHexBytes $0 "4C0000000114020000000000C000000000000046"
!insertmacro FileWriteHexBytes $0 48010400 ;flags
!insertmacro FileWriteHexBytes $0 00000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000
StrLen $1 $9 ;must be < 255!
FileWriteByte $0 $1
FileWriteByte $0 0
FileWrite $0 "$9" ;relative target path
!if 0
;The icon is problematic, does not seem like it works with relative paths (but you can use system icons...)
StrCpy $9 "explorer.exe"
StrLen $1 $9
FileWriteByte $0 $1
FileWriteByte $0 0
FileWrite $0 "$9"
!else
!insertmacro FileWriteHexBytes $0 05003e2e657865 ;fake default .exe icon
!endif
clean:
FileClose $0
pop $1
pop $0
pop $9
FunctionEnd
Call it like this:
push "$temp\testlink.lnk"
push "testdir\testapp.exe" ;full path to this is $temp\testdir\testapp.exe
call CreateRelativeLnk
While the generated .lnk seems to work, I'm not sure if I would use this in production code
A much better solution is to create a little NSIS app like Oleg suggests (NSIS applications can contain embedded data at the end of the .exe that it can read from itself at runtime etc..)
Install "Relative"
I'm using a bit of a hack. The approach is shown in this screenshot:
It starts explorer.exe and then passes a relative path like so:
%windir%\explorer.exe path\to\your\files\youFileName.example
I'm using a small tool called "Relative" for this. After you install it, you can right-click a file and then select Create relative shortcut.... It will then pop up a Save as... box. This is not quite as comfortable as simply dragging and dropping but it helps. (It also uses its own special icon for the links it creates. So you no longer have the original icon in the link. See above. You may or may not like this.)
You are right. You can not use shortcuts (.lnk) on a removable media, because like you wrote yourself the removable media can have different drive letters in different situations.
If you need to have something in a root directory of the drive one use a CMD, VBS or JS instead of a shortcut. In some situation one use also a standard EXE which use a config file like INI file to start another program from the other subdirectory. It is a typical practice for CD/DVD to implement starting of a setup program after inserting of the disk. Probably this way will be OK in for your requirements also?
I believe that the NT 4 Resource Kit command SHORTCUT.exe would create relative linked shortcuts. I wish that Microsoft would create a new supported utility or Powershell Cmdlet to facilitate the creation of relative .lnk files or make NTFS links work more like Linux symbolic links. Until then, I use .cmd/.bat and .ps1 files for this purpose.
Program.cmd
#"%~dp0Relative\Path\Program.exe" %*
# = Suppresses the command echo.
%~dp0 = expands to the script's directory.
Relative\Path = could include .. to backup a directory.
%* = passes any parameters received by the script on to Program.exe.
Program.ps1
Unfortunately, though .cmd/.bat files will run from any context (the run dialog, a CMD prompt, in Powershell, double clicking in File Explorer, etc), they cannot call things stored on a UNC path. For exposing things in a UNC path, in Powershell (I do this for tools like Git and Mercurial), I will create a Powershell version of the above script.
&"$PSScriptRoot\Relative\Path\Program.exe" #args
& = puts Powershell into command mode, so that the string in quotes gets ran.
"" = contains a string, expanding any variables.
$PSScriptRoot = expands to the script's directory.
Relative\Path = could include .. to backup a directory.
#args = passes any parameters received by the script on to Program.exe.