Here is the part os nsis script (.nsi):
!ifndef QTDIR
!error "Please define QT installation directory via /DQTDIR=C:\qt\4.8.4"
!endif
But after executing this command:
set QTDIR=C:\path\to\qt
the erorr still occurs. The same result on two computers, both windows 7. Nsis version is 2.46 .
!ifdef and !ifndef operate on defines internally in the compiler process. You can set one in your script with !define or use the -D MakeNSIS command line argument.
MakeNSIS can also read Windows environment variables: !echo "The value of QTDIR is $%QTDIR%".
You can also support both:
!ifndef QTDIR
!define QTDIR "$%QTDIR%"
!endif
!if ! /fileexists "${QTDIR}"
!error "QTDIR not valid"
!endif
Related
I am porting our NSI installer to Linux and Mac instead of Windows to better integrate with our Maven build system.
We need to sign our installer and uninstaller. This was done as suggested at http://nsis.sourceforge.net/Signing_an_Uninstaller, but I just realized that it tries to run the tempinstaller to force it to produce the uninstaller.exe which can then be signed.
Obviously this trick doesn't work too well on *Nix systems and make this part of the process non-portable.
Does anyone has a better solution. I'm no expert at NSIS and wondering if there is a clever way to get the uninstall.exe so that it can be signed?
I don't think there is a real solution to this.
The installer and uninstaller uses the same exe code and only checks a flag (FH_FLAGS_UNINSTALL in firstheader) on startup to see if it is a uninstaller. Just flipping this bit is not enough though, the program would fail the CRC check and even if you bypass that the uninstaller data is compressed so you would have to decompress that to the correct location in the file. To actually accomplish this you would have to write a custom tool. You can see this operation in the NSIS source in exec.c if you search for EW_WRITEUNINSTALLER.
We need to sign our installer and uninstaller. This was done as suggested at http://nsis.sourceforge.net/Signing_an_Uninstaller, but I just realized that it tries to run the tempinstaller to force it to produce the uninstaller.exe which can then be signed. [...] this trick doesn't work too well on *Nix systems and make this part of the process non-portable.
If you exploit a stub installer for uninstall operations (no payload), this appears to be possible.
It will spawn an uninstall.exe process from the $TEMP folder, which is then capable of deleting $INSTDIR.
This script will create a stub (un)installer which can then be Authenticode Signed. It will compile on Windows, MacOS and Linux.
Caveats:
You'll have to manually bundle this into the installer (trivial)
You'll have to manage your own uninstall registry entries (trivial)
The look and feel may not match NSIS's default for uninstallers
You'll see the installer open twice (first from $INSTDIR, second from $TEMP). This is a child process which allows uninstall.exe to delete itself, similar to how NSIS does it in the Section "Uninstall".
You'll need a secondary .nsi script dedicated to uninstall operations, cumbersome if you have a lot of shared logic between your install/uninstall sections.
Worse, you'll have to AVOID the "Uninstall" section title, as you'll be placed into the same problem as the OP when that bytecode is generated.
When explicitly running from $TEMP some relative file logic will be incorrect. The example passes these back in as a $DELETE_DIR, $DELETE_EXE respectively.
The code:
!include MUI2.nsh
!include x64.nsh
!include LogicLib.nsh
!include FileFunc.nsh
!include WinMessages.nsh
!define MUI_PRODUCT "My App"
!define MUI_VERSION "1.0.0"
; Masquerade the title
!define MUI_PAGE_HEADER_TEXT "Uninstall My App"
!define MUI_PAGE_HEADER_SUBTEXT "Remove My App from your computer"
!define MUI_INSTFILESPAGE_FINISHHEADER_TEXT "Uninstallation Complete"
!define MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT "Uninstall was completed successfully."
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
!insertmacro GetParameters
RequestExecutionLevel admin
CRCCheck On
OutFile "uninstall.exe"
Name "Uninstall"
Var /GLOBAL RESPAWN
Var /GLOBAL DELETE_DIR
Var /GLOBAL DELETE_EXE
Section
; Masquerade as uninstall
SendMessage $HWNDPARENT ${WM_SETTEXT} 0 "STR:Uninstall"
${GetParameters} $0
${GetOptions} "$0" "/RESPAWN=" $RESPAWN
${GetOptions} "$0" "/DELETE_DIR=" $DELETE_DIR
${GetOptions} "$0" "/DELETE_EXE=" $DELETE_EXE
${If} $RESPAWN != ""
; We're running from $TEMP; Perform the uninstall
!define yay "We're running from $EXEPATH, yay, we can remove the install directory!$\n$\n"
!define myvars "$\tRESPAWN$\t$RESPAWN$\n$\tDELETE_EXE$\t$DELETE_EXE$\n$\tDELETE_DIR$\t$DELETE_DIR"
MessageBox MB_OK "${yay}${myvars}"
; Your uninstall code goes here
; RMDir /r $DELETE_DIR\*.*
; Delete "$DESKTOP\${MUI_PRODUCT}.lnk"
; Delete "$SMPROGRAMS\${MUI_PRODUCT}\*.*"
; RmDir "$SMPROGRAMS\${MUI_PRODUCT}"
; Delete Uninstaller And Unistall Registry Entries
; DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\${MUI_PRODUCT}"
; DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${MUI_PRODUCT}"
; Remove the old version of ourself
ClearErrors
Delete $DELETE_EXE
IfErrors 0 +3
MessageBox MB_OK "File could NOT be deleted: $DELETE_EXE"
Goto +2
MessageBox MB_OK "File was successfully deleted: $DELETE_EXE"
; Remove ourself from $TEMP after reboot
Delete /REBOOTOK $EXEPATH
; ${If} ${RunningX64}
; ${EnableX64FSRedirection}
; ${EndIf}
SetDetailsPrint textonly
DetailPrint "Completed"
${Else}
; We're NOT running from $TEMP, copy to temp and respawn ourself
GetTempFileName $0
CopyFiles "$EXEPATH" "$0"
Exec '"$0" /RESPAWN=1 /DELETE_DIR="$EXEDIR" /DELETE_EXE="$EXEPATH"'
Quit
${EndIf}
SectionEnd
Function .onInit
; ${If} ${RunningX64}
; SetRegView 64
; ${DisableX64FSRedirection}
; ${EndIf}
FunctionEnd
Is it possible to conditionally add a file/folder and installation option to a NSIS installer?
My idea is that if the folder Foo exists at a given location it should be added to the installer and the option to install Foo should be added to the installer as well. But if the folder Foo does not exist, the NSIS script should just create the installer but leave Foo and the option to select Foo out of it.
You can try to include a file with /NONFATAL. If it exists, it will be included by the compiler. In runtime, you can check if installer was able to extract it.
File /NONFATAL "file.zip"
${If} ${FileExists} "$OUTDIR\file.zip"
...
${EndIf}
In NSIS 2 File /NONFATAL /R "c:\foo" is the best you can do without external tools and you need a little hack to hide the section when there are no files:
!include LogicLib.nsh
Page Components
Page InstFiles
Section "Main"
SetOutPath $InstDir
# File "C:\myfiles\myapp.exe"
SectionEnd
Section "Install Foo" SID_FOO
SetOutPath $InstDir
File /NONFATAL /r "C:\myfiles\foo\*.*"
SectionEnd
Function .onInit
SectionGetSize ${SID_FOO} $0
StrCmp $0 0 "" +3
SectionSetFlags ${SID_FOO} 0 ; Force all flags off including the checkmark
SectionSetText ${SID_FOO} "" ; Hide the section because its size is 0
FunctionEnd
If this is unacceptable you can use !system and get a little help from cmd.exe to check if something exists:
!tempfile INCEXIST
!system 'if exist "C:\myfiles\foo\*.*" echo !define HAVE_FOO > "${INCEXIST}"'
!include "${INCEXIST}"
!delfile "${INCEXIST}"
!ifdef HAVE_FOO
Section "Install Foo"
SetOutPath $InstDir
File /r "C:\myfiles\foo\*.*"
SectionEnd
!endif
In NSIS 3 !if supports a /FileExists switch:
!if /FileExists "C:\myfiles\foo\*.*"
Section "Install Foo"
SetOutPath $InstDir
File /r "C:\myfiles\foo\*.*"
SectionEnd
!endif
Example to replace file what depends on running service and exists or not at targget location
IfFileExists "$SYSDIR\my_file.dll" exist notexist
exist:
ExecWait 'net stop desired_service'
SetOutPath $SYSDIR
SetOverwrite on
File "/oname=$SYSDIR\my_file.dll" "Path to my file\my_file.dll"
ExecWait 'net start desired_service'
notexist:
.....what you want to do if doesn't exists
I have builded nsis script successfully for my java project.I have a batch file that I need to run within my NSIS installer.It must run after all the files have been extracted.I have tried following commands
!define MUI_FINISHPAGE_RUN $INSTDIR\bin\batch.bat
This one also have tried:
SetOutPath $INSTDIR
ExpandEnvStrings $0 %COMSPEC%
nsExec::ExecToStack '"$INSTDIR\batch.bat"'
I have referred this link.
My Requirement is:
1.How to start batch file after installation completion using Nsis script?
Why call ExpandEnvStrings if you are not going to use the result? The path does not even match in your two examples.
As long as you get the path and quotes correct it should work:
!include MUI2.nsh
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION RunBatch
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE English
Function RunBatch
;The most basic version, runs with visible console:
ExecWait '"$temp\test.cmd" /foo "bar baz" /blergh'
;You must use cmd.exe if you want redirection (With stupid extra quotes for cmd.exe):
ExpandEnvStrings $0 %COMSPEC%
ExecWait '"$0" /C ""$temp\test.cmd" /foo "bar baz" /blergh > "$temp\stdout.txt""'
;Use one of the exec plugins if you want to hide the console:
nsExec::Exec '"$temp\test.cmd" /foo "bar baz" /blergh'
FunctionEnd
There are several exec plugins you can use depending on your needs: nsExec, ExecDos
or ExecCmd
I have 2 versions of the same exe file for my project. The installer is supposed to pick one of the 2 versions depending on some conditions.
In a normal case i would do File executable\myExe.exe. Because i now have 2 versions of the file, i would have to do something like File "${ExeSourcePath}\myExe.exe", and $ExeSourcePath is determined by checking various conditions. When compiling this code i get
File: "${ExeSourcePath}\myExe.exe" -> no files found.
Anyone knows why? I'm only allowed to use fixed paths with the File command or am i doing something wrong?
${ExeSourcePath} is a precompiler define and $ExeSourcePath is a variable used at runtime, the File command can only use precompiler defines.
There are two ways you can handle this:
A) Include both files and decide at runtime based on the users system or choices made during install:
!include LogicLib.nsh
Section
ReadRegStr $0 HKLM "Software\foo\bar" baz
${If} $0 > 5
File "c:\myproject\version2\app.exe"
${Else}
File "c:\myproject\version1\app.exe"
${EndIf}
SectionEnd
B) Only include one file based on command line passed to makensis (/Dusev2 app.nsi) or something on your system:
Section
!define projectroot "c:\myproject"
!searchparse /noerrors /file ....... usev2 ;Or you can use !system etc
!ifdef usev2
File "${projectroot}\version2\app.exe"
!else
File "${projectroot}\version1\app.exe"
!endif
SectionEnd
I have an NSIS based installer that I need to be able generate slightly different versions of under different conditions.
The conditions are easy to establish at compile time, if a particular file exists on the disk then the alternative branding can be used. I know I could use a command-line option to the makensis.exe to provide this behaviour, but it would be better if the compiler could take care of this for me.
Is there a way of making a compile time "IfExist" type logic?
!macro CompileTimeIfFileExist path define
!tempfile tmpinc
!system 'IF EXIST "${path}" echo !define ${define} > "${tmpinc}"'
!include "${tmpinc}"
!delfile "${tmpinc}"
!undef tmpinc
!macroend
Section
!insertmacro CompileTimeIfFileExist "$%windir%\explorer.exe" itsThere
!ifdef itsThere
MessageBox mb_Topmost yes
!else
MessageBox mb_Topmost no
!endif
SectionEnd
Note: the !system command used here assumes you are compiling on windows
I don't have the answer to your generic question of compile-time file detection, but I do have a solution to what it sounds like you're trying to accomplish.
My installers use something like this:
In the file CustomBranding.nsh:
!define CUSTOM_BRANDING
!define APPNAME "Brand X"
!define LOGO "C:\Brand_X_Logo.png"
In the main installer script:
!include /NONFATAL "CustomBranding.nsh"
!ifndef CUSTOM_BRANDING
!define APPNAME "Generic"
!define LOGO "C:\Generic_Logo.png"
!endif
Is that the kind of "alternative branding" you're asking about?