Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Report installation messages to console #847

Merged
merged 22 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 114 additions & 45 deletions constructor/nsis/main.nsi.tmpl
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Installer template file for creating a Windows installer using NSIS.

# Dependencies:
# NSIS >=3.08 conda install "nsis>=3.08" (includes extra unicode plugins)
!if "${NSIS_PACKEDVERSION}" < 0x3008000
!error "NSIS 3.08 or higher is required to build this installer!"
# conda install "nsis>=3.08" (includes extra unicode plugins)
!endif

Unicode "true"
Unicode true

#if enable_debugging is True
# Special logging build needed for ENABLE_LOGGING
Expand All @@ -26,6 +28,32 @@ Unicode "true"
!endif
!macroend

!define Print "!insertmacro PrintMacro"
!macro PrintMacro INPUT_TEXT
DetailPrint "${INPUT_TEXT}"
${If} ${Silent}
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::Call 'kernel32::AttachConsole(i -1)i.r1'
${If} $0 = 0
${OrIf} $1 = 0
System::Call 'kernel32::AllocConsole()'
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
FileWrite $0 "${INPUT_TEXT}$\n"
${EndIf}
${EndIf}
!macroend

!define CloseStdout "!insertmacro CloseStdout"
!macro CloseStdout
${If} ${Silent}
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
FileClose $0
System::Free $0
System::Free $1
System::Call 'kernel32::FreeConsole()'
${EndIf}
!macroend

