summaryrefslogtreecommitdiff
path: root/var
diff options
context:
space:
mode:
Diffstat (limited to 'var')
-rw-r--r--var/spack/repos/builtin/packages/python/package.py155
1 files changed, 56 insertions, 99 deletions
diff --git a/var/spack/repos/builtin/packages/python/package.py b/var/spack/repos/builtin/packages/python/package.py
index 4ae9092d5a..e0ac0efd7a 100644
--- a/var/spack/repos/builtin/packages/python/package.py
+++ b/var/spack/repos/builtin/packages/python/package.py
@@ -3,18 +3,14 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
-import ast
import os
import platform
import re
import llnl.util.tty as tty
from llnl.util.lang import match_predicate
-from llnl.util.filesystem import (force_remove, get_filetype,
- path_contains_subdirectory)
+from llnl.util.filesystem import get_filetype, path_contains_subdirectory
-import spack.store
-import spack.util.spack_json as sjson
from spack.util.environment import is_system_path
from spack.util.prefix import Prefix
from spack import *
@@ -221,10 +217,6 @@ class Python(AutotoolsPackage):
conflicts('%nvhpc')
- _DISTUTIL_VARS_TO_SAVE = ['LDSHARED']
- _DISTUTIL_CACHE_FILENAME = 'sysconfig.json'
- _distutil_vars = None
-
# Used to cache home locations, since computing them might be expensive
_homes = {}
@@ -482,90 +474,6 @@ class Python(AutotoolsPackage):
return config_args
@run_after('install')
- def _save_distutil_vars(self):
- """
- Run before changing automatically generated contents of the
- _sysconfigdata.py, which is used by distutils to figure out what
- executables to use while compiling and linking extensions. If we build
- extensions with spack those executables should be spack's wrappers.
- Spack partially covers this by setting environment variables that
- are also accounted for by distutils. Currently there is one more known
- variable that must be set, which is LDSHARED, so the method saves its
- autogenerated value to pass it to the dependent package's setup script.
- """
-
- self._distutil_vars = {}
-
- input_filename = self.get_sysconfigdata_name()
- input_dict = None
- try:
- with open(input_filename) as input_file:
- match = re.search(r'build_time_vars\s*=\s*(?P<dict>{.*})',
- input_file.read(),
- flags=re.DOTALL)
-
- if match:
- input_dict = ast.literal_eval(match.group('dict'))
- except (IOError, SyntaxError):
- pass
-
- if not input_dict:
- tty.warn("Failed to find 'build_time_vars' dictionary in file "
- "'%s'. This might cause the extensions that are "
- "installed with distutils to call compilers directly "
- "avoiding Spack's wrappers." % input_filename)
- return
-
- for var_name in Python._DISTUTIL_VARS_TO_SAVE:
- if var_name in input_dict:
- self._distutil_vars[var_name] = input_dict[var_name]
- else:
- tty.warn("Failed to find key '%s' in 'build_time_vars' "
- "dictionary in file '%s'. This might cause the "
- "extensions that are installed with distutils to "
- "call compilers directly avoiding Spack's wrappers."
- % (var_name, input_filename))
-
- if len(self._distutil_vars) > 0:
- output_filename = None
- try:
- output_filename = join_path(
- spack.store.layout.metadata_path(self.spec),
- Python._DISTUTIL_CACHE_FILENAME)
- with open(output_filename, 'w') as output_file:
- sjson.dump(self._distutil_vars, output_file)
- except Exception:
- tty.warn("Failed to save metadata for distutils. This might "
- "cause the extensions that are installed with "
- "distutils to call compilers directly avoiding "
- "Spack's wrappers.")
- # We make the cache empty if we failed to save it to file
- # to provide the same behaviour as in the case when the cache
- # is initialized by the method load_distutils_data().
- self._distutil_vars = {}
- if output_filename:
- force_remove(output_filename)
-
- def _load_distutil_vars(self):
- # We update and keep the cache unchanged only if the package is
- # installed.
- if not self._distutil_vars and self.installed:
- try:
- input_filename = join_path(
- spack.store.layout.metadata_path(self.spec),
- Python._DISTUTIL_CACHE_FILENAME)
- if os.path.isfile(input_filename):
- with open(input_filename) as input_file:
- self._distutil_vars = sjson.load(input_file)
- except Exception:
- pass
-
- if not self._distutil_vars:
- self._distutil_vars = {}
-
- return self._distutil_vars
-
- @run_after('install')
def filter_compilers(self):
"""Run after install to tell the configuration files and Makefiles
to use the compilers that Spack built the package with.
@@ -946,6 +854,61 @@ class Python(AutotoolsPackage):
pythonpath = ':'.join(python_paths)
env.set('PYTHONPATH', pythonpath)
+ # We need to make sure that the extensions are compiled and linked with
+ # the Spack wrapper. Paths to the executables that are used for these
+ # operations are normally taken from the sysconfigdata file, which we
+ # modify after the installation (see method filter compilers). The
+ # modified file contains paths to the real compilers, not the wrappers.
+ # The values in the file, however, can be overridden with environment
+ # variables. The first variable, CC (CXX), which is used for
+ # compilation, is set by Spack for the dependent package by default.
+ # That is not 100% correct because the value for CC (CXX) in the
+ # sysconfigdata file often contains additional compiler flags (e.g.
+ # -pthread), which we lose by simply setting CC (CXX) to the path to the
+ # Spack wrapper. Moreover, the user might try to build an extension with
+ # a compiler that is different from the one that was used to build
+ # Python itself, which might have unexpected side effects. However, the
+ # experience shows that none of the above is a real issue and we will
+ # not try to change the default behaviour. Given that, we will simply
+ # try to modify LDSHARED (LDCXXSHARED), the second variable, which is
+ # used for linking, in a consistent manner.
+
+ for compile_var, link_var in [('CC', 'LDSHARED'),
+ ('CXX', 'LDCXXSHARED')]:
+ # First, we get the values from the sysconfigdata:
+ config_compile = self.get_config_var(compile_var)
+ config_link = self.get_config_var(link_var)
+
+ # The dependent environment will have the compilation command set to
+ # the following:
+ new_compile = join_path(
+ spack.paths.build_env_path,
+ dependent_spec.package.compiler.link_paths[compile_var.lower()])
+
+ # Normally, the link command starts with the compilation command:
+ if config_link.startswith(config_compile):
+ new_link = new_compile + config_link[len(config_compile):]
+ else:
+ # Otherwise, we try to replace the compiler command if it
+ # appears "in the middle" of the link command; to avoid
+ # mistaking some substring of a path for the compiler (e.g. to
+ # avoid replacing "gcc" in "-L/path/to/gcc/"), we require that
+ # the compiler command be surrounded by spaces. Note this may
+ # leave "config_link" unchanged if the compilation command does
+ # not appear in the link command at all, for example if "ld" is
+ # invoked directly (no change would be required in that case
+ # because Spack arranges for the Spack ld wrapper to be the
+ # first instance of "ld" in PATH).
+ new_link = config_link.replace(" {0} ".format(config_compile),
+ " {0} ".format(new_compile))
+
+ # There is logic in the sysconfig module that is sensitive to the
+ # fact that LDSHARED is set in the environment, therefore we export
+ # the variable only if the new value is different from what we got
+ # from the sysconfigdata file:
+ if config_link != new_link:
+ env.set(link_var, new_link)
+
def setup_dependent_run_environment(self, env, dependent_spec):
python_paths = []
for d in dependent_spec.traverse(deptype='run'):
@@ -973,12 +936,6 @@ class Python(AutotoolsPackage):
module.setup_py = Executable(
self.command.path + ' setup.py --no-user-cfg')
- distutil_vars = self._load_distutil_vars()
-
- if distutil_vars:
- for key, value in distutil_vars.items():
- module.setup_py.add_default_env(key, value)
-
# Add variables for lib/pythonX.Y and lib/pythonX.Y/site-packages dirs.
module.python_lib_dir = join_path(dependent_spec.prefix,
self.python_lib_dir)