Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
452dd15
Remove unused utility functions
marcoesters Nov 6, 2025
73d0574
Ensure that log file is UTF-16
marcoesters Nov 7, 2025
25b4115
Only start logging after INSTDIR has been created
marcoesters Nov 7, 2025
7447266
Stop logging before INSTDIR is removed to allow removal without reboot
marcoesters Nov 7, 2025
795a3f1
Move registry clean-up to the beginning of the uninstallation
marcoesters Nov 7, 2025
e928f96
Prevent variable overwrite with Print macro
marcoesters Nov 7, 2025
690c2cf
Use R* registers when using Print macro
marcoesters Nov 10, 2025
02429fb
Only add newline to print when input string does not end in a linefeed
marcoesters Nov 10, 2025
75abca8
Implement step log parsing
marcoesters Nov 10, 2025
fd720be
Wrap commands into cmd.exe call for better error output
marcoesters Nov 10, 2025
9c43c6d
Add STEP_LOG defintion
marcoesters Nov 10, 2025
f93f151
Enable logging for conda-standalone
marcoesters Nov 11, 2025
21cd895
Require conda-standalone 24.1.2 for logged package extraction
marcoesters Nov 11, 2025
3cc301b
Add comment to explain usage of subshells for command execution
marcoesters Nov 11, 2025
9679e95
Merge branch 'main' of https://github.com/conda/constructor into add-…
marcoesters Nov 11, 2025
b536fa0
Add message box to icacls failure
marcoesters Nov 11, 2025
723c5bb
Do not use pythonw.exe anymore
marcoesters Nov 11, 2025
5bddb4a
Remove misleading warning about old paths
marcoesters Nov 11, 2025
4c014f2
Add news
marcoesters Nov 11, 2025
733915b
Account for unknown conda.exe types
marcoesters Nov 11, 2025
752f740
Make abort default for silent installations
marcoesters Nov 11, 2025
35cef88
Do not assume that install.log exists after uninstallation
marcoesters Nov 11, 2025
4973274
Re-add DetailPrint to Print macro to output to log
marcoesters Nov 12, 2025
bf96a4a
Merge branch 'main' of https://github.com/conda/constructor into add-…
marcoesters Nov 21, 2025
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
28 changes: 26 additions & 2 deletions constructor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import sys
from os.path import abspath, expanduser, isdir, join
from pathlib import Path
from tempfile import TemporaryDirectory
from textwrap import dedent

from . import __version__
Expand Down Expand Up @@ -77,7 +78,22 @@ def get_output_filename(info):
)


def _win_install_needs_python_exe(conda_exe: str) -> bool:
def _conda_exe_supports_logging(conda_exe: str, conda_exe_type: StandaloneExe | None) -> bool:
"""Test if the standalone binary supports the the --log-file argument.

Only available for conda-standalone.
"""
if not conda_exe_type:
return False
with TemporaryDirectory() as tmpdir:
logfile = Path(tmpdir, "conda.log")
subprocess.run([conda_exe, "--version", f"--log-file={logfile}"])
return logfile.exists()


def _win_install_needs_python_exe(conda_exe: str, conda_exe_type: StandaloneExe | None) -> bool:
if not conda_exe_type:
return True
results = subprocess.run(
[conda_exe, "constructor", "windows", "--help"],
capture_output=True,
Expand Down Expand Up @@ -270,6 +286,11 @@ def is_conda_meta_frozen(path_str: str) -> bool:
else:
info["_ignore_condarcs_arg"] = ""

info["_conda_exe_supports_logging"] = _conda_exe_supports_logging(
info["_conda_exe"],
info["_conda_exe_type"],
)

if "pkg" in itypes:
if (domains := info.get("pkg_domains")) is not None:
domains = {key: str(val).lower() for key, val in domains.items()}
Expand All @@ -289,7 +310,10 @@ def is_conda_meta_frozen(path_str: str) -> bool:
}

if osname == "win":
info["_win_install_needs_python_exe"] = _win_install_needs_python_exe(info["_conda_exe"])
info["_win_install_needs_python_exe"] = _win_install_needs_python_exe(
info["_conda_exe"],
info["_conda_exe_type"],
)

info["installer_type"] = itypes[0]
fcp_main(info, verbose=verbose, dry_run=dry_run, conda_exe=conda_exe)
Expand Down
123 changes: 0 additions & 123 deletions constructor/nsis/Utils.nsh
Original file line number Diff line number Diff line change
@@ -1,128 +1,5 @@
# Miscellaneous helpers.

# We're not using RIndexOf at the moment, so ifdef it out for now (which
# prevents the compiler warnings about an unused function).
!ifdef INDEXOF
Function IndexOf
Exch $R0
Exch
Exch $R1
Push $R2
Push $R3

StrCpy $R3 $R0
StrCpy $R0 -1
IntOp $R0 $R0 + 1

StrCpy $R2 $R3 1 $R0
StrCmp $R2 "" +2
StrCmp $R2 $R1 +2 -3

StrCpy $R0 -1

Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd

