- 
                Notifications
    You must be signed in to change notification settings 
- Fork 174
Remove custom python commands from EXE installers #1089
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
base: main
Are you sure you want to change the base?
Changes from all commits
31e99e3
              4b71bb6
              6234d6c
              65f4c94
              9eed660
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -15,21 +15,26 @@ | |
| import json | ||
| import logging | ||
| import os | ||
| import subprocess | ||
| import sys | ||
| from os.path import abspath, expanduser, isdir, join | ||
| from pathlib import Path | ||
| from textwrap import dedent | ||
| from typing import TYPE_CHECKING | ||
|  | ||
| from . import __version__ | ||
| from .build_outputs import process_build_outputs | ||
| from .conda_interface import SUPPORTED_PLATFORMS, cc_platform | ||
| from .conda_interface import SUPPORTED_PLATFORMS, MatchSpec, cc_platform | ||
| from .conda_interface import VersionOrder as Version | ||
| from .construct import SCHEMA_PATH, ns_platform | ||
| from .construct import parse as construct_parse | ||
| from .construct import verify as construct_verify | ||
| from .fcp import main as fcp_main | ||
| from .utils import StandaloneExe, check_version, identify_conda_exe, normalize_path, yield_lines | ||
|  | ||
| if TYPE_CHECKING: | ||
| from typing import Any | ||
|  | ||
| DEFAULT_CACHE_DIR = os.getenv("CONSTRUCTOR_CACHE", "~/.conda/constructor") | ||
|  | ||
| logger = logging.getLogger(__name__) | ||
|  | @@ -76,6 +81,24 @@ def get_output_filename(info): | |
| ) | ||
|  | ||
|  | ||
| def _base_needs_python(info: dict[str, Any]) -> bool: | ||
| if sys.platform == "win32" and info.get("_conda_exe_type") == StandaloneExe.CONDA: | ||
| conda_exe = info["_conda_exe"] | ||
| results = subprocess.run( | ||
| [conda_exe, "constructor", "windows", "--help"], | ||
| capture_output=True, | ||
| check=False, | ||
| ) | ||
| if results.returncode == 2: | ||
| return True | ||
| specs = {MatchSpec(spec) for spec in info.get("specs", ())} | ||
| # mamba 2 and newer does not need Python. However, without running the solver, | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should assume we'd get  | ||
| # we won't know for sure which version of mamba is requested | ||
| if sys.platform == "linux" and "mamba" in {spec.name for spec in specs}: | ||
| return True | ||
| return False | ||
|  | ||
|  | ||
| def main_build( | ||
| dir_path, | ||
| output_dir=".", | ||
|  | @@ -275,6 +298,8 @@ def is_conda_meta_frozen(path_str: str) -> bool: | |
| "enable_currentUserHome": "true", | ||
| } | ||
|  | ||
| info["_base_needs_python"] = _base_needs_python(info) | ||
|  | ||
| info["installer_type"] = itypes[0] | ||
| fcp_main(info, verbose=verbose, dry_run=dry_run, conda_exe=conda_exe) | ||
| if dry_run: | ||
|  | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|  | @@ -77,10 +77,12 @@ var /global StdOutHandleSet | |||||||||||||||
| !define ARCH {{ arch }} | ||||||||||||||||
| !define PLATFORM {{ installer_platform }} | ||||||||||||||||
| !define CONSTRUCTOR_VERSION {{ constructor_version }} | ||||||||||||||||
| {%- if has_python %} | ||||||||||||||||
| !define PY_VER {{ pyver_components[:2] | join(".") }} | ||||||||||||||||
| !define PYVERSION_JUSTDIGITS {{ pyver_components | join("") }} | ||||||||||||||||
| !define PYVERSION {{ pyver_components | join(".") }} | ||||||||||||||||
| !define PYVERSION_MAJOR {{ pyver_components[0] }} | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| !define DEFAULT_PREFIX {{ default_prefix }} | ||||||||||||||||
| !define DEFAULT_PREFIX_DOMAIN_USER {{ default_prefix_domain_user }} | ||||||||||||||||
| !define DEFAULT_PREFIX_ALL_USERS {{ default_prefix_all_users }} | ||||||||||||||||
|  | @@ -298,7 +300,9 @@ FunctionEnd | |||||||||||||||
| /InstallationType=AllUsers [default: JustMe]$\n\ | ||||||||||||||||
| /AddToPath=[0|1] [default: 0]$\n\ | ||||||||||||||||
| /KeepPkgCache=[0|1] [default: {{ 1 if keep_pkgs else 0 }}]$\n\ | ||||||||||||||||
| {%- if has_python %} | ||||||||||||||||
| /RegisterPython=[0|1] [default: AllUsers: 1, JustMe: 0]$\n\ | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| /NoRegistry=[0|1] [default: AllUsers: 0, JustMe: 0]$\n\ | ||||||||||||||||
| /NoScripts=[0|1] [default: 0]$\n\ | ||||||||||||||||
| /NoShortcuts=[0|1] [default: 0]$\n\ | ||||||||||||||||
|  | @@ -317,8 +321,13 @@ FunctionEnd | |||||||||||||||
| Install for all users, but don't add to PATH env var:$\n\ | ||||||||||||||||
| > $EXEFILE /InstallationType=AllUsers$\n\ | ||||||||||||||||
| $\n\ | ||||||||||||||||
| {%- if has_python %} | ||||||||||||||||
| Install for just me, add to PATH and register as system Python:$\n\ | ||||||||||||||||
| > $EXEFILE /RegisterPython=1 /AddToPath=1$\n\ | ||||||||||||||||
| {%- else %} | ||||||||||||||||
| Install for just me and add to PATH:$\n\ | ||||||||||||||||
| > $EXEFILE /AddToPath=1$\n\ | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| 
      Comment on lines
    
      +327
     to 
      +330
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this option depend on having Python or not? I'd say the are independent: 
        Suggested change
       
 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't depend on Python, no, but I figured I'd not add an extra line to the output since it already has to be broken apart. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
 I don't get this part. Are they mutually exclusive or not? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are not mutually exclusive, I just didn't want the message to be overly long with examples. I was referring to the comment a few lines above: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case we can split the help message in one more call. I'd rather leave the examples there. | ||||||||||||||||
