# Installer template file for creating a Windows installer using NSIS. # Dependencies: # NSIS >=3.08 conda install "nsis>=3.08" (includes extra unicode plugins) Unicode "true" #if enable_debugging is True # Special logging build needed for ENABLE_LOGGING # See https://nsis.sourceforge.io/Special_Builds !define ENABLE_LOGGING #endif # Comes from https://nsis.sourceforge.io/Logging:Enable_Logs_Quickly !define LogSet "!insertmacro LogSetMacro" !macro LogSetMacro SETTING !ifdef ENABLE_LOGGING LogSet ${SETTING} !endif !macroend !define LogText "!insertmacro LogTextMacro" !macro LogTextMacro INPUT_TEXT !ifdef ENABLE_LOGGING LogText ${INPUT_TEXT} !endif !macroend !include "WinMessages.nsh" !include "WordFunc.nsh" !include "LogicLib.nsh" !include "WinVer.nsh" !include "MUI2.nsh" !include "x64.nsh" !include "FileFunc.nsh" !insertmacro GetParameters !insertmacro GetOptions !include "UAC.nsh" !include "nsDialogs.nsh" !include "Utils.nsh" !define NAME __NAME__ !define VERSION __VERSION__ !define COMPANY __COMPANY__ !define ARCH __ARCH__ !define PLATFORM __PLATFORM__ !define CONSTRUCTOR_VERSION __CONSTRUCTOR_VERSION__ !define PY_VER __PY_VER__ !define PYVERSION_JUSTDIGITS __PYVERSION_JUSTDIGITS__ !define PYVERSION __PYVERSION__ !define PYVERSION_MAJOR __PYVERSION_MAJOR__ !define DEFAULT_PREFIX __DEFAULT_PREFIX__ !define DEFAULT_PREFIX_DOMAIN_USER __DEFAULT_PREFIX_DOMAIN_USER__ !define DEFAULT_PREFIX_ALL_USERS __DEFAULT_PREFIX_ALL_USERS__ !define PRE_INSTALL_DESC __PRE_INSTALL_DESC__ !define POST_INSTALL_DESC __POST_INSTALL_DESC__ !define ENABLE_SHORTCUTS __ENABLE_SHORTCUTS__ !define SHOW_REGISTER_PYTHON __SHOW_REGISTER_PYTHON__ !define SHOW_ADD_TO_PATH __SHOW_ADD_TO_PATH__ !define PRODUCT_NAME "${NAME} ${VERSION} (${ARCH})" !define UNINSTALL_NAME "@UNINSTALL_NAME@" !define UNINSTREG "SOFTWARE\Microsoft\Windows\CurrentVersion\ \Uninstall\${UNINSTALL_NAME}" var /global INSTDIR_JUSTME var /global INSTALLER_VERSION var /global INSTALLER_NAME_FULL # UAC shield overlay !ifndef BCM_SETSHIELD !define BCM_SETSHIELD 0x0000160C !endif var /global ARGV var /global ARGV_Help var /global ARGV_InstallationType var /global ARGV_AddToPath var /global ARGV_KeepPkgCache var /global ARGV_RegisterPython var /global ARGV_NoRegistry var /global ARGV_NoScripts var /global ARGV_NoShortcuts var /global ARGV_CheckPathLength var /global IsDomainUser var /global CheckPathLength var /global LongPathsEnabled var /global InstDirLen var /global InstModePage_RadioButton_JustMe var /global InstModePage_RadioButton_AllUsers var /global InstMode # 0 = Just Me, 1 = All Users. !define JUST_ME 0 !define ALL_USERS 1 # Include this one after our defines !include "OptionsDialog.nsh" CRCCheck On # Basic options Name "${PRODUCT_NAME}" OutFile __OUTFILE__ ShowInstDetails "hide" ShowUninstDetails "hide" # This installer contains tar.bz2 files, which are already compressed SetCompress "off" # Start off with the lowest permissions and work our way up. RequestExecutionLevel user # Version information & branding text VIAddVersionKey "ProductName" "${PRODUCT_NAME}" VIAddVersionKey "FileVersion" "${VERSION}" VIAddVersionKey "ProductVersion" "${VERSION}" VIAddVersionKey "CompanyName" "${COMPANY}" VIAddVersionKey "LegalCopyright" "(c) ${COMPANY}" VIAddVersionKey "FileDescription" "${NAME} Installer" VIAddVersionKey "Comments" "Created by constructor ${CONSTRUCTOR_VERSION}" VIProductVersion __VIPV__ BrandingText /TRIMLEFT "${COMPANY}" # Interface configuration !define MUI_ICON __ICONFILE__ !define MUI_UNICON __ICONFILE__ !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP __HEADERIMAGE__ !define MUI_HEADERIMAGE_UNBITMAP __HEADERIMAGE__ !define MUI_ABORTWARNING !define MUI_FINISHPAGE_NOAUTOCLOSE !define MUI_UNFINISHPAGE_NOAUTOCLOSE !define MUI_WELCOMEFINISHPAGE_BITMAP __WELCOMEIMAGE__ !define MUI_UNWELCOMEFINISHPAGE_BITMAP __WELCOMEIMAGE__ #!define MUI_CUSTOMFUNCTION_GUIINIT GuiInit # Pages #!define MUI_PAGE_CUSTOMFUNCTION_SHOW OnStartup #if custom_welcome # Custom welcome file(s) @CUSTOM_WELCOME_FILE@ #else !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipPageIfUACInnerInstance !insertmacro MUI_PAGE_WELCOME #endif !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipPageIfUACInnerInstance !insertmacro MUI_PAGE_LICENSE __LICENSEFILE__ Page Custom InstModePage_Create InstModePage_Leave !define MUI_PAGE_CUSTOMFUNCTION_PRE DisableBackButtonIfUACInnerInstance !define MUI_PAGE_CUSTOMFUNCTION_LEAVE OnDirectoryLeave !insertmacro MUI_PAGE_DIRECTORY # Custom options now differ depending on installation mode. #Page Custom mui_AnaCustomOptions_Show !insertmacro MUI_PAGE_INSTFILES #if with_conclusion_text is True !define MUI_FINISHPAGE_TITLE __CONCLUSION_TITLE__ !define MUI_FINISHPAGE_TITLE_3LINES !define MUI_FINISHPAGE_TEXT __CONCLUSION_TEXT__ #endif #if custom_conclusion # Custom conclusion file(s) @CUSTOM_CONCLUSION_FILE@ #else !insertmacro MUI_PAGE_FINISH #endif !insertmacro MUI_UNPAGE_WELCOME !define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.OnDirectoryLeave !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH # Language !insertmacro MUI_LANGUAGE "English" Function SkipPageIfUACInnerInstance ${LogSet} on ${If} ${UAC_IsInnerInstance} Abort ${EndIf} FunctionEnd !macro DoElevation GetDlgItem $1 $HWNDParent 1 System::Call user32::GetFocus()i.s # Disable 'Next' button. EnableWindow $1 0 !insertmacro UAC_PageElevation_RunElevated EnableWindow $1 1 System::call user32::SetFocus(is) ${If} $2 = 0x666 MessageBox MB_ICONEXCLAMATION \ "You need to log in with an administrative account \ in order to perform an 'All Users' installation." Abort ${ElseIf} $0 = 1223 # UAC canceled by user. Abort ${Else} ${If} $0 <> 0 ${If} $0 = 1062 MessageBox MB_ICONSTOP \ "Elevation failed; Secondary Logon service is \ not running." ${Else} MessageBox MB_ICONSTOP \ "Elevation failed; error code: $0." ${EndIf} Abort ${EndIf} ${EndIf} # UAC worked, we're the outer installer, so we can quit. Quit !macroend !macro ParseCommandLineArgs ClearErrors ${GetParameters} $ARGV ${GetOptions} $ARGV "/?" $ARGV_Help ${IfNot} ${Errors} MessageBox MB_OK|MB_ICONEXCLAMATION \ "Usage: $EXEFILE [options]$\n\ Options:$\n$\n\ /InstallationType=AllUsers [default: JustMe]$\n$\n\ /AddToPath=[0|1] [default: 0]$\n$\n\ #if keep_pkgs is True /KeepPkgCache=[0|1] [default: 1]$\n$\n\ #endif #if keep_pkgs is False /KeepPkgCache=[0|1] [default: 0]$\n$\n\ #endif /RegisterPython=[0|1] [default: AllUsers: 1, JustMe: 0]$\n$\n\ /NoRegistry=[0|1] [default: AllUsers: 0, JustMe: 0]$\n$\n\ /NoScripts=[0|1] [default: 0]$\n$\n\ /NoShortcuts=[0|1] [default: 0]$\n$\n\ /CheckPathLength=[0|1] [default: 1]$\n$\n\ Examples:$\n\ Install for all users, but don't add to PATH env var:$\n\ $EXEFILE /InstallationType=AllUsers$\n$\n\ Install for just me, add to PATH and register as system Python:$\n\ $EXEFILE /RegisterPython=1 /AddToPath=1$\n$\n\ Install for just me, with no registry modification (for CI):$\n\ $EXEFILE /NoRegistry=1$\n$\n\ NOTE: If you install for AllUsers, then the option to AddToPath$\n\ is disabled (i.e. if ./InstallationType=AllUsers, then$\n\ /AddToPath=1 will be ignored).$\n" \ /SD IDOK Abort ${EndIf} ClearErrors ${GetOptions} $ARGV "/InstallationType=" $ARGV_InstallationType ${IfNot} ${Errors} ${If} $ARGV_InstallationType == "AllUsers" StrCpy $InstMode ${ALL_USERS} ${Else} StrCpy $InstMode ${JUST_ME} ${EndIf} ${EndIf} ClearErrors ${GetOptions} $ARGV "/RegisterPython=" $ARGV_RegisterPython ${IfNot} ${Errors} ${If} $ARGV_RegisterPython = "1" StrCpy $Ana_RegisterSystemPython_State ${BST_CHECKED} ${ElseIf} $ARGV_RegisterPython = "0" StrCpy $Ana_RegisterSystemPython_State ${BST_UNCHECKED} ${EndIf} ${EndIf} ClearErrors ${GetOptions} $ARGV "/KeepPkgCache=" $ARGV_KeepPkgCache ${If} ${Errors} StrCpy $ARGV_KeepPkgCache "@KEEP_PKGS@" ${EndIf} ClearErrors ${GetOptions} $ARGV "/NoRegistry=" $ARGV_NoRegistry ${If} ${Errors} StrCpy $ARGV_NoRegistry "0" ${EndIf} ClearErrors ${GetOptions} $ARGV "/NoScripts=" $ARGV_NoScripts ${IfNot} ${Errors} ${If} $ARGV_NoScripts = "1" StrCpy $Ana_PostInstall_State ${BST_UNCHECKED} ${ElseIf} $ARGV_NoScripts = "0" StrCpy $Ana_PostInstall_State ${BST_CHECKED} ${EndIf} ${EndIf} ${If} "${ENABLE_SHORTCUTS}" == "yes" ClearErrors ${GetOptions} $ARGV "/NoShortcuts=" $ARGV_NoShortcuts ${IfNot} ${Errors} ${If} $ARGV_NoShortcuts = "1" StrCpy $Ana_CreateShortcuts_State ${BST_UNCHECKED} ${ElseIf} $ARGV_NoShortcuts = "0" StrCpy $Ana_CreateShortcuts_State ${BST_CHECKED} ${EndIf} ${EndIf} ${Else} StrCpy $Ana_CreateShortcuts_State ${BST_UNCHECKED} ${EndIf} ClearErrors ${GetOptions} $ARGV "/CheckPathLength=" $ARGV_CheckPathLength ${IfNot} ${Errors} ${If} $ARGV_CheckPathLength = "0" StrCpy $CheckPathLength "0" ${ElseIf} $ARGV_CheckPathLength = "1" StrCpy $CheckPathLength "1" ${EndIf} ${EndIf} !macroend Function OnInit_Release ${LogSet} on !insertmacro ParseCommandLineArgs # Parsing the AddToPath option here (and not in ParseCommandLineArgs) to prevent the MessageBox from showing twice. # For more context, see https://github.com/conda/constructor/pull/584#issuecomment-1347688020 ClearErrors ${GetOptions} $ARGV "/AddToPath=" $ARGV_AddToPath ${IfNot} ${Errors} ${If} $ARGV_AddToPath = "1" ${If} $InstMode == ${ALL_USERS} # To address CVE-2022-26526. # In AllUsers install mode, do not allow AddToPath as an option. MessageBox MB_OK|MB_ICONEXCLAMATION "/AddToPath=1 is disabled and ignored in 'All Users' installations" /SD IDOK StrCpy $Ana_AddToPath_State ${BST_UNCHECKED} ${Else} StrCpy $Ana_AddToPath_State ${BST_CHECKED} ${EndIf} ${ElseIf} $ARGV_AddToPath = "0" StrCpy $Ana_AddToPath_State ${BST_UNCHECKED} ${EndIf} ${EndIf} FunctionEnd Function InstModePage_RadioButton_OnClick ${LogSet} on Exch $0 Push $1 Push $2 nsDialogs::GetUserData $0 Pop $1 GetDlgItem $2 $HWNDParent 1 SendMessage $2 ${BCM_SETSHIELD} 0 $1 Pop $2 Pop $1 Exch $0 FunctionEnd Function InstModePage_Create ${LogSet} on Push $0 Push $1 Push $2 Push $3 ${If} ${UAC_IsInnerInstance} Abort ${EndIf} !insertmacro MUI_HEADER_TEXT_PAGE \ "Select Installation Type" \ "Please select the type of installation you would like to perform \ for ${PRODUCT_NAME}." GetFunctionAddress $0 InstModePage_RadioButton_OnClick nsDialogs::Create /NOUNLOAD 1018 Pop $1 ${NSD_OnBack} RemoveNextBtnShield ${NSD_CreateLabel} 0 20u 75% 20u "Install for:" ${NSD_CreateRadioButton} 0 40u 75% 15u "Just Me (recommended)" Pop $2 #MessageBox MB_OK "OnClick 2! 0: $0, 1: $1, 2: $2" StrCpy $InstModePage_RadioButton_JustMe $2 nsDialogs::OnClick $2 $0 nsDialogs::SetUserData $2 0 SendMessage $2 ${BM_CLICK} 0 0 ${NSD_CreateRadioButton} 0 60u 75% 15u \ "All Users (requires admin privileges)" #MessageBox MB_OK "OnClick 3! 0: $0, 1: $1, 2: $2, 3: $3" Pop $3 StrCpy $InstModePage_RadioButton_AllUsers $3 nsDialogs::OnClick $3 $0 nsDialogs::SetUserData $3 1 ${IfThen} $InstMode <> ${JUST_ME} ${|} SendMessage $3 ${BM_CLICK} 0 0 ${|} Push $3 nsDialogs::Show Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd Function DisableBackButtonIfUACInnerInstance ${LogSet} on Push $0 ${If} ${UAC_IsInnerInstance} GetDlgItem $0 $HWNDParent 3 EnableWindow $0 0 ${EndIf} Pop $0 FunctionEnd Function RemoveNextBtnShield ${LogSet} on Push $0 GetDlgItem $0 $HWNDParent 1 SendMessage $0 ${BCM_SETSHIELD} 0 0 Pop $0 FunctionEnd Function InstModeChanged ${LogSet} on # When using the installer with /S (silent mode), the /D option sets $INSTDIR, # and it is therefore important not to overwrite $INSTDIR here, but it is also # important that we do call SetShellVarContext with the appropriate value. Push $0 ${If} $InstMode = ${JUST_ME} SetShellVarContext Current # If we're on Vista+, the installation directory will # have a nice, no-space name like: # C:\Users\Trent\AppData\Local\Continuum\Anaconda. # On 2003/XP, it will be in C:\Documents and Settings, # with a space. We're allowing spaces now. ${IfNot} ${Silent} StrCpy $INSTDIR $INSTDIR_JUSTME ${EndIf} ${Else} SetShellVarContext All ${IfNot} ${Silent} ExpandEnvStrings $0 ${DEFAULT_PREFIX_ALL_USERS} StrCpy $INSTDIR $0 ${Endif} ${EndIf} Pop $0 FunctionEnd !macro SetInstMode mode StrCpy $InstMode ${mode} Call InstModeChanged !macroend Function InstModePage_Leave ${LogSet} on Push $0 Push $1 Push $2 ${NSD_GetState} $InstModePage_RadioButton_AllUsers $0 ${If} $0 = 0 !insertmacro SetInstMode ${JUST_ME} ${Else} !insertmacro SetInstMode ${ALL_USERS} ${IfNot} ${UAC_IsAdmin} !insertmacro DoElevation ${EndIf} ${EndIf} Pop $2 Pop $1 Pop $0 FunctionEnd Function .onInit ${LogSet} on Push $0 Push $1 Push $2 Push $R1 Push $R2 InitPluginsDir @TEMP_EXTRA_FILES@ !insertmacro ParseCommandLineArgs # Select the correct registry to look at, depending # on whether it's a 32-bit or 64-bit installer SetRegView @BITS@ #if win64 # If we're a 64-bit installer, make sure it's 64-bit Windows ${IfNot} ${RunningX64} MessageBox MB_OK|MB_ICONEXCLAMATION \ "This installer is for a 64-bit version for ${NAME}$\n\ but your system is 32-bit. Please use the 32-bit Windows$\n\ ${NAME} installer." \ /SD IDOK Abort ${EndIf} #endif !insertmacro UAC_PageElevation_OnInit ${If} ${UAC_IsInnerInstance} ${AndIfNot} ${UAC_IsAdmin} SetErrorLevel 0x666 Quit ${EndIf} # Look for a number of signs that indicate the user is a domain user and # alter the default installation directory for 'Just Me' accordingly. We # want to ensure that if we're a user domain account, we always install to # %LOCALAPPDATA% (i.e. C:\Users\Trent\AppData\Local\Continuum\Anaconda), # as this is the only place guaranteed to not be backed by a network share # or included in a user's roaming profile. However, if we're a normal user # account, then C:\Users\Trent\Anaconda is fine. ReadEnvStr $0 USERDNSDOMAIN ${If} $0 != "" # If not null, USERDNSDOMAIN is an unambiguous indication that we're # logged into a domain account. StrCpy $IsDomainUser 1 ${Else} # If it's not set, apply some simple heuristics to discern whether or # not we're logged in as a domain user. ReadEnvStr $0 LOGONSERVER ${If} $0 == "" # This should never be unset; but if it is, we're definitely not # a domain user. StrCpy $IsDomainUser 0 ${Else} StrCpy $1 $0 "" 2 # lop-off the leading \\. ${StrFilter} $1 "+" "" "" $2 # convert to uppercase, store in $2 ${If} $2 == "MICROSOFTACCOUNT" # The new Windows 8.x live accounts have \\MicrosoftAccount # set as LOGONSERVER; interpret this as being a non-domain # user. StrCpy $IsDomainUser 0 ${Else} ReadEnvStr $R1 COMPUTERNAME ${If} $R1 == "" # This should never be unset either; if it is, assume # we're not a domain user. StrCpy $IsDomainUser 0 ${Else} # We've got a value for both LOGONSERVER and COMPUTERNAME # environment variables (which should always be the case). # Proceed to compare LOGONSERVER[-2:] to COMPUTERNAME; if # they match, assume we're not a domain user account. ${StrFilter} $R1 "+" "" "" $R2 # convert to uppercase ${If} $2 != $R2 # COMPUTERNAME doesn't match LOGONSERVER; assume we're # logged in via a domain account. StrCpy $IsDomainUser 1 ${Else} # COMPUTERNAME matches LOGONSERVER; safe to assume # we're logged in as a user account. (I guess there's # the remote possibility a domain user has logged onto # a server that has the same NetBIOS name as the Active # Directory name... if that's the case, potentially # installing Anaconda into an area that gets picked up # by a roaming profile is the very least of your # problems.) StrCpy $IsDomainUser 0 ${EndIf} # LOGONSERVER[-2:] != COMPUTERNAME ${EndIf} # COMPUTERNAME != "" ${EndIf} # LOGONSERVER != "\\MicrosoftAccount" ${EndIf} # LOGONSERVER != "" ${EndIf} # USERDNSDOMAIN != "" ${If} $IsDomainUser = 0 ExpandEnvStrings $0 ${DEFAULT_PREFIX} StrCpy $INSTDIR_JUSTME $0 ${ElseIf} $IsDomainUser = 1 ExpandEnvStrings $0 ${DEFAULT_PREFIX_DOMAIN_USER} StrCpy $INSTDIR_JUSTME $0 ${Else} # Should never happen; indicates a logic error above. MessageBox MB_OK "Internal error: IsUserDomain not set properly!" \ /SD IDOK Abort ${EndIf} ${If} $InstMode == "" StrCpy $InstMode ${JUST_ME} ${IfThen} ${UAC_IsAdmin} ${|} StrCpy $InstMode ${ALL_USERS} ${|} # If running as 'SYSTEM' then JustMe is not appropriate; note that # we should advise against this. SCCM has an option to run as user System::Call "advapi32::GetUserName(t .r0, *i ${NSIS_MAX_STRLEN} r1) i.r2" ${IfThen} $0 == "SYSTEM" ${|} StrCpy $InstMode ${ALL_USERS} ${|} ${EndIf} call InstModeChanged ${If} ${Silent} ${If} $InstMode == ${ALL_USERS} ${IfNot} ${UAC_IsAdmin} MessageBox MB_ICONSTOP "Installation for all users requires an elevated prompt." Abort ${EndIF} ${EndIF} ${EndIF} ; /D was not used, add default based on install type ${If} $InstDir == "" ${If} $InstMode == ${ALL_USERS} ExpandEnvStrings $0 ${DEFAULT_PREFIX_ALL_USERS} StrCpy $INSTDIR $0 ${Else} strcpy $INSTDIR $INSTDIR_JUSTME ${EndIf} ${EndIf} ; Set default value ${If} $CheckPathLength == "" StrCpy $CheckPathLength "1" ${EndIf} # Initialize the default settings for the anaconda custom options Call mui_AnaCustomOptions_InitDefaults # Override custom options with explicitly given values from contruct.yaml. # If initialize_by_default (register_python_default) is None, do nothing. #if initialize_conda is True and initialize_by_default is True ${If} $InstMode == ${JUST_ME} StrCpy $Ana_AddToPath_State ${BST_CHECKED} ${EndIF} #endif #if initialize_conda is True and initialize_by_default is False StrCpy $Ana_AddToPath_State ${BST_UNCHECKED} #endif #if register_python is True and register_python_default is True StrCpy $Ana_RegisterSystemPython_State ${BST_CHECKED} #endif #if register_python is True and register_python_default is False StrCpy $Ana_RegisterSystemPython_State ${BST_UNCHECKED} #endif #if check_path_length is True StrCpy $CheckPathLength "1" #endif #if check_path_length is False StrCpy $CheckPathLength "0" #endif #if keep_pkgs is True StrCpy $Ana_ClearPkgCache_State ${BST_UNCHECKED} #endif #if keep_pkgs is False StrCpy $Ana_ClearPkgCache_State ${BST_CHECKED} #endif #if pre_install_exists is True StrCpy $Ana_PreInstall_State ${BST_CHECKED} #endif #if pre_install_exists is False StrCpy $Ana_PreInstall_State ${BST_UNCHECKED} #endif #if post_install_exists is True StrCpy $Ana_PostInstall_State ${BST_CHECKED} #endif #if post_install_exists is False StrCpy $Ana_PostInstall_State ${BST_UNCHECKED} #endif Call OnInit_Release Pop $R2 Pop $R1 Pop $2 Pop $1 Pop $0 FunctionEnd Function un.onInit Push $0 Push $1 Push $2 Push $3 # Resolve INSTDIR GetFullPathName $0 $INSTDIR # If the directory does not exist or cannot be resolved, $0 will be empty StrCmp $0 "" invalid_dir StrCpy $INSTDIR $0 # Never run the uninstaller when $INSTDIR points at system-critical directories StrLen $InstDirLen $INSTDIR # INSTDIR is a full path and has no trailing backslash, # so if its length is 2, it is pointed at a system root StrCmp $InstdirLen 2 invalid_dir # Never delete anything inside Windows StrCpy $0 $INSTDIR 7 3 StrCmp $0 "Windows" invalid_dir StrCpy $0 "ALLUSERSPROFILE APPDATA LOCALAPPDATA PROGRAMDATA PROGRAMFILES PROGRAMFILES(x86) PUBLIC SYSTEMDRIVE SYSTEMROOT USERPROFILE" StrCpy $1 1 loop_critical: ${WordFind} $0 " " "E+$1" $2 IfErrors endloop_critical ReadEnvStr $3 $2 StrCmp $3 $INSTDIR invalid_dir IntOp $1 $1 + 1 goto loop_critical endloop_critical: # Primitive check to see that $INSTDIR points to a conda directory StrCpy $0 "_conda.exe conda-meta\history" StrCpy $1 1 loop_conda: ${WordFind} $0 " " "E+$1" $2 IfErrors endloop_conda IfFileExists $INSTDIR\$2 0 invalid_dir IntOp $1 $1 + 1 goto loop_conda endloop_conda: # All checks have passed goto valid_dir invalid_dir: MessageBox MB_OK|MB_ICONSTOP \ "Error: $INSTDIR is not a valid conda directory. Please run the uninstaller from a conda directory." \ /SD IDABORT abort valid_dir: # Select the correct registry to look at, depending # on whether it's a 32-bit or 64-bit installer SetRegView @BITS@ # Since the switch to a dual-mode installer (All Users/Just Me), the # uninstaller will inherit the requested execution level of the main # installer -- which we now have to set to 'user'. Thus, Windows will # not automatically elevate the uninstaller for us -- we need to do it # ourselves if we're not a 'Just Me' installation. !insertmacro UAC_PageElevation_OnInit ${IfNot} ${FileExists} "$INSTDIR\.nonadmin" ${AndIfNot} ${UAC_IsAdmin} !insertmacro DoElevation ${EndIf} ${If} ${FileExists} "$INSTDIR\.nonadmin" SetShellVarContext Current ${Else} SetShellVarContext All ${EndIf} Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd # http://nsis.sourceforge.net/Check_for_spaces_in_a_directory_path Function CheckForSpaces ${LogSet} on Exch $R0 Push $R1 Push $R2 Push $R3 StrCpy $R1 -1 StrCpy $R3 $R0 StrCpy $R0 0 loop: StrCpy $R2 $R3 1 $R1 IntOp $R1 $R1 - 1 StrCmp $R2 "" done StrCmp $R2 " " 0 loop IntOp $R0 $R0 + 1 Goto loop done: Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEnd # http://nsis.sourceforge.net/StrCSpn,_StrCSpnReverse:_Scan_strings_for_characters Function StrCSpn ${LogSet} on Exch $R0 ; string to check Exch Exch $R1 ; string of chars Push $R2 ; current char Push $R3 ; current char Push $R4 ; char loop Push $R5 ; char loop StrCpy $R4 -1 NextChar: StrCpy $R2 $R1 1 $R4 IntOp $R4 $R4 - 1 StrCmp $R2 "" StrOK StrCpy $R5 -1 NextCharCheck: StrCpy $R3 $R0 1 $R5 IntOp $R5 $R5 - 1 StrCmp $R3 "" NextChar StrCmp $R3 $R2 0 NextCharCheck StrCpy $R0 $R2 Goto Done StrOK: StrCpy $R0 "" Done: Pop $R5 Pop $R4 Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEnd # http://stackoverflow.com/a/29569614/1170370 !macro _IsNonEmptyDirectory _a _b _t _f !insertmacro _LOGICLIB_TEMP !insertmacro _IncreaseCounter Push $0 FindFirst $0 $_LOGICLIB_TEMP "${_b}\*" _IsNonEmptyDirectory_loop${LOGICLIB_COUNTER}: StrCmp "" $_LOGICLIB_TEMP _IsNonEmptyDirectory_done${LOGICLIB_COUNTER} StrCmp "." $_LOGICLIB_TEMP +2 StrCmp ".." $_LOGICLIB_TEMP 0 _IsNonEmptyDirectory_done${LOGICLIB_COUNTER} FindNext $0 $_LOGICLIB_TEMP Goto _IsNonEmptyDirectory_loop${LOGICLIB_COUNTER} _IsNonEmptyDirectory_done${LOGICLIB_COUNTER}: FindClose $0 Pop $0 !insertmacro _!= "" $_LOGICLIB_TEMP `${_t}` `${_f}` !macroend !define IsNonEmptyDirectory `"" IsNonEmptyDirectory` Function OnDirectoryLeave ${LogSet} on ${If} ${IsNonEmptyDirectory} "$InstDir" DetailPrint "::error:: Directory '$INSTDIR' is not empty, please choose a different location." MessageBox MB_OK|MB_ICONEXCLAMATION \ "Directory '$INSTDIR' is not empty,$\n\ please choose a different location." \ /SD IDOK Abort ${EndIf} ReadRegStr $LongPathsEnabled HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" StrLen $InstDirLen "$InstDir" ${If} $CheckPathLength == "1" ${AndIf} $LongPathsEnabled == "0" ${AndIf} $InstDirLen > 46 ; With windows 10, we can enable support for long path, for earlier ; version, suggest user to use shorter installation path ${If} ${AtLeastWin10} ${AndIfNot} $ARGV_NoRegistry = "1" ; If we have admin right, we enable long path on windows ${If} ${UAC_IsAdmin} WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ; If we don't have admin right, we suggest a shorter path or suggest to run with admin right ${Else} DetailPrint "::error:: The installation path should be shorter than 46 characters or \ the installation requires administrator rights to enable long \ path on Windows." MessageBox MB_OK|MB_ICONSTOP "The installation path should be shorter than 46 characters or \ the installation requires administrator rights to enable long \ path on Windows." \ /SD IDOK Abort ${EndIf} ; If we don't have admin right, we suggest a shorter path or suggest to run with admin right ${Else} DetailPrint "::error:: The installation path should be shorter than 46 characters. \ Please choose another location." MessageBox MB_OK|MB_ICONSTOP "The installation path should be shorter than 46 characters. \ Please choose another location." \ /SD IDOK Abort ${EndIf} ${EndIf} # Call the CheckForSpaces function. Push $INSTDIR # Input string (install path). Call CheckForSpaces Pop $R0 # The function returns the number of spaces found in the input string. Push $R7 Push $R8 Push $R9 # Check if any spaces exist in $INSTDIR. StrCmp $R0 0 NoSpaces # Plural if more than 1 space in $INSTDIR. StrCmp $R0 1 0 +3 StrCpy $R1 "" Goto +2 StrCpy $R1 "s" ${If} ${Silent} StrCpy $R7 " " ${Else} StrCpy $R7 "$\n" ${EndIf} StrCpy $R8 "'Destination Folder' contains $R0 space$R1.$R7This can cause problems with several conda packages.$R7" #if check_path_spaces is True StrCpy $R8 "$R8Please remove the space$R1 from the destination folder." StrCpy $R9 "Error" #else StrCpy $R8 "$R8Please consider removing the space$R1." StrCpy $R9 "Warning" #endif # Show message box then take the user back to the Directory page. ${If} ${Silent} DetailPrint "::$R9:: $R8" ${Else} MessageBox MB_OK|MB_ICONINFORMATION "$R9: $R8" /SD IDOK ${EndIf} #if check_path_spaces is True abort #endif NoSpaces: Pop $R7 Pop $R8 Pop $R9 # List of characters not allowed anywhere in $INSTDIR Push "^%!=,()" Push $INSTDIR Call StrCSpn Pop $R0 StrCmp $R0 "" NoInvalidCharaceters DetailPrint "::error:: 'Destination Folder' contains the following invalid character: $R0" MessageBox MB_OK|MB_ICONEXCLAMATION \ "Error: 'Destination Folder' contains the following invalid character: $R0" \ /SD IDOK abort NoInvalidCharaceters: UnicodePathTest::SpecialCharPathTest $INSTDIR Pop $R1 StrCmp $R1 "nothingspecial" nothing_special_path DetailPrint "::error:: 'Destination Folder' contains the following invalid character$R1" MessageBox MB_OK|MB_ICONEXCLAMATION \ "Error: 'Destination Folder' contains the following invalid character$R1" \ /SD IDOK abort nothing_special_path: ; test if path contains unicode characters UnicodePathTest::UnicodePathTest $INSTDIR Pop $R1 # Python 3 can be installed in a CP_ACP path until MKL is Unicode capable. # (mkl_rt.dll calls LoadLibraryA() to load mkl_intel_thread.dll) # Python 2 can only be installed to an ASCII path. StrCmp $R1 "ascii" valid_path StrCmp ${PY_VER} "2.7" not_cp_acp_capable StrCmp $R1 "ascii_cp_acp" valid_path not_cp_acp_capable: DetailPrint "::error:: Due to incompatibility with several \ Python libraries, 'Destination Folder' cannot contain non-ascii characters \ (special characters or diacritics). Please choose another location." MessageBox MB_OK|MB_ICONEXCLAMATION "Error: Due to incompatibility with several \ Python libraries, 'Destination Folder' cannot contain non-ascii characters \ (special characters or diacritics). Please choose another location." \ /SD IDOK abort valid_path: Push $R1 ${IsWritable} $INSTDIR $R1 IntCmp $R1 0 pathgood Pop $R1 DetailPrint "::error: Path $INSTDIR is not writable. Please check permissions or \ try respawning the installer with elevated privileges." MessageBox MB_OK|MB_ICONEXCLAMATION \ "Error: Path $INSTDIR is not writable. Please check permissions or \ try respawning the installer with elevated privileges." \ /SD IDOK Abort pathgood: Pop $R1 FunctionEnd Function .onVerifyInstDir ${LogSet} on StrLen $0 $Desktop StrCpy $0 $INSTDIR $0 StrCmp $0 $Desktop 0 PathGood Abort PathGood: FunctionEnd Function un.OnDirectoryLeave MessageBox MB_YESNO \ "Are you sure you want to remove '$INSTDIR' and all of its contents?" \ /SD IDYES \ IDYES confirmed_yes IDNO confirmed_no confirmed_no: MessageBox MB_OK|MB_ICONSTOP "Uninstallation aborted by user." /SD IDOK Quit confirmed_yes: FunctionEnd # Make function available for both installer and uninstaller # Uninstaller functions need an `un.` prefix, so we use a macro to do both # see https://nsis.sourceforge.io/Sharing_functions_between_Installer_and_Uninstaller !macro AbortRetryNSExecWaitMacro un Function ${un}AbortRetryNSExecWait # This function expects three arguments in the stack # $1: 'WithLog' or 'NoLog': Use ExecToLog or just Exec, respectively # $2: The message to show if an error occurred # $3: The command to run, quoted # Note that the args need to be pushed to the stack in reverse order! # Search 'AbortRetryNSExecWait' in this script to see examples ${LogSet} on Pop $1 Pop $2 Pop $3 ${Do} ${If} $1 == "WithLog" nsExec::ExecToLog $3 ${ElseIf} $1 == "NoLog" nsExec::Exec $3 ${Else} DetailPrint "::error:: AbortRetryNSExecWait: 1st argument must be 'WithLog' or 'NoLog'. You used: $1" Abort ${EndIf} pop $0 ${If} $0 != "0" DetailPrint "::error:: $2" MessageBox MB_ABORTRETRYIGNORE|MB_ICONEXCLAMATION|MB_DEFBUTTON3 \ $2 /SD IDIGNORE IDABORT abort IDRETRY retry ; IDIGNORE: Continue anyway StrCpy $0 "0" goto retry abort: ; Abort installation Abort retry: ; Retry the nsExec command ${EndIf} ${LoopWhile} $0 != "0" FunctionEnd !macroend !insertmacro AbortRetryNSExecWaitMacro "" !insertmacro AbortRetryNSExecWaitMacro "un." # Installer sections Section "Install" ${LogSet} on ${If} ${Silent} call OnDirectoryLeave ${EndIf} SetOutPath "$INSTDIR\Lib" File "@NSIS_DIR@\_nsis.py" File "@NSIS_DIR@\_system_path.py" # Resolve INSTDIR so that paths and registry keys do not contain '..' or similar strings. # $0 is empty if the directory doesn't exist, but the File commands should have created it already. GetFullPathName $0 $INSTDIR ${If} $0 == "" MessageBox MB_ICONSTOP "Error resolving installation directory." /SD IDABORT Quit ${EndIf} StrCpy $INSTDIR $0 ReadEnvStr $0 SystemRoot # set PATH for the installer process, so that MSVC runtimes get found OK # This is also isolating PATH to be just us and Windows core stuff, which hopefully avoids # clashes with other stuff on PATH System::Call 'kernel32::SetEnvironmentVariable(t,t)i("PATH", \ "$INSTDIR;$INSTDIR\Library\mingw-w64\bin;$INSTDIR\Library\usr\bin;$INSTDIR\Library\bin;$INSTDIR\Scripts;$INSTDIR\bin;$0;$0\system32;$0\system32\Wbem").r0' # A conda-meta\history file is required for a valid conda prefix SetOutPath "$INSTDIR\conda-meta" File __CONDA_HISTORY__ SetOutPath "$INSTDIR" File __CONDA_EXE__ File __PRE_UNINSTALL__ # Copy extra files (code generated on winexe.py) @EXTRA_FILES@ ${If} $InstMode = ${JUST_ME} SetOutPath "$INSTDIR" FileOpen $0 ".nonadmin" w FileClose $0 ${EndIf} SetOutPath "$INSTDIR\pkgs" File __URLS_FILE__ File __URLS_TXT_FILE__ #if pre_install_exists is True File __PRE_INSTALL__ #endif File __POST_INSTALL__ File /nonfatal /r __INDEX_CACHE__ File /r __REPODATA_RECORD__ @SCRIPT_ENV_VARIABLES@ System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_SAFETY_CHECKS", "disabled").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_EXTRA_SAFETY_CHECKS", "no").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_ROOT_PREFIX", "$INSTDIR")".r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_PKGS_DIRS", "$INSTDIR\pkgs")".r0' # Extra info for pre and post install scripts # NOTE: If more vars are added, make sure to update the examples/scripts tests too # There's a similar block for the pre_uninstall script, further down this file. # Update that one as well! System::Call 'kernel32::SetEnvironmentVariable(t,t)i("PREFIX", "$INSTDIR").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_NAME", "${NAME}").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_VER", "${VERSION}").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_PLAT", "${PLATFORM}").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_TYPE", "EXE").r0' @PKG_COMMANDS@ SetDetailsPrint TextOnly DetailPrint "Setting up the package cache..." push '"$INSTDIR\_conda.exe" constructor --prefix "$INSTDIR" --extract-conda-pkgs' push 'Failed to extract packages' push 'NoLog' # We use NoLog here because TQDM progress bars are parsed as a single line in NSIS 3.08 # These can crash the installer if they get too long (a few packages is enough!) call AbortRetryNSExecWait SetDetailsPrint both IfFileExists "$INSTDIR\pkgs\pre_install.bat" 0 NoPreInstall DetailPrint "Running pre_install scripts..." ReadEnvStr $5 SystemRoot ReadEnvStr $6 windir # This 'FileExists' also returns True for directories ${If} ${FileExists} "$5" push '"$5\System32\cmd.exe" /D /C "$INSTDIR\pkgs\pre_install.bat"' ${ElseIf} ${FileExists} "$6" push '"$6\System32\cmd.exe" /D /C "$INSTDIR\pkgs\pre_install.bat"' ${Else} # Cross our fingers CMD is in PATH push 'cmd.exe /D /C "$INSTDIR\pkgs\pre_install.bat"' ${EndIf} push "Failed to run pre_install" push 'WithLog' call AbortRetryNSExecWait NoPreInstall: @SETUP_ENVS@ @WRITE_CONDARC@ AddSize @SIZE@ #if has_conda is True DetailPrint "Initializing conda directories..." push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" mkdirs' push 'Failed to initialize conda directories' push 'WithLog' call AbortRetryNSExecWait #endif ${If} $Ana_PostInstall_State = ${BST_CHECKED} DetailPrint "Running post install..." push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" post_install' push 'Failed to run post install script' push 'WithLog' call AbortRetryNSExecWait ${EndIf} ${If} $Ana_ClearPkgCache_State = ${BST_CHECKED} DetailPrint "Clearing package cache..." push '"$INSTDIR\_conda.exe" clean --all --yes' push 'Failed to clear package cache' push 'WithLog' call AbortRetryNSExecWait ${EndIf} ${If} $Ana_AddToPath_State = ${BST_CHECKED} DetailPrint "Adding to PATH..." push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" addpath ${PYVERSION} ${NAME} ${VERSION} ${ARCH}' push 'Failed to add @NAME@ to the system PATH' push 'WithLog' call AbortRetryNSExecWait ${EndIf} # Create registry entries saying this is the system Python # (for this version) !define PYREG "Software\Python\PythonCore\${PY_VER}" ${If} $Ana_RegisterSystemPython_State == ${BST_CHECKED} WriteRegStr SHCTX "${PYREG}\Help\Main Python Documentation" \ "Main Python Documentation" \ "$INSTDIR\Doc\python${PYVERSION_JUSTDIGITS}.chm" WriteRegStr SHCTX "${PYREG}\InstallPath" "" "$INSTDIR" WriteRegStr SHCTX "${PYREG}\InstallPath\InstallGroup" \ "" "Python ${PY_VER}" WriteRegStr SHCTX "${PYREG}\Modules" "" "" WriteRegStr SHCTX "${PYREG}\PythonPath" \ "" "$INSTDIR\Lib;$INSTDIR\DLLs" ${EndIf} ${If} $ARGV_NoRegistry == "0" # Delete registry entries for environment variables set by PothosSDR # With admin rights, we can delete them ${If} ${UAC_IsAdmin} DetailPrint "Deleting PothosSDR registry environment variables..." !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' DeleteRegValue ${env_hklm} "GR_PREFIX" DeleteRegValue ${env_hklm} "GRC_BLOCKS_PATH" DeleteRegValue ${env_hklm} "UHD_PKG_PATH" DeleteRegValue ${env_hklm} "VOLK_PREFIX" # Without admin rights, we have to shadow them with empty values set for the user ${Else} DetailPrint "Overriding PothosSDR registry environment variables for user..." !define env_hkcu 'HKCU "Environment"' WriteRegExpandStr ${env_hkcu} "GR_PREFIX" "" WriteRegExpandStr ${env_hkcu} "GRC_BLOCKS_PATH" "" WriteRegExpandStr ${env_hkcu} "UHD_PKG_PATH" "" WriteRegExpandStr ${env_hkcu} "VOLK_PREFIX" "" ${EndIf} SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 # Registry uninstall info WriteRegStr SHCTX "${UNINSTREG}" "DisplayName" "${UNINSTALL_NAME}" WriteRegStr SHCTX "${UNINSTREG}" "DisplayVersion" "${VERSION}" WriteRegStr SHCTX "${UNINSTREG}" "Publisher" "${COMPANY}" WriteRegStr SHCTX "${UNINSTREG}" "UninstallString" \ "$\"$INSTDIR\Uninstall-${NAME}.exe$\"" WriteRegStr SHCTX "${UNINSTREG}" "QuietUninstallString" \ "$\"$INSTDIR\Uninstall-${NAME}.exe$\" /S" WriteRegStr SHCTX "${UNINSTREG}" "DisplayIcon" \ "$\"$INSTDIR\Uninstall-${NAME}.exe$\"" WriteRegDWORD SHCTX "${UNINSTREG}" "NoModify" 1 WriteRegDWORD SHCTX "${UNINSTREG}" "NoRepair" 1 ${EndIf} WriteUninstaller "$INSTDIR\Uninstall-${NAME}.exe" # To address CVE-2022-26526. # Revoke the write permission on directory "$INSTDIR" for Users if this is # being run with administrative privileges. Users are: # AU - authenticated users # BU - built-in (local) users # DU - domain users ${If} ${UAC_IsAdmin} DetailPrint "Setting installation directory permissions..." AccessControl::DisableFileInheritance "$INSTDIR" AccessControl::RevokeOnFile "$INSTDIR" "(AU)" "GenericWrite" AccessControl::RevokeOnFile "$INSTDIR" "(DU)" "GenericWrite" AccessControl::RevokeOnFile "$INSTDIR" "(BU)" "GenericWrite" AccessControl::SetOnFile "$INSTDIR" "(BU)" "GenericRead + GenericExecute" AccessControl::SetOnFile "$INSTDIR" "(DU)" "GenericRead + GenericExecute" ${EndIf} SectionEnd !macro AbortRetryNSExecWaitLibNsisCmd cmd SetDetailsPrint both DetailPrint "Running ${cmd} scripts..." SetDetailsPrint listonly ${If} ${Silent} push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" ${cmd}' ${Else} push '"$INSTDIR\python.exe" -E -s "$INSTDIR\Lib\_nsis.py" ${cmd}' ${EndIf} push "Failed to run ${cmd}" push 'WithLog' call un.AbortRetryNSExecWait SetDetailsPrint both !macroend Section "Uninstall" ${LogSet} on # Remove menu items, path entries System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_ROOT_PREFIX", "$INSTDIR")".r0' @UNINSTALL_MENUS@ # ensure that MSVC runtime DLLs are on PATH during uninstallation ReadEnvStr $0 PATH # set PATH for the installer process, so that MSVC runtimes get found OK System::Call 'kernel32::SetEnvironmentVariable(t,t)i("PATH", \ "$INSTDIR;$INSTDIR\Library\mingw-w64\bin;$INSTDIR\Library\usr\bin;$INSTDIR\Library\bin;$INSTDIR\Scripts;$INSTDIR\bin;$0;$0\system32;$0\system32\Wbem").r0' # our newest Python builds have a patch that allows us to control the PATH search stuff much more # carefully. More info at https://docs.conda.io/projects/conda/en/latest/user-guide/troubleshooting.html#solution System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_DLL_SEARCH_MODIFICATION_ENABLE", "1").r0' # Read variables the uninstaller needs from the registry StrCpy $R0 "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" StrLen $R1 "Uninstall-${NAME}.exe" IntOp $R1 $R1 + 3 StrCpy $0 0 loop_path: EnumRegKey $1 SHCTX $R0 $0 StrCmp $1 "" endloop_path StrCpy $2 "$R0\$1" ReadRegStr $4 SHCTX $2 "UninstallString" StrLen $5 $4 IntOp $5 $5 - $R1 StrCpy $4 $4 $5 1 ${If} $4 == $INSTDIR StrCpy $INSTALLER_NAME_FULL $1 ReadRegStr $INSTALLER_VERSION SHCTX $2 "DisplayVersion" goto endloop_path ${EndIf} IntOp $0 $0 + 1 goto loop_path endloop_path: # Extra info for pre_uninstall scripts System::Call 'kernel32::SetEnvironmentVariable(t,t)i("PREFIX", "$INSTDIR").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_NAME", "${NAME}").r0' StrCpy $0 ${VERSION} ${If} $INSTALLER_VERSION != "" StrCpy $0 $INSTALLER_VERSION ${EndIf} System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_VER", "$0").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_PLAT", "${PLATFORM}").r0' System::Call 'kernel32::SetEnvironmentVariable(t,t)i("INSTALLER_TYPE", "EXE").r0' !insertmacro AbortRetryNSExecWaitLibNsisCmd "pre_uninstall" !insertmacro AbortRetryNSExecWaitLibNsisCmd "rmpath" !insertmacro AbortRetryNSExecWaitLibNsisCmd "rmreg" DetailPrint "Removing files and folders..." nsExec::Exec 'cmd.exe /D /C RMDIR /Q /S "$INSTDIR"' # In case the last command fails, run the slow method to remove leftover RMDir /r /REBOOTOK "$INSTDIR" # Delete user environment variables that we set during installation ${IfNot} ${UAC_IsAdmin} DeleteRegValue ${env_hkcu} "GR_PREFIX" DeleteRegValue ${env_hkcu} "GRC_BLOCKS_PATH" DeleteRegValue ${env_hkcu} "UHD_PKG_PATH" DeleteRegValue ${env_hkcu} "VOLK_PREFIX" SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 ${EndIf} ${If} $INSTALLER_NAME_FULL != "" DeleteRegKey SHCTX "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$INSTALLER_NAME_FULL" ${EndIf} # If Anaconda was registered as the official Python for this version, # remove it from the registry StrCpy $R0 "SOFTWARE\Python\PythonCore" StrCpy $0 0 loop_py: EnumRegKey $1 SHCTX $R0 $0 StrCmp $1 "" endloop_py ReadRegStr $2 SHCTX "$R0\$1\InstallPath" "" ${If} $2 == $INSTDIR StrCpy $R1 $1 DeleteRegKey SHCTX "$R0\$1" goto endloop_py ${EndIf} IntOp $0 $0 + 1 goto loop_py endloop_py: SectionEnd !if '@SIGNTOOL_COMMAND@' != '' # Signing for installer and uninstaller; nsis 3.08 required for uninstfinalize! # "= 0" comparison required to prevent both tasks running in parallel, which would cause signtool to fail # %1 is replaced by the installer and uninstaller paths, respectively !finalize '@SIGNTOOL_COMMAND@ "%1"' = 0 !uninstfinalize '@SIGNTOOL_COMMAND@ "%1"' = 0 !endif