2023-07-27 18:55:32 -04:00
|
|
|
|
# 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__
|
2024-01-22 12:03:29 -05:00
|
|
|
|
!define ENABLE_SHORTCUTS __ENABLE_SHORTCUTS__
|
2023-07-27 18:55:32 -04:00
|
|
|
|
!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
|
|
|
|
|
|
2023-11-06 15:52:07 -05:00
|
|
|
|
#if custom_conclusion
|
2023-07-27 18:55:32 -04:00
|
|
|
|
# Custom conclusion file(s)
|
|
|
|
|
@CUSTOM_CONCLUSION_FILE@
|
2023-11-06 15:52:07 -05:00
|
|
|
|
#else
|
2023-07-27 18:55:32 -04:00
|
|
|
|
!insertmacro MUI_PAGE_FINISH
|
2023-11-06 15:52:07 -05:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-07-27 18:55:32 -04:00
|
|
|
|
|
|
|
|
|
!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}
|
|
|
|
|
|
2024-01-22 12:03:29 -05:00
|
|
|
|
${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}
|
2023-07-27 18:55:32 -04:00
|
|
|
|
${EndIf}
|
2024-01-22 12:03:29 -05:00
|
|
|
|
${Else}
|
|
|
|
|
StrCpy $Ana_CreateShortcuts_State ${BST_UNCHECKED}
|
2023-07-27 18:55:32 -04:00
|
|
|
|
${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
|
2023-11-06 15:52:07 -05:00
|
|
|
|
Push "^%!=,()"
|
2023-07-27 18:55:32 -04:00
|
|
|
|
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__
|
|
|
|
|
|
2023-11-06 15:52:07 -05:00
|
|
|
|
@SCRIPT_ENV_VARIABLES@
|
2023-07-27 18:55:32 -04:00
|
|
|
|
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'
|
2024-01-22 12:03:29 -05:00
|
|
|
|
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_ROOT_PREFIX", "$INSTDIR")".r0'
|
2023-07-27 18:55:32 -04:00
|
|
|
|
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 --force-pkgs-dirs --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"
|
|
|
|
|
# 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"
|
2024-01-22 12:03:29 -05:00
|
|
|
|
${LogSet} on
|
2023-07-27 18:55:32 -04:00
|
|
|
|
|
2024-01-22 12:03:29 -05:00
|
|
|
|
# Remove menu items, path entries
|
|
|
|
|
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_ROOT_PREFIX", "$INSTDIR")".r0'
|
|
|
|
|
@UNINSTALL_MENUS@
|
2023-07-27 18:55:32 -04:00
|
|
|
|
|
|
|
|
|
# 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"
|
|
|
|
|
|
|
|
|
|
${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
|