!macro IndexOf Var Str Char
Push "${Char}"
Push "${Str}"
Call IndexOf
Pop "${Var}"
!macroend
!define IndexOf "!insertmacro IndexOf"

Function RIndexOf
Exch $R0
Exch
Exch $R1
Push $R2
Push $R3

StrCpy $R3 $R0
StrCpy $R0 0
IntOp $R0 $R0 + 1
StrCpy $R2 $R3 1 -$R0
StrCmp $R2 "" +2
StrCmp $R2 $R1 +2 -3

StrCpy $R0 -1

Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd

!macro RIndexOf Var Str Char
Push "${Char}"
Push "${Str}"
Call RIndexOf
Pop "${Var}"
!macroend

!define RIndexOf "!insertmacro RIndexOf"
!endif

!macro StrStr
Exch $R1 ; st=haystack,old$R1, $R1=needle
Exch ; st=old$R1,haystack
Exch $R2 ; st=old$R1,old$R2, $R2=haystack
Push $R3
Push $R4
Push $R5
StrLen $R3 $R1
StrCpy $R4 0
; $R1=needle
; $R2=haystack
; $R3=len(needle)
; $R4=cnt
; $R5=tmp
loop:
StrCpy $R5 $R2 $R3 $R4
StrCmp $R5 $R1 done
StrCmp $R5 "" done
IntOp $R4 $R4 + 1
Goto loop
done:
StrCpy $R1 $R2 "" $R4
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Exch $R1
!macroend

!macro GetShortPathName
Pop $0
# Return the 8.3 short path name for $0. We ensure $0 exists by calling
# SetOutPath first (kernel32::GetShortPathName() fails otherwise).
SetOutPath $0
Push $0
Push ' '
Call StrStr
Pop $1
${If} $1 != ""
# Our installation directory has a space, so use the short name from
# here in. (This ensures no directories with spaces are written to
# registry values or configuration files.) After GetShortPathName(),
# $0 will have the new name and $1 will have the length (if it's 0,
# assume an error occurred and leave $INSTDIR as it is).
System::Call "kernel32::GetShortPathName(\
t'$RootDir', \
t.R0, \
i${NSIS_MAX_STRLEN}) i.R1"

${If} $R1 > 0
Push $R0
${EndIf}
${Else}
Push $0
${EndIf}
!macroend

; Slightly modified version of http://nsis.sourceforge.net/IsWritable
Function IsWritable
!define IsWritable `!insertmacro IsWritableCall`
Expand Down
19 changes: 1 addition & 18 deletions constructor/nsis/_nsis.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,7 @@ def gui_excepthook(exctype, value, tb):

sys.excepthook = gui_excepthook

# If pythonw is being run, there may be no write function
if sys.stdout and sys.stdout.write:
out = sys.stdout.write
err = sys.stderr.write
else:
import ctypes
OutputDebugString = ctypes.windll.kernel32.OutputDebugStringW
OutputDebugString.argtypes = [ctypes.c_wchar_p]

def out(x):
OutputDebugString('_nsis.py: ' + x)

def err(x):
OutputDebugString('_nsis.py: Error: ' + x)


allusers = (not exists(join(ROOT_PREFIX, '.nonadmin')))
# out('allusers is %s\n' % allusers)

# This must be the same as conda's binpath_from_arg() in conda/cli/activate.py
PATH_SUFFIXES = ('',
Expand Down Expand Up @@ -96,7 +79,7 @@ def add_to_path(pyversion, arch):
except IOError:
old_prefixes = []
for prefix in old_prefixes:
out('Removing old installation at %s from PATH (if any entries get found)\n' % (prefix))
print('Removing old installation at %s from PATH (if any entries get found)\n' % (prefix))
remove_from_path(prefix)

# add Anaconda to the path
Expand Down
23 changes: 1 addition & 22 deletions constructor/nsis/_system_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,10 @@
import ctypes
import os
import re
import sys
from ctypes import wintypes
from os import path

if sys.version_info[0] >= 3:
import winreg as reg
else:
import _winreg as reg

# If pythonw is being run, there may be no write function
if sys.stdout and sys.stdout.write:
out = sys.stdout.write
err = sys.stderr.write
else:
OutputDebugString = ctypes.windll.kernel32.OutputDebugStringW
OutputDebugString.argtypes = [ctypes.c_wchar_p]

def out(x):
OutputDebugString('_nsis.py: ' + x)

def err(x):
OutputDebugString('_nsis.py: Error: ' + x)
import winreg as reg

HWND_BROADCAST = 0xffff
WM_SETTINGCHANGE = 0x001A
Expand Down Expand Up @@ -159,9 +141,6 @@ def add_to_system_path(paths, allusers=True, path_env_var='PATH'):
final_value = final_value.replace('"', '')
# Warn about directories that do not exist.
directories = final_value.split(';')
for directory in directories:
if '%' not in directory and not os.path.exists(directory):
out("WARNING: Old PATH entry '%s' does not exist\n" % (directory))
reg.SetValueEx(key, path_env_var, 0, reg_type, final_value)

finally:
Expand Down
Loading
Loading