mirror of
https://github.com/ryanvolz/radioconda.git
synced 2024-11-14 08:11:48 -05:00
1389 lines
46 KiB
Cheetah
1389 lines
46 KiB
Cheetah
# 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
|