| $\n\ | ||||||||||||||||
| Install for just me, with no registry modification (for CI):$\n\ | ||||||||||||||||
| > $EXEFILE /NoRegistry=1$\n\ | ||||||||||||||||
|  | @@ -343,6 +352,7 @@ FunctionEnd | |||||||||||||||
| ${EndIf} | ||||||||||||||||
|  | ||||||||||||||||
| ClearErrors | ||||||||||||||||
| {%- if has_python %} | ||||||||||||||||
| ${GetOptions} $ARGV "/RegisterPython=" $ARGV_RegisterPython | ||||||||||||||||
| ${IfNot} ${Errors} | ||||||||||||||||
| ${If} $ARGV_RegisterPython = "1" | ||||||||||||||||
|  | @@ -351,6 +361,7 @@ FunctionEnd | |||||||||||||||
| StrCpy $Ana_RegisterSystemPython_State ${BST_UNCHECKED} | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
|  | ||||||||||||||||
| ClearErrors | ||||||||||||||||
| ${GetOptions} $ARGV "/KeepPkgCache=" $ARGV_KeepPkgCache | ||||||||||||||||
|  | @@ -1136,6 +1147,7 @@ Function OnDirectoryLeave | |||||||||||||||
| UnicodePathTest::UnicodePathTest $INSTDIR | ||||||||||||||||
| Pop $R1 | ||||||||||||||||
|  | ||||||||||||||||
| {%- if has_python %} | ||||||||||||||||
| # 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. | ||||||||||||||||
|  | @@ -1153,6 +1165,7 @@ Function OnDirectoryLeave | |||||||||||||||
| abort | ||||||||||||||||
|  | ||||||||||||||||
| valid_path: | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
|  | ||||||||||||||||
| Push $R1 | ||||||||||||||||
| ${IsWritable} $INSTDIR $R1 | ||||||||||||||||
|  | @@ -1235,6 +1248,36 @@ FunctionEnd | |||||||||||||||
| !insertmacro AbortRetryNSExecWaitMacro "" | ||||||||||||||||
| !insertmacro AbortRetryNSExecWaitMacro "un." | ||||||||||||||||
|  | ||||||||||||||||
| {% if not base_needs_python %} | ||||||||||||||||
| !macro AddRemovePath add_remove un | ||||||||||||||||
| {%- if initialize_conda == 'condabin' %} | ||||||||||||||||
| push '"$INSTDIR\_conda.exe" constructor windows path --${add_remove}=user --prefix "$INSTDIR\condabin"' | ||||||||||||||||
| push 'Failed to add condabin to PATH' | ||||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call ${un}AbortRetryNSExecWait | ||||||||||||||||
| {%- else %} | ||||||||||||||||
| StrCpy $R0 "Library\mingw-w64\bin Library\usr\bin Library\bin Scripts" | ||||||||||||||||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wish we didn't need to hardcode this here. Wouldn't it be more adequate to have this logic in conda-standalone too? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It has to be hard-coded somewhere. I made the choice to have an abstract feature in  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My concern is that these directories are defined by convention (although we're trying to standardize it here when building packages, so if at some point we decide to put binaries somewhere else in Windows (e.g. removing the  A future addition might be an  That said, we can address that in a future refactor. For now let's link to the CEP draft. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little concerned to leave this to a future refactor. If this gets moved into  | ||||||||||||||||
| StrCpy $R1 0 | ||||||||||||||||
| loop_add_remove_path: | ||||||||||||||||
| # Add INSTDIR first | ||||||||||||||||
| ${If} $R1 == 0 | ||||||||||||||||
| StrCpy $R2 "$INSTDIR" | ||||||||||||||||
| ${Else} | ||||||||||||||||
| ${WordFind} $R0 " " "E+$R1" $R2 | ||||||||||||||||
| IfErrors endloop_add_remove_path | ||||||||||||||||
| StrCpy $R2 "$INSTDIR\$R2" | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| push '"$INSTDIR\_conda.exe" constructor windows path --${add_remove}=user --prefix "$R2"' | ||||||||||||||||
| push 'Failed to add $R2 to PATH' | ||||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call ${un}AbortRetryNSExecWait | ||||||||||||||||
| IntOp $R1 $R1 + 1 | ||||||||||||||||
| goto loop_add_remove_path | ||||||||||||||||
| endloop_add_remove_path: | ||||||||||||||||
| {% endif %} | ||||||||||||||||
| !macroend | ||||||||||||||||
| {% endif %} | ||||||||||||||||
|  | ||||||||||||||||
| # Installer sections | ||||||||||||||||
| Section "Install" | ||||||||||||||||
| ${LogSet} on | ||||||||||||||||
|  | @@ -1244,9 +1287,12 @@ Section "Install" | |||||||||||||||
|  | ||||||||||||||||
| !insertmacro FindCmdExe | ||||||||||||||||
|  | ||||||||||||||||
| SetOutPath "$INSTDIR" | ||||||||||||||||
| {% if base_needs_python %} | ||||||||||||||||
| SetOutPath "$INSTDIR\Lib" | ||||||||||||||||
| File "{{ NSIS_DIR }}\_nsis.py" | ||||||||||||||||
| File "{{ NSIS_DIR }}\_system_path.py" | ||||||||||||||||
| {% endif %} | ||||||||||||||||
|  | ||||||||||||||||
| # 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. | ||||||||||||||||
|  | @@ -1448,20 +1494,32 @@ Section "Install" | |||||||||||||||
| ${EndIf} | ||||||||||||||||
|  | ||||||||||||||||
| {% if initialize_conda %} | ||||||||||||||||
| ${If} $Ana_AddToPath_State = ${BST_CHECKED} | ||||||||||||||||
| ${If} ${FileExists} "$INSTDIR\.nonadmin" | ||||||||||||||||
| ${If} $Ana_AddToPath_State = ${BST_CHECKED} | ||||||||||||||||
| {%- if base_needs_python %} | ||||||||||||||||
| {%- if initialize_conda == 'condabin' %} | ||||||||||||||||
| ${Print} "Adding $INSTDIR\condabin to PATH..." | ||||||||||||||||
| push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" addcondabinpath' | ||||||||||||||||
| ${Print} "Adding $INSTDIR\condabin to PATH..." | ||||||||||||||||
| push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" addcondabinpath' | ||||||||||||||||
| {%- else %} | ||||||||||||||||
| ${Print} "Adding $INSTDIR\Scripts & Library\bin to PATH..." | ||||||||||||||||
| push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" addpath ${PYVERSION} ${NAME} ${VERSION} ${ARCH}' | ||||||||||||||||
| ${Print} "Adding $INSTDIR\Scripts & Library\bin to PATH..." | ||||||||||||||||
| push '"$INSTDIR\pythonw.exe" -E -s "$INSTDIR\Lib\_nsis.py" addpath ${PYVERSION} ${NAME} ${VERSION} ${ARCH}' | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| push 'Failed to add {{ NAME }} to PATH' | ||||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call AbortRetryNSExecWait | ||||||||||||||||
| push 'Failed to add {{ NAME }} to PATH' | ||||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call AbortRetryNSExecWait | ||||||||||||||||
| 
      Comment on lines
    
      +1499
     to 
      +1509
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we implement this twice? Can't we just depend on the new NSIS macro? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you suggesting that I move the  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I can understand, the  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The macro implements the same behavior using  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, your comment makes me think that it's better to move that logic into the macro, too. I will go ahead and do that - it should clear this up a little more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Works for me. Let's leave a comment to deprecate that part once we bump the minimum conda requirement. | ||||||||||||||||
| {%- else %} | ||||||||||||||||
| {%- if initialize_conda == 'condabin' %} | ||||||||||||||||
| ${Print} "Adding $INSTDIR\condabin to PATH..." | ||||||||||||||||
| {%- else %} | ||||||||||||||||
| ${Print} "Adding $INSTDIR\Scripts & Library\bin to PATH..." | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| !insertmacro AddRemovePath "prepend" "" | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
|  | ||||||||||||||||
| {%- if has_python %} | ||||||||||||||||
| # Create registry entries saying this is the system Python | ||||||||||||||||
| # (for this version) | ||||||||||||||||
| !define PYREG "Software\Python\PythonCore\${PY_VER}" | ||||||||||||||||
|  | @@ -1479,6 +1537,7 @@ Section "Install" | |||||||||||||||
| WriteRegStr SHCTX "${PYREG}\PythonPath" \ | ||||||||||||||||
| "" "$INSTDIR\Lib;$INSTDIR\DLLs" | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
|  | ||||||||||||||||
| ${If} $ARGV_NoRegistry == "0" | ||||||||||||||||
| # Registry uninstall info | ||||||||||||||||
|  | @@ -1532,6 +1591,7 @@ Section "Install" | |||||||||||||||
| ${Print} "Done!" | ||||||||||||||||
| SectionEnd | ||||||||||||||||
|  | ||||||||||||||||
| {% if base_needs_python %} | ||||||||||||||||
| !macro AbortRetryNSExecWaitLibNsisCmd cmd | ||||||||||||||||
| SetDetailsPrint both | ||||||||||||||||
| ${Print} "Running ${cmd} scripts..." | ||||||||||||||||
|  | @@ -1546,6 +1606,7 @@ SectionEnd | |||||||||||||||
| call un.AbortRetryNSExecWait | ||||||||||||||||
| SetDetailsPrint both | ||||||||||||||||
| !macroend | ||||||||||||||||
| {% endif %} | ||||||||||||||||
|  | ||||||||||||||||
| Section "Uninstall" | ||||||||||||||||
| ${LogSet} on | ||||||||||||||||
|  | @@ -1617,7 +1678,13 @@ Section "Uninstall" | |||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call un.AbortRetryNSExecWait | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| {% if base_needs_python %} | ||||||||||||||||
| !insertmacro AbortRetryNSExecWaitLibNsisCmd "rmpath" | ||||||||||||||||
| {% else %} | ||||||||||||||||
| ${If} ${FileExists} "$INSTDIR\.nonadmin" | ||||||||||||||||
| !insertmacro AddRemovePath "remove" "un." | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| {% endif %} | ||||||||||||||||
|  | ||||||||||||||||
| # Parse arguments | ||||||||||||||||
| StrCpy $R0 "" | ||||||||||||||||
|  | @@ -1672,7 +1739,11 @@ Section "Uninstall" | |||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call un.AbortRetryNSExecWait | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| !insertmacro AbortRetryNSExecWaitLibNsisCmd "rmpath" | ||||||||||||||||
| ${If} ${FileExists} "$INSTDIR\.nonadmin" | ||||||||||||||||
| StrCpy $R0 "user" | ||||||||||||||||
| ${Else} | ||||||||||||||||
| StrCpy $R0 "system" | ||||||||||||||||
| ${EndIf} | ||||||||||||||||
| {%- if has_conda %} | ||||||||||||||||
| ${If} ${FileExists} "$INSTDIR\.nonadmin" | ||||||||||||||||
| StrCpy $R0 "user" | ||||||||||||||||
|  | @@ -1688,6 +1759,11 @@ Section "Uninstall" | |||||||||||||||
| push 'WithLog' | ||||||||||||||||
| call un.AbortRetryNSExecWait | ||||||||||||||||
| {%- endif %} | ||||||||||||||||
| {% if base_needs_python %} | ||||||||||||||||
| !insertmacro AbortRetryNSExecWaitLibNsisCmd "rmpath" | ||||||||||||||||
| {% else %} | ||||||||||||||||
| !insertmacro AddRemovePath "remove" "un." | ||||||||||||||||
| {% endif %} | ||||||||||||||||
|  | ||||||||||||||||
| ${Print} "Removing files and folders..." | ||||||||||||||||
| nsExec::Exec 'cmd.exe /D /C RMDIR /Q /S "$INSTDIR"' | ||||||||||||||||
|  | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarity, could we add a comment here explaining what returncode
2means?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed