Archive: Questions about a large installer.


Questions about a large installer.
Hello, I'm constructing a rather large intaller to be distributed on DVD. It contains about 3 GB of data and will probably be about 1.4 GB after compression (I usually use lzma /solid). I've compiled a dozen or so of these installers before, ranging from one to several hundred MB in size at the end. Apparently, those worked without problems, since no one complained. I didn't bother with uninstallers.

My understanding of NSIS is pretty rudimentary and I've mostly cookbooked my way along. The idea of loading a gigabyte sized file into memory from DVD and decompressing it sets off a lot of alarm bells for me. Can (or should) I break the installer file into smaller pieces, and if so how would I do this? My installer script is pretty basic, using a few File /r "Install\*.*" commands, without reference to external ini files, etc.

I am also having trouble with creating an uninstaller. This is data for other applications, so it is installed into the existing application directory structure. My installers use the application directory as $INSTDIR and install to appropriate sub-directories via the File /r "Install\*.*" commands. Nothing is actually written to $INSTDIR, just its subdirectories. Generally there are AppDir\DataDir\MyDataDir\*.* and AppDir\ConfigDir\MyConfigFile.ext. I'm using the directory structure of "Install\*.*" to put everything where it belongs. MyDataDir contains about 1200 branching subdirectories and tens of thousands of small data files.

I can uninstall the data files by writing uninstall.exe to MyDataDir and using RMDir /r "$INSTDIR". This approach sets off more alarm bells and doesn't remove MyConfigFile.ext. I've tried to create a variable for this file and explicitly delete it, without success. As I say, my grasp of NSIS is very limited. Since the number of files and folders is so large, I don't really want to try to create a log file of all the individual files and paths during installation. Could someone recommend a good solution to this?

Also, are there any built-in strings for the uninstaller that would appear in the uninstaller in other languages (like the installer pages)?

The uninstaller part of my script is:

Section -Post
WriteUninstaller "$INSTDIR\MyDataDir\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
"DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
"UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
"DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
"URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
"Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd

Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully
removed from your computer."
FunctionEnd

Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you
want to completely remove $(^Name) and all of its components?
This will delete the directory $INSTDIR and all files it contains." IDYES +2
Abort
FunctionEnd

Section Uninstall
Delete "$INSTDIR\ConfigDir\MyConfigFile.ext" ### I know this is wrong!

Delete "$SMPROGRAMS\NSIS_Test-00\Uninstall.lnk"
Delete "$SMPROGRAMS\NSIS_Test-00\Website.lnk"

RMDir /r "$SMPROGRAMS\NSIS_Test-00"
RMDir /r "$INSTDIR"

DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
SetAutoClose true
SectionEnd


Thanks

If the file is not being removed with a recursive deletion them it is locked by another process.

Stu


Thanks for your reply

The deltree portion of the uninstallation works as expected (deletes the tree ;) ). I'm just concerned about the blunt weapon nature of this operation. I put an explicit path statement in the warning confirmation because of this, maybe that's enough. Enumerating all the installed files during installation doesn't seem desirable, given the huge number of small files that I have.

The part that doesn't work at all is removal of MyConfigFile.ext. I realize that this probably isn't a difficult thing, but I don't seem to be able to get the syntax and placement right. I tried some things with un.GetParent, like:

!include "FileFunc.nsh"
!insertmacro un.GetParent
and
${un.GetParent} "$INSTDIR\ConfigDir\MyConfigFile.ext" $R0
Delete "$R0\MyConfigFile.ext"

but I don't understand the structure and locations of these very well. Either they gave errors during compile or didn't delete the desired file.

Do you have any suggestions about using a single-file installer or trying to break it into smaller pieces?

Thanks

My understanding of NSIS is pretty rudimentary and I've mostly cookbooked my way along. The idea of loading a gigabyte sized file into memory from DVD and decompressing it sets off a lot of alarm bells for me. Can (or should) I break the installer file into smaller pieces, and if so how would I do this? My installer script is pretty basic, using a few File /r "Install\*.*" commands, without reference to external ini files, etc.
If you're worried about memory requirements, there's no need to split the installer. It will never be loaded into memory in one piece.

As for the uninstaller, write it to $INSTDIR. The directory from which the uninstaller is executed is used as $INSTDIR when the uninstaller runs. Because you write it to $INSTDIR\Something, $INSTDIR won't have the same value in the installer and uninstaller.

To remove only the files you installed without resorting to RMDir /r, use on of the following.

http://nsis.sourceforge.net/Uninstal...nstalled_files
http://nsis.sourceforge.net/Uninstal...your_installer
http://nsis.sourceforge.net/Advanced...og_NSIS_Header

Thanks, kichik

If you're worried about memory requirements, there's no need to split the installer. It will never be loaded into memory in one piece.
Good, so one monster installer file should work without problems.

