diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/build_systems/python.py | 107 |
1 files changed, 8 insertions, 99 deletions
diff --git a/lib/spack/spack/build_systems/python.py b/lib/spack/spack/build_systems/python.py index 7f71cbae70..521994b1ec 100644 --- a/lib/spack/spack/build_systems/python.py +++ b/lib/spack/spack/build_systems/python.py @@ -6,7 +6,6 @@ import inspect import os import re import shutil -import stat from typing import Optional import archspec @@ -26,7 +25,6 @@ import spack.store from spack.directives import build_system, depends_on, extends, maintainers from spack.error import NoHeadersError, NoLibrariesError from spack.install_test import test_part -from spack.util.executable import Executable from ._checks import BaseBuilder, execute_install_time_tests @@ -369,51 +367,6 @@ class PythonPackage(PythonExtension): raise NoLibrariesError(msg.format(self.spec.name, root)) -def fixup_shebangs(path: str, old_interpreter: bytes, new_interpreter: bytes): - # Recurse into the install prefix and fixup shebangs - exe = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH - dirs = [path] - hardlinks = set() - - while dirs: - with os.scandir(dirs.pop()) as entries: - for entry in entries: - if entry.is_dir(follow_symlinks=False): - dirs.append(entry.path) - continue - - # Only consider files, not symlinks - if not entry.is_file(follow_symlinks=False): - continue - - lstat = entry.stat(follow_symlinks=False) - - # Skip over files that are not executable - if not (lstat.st_mode & exe): - continue - - # Don't modify hardlinks more than once - if lstat.st_nlink > 1: - key = (lstat.st_ino, lstat.st_dev) - if key in hardlinks: - continue - hardlinks.add(key) - - # Finally replace shebangs if any. - with open(entry.path, "rb+") as f: - contents = f.read(2) - if contents != b"#!": - continue - contents += f.read() - - if old_interpreter not in contents: - continue - - f.seek(0) - f.write(contents.replace(old_interpreter, new_interpreter)) - f.truncate() - - @spack.builder.builder("python_pip") class PythonPipBuilder(BaseBuilder): phases = ("install",) @@ -511,36 +464,8 @@ class PythonPipBuilder(BaseBuilder): """ return [] - @property - def _build_venv_path(self): - """Return the path to the virtual environment used for building when - python is external.""" - return os.path.join(self.spec.package.stage.path, "build_env") - - @property - def _build_venv_python(self) -> Executable: - """Return the Python executable in the build virtual environment when - python is external.""" - return Executable(os.path.join(self._build_venv_path, "bin", "python")) - def install(self, pkg, spec, prefix): """Install everything from build directory.""" - python: Executable = spec["python"].command - # Since we invoke pip with --no-build-isolation, we have to make sure that pip cannot - # execute hooks from user and system site-packages. - if spec["python"].external: - # There are no environment variables to disable the system site-packages, so we use a - # virtual environment instead. The downside of this approach is that pip produces - # incorrect shebangs that refer to the virtual environment, which we have to fix up. - python("-m", "venv", "--without-pip", self._build_venv_path) - pip = self._build_venv_python - else: - # For a Spack managed Python, system site-packages is empty/unused by design, so it - # suffices to disable user site-packages, for which there is an environment variable. - pip = python - pip.add_default_env("PYTHONNOUSERSITE", "1") - pip.add_default_arg("-m") - pip.add_default_arg("pip") args = PythonPipBuilder.std_args(pkg) + [f"--prefix={prefix}"] @@ -556,31 +481,15 @@ class PythonPipBuilder(BaseBuilder): else: args.append(".") + pip = spec["python"].command + # Hide user packages, since we don't have build isolation. This is + # necessary because pip / setuptools may run hooks from arbitrary + # packages during the build. There is no equivalent variable to hide + # system packages, so this is not reliable for external Python. + pip.add_default_env("PYTHONNOUSERSITE", "1") + pip.add_default_arg("-m") + pip.add_default_arg("pip") with fs.working_dir(self.build_directory): pip(*args) - @spack.builder.run_after("install") - def fixup_shebangs_pointing_to_build(self): - """When installing a package using an external python, we use a temporary virtual - environment which improves build isolation. The downside is that pip produces shebangs - that point to the temporary virtual environment. This method fixes them up to point to the - underlying Python.""" - # No need to fixup shebangs if no build venv was used. (this post install function also - # runs when install was overridden in another package, so check existence of the venv path) - if not os.path.exists(self._build_venv_path): - return - - # Use sys.executable, since that's what pip uses. - interpreter = ( - lambda python: python("-c", "import sys; print(sys.executable)", output=str) - .strip() - .encode("utf-8") - ) - - fixup_shebangs( - path=self.spec.prefix, - old_interpreter=interpreter(self._build_venv_python), - new_interpreter=interpreter(self.spec["python"].command), - ) - spack.builder.run_after("install")(execute_install_time_tests) |