Getting folder name in NSIS at compile time - windows

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

Related

NSIS - EnvVarUpdate overwrites system path when path is too long, is there a workaround?

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

Using NSIS command line to globally define product definitions

I'd like to control the branding for my NSIS installer by passing a parameter to makensis:
makensis.exe /DCOMPANY_X=1 installer.nsi
The following are the first few lines listed in my NSI file:
!ifdef ${COMPANY_X}
!define PRODUCT_NAME "Widget Pro"
!define PRODUCT_VERSION "v2.0"
!define PRODUCT_PUBLISHER "ACE Company"
!define PRODUCT_WEB_SITE "www.ace.com"
!define PRODUCT_COPYRIGHT "Copyright 2013 Ace"
!else
!define PRODUCT_NAME "Widget Maker"
!define PRODUCT_VERSION "v12.3"
!define PRODUCT_PUBLISHER "ACME CO"
!define PRODUCT_WEB_SITE "www.acme.com"
!define PRODUCT_COPYRIGHT "Copyright 2013 ACME"
!endif
I use these defines throughout my script.
The problem I'm encountering is that even with COMPANY_X defined on the command line, execution is passing through to the second block of defines (ACME).
Being new to NSIS, I'm sure there's a better way to handle this. I'd also like to use a switch statement to define multiple companies. If I do this, the compiler warns me that I need to place this code in a section or function.
One thing that might complicate a solution for this is that I'm signing my uinstaller with a two pass process:
http://nsis.sourceforge.net/Signing_an_Uninstaller
Kudos to the NSIS team and all the contributors. No way I'd ever go back to InstallShield.
Thanks in advance for any help.
!ifdef ${COMPANY_X} is actually going to check if 1 is defined!
The syntax is !ifdef name_of_define: !ifdef COMPANY_X
...or if the content of the define matters: !if ${COMPANY_X} = 1

How to use String Replace in NSIS

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.

Create more than 1 uninstaller in a NSIS Section

Can a NSIS Section create more than 1 uninstaller?
My installer can install plugins for 3 different versions of an Application - therefore theres 3 different directories where the installer will install the files.
In each of those directories I want to add an uninstaller file that will remove only the files in that directory.
Each of the 3 uninstall files are created within the same Section area, is this invalid? How can I get my script to create 3 uninstallers(if possible)?
The following Section only creates one uninstaller, the last one(Version 10 uninstaller):
Section "Install Plugin Files" MainSetup
CheckInstallVers8:
IntCmp $installVers8 1 InstallVersion8 CheckInstallVers9 InstallVersion8
CheckInstallVers9:
IntCmp $installVers9 1 InstallVersion9 CheckInstallVers10 InstallVersion9
CheckInstallVers10:
IntCmp $installVers10 1 InstallVersion10 MainInstallation InstallVersion10
InstallVersion8:
# install plugins...
SetOutPath $VERS8DIR
writeUninstaller "${APPNAME} Uninstall.exe"
GoTo CheckInstallVers9
InstallVersion9:
SetOutPath $VERS9DIR
writeUninstaller "${APPNAME} Uninstall.exe"
GoTo CheckInstallVers10
InstallVersion10:
SetOutPath $VERS10DIR
writeUninstaller "${APPNAME} Uninstall.exe"
SectionEnd
You can call WriteUninstaller as many times as you want but you should use the full path name (writeUninstaller "$VERSxDIR\${APPNAME} Uninstall.exe")
You did not post a full script so it is hard to tell what is wrong with the logic (You might want to use LogicLib.nsh so you can do {IF}s) but you should be able to "MessageBox debug" your way to the solution.
One thing you did not talk about that might be relevant is the uninstaller logic. If the 3 uninstallers all do the exact same task then this is not an issue but I'd expect at least a difference in the uninstaller registry registration.
There are two ways to deal with this:
Tag data to the end of the uninstaller (or a .ini in the same directory)
Use !system to call makensis.exe and generate uninstallers at compile time that you include as normal Files
A different solution that might be relevant for plugins in sub-directories is to use a component page in the uninstaller and only delete the uninstaller when all 3 plugins have been removed...

Copy Files in NSIS

I am using the following command to copy files.
After setting output path...
File "Documents\*"
This action works flawlessly. There are no issues coping the files in the Documents directory until...
if there is a copy of an existing file (with a different name) in the directory only the first instance of the file gets copied regardless of name.
How do I make it so it will copy ALL files regardless if they are copies of other files?
Correction/better explanation (maybe)
I apologize for the confusion. Allow me to try to restate the problem. The files being extracted by using the FILE command is the issue here. The files consists of original files and copies of the same files (only with a different name).
example: MyDocument.txt and copyOfMyDocument.txt and so on..
When the File command is applied, in order to be extract the files to the current output path, only the first instance of the file is extracted (either the copy or the original... but not both). Again, I am sorry for the confusing but this is the first time I've had to work with NSIS. I need to extract ALL files.
The easiest way to do this will be to put it in a different directory which you've created. Then, if you need to worry about renaming (as the commentators have noted your question doesn't make much sense), you can attack it file by file.
# Extract the files to a directory which can't exist beforehand
CreateDirectory $PLUGINSDIR\extracting
SetOutPath $PLUGINSDIR\extracting
File Documents\*
# Now go through file by file
FindFirst $0 $1 $OUTDIR\*
${While} $1 != ""
${If} ${FileExists} $DOCUMENTS\$1
# This still isn't infallible, of course.
Rename $DOCUMENTS\$1 $DOCUMENTS\$1.local-backup
${EndIf}
Rename $OUTDIR\$1 $DOCUMENTS\$1
FindNext $0 $1
${Loop}
FindClose $0
SetOutPath $INSTDIR # Or somewhere else
RMDir $PLUGINSDIR\extracting
(Note that that's using LogicLib.)
This doesn't become a very neat way of doing it though and if you can avoid it, do.
i thought i understood what you were after, until i started reading the responses; i'll go with my initial interpretation: given a directory named "Documents", with a bunch of files in it (what they're called, and their contents should not matter), you want an installer that will copy the files into some output directory. I've created a test installer for this scenario here, and it works for me. What am I missing in what you're after?

Resources