!include "WinMessages.nsh"
!include "WordFunc.nsh"
!include "LogicLib.nsh"
Expand Down Expand Up @@ -224,34 +252,57 @@ FunctionEnd
${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\
SetSilent silent
${Print} "\
Installs ${NAME} ${VERSION}$\n\
$\n\
USAGE$\n\
-----$\n\
$\n\
$EXEFILE [options]$\n\
$\n\
OPTIONS$\n\
-------$\n\
$\n\
/InstallationType=AllUsers [default: JustMe]$\n\
/AddToPath=[0|1] [default: 0]$\n\
#if keep_pkgs is True
/KeepPkgCache=[0|1] [default: 1]$\n$\n\
/KeepPkgCache=[0|1] [default: 1]$\n\
#endif
#if keep_pkgs is False
/KeepPkgCache=[0|1] [default: 0]$\n$\n\
/KeepPkgCache=[0|1] [default: 0]$\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\
/RegisterPython=[0|1] [default: AllUsers: 1, JustMe: 0]$\n\
/NoRegistry=[0|1] [default: AllUsers: 0, JustMe: 0]$\n\
/NoScripts=[0|1] [default: 0]$\n\
/NoShortcuts=[0|1] [default: 0]$\n\
/CheckPathLength=[0|1] [default: 1]$\n\
/? (show this help message)$\n\
/S (run in CLI/headless mode)$\n\
/D=[installation directory] (must be last parameter)$\n"
# There seems to be a limit to how many chars per ${Print} we can pass.
# The message will get truncated silently, no errors.
# That's why we split the help message in two calls.
${Print} "\
EXAMPLES$\n\
--------$\n\
$\n\
Install for all users, but don't add to PATH env var:$\n\
$EXEFILE /InstallationType=AllUsers$\n$\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\
> $EXEFILE /RegisterPython=1 /AddToPath=1$\n\
$\n\
Install for just me, with no registry modification (for CI):$\n\
$EXEFILE /NoRegistry=1$\n$\n\
> $EXEFILE /NoRegistry=1$\n\
$\n\
Install via CLI (no GUI) into C:\${NAME}$\n\
> cmd /C START /WAIT $EXEFILE /S /D=C:\${NAME}$\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
is disabled (i.e. if /InstallationType=AllUsers, then$\n\
/AddToPath=1 will be ignored)."
Abort
${EndIf}

ClearErrors
Expand Down Expand Up @@ -336,6 +387,7 @@ Function OnInit_Release
# 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
${Print} "/AddToPath=1 is disabled and ignored in 'All Users' installations"
StrCpy $Ana_AddToPath_State ${BST_UNCHECKED}
${Else}
StrCpy $Ana_AddToPath_State ${BST_CHECKED}
Expand Down Expand Up @@ -671,6 +723,8 @@ Function .onInit

Call OnInit_Release

${Print} "Welcome to ${NAME} ${VERSION}$\n"

Pop $R2
Pop $R1
Pop $2
Expand Down Expand Up @@ -727,6 +781,7 @@ Function un.onInit
goto valid_dir

invalid_dir:
${Print} "::error:: $INSTDIR is not a valid conda directory. Please run the uninstaller from a conda directory."
MessageBox MB_OK|MB_ICONSTOP \
"Error: $INSTDIR is not a valid conda directory. Please run the uninstaller from a conda directory." \
/SD IDABORT
Expand Down Expand Up @@ -848,7 +903,7 @@ Pop $0
Function OnDirectoryLeave
${LogSet} on
${If} ${IsNonEmptyDirectory} "$InstDir"
DetailPrint "::error:: Directory '$INSTDIR' is not empty, please choose a different location."
${Print} "::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." \
Expand All @@ -871,7 +926,7 @@ Function OnDirectoryLeave
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 \
${Print} "::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 \
Expand All @@ -882,7 +937,7 @@ Function OnDirectoryLeave
${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. \
${Print} "::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." \
Expand Down Expand Up @@ -923,7 +978,7 @@ Function OnDirectoryLeave
#endif
# Show message box then take the user back to the Directory page.
${If} ${Silent}
DetailPrint "::$R9:: $R8"
${Print} "::$R9:: $R8"
${Else}
MessageBox MB_OK|MB_ICONINFORMATION "$R9: $R8" /SD IDOK
${EndIf}
Expand All @@ -942,7 +997,7 @@ Function OnDirectoryLeave
Pop $R0

StrCmp $R0 "" NoInvalidCharaceters
DetailPrint "::error:: 'Destination Folder' contains the following invalid character: $R0"
${Print} "::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
Expand All @@ -952,7 +1007,7 @@ Function OnDirectoryLeave
UnicodePathTest::SpecialCharPathTest $INSTDIR
Pop $R1
StrCmp $R1 "nothingspecial" nothing_special_path
DetailPrint "::error:: 'Destination Folder' contains the following invalid character$R1"
${Print} "::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
Expand All @@ -970,7 +1025,7 @@ Function OnDirectoryLeave
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 \
${Print} "::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 \
Expand All @@ -985,7 +1040,7 @@ Function OnDirectoryLeave
${IsWritable} $INSTDIR $R1
IntCmp $R1 0 pathgood
Pop $R1
DetailPrint "::error: Path $INSTDIR is not writable. Please check permissions or \
${Print} "::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 \
Expand Down Expand Up @@ -1039,12 +1094,12 @@ FunctionEnd
${ElseIf} $1 == "NoLog"
nsExec::Exec $3
${Else}
DetailPrint "::error:: AbortRetryNSExecWait: 1st argument must be 'WithLog' or 'NoLog'. You used: $1"
${Print} "::error:: AbortRetryNSExecWait: 1st argument must be 'WithLog' or 'NoLog'. You used: $1"
Abort
${EndIf}
pop $0
${If} $0 != "0"
DetailPrint "::error:: $2"
${Print} "::error:: $2"
MessageBox MB_ABORTRETRYIGNORE|MB_ICONEXCLAMATION|MB_DEFBUTTON3 \
$2 /SD IDIGNORE IDABORT abort IDRETRY retry
; IDIGNORE: Continue anyway
Expand All @@ -1065,7 +1120,6 @@ FunctionEnd
# Installer sections
Section "Install"
${LogSet} on

${If} ${Silent}
call OnDirectoryLeave
${EndIf}
Expand All @@ -1083,13 +1137,26 @@ Section "Install"
${EndIf}
StrCpy $INSTDIR $0

#if has_license
SetOutPath "$INSTDIR"
File __LICENSEFILE__
${Print} "By continuing this installation you are accepting this license agreement:"
${Print} "$INSTDIR\@LICENSEFILENAME@"
${Print} "Please run the installer in GUI mode to read the details.$\n"
#endif

${Print} "${NAME} will now be installed into this location:"
${Print} "$INSTDIR$\n"

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'

${Print} "Unpacking payload..."

# A conda-meta\history file is required for a valid conda prefix
SetOutPath "$INSTDIR\conda-meta"
File __CONDA_HISTORY__
Expand Down Expand Up @@ -1138,9 +1205,9 @@ Section "Install"
# https://github.com/conda/conda-libmamba-solver/issues/480
System::Call 'kernel32::SetEnvironmentVariable(t,t)i("CONDA_SOLVER", "classic").r0'
SetDetailsPrint TextOnly
DetailPrint "Checking virtual specs..."
${Print} "Checking virtual specs compatibility: @VIRTUAL_SPECS_DEBUG@"
push '"$INSTDIR\_conda.exe" create --dry-run --prefix "$INSTDIR\envs\_virtual_specs_checks" --offline @VIRTUAL_SPECS@'
push 'Failed to check virtual specs: @VIRTUAL_SPECS@'
push 'Failed to check virtual specs: @VIRTUAL_SPECS_DEBUG@'
push 'WithLog'
call AbortRetryNSExecWait
SetDetailsPrint both
Expand All @@ -1150,7 +1217,7 @@ Section "Install"
@PKG_COMMANDS@

SetDetailsPrint TextOnly
DetailPrint "Setting up the package cache..."
${Print} "Setting up the package cache..."
push '"$INSTDIR\_conda.exe" constructor --prefix "$INSTDIR" --extract-conda-pkgs'
push 'Failed to extract packages'
push 'NoLog'
Expand All @@ -1160,7 +1227,7 @@ Section "Install"
SetDetailsPrint both

IfFileExists "$INSTDIR\pkgs\pre_install.bat" 0 NoPreInstall
DetailPrint "Running pre_install scripts..."
${Print} "Running pre_install scripts..."
ReadEnvStr $5 SystemRoot
ReadEnvStr $6 windir
# This 'FileExists' also returns True for directories
Expand All @@ -1184,31 +1251,31 @@ Section "Install"
AddSize @SIZE@

#if has_conda is True
DetailPrint "Initializing conda directories..."
${Print} "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..."
${Print} "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..."
${Print} "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..."
${Print} "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'
Expand Down Expand Up @@ -1258,19 +1325,21 @@ Section "Install"
# BU - built-in (local) users
# DU - domain users
${If} ${UAC_IsAdmin}
DetailPrint "Setting installation directory permissions..."
${Print} "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}
${Print} "Done!"
${CloseStdout}
SectionEnd

!macro AbortRetryNSExecWaitLibNsisCmd cmd
SetDetailsPrint both
DetailPrint "Running ${cmd} scripts..."
${Print} "Running ${cmd} scripts..."
SetDetailsPrint listonly
${If} ${Silent}
push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" ${cmd}'
Expand Down Expand Up @@ -1337,7 +1406,7 @@ Section "Uninstall"
!insertmacro AbortRetryNSExecWaitLibNsisCmd "rmpath"
!insertmacro AbortRetryNSExecWaitLibNsisCmd "rmreg"

DetailPrint "Removing files and folders..."
${Print} "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
Expand Down
2 changes: 1 addition & 1 deletion constructor/shar.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def read_header_template():
def get_header(conda_exec, tarball, info):
name = info['name']

has_license = bool('license_file' in info)
has_license = bool(info.get('license_file'))
ppd = ns_platform(info['_platform'])
ppd['keep_pkgs'] = bool(info.get('keep_pkgs', False))
ppd['batch_mode'] = bool(info.get('batch_mode', False))
Expand Down
Loading
Loading