Because you write it to $INSTDIR\Something, $INSTDIR won't have the same value in the installer and uninstaller.
Yes, I understand that. I put uninstall.exe in MyDataDir so $INSTDIR becomes MyDataDir during uninstall. I don't want to delete the $INSTDIR (AppDir) used initially, since this is a data package for an existing application. My files are a big tree of files in AppDir\DataDir\MyDataDir\*.* and a single file in AppDir\ConfigDir\, MyConfigFile.ext. The RMDir /r "$INSTDIR" line deletes MyDataDir\*.*, since I put uninstall.exe in that subdirectory (AppDir\DataDir\MyDataDir\uninstall.exe). The file AppDir\ConfigDir\MyConfigFile.ext also must be selectively deleted during uninstall. I'm trying to use un.GetParent earlier in the script when $INSTDIR is still the AppDir target, feed that value to a variable and use the variable to define the Delete "$R0\MyConfigFile.ext" command.

I put !include "FileFunc.nsh" at the beginning of the script with the other !include statements. I put the ${un.GetParent} "$INSTDIR\ConfigDir\MyConfigFile.ext" $R0 command in the '-Post' section and the Delete command in the 'Uninstall' section. I never could figure out where to put !insertmacro un.GetParent, so nothing else worked.

I found the references you listed while researching this. My log file would end up with about 35,000 entries for a full installation. http://nsis.sourceforge.net/Uninstal...your_installer might be simple enough for me to figure out, eventually. Since I'm having trouble implementing Delete "OneStupidFile.ext" correctly, I hoped to keep it simple.

Thanks

But the parent of $INSTDIR\ConfigDir\MyConfigFile.ext is $INSTDIR\ConfigDir and $INSTDIR itself is $INSTALLER_INSTDIR\AppDir. Use un.GetParent on $INSTDIR, not on $INSTDIR\ConfigDir\MyConfigFile.ext.


But the parent of $INSTDIR\ConfigDir\MyConfigFile.ext is $INSTDIR\ConfigDir
Yes, I was trying to pass '$INSTDIR\ConfigDir' to the uninstall routine.

$INSTDIR itself is $INSTALLER_INSTDIR\AppDir
Actually, $INSTALLER_INSTDIR = AppDir

Before installation, the directories on the target computer are:

AppDir\
\ConfigDir\
Existing Config Files
\DataDir\
\Existing Data Directories\

The installer uses AppDir as $INSTDIR and File /r "Install\*.*". The source \Install\ directory contains \ConfigDir\ and \DataDir\MyDataDir\ folders with my files.

After installation, the directories on the target computer are:

AppDir\
\ConfigDir\
Existing Config Files
+MyConfigFile.ext
\DataDir\
\Existing Data Directories\
+\MyDataDir\


At this point I have the following after the installation sections:

!include "FileFunc.nsh"
;!insertmacro un.GetParent
!insertmacro GetParent

Section -Post
${GetParent} "$INSTDIR" $R0
WriteUninstaller "$INSTDIR\DataDir\MyDataDir\uninst.exe"
<snip>
SectionEnd

Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
FunctionEnd

Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure..." IDYES +2
Abort
FunctionEnd

Section Uninstall
; ${un.GetParent} "$INSTDIR" $R0
Delete "$SMPROGRAMS\NSIS_Test-00\Uninstall.lnk"
Delete "$SMPROGRAMS\NSIS_Test-00\Website.lnk"

RMDir /r "$SMPROGRAMS\NSIS_Test-00"
RMDir /r "$INSTDIR"
Delete "$R0\ConfigDir\MyConfigFile.ext"

DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
SetAutoClose true
SectionEnd


It does compile ok without error messages, but it still doesn't delete MyConfigFile.ext. I commented out the un.GetParent statements since, if I understand, $INSTDIR in the uninstall section would reference the location of uninstall.exe rather than AppDir (the initial $INSTDIR). I tried un.GetParent in the uninstall section, but that didn't delete AppDir\ConfigDir\MyConfigFile.ext either. Placing the un.GetParent command in the '-Post' section gave an error that 'un.' commands had to be in uninstall sections.

Should $R0 be passed correctly the way I have this? If I change WriteUninstaller "$INSTDIR\MyDataDir\uninst.exe" to WriteUninstaller "$R0\MyDataDir\uninst.exe", then uninst.exe is no longer created at all.

It'd probably be best if you add some messages boxes to debug the values of $INSTDIR and $R0 after each change. Then you'll see exactly what happens and why the installer's $INSTDIR is not the same as the parent of the uninstaller's $INSTDIR. You need to run GetParent twice there.


Ok, I'll try to get that to work. At this point, I seem to be getting an empty value for $R0. I tried to change $R0 to $R7, without effect. Unfortunately, I'm not entirely confident that I have those sequences correct either :igor: . I'll keep trying.

Thanks for your help.