Environment modifications: de-prioritize external packages (#23824)
Prior to any Spack build, Spack modifies PATH etc. to help the build find the dependencies it needs. It also allows any package to define custom environment modifications (and furthermore a package can specify environment modifications to apply when it is used as a dependency). If an external package defines custom environment modifications that alter PATH, and the external package is in a merged or system prefix, then that prefix could "override" the Spack-built packages. This commit reorders environment modifications so that PrependPath actions which expose Spack-built packages override PrependPath actions for custom environment modifications of external packages. In more detail, the original order of environment modifications is: * Modules * Compiler flag variables * PATH, CMAKE_PREFIX_PATH, and PKG_CONFIG_PATH for dependencies * Custom package.py modifications in the following order: * dependencies * root This commit changes the order: * Modules * Compiler flag variables * For each external dependency * PATH, CMAKE_PREFIX_PATH, and PKG_CONFIG_PATH modifications * Custom modifications * For each Spack-built dependency * PATH, CMAKE_PREFIX_PATH, and PKG_CONFIG_PATH modifications * Custom modifications
This commit is contained in:
parent
d7405ddd39
commit
916cdfbb56
@ -78,7 +78,7 @@
|
|||||||
|
|
||||||
#
|
#
|
||||||
# These environment variables are set by
|
# These environment variables are set by
|
||||||
# set_build_environment_variables and used to pass parameters to
|
# set_wrapper_variables and used to pass parameters to
|
||||||
# Spack's compiler wrappers.
|
# Spack's compiler wrappers.
|
||||||
#
|
#
|
||||||
SPACK_ENV_PATH = 'SPACK_ENV_PATH'
|
SPACK_ENV_PATH = 'SPACK_ENV_PATH'
|
||||||
@ -159,6 +159,8 @@ def clean_environment():
|
|||||||
env.unset('CPLUS_INCLUDE_PATH')
|
env.unset('CPLUS_INCLUDE_PATH')
|
||||||
env.unset('OBJC_INCLUDE_PATH')
|
env.unset('OBJC_INCLUDE_PATH')
|
||||||
|
|
||||||
|
env.unset('CMAKE_PREFIX_PATH')
|
||||||
|
|
||||||
# Avoid that libraries of build dependencies get hijacked.
|
# Avoid that libraries of build dependencies get hijacked.
|
||||||
env.unset('LD_PRELOAD')
|
env.unset('LD_PRELOAD')
|
||||||
env.unset('DYLD_INSERT_LIBRARIES')
|
env.unset('DYLD_INSERT_LIBRARIES')
|
||||||
@ -310,111 +312,20 @@ def set_compiler_environment_variables(pkg, env):
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def _place_externals_last(spec_container):
|
def set_wrapper_variables(pkg, env):
|
||||||
|
"""Set environment variables used by the Spack compiler wrapper
|
||||||
|
(which have the prefix `SPACK_`) and also add the compiler wrappers
|
||||||
|
to PATH.
|
||||||
|
|
||||||
|
This determines the injected -L/-I/-rpath options; each
|
||||||
|
of these specifies a search order and this function computes these
|
||||||
|
options in a manner that is intended to match the DAG traversal order
|
||||||
|
in `modifications_from_dependencies`: that method uses a post-order
|
||||||
|
traversal so that `PrependPath` actions from dependencies take lower
|
||||||
|
precedence; we use a post-order traversal here to match the visitation
|
||||||
|
order of `modifications_from_dependencies` (so we are visiting the
|
||||||
|
lowest priority packages first).
|
||||||
"""
|
"""
|
||||||
For a (possibly unordered) container of specs, return an ordered list
|
|
||||||
where all external specs are at the end of the list. External packages
|
|
||||||
may be installed in merged prefixes with other packages, and so
|
|
||||||
they should be deprioritized for any search order (i.e. in PATH, or
|
|
||||||
for a set of -L entries in a compiler invocation).
|
|
||||||
"""
|
|
||||||
# Establish an arbitrary but fixed ordering of specs so that resulting
|
|
||||||
# environment variable values are stable
|
|
||||||
spec_container = sorted(spec_container, key=lambda x: x.name)
|
|
||||||
first = list(x for x in spec_container if not x.external)
|
|
||||||
second = list(x for x in spec_container if x.external)
|
|
||||||
return first + second
|
|
||||||
|
|
||||||
|
|
||||||
def set_build_environment_variables(pkg, env, dirty):
|
|
||||||
"""Ensure a clean install environment when we build packages.
|
|
||||||
|
|
||||||
This involves unsetting pesky environment variables that may
|
|
||||||
affect the build. It also involves setting environment variables
|
|
||||||
used by Spack's compiler wrappers.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
pkg: The package we are building
|
|
||||||
env: The build environment
|
|
||||||
dirty (bool): Skip unsetting the user's environment settings
|
|
||||||
"""
|
|
||||||
# Gather information about various types of dependencies
|
|
||||||
build_deps = set(pkg.spec.dependencies(deptype=('build', 'test')))
|
|
||||||
link_deps = set(pkg.spec.traverse(root=False, deptype=('link')))
|
|
||||||
build_link_deps = build_deps | link_deps
|
|
||||||
rpath_deps = get_rpath_deps(pkg)
|
|
||||||
# This includes all build dependencies and any other dependencies that
|
|
||||||
# should be added to PATH (e.g. supporting executables run by build
|
|
||||||
# dependencies)
|
|
||||||
build_and_supporting_deps = set()
|
|
||||||
for build_dep in build_deps:
|
|
||||||
build_and_supporting_deps.update(build_dep.traverse(deptype='run'))
|
|
||||||
|
|
||||||
# External packages may be installed in a prefix which contains many other
|
|
||||||
# package installs. To avoid having those installations override
|
|
||||||
# Spack-installed packages, they are placed at the end of search paths.
|
|
||||||
# System prefixes are removed entirely later on since they are already
|
|
||||||
# searched.
|
|
||||||
build_deps = _place_externals_last(build_deps)
|
|
||||||
link_deps = _place_externals_last(link_deps)
|
|
||||||
build_link_deps = _place_externals_last(build_link_deps)
|
|
||||||
rpath_deps = _place_externals_last(rpath_deps)
|
|
||||||
build_and_supporting_deps = _place_externals_last(
|
|
||||||
build_and_supporting_deps)
|
|
||||||
|
|
||||||
link_dirs = []
|
|
||||||
include_dirs = []
|
|
||||||
rpath_dirs = []
|
|
||||||
|
|
||||||
# The top-level package is always RPATHed. It hasn't been installed yet
|
|
||||||
# so the RPATHs are added unconditionally (e.g. even though lib64/ may
|
|
||||||
# not be created for the install).
|
|
||||||
for libdir in ['lib', 'lib64']:
|
|
||||||
lib_path = os.path.join(pkg.prefix, libdir)
|
|
||||||
rpath_dirs.append(lib_path)
|
|
||||||
|
|
||||||
# Set up link, include, RPATH directories that are passed to the
|
|
||||||
# compiler wrapper
|
|
||||||
for dep in link_deps:
|
|
||||||
if is_system_path(dep.prefix):
|
|
||||||
continue
|
|
||||||
query = pkg.spec[dep.name]
|
|
||||||
dep_link_dirs = list()
|
|
||||||
try:
|
|
||||||
dep_link_dirs.extend(query.libs.directories)
|
|
||||||
except NoLibrariesError:
|
|
||||||
tty.debug("No libraries found for {0}".format(dep.name))
|
|
||||||
|
|
||||||
for default_lib_dir in ['lib', 'lib64']:
|
|
||||||
default_lib_prefix = os.path.join(dep.prefix, default_lib_dir)
|
|
||||||
if os.path.isdir(default_lib_prefix):
|
|
||||||
dep_link_dirs.append(default_lib_prefix)
|
|
||||||
|
|
||||||
link_dirs.extend(dep_link_dirs)
|
|
||||||
if dep in rpath_deps:
|
|
||||||
rpath_dirs.extend(dep_link_dirs)
|
|
||||||
|
|
||||||
try:
|
|
||||||
include_dirs.extend(query.headers.directories)
|
|
||||||
except NoHeadersError:
|
|
||||||
tty.debug("No headers found for {0}".format(dep.name))
|
|
||||||
|
|
||||||
link_dirs = list(dedupe(filter_system_paths(link_dirs)))
|
|
||||||
include_dirs = list(dedupe(filter_system_paths(include_dirs)))
|
|
||||||
rpath_dirs = list(dedupe(filter_system_paths(rpath_dirs)))
|
|
||||||
|
|
||||||
env.set(SPACK_LINK_DIRS, ':'.join(link_dirs))
|
|
||||||
env.set(SPACK_INCLUDE_DIRS, ':'.join(include_dirs))
|
|
||||||
env.set(SPACK_RPATH_DIRS, ':'.join(rpath_dirs))
|
|
||||||
|
|
||||||
build_and_supporting_prefixes = filter_system_paths(
|
|
||||||
x.prefix for x in build_and_supporting_deps)
|
|
||||||
build_link_prefixes = filter_system_paths(
|
|
||||||
x.prefix for x in build_link_deps)
|
|
||||||
|
|
||||||
# Add dependencies to CMAKE_PREFIX_PATH
|
|
||||||
env.set_path('CMAKE_PREFIX_PATH', get_cmake_prefix_path(pkg))
|
|
||||||
|
|
||||||
# Set environment variables if specified for
|
# Set environment variables if specified for
|
||||||
# the given compiler
|
# the given compiler
|
||||||
compiler = pkg.compiler
|
compiler = pkg.compiler
|
||||||
@ -424,16 +335,6 @@ def set_build_environment_variables(pkg, env, dirty):
|
|||||||
extra_rpaths = ':'.join(compiler.extra_rpaths)
|
extra_rpaths = ':'.join(compiler.extra_rpaths)
|
||||||
env.set('SPACK_COMPILER_EXTRA_RPATHS', extra_rpaths)
|
env.set('SPACK_COMPILER_EXTRA_RPATHS', extra_rpaths)
|
||||||
|
|
||||||
# Add bin directories from dependencies to the PATH for the build.
|
|
||||||
# These directories are added to the beginning of the search path, and in
|
|
||||||
# the order given by 'build_and_supporting_prefixes' (the iteration order
|
|
||||||
# is reversed because each entry is prepended)
|
|
||||||
for prefix in reversed(build_and_supporting_prefixes):
|
|
||||||
for dirname in ['bin', 'bin64']:
|
|
||||||
bin_dir = os.path.join(prefix, dirname)
|
|
||||||
if os.path.isdir(bin_dir):
|
|
||||||
env.prepend_path('PATH', bin_dir)
|
|
||||||
|
|
||||||
# Add spack build environment path with compiler wrappers first in
|
# Add spack build environment path with compiler wrappers first in
|
||||||
# the path. We add the compiler wrapper path, which includes default
|
# the path. We add the compiler wrapper path, which includes default
|
||||||
# wrappers (cc, c++, f77, f90), AND a subdirectory containing
|
# wrappers (cc, c++, f77, f90), AND a subdirectory containing
|
||||||
@ -453,6 +354,7 @@ def set_build_environment_variables(pkg, env, dirty):
|
|||||||
if os.path.isdir(ci):
|
if os.path.isdir(ci):
|
||||||
env_paths.append(ci)
|
env_paths.append(ci)
|
||||||
|
|
||||||
|
tty.debug("Adding compiler bin/ paths: " + " ".join(env_paths))
|
||||||
for item in env_paths:
|
for item in env_paths:
|
||||||
env.prepend_path('PATH', item)
|
env.prepend_path('PATH', item)
|
||||||
env.set_path(SPACK_ENV_PATH, env_paths)
|
env.set_path(SPACK_ENV_PATH, env_paths)
|
||||||
@ -471,14 +373,69 @@ def set_build_environment_variables(pkg, env, dirty):
|
|||||||
raise RuntimeError("No ccache binary found in PATH")
|
raise RuntimeError("No ccache binary found in PATH")
|
||||||
env.set(SPACK_CCACHE_BINARY, ccache)
|
env.set(SPACK_CCACHE_BINARY, ccache)
|
||||||
|
|
||||||
# Add any pkgconfig directories to PKG_CONFIG_PATH
|
# Gather information about various types of dependencies
|
||||||
for prefix in reversed(build_link_prefixes):
|
link_deps = set(pkg.spec.traverse(root=False, deptype=('link')))
|
||||||
for directory in ('lib', 'lib64', 'share'):
|
rpath_deps = get_rpath_deps(pkg)
|
||||||
pcdir = os.path.join(prefix, directory, 'pkgconfig')
|
|
||||||
if os.path.isdir(pcdir):
|
|
||||||
env.prepend_path('PKG_CONFIG_PATH', pcdir)
|
|
||||||
|
|
||||||
return env
|
link_dirs = []
|
||||||
|
include_dirs = []
|
||||||
|
rpath_dirs = []
|
||||||
|
|
||||||
|
def _prepend_all(list_to_modify, items_to_add):
|
||||||
|
# Update the original list (creating a new list would be faster but
|
||||||
|
# may not be convenient)
|
||||||
|
for item in reversed(list(items_to_add)):
|
||||||
|
list_to_modify.insert(0, item)
|
||||||
|
|
||||||
|
def update_compiler_args_for_dep(dep):
|
||||||
|
if dep in link_deps and (not is_system_path(dep.prefix)):
|
||||||
|
query = pkg.spec[dep.name]
|
||||||
|
dep_link_dirs = list()
|
||||||
|
try:
|
||||||
|
dep_link_dirs.extend(query.libs.directories)
|
||||||
|
except NoLibrariesError:
|
||||||
|
tty.debug("No libraries found for {0}".format(dep.name))
|
||||||
|
|
||||||
|
for default_lib_dir in ['lib', 'lib64']:
|
||||||
|
default_lib_prefix = os.path.join(
|
||||||
|
dep.prefix, default_lib_dir)
|
||||||
|
if os.path.isdir(default_lib_prefix):
|
||||||
|
dep_link_dirs.append(default_lib_prefix)
|
||||||
|
|
||||||
|
_prepend_all(link_dirs, dep_link_dirs)
|
||||||
|
if dep in rpath_deps:
|
||||||
|
_prepend_all(rpath_dirs, dep_link_dirs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
_prepend_all(include_dirs, query.headers.directories)
|
||||||
|
except NoHeadersError:
|
||||||
|
tty.debug("No headers found for {0}".format(dep.name))
|
||||||
|
|
||||||
|
for dspec in pkg.spec.traverse(root=False, order='post'):
|
||||||
|
if dspec.external:
|
||||||
|
update_compiler_args_for_dep(dspec)
|
||||||
|
|
||||||
|
# Just above, we prepended entries for -L/-rpath for externals. We
|
||||||
|
# now do this for non-external packages so that Spack-built packages
|
||||||
|
# are searched first for libraries etc.
|
||||||
|
for dspec in pkg.spec.traverse(root=False, order='post'):
|
||||||
|
if not dspec.external:
|
||||||
|
update_compiler_args_for_dep(dspec)
|
||||||
|
|
||||||
|
# The top-level package is always RPATHed. It hasn't been installed yet
|
||||||
|
# so the RPATHs are added unconditionally (e.g. even though lib64/ may
|
||||||
|
# not be created for the install).
|
||||||
|
for libdir in ['lib64', 'lib']:
|
||||||
|
lib_path = os.path.join(pkg.prefix, libdir)
|
||||||
|
rpath_dirs.insert(0, lib_path)
|
||||||
|
|
||||||
|
link_dirs = list(dedupe(filter_system_paths(link_dirs)))
|
||||||
|
include_dirs = list(dedupe(filter_system_paths(include_dirs)))
|
||||||
|
rpath_dirs = list(dedupe(filter_system_paths(rpath_dirs)))
|
||||||
|
|
||||||
|
env.set(SPACK_LINK_DIRS, ':'.join(link_dirs))
|
||||||
|
env.set(SPACK_INCLUDE_DIRS, ':'.join(include_dirs))
|
||||||
|
env.set(SPACK_RPATH_DIRS, ':'.join(rpath_dirs))
|
||||||
|
|
||||||
|
|
||||||
def determine_number_of_jobs(
|
def determine_number_of_jobs(
|
||||||
@ -716,15 +673,6 @@ def get_rpaths(pkg):
|
|||||||
return list(dedupe(filter_system_paths(rpaths)))
|
return list(dedupe(filter_system_paths(rpaths)))
|
||||||
|
|
||||||
|
|
||||||
def get_cmake_prefix_path(pkg):
|
|
||||||
build_deps = set(pkg.spec.dependencies(deptype=('build', 'test')))
|
|
||||||
link_deps = set(pkg.spec.traverse(root=False, deptype=('link')))
|
|
||||||
build_link_deps = build_deps | link_deps
|
|
||||||
build_link_deps = _place_externals_last(build_link_deps)
|
|
||||||
build_link_prefixes = filter_system_paths(x.prefix for x in build_link_deps)
|
|
||||||
return build_link_prefixes
|
|
||||||
|
|
||||||
|
|
||||||
def get_std_cmake_args(pkg):
|
def get_std_cmake_args(pkg):
|
||||||
"""List of standard arguments used if a package is a CMakePackage.
|
"""List of standard arguments used if a package is a CMakePackage.
|
||||||
|
|
||||||
@ -792,42 +740,40 @@ def load_external_modules(pkg):
|
|||||||
|
|
||||||
def setup_package(pkg, dirty, context='build'):
|
def setup_package(pkg, dirty, context='build'):
|
||||||
"""Execute all environment setup routines."""
|
"""Execute all environment setup routines."""
|
||||||
|
if context not in ['build', 'test']:
|
||||||
|
raise ValueError(
|
||||||
|
"'context' must be one of ['build', 'test'] - got: {0}"
|
||||||
|
.format(context))
|
||||||
|
|
||||||
|
set_module_variables_for_package(pkg)
|
||||||
|
|
||||||
env = EnvironmentModifications()
|
env = EnvironmentModifications()
|
||||||
|
|
||||||
if not dirty:
|
if not dirty:
|
||||||
clean_environment()
|
clean_environment()
|
||||||
|
|
||||||
# setup compilers and build tools for build contexts
|
# setup compilers for build contexts
|
||||||
need_compiler = context == 'build' or (context == 'test' and
|
need_compiler = context == 'build' or (context == 'test' and
|
||||||
pkg.test_requires_compiler)
|
pkg.test_requires_compiler)
|
||||||
if need_compiler:
|
if need_compiler:
|
||||||
set_compiler_environment_variables(pkg, env)
|
set_compiler_environment_variables(pkg, env)
|
||||||
set_build_environment_variables(pkg, env, dirty)
|
set_wrapper_variables(pkg, env)
|
||||||
|
|
||||||
|
env.extend(modifications_from_dependencies(
|
||||||
|
pkg.spec, context, custom_mods_only=False))
|
||||||
|
|
||||||
# architecture specific setup
|
# architecture specific setup
|
||||||
pkg.architecture.platform.setup_platform_environment(pkg, env)
|
pkg.architecture.platform.setup_platform_environment(pkg, env)
|
||||||
|
|
||||||
if context == 'build':
|
if context == 'build':
|
||||||
# recursive post-order dependency information
|
pkg.setup_build_environment(env)
|
||||||
env.extend(
|
|
||||||
modifications_from_dependencies(pkg.spec, context=context)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (not dirty) and (not env.is_unset('CPATH')):
|
if (not dirty) and (not env.is_unset('CPATH')):
|
||||||
tty.debug("A dependency has updated CPATH, this may lead pkg-"
|
tty.debug("A dependency has updated CPATH, this may lead pkg-"
|
||||||
"config to assume that the package is part of the system"
|
"config to assume that the package is part of the system"
|
||||||
" includes and omit it when invoked with '--cflags'.")
|
" includes and omit it when invoked with '--cflags'.")
|
||||||
|
|
||||||
# setup package itself
|
|
||||||
set_module_variables_for_package(pkg)
|
|
||||||
pkg.setup_build_environment(env)
|
|
||||||
elif context == 'test':
|
elif context == 'test':
|
||||||
import spack.user_environment as uenv # avoid circular import
|
pkg.setup_run_environment(env)
|
||||||
env.extend(uenv.environment_modifications_for_spec(pkg.spec))
|
|
||||||
env.extend(
|
|
||||||
modifications_from_dependencies(pkg.spec, context=context)
|
|
||||||
)
|
|
||||||
set_module_variables_for_package(pkg)
|
|
||||||
env.prepend_path('PATH', '.')
|
env.prepend_path('PATH', '.')
|
||||||
|
|
||||||
# Loading modules, in particular if they are meant to be used outside
|
# Loading modules, in particular if they are meant to be used outside
|
||||||
@ -869,39 +815,173 @@ def setup_package(pkg, dirty, context='build'):
|
|||||||
env.apply_modifications()
|
env.apply_modifications()
|
||||||
|
|
||||||
|
|
||||||
def modifications_from_dependencies(spec, context):
|
def _make_runnable(pkg, env):
|
||||||
|
# Helper method which prepends a Package's bin/ prefix to the PATH
|
||||||
|
# environment variable
|
||||||
|
prefix = pkg.prefix
|
||||||
|
|
||||||
|
for dirname in ['bin', 'bin64']:
|
||||||
|
bin_dir = os.path.join(prefix, dirname)
|
||||||
|
if os.path.isdir(bin_dir):
|
||||||
|
env.prepend_path('PATH', bin_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def modifications_from_dependencies(spec, context, custom_mods_only=True):
|
||||||
"""Returns the environment modifications that are required by
|
"""Returns the environment modifications that are required by
|
||||||
the dependencies of a spec and also applies modifications
|
the dependencies of a spec and also applies modifications
|
||||||
to this spec's package at module scope, if need be.
|
to this spec's package at module scope, if need be.
|
||||||
|
|
||||||
|
Environment modifications include:
|
||||||
|
|
||||||
|
- Updating PATH so that executables can be found
|
||||||
|
- Updating CMAKE_PREFIX_PATH and PKG_CONFIG_PATH so that their respective
|
||||||
|
tools can find Spack-built dependencies
|
||||||
|
- Running custom package environment modifications
|
||||||
|
|
||||||
|
Custom package modifications can conflict with the default PATH changes
|
||||||
|
we make (specifically for the PATH, CMAKE_PREFIX_PATH, and PKG_CONFIG_PATH
|
||||||
|
environment variables), so this applies changes in a fixed order:
|
||||||
|
|
||||||
|
- All modifications (custom and default) from external deps first
|
||||||
|
- All modifications from non-external deps afterwards
|
||||||
|
|
||||||
|
With that order, `PrependPath` actions from non-external default
|
||||||
|
environment modifications will take precedence over custom modifications
|
||||||
|
from external packages.
|
||||||
|
|
||||||
|
A secondary constraint is that custom and default modifications are
|
||||||
|
grouped on a per-package basis: combined with the post-order traversal this
|
||||||
|
means that default modifications of dependents can override custom
|
||||||
|
modifications of dependencies (again, this would only occur for PATH,
|
||||||
|
CMAKE_PREFIX_PATH, or PKG_CONFIG_PATH).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
spec (Spec): spec for which we want the modifications
|
spec (Spec): spec for which we want the modifications
|
||||||
context (str): either 'build' for build-time modifications or 'run'
|
context (str): either 'build' for build-time modifications or 'run'
|
||||||
for run-time modifications
|
for run-time modifications
|
||||||
"""
|
"""
|
||||||
|
if context not in ['build', 'run', 'test']:
|
||||||
|
raise ValueError(
|
||||||
|
"Expecting context to be one of ['build', 'run', 'test'], "
|
||||||
|
"got: {0}".format(context))
|
||||||
|
|
||||||
env = EnvironmentModifications()
|
env = EnvironmentModifications()
|
||||||
pkg = spec.package
|
|
||||||
|
|
||||||
# Maps the context to deptype and method to be called
|
# Note: see computation of 'custom_mod_deps' and 'exe_deps' later in this
|
||||||
deptype_and_method = {
|
# function; these sets form the building blocks of those collections.
|
||||||
'build': (('build', 'link', 'test'),
|
build_deps = set(spec.dependencies(deptype=('build', 'test')))
|
||||||
'setup_dependent_build_environment'),
|
link_deps = set(spec.traverse(root=False, deptype='link'))
|
||||||
'run': (('link', 'run'), 'setup_dependent_run_environment'),
|
build_link_deps = build_deps | link_deps
|
||||||
'test': (('link', 'run', 'test'), 'setup_dependent_run_environment')
|
build_and_supporting_deps = set()
|
||||||
}
|
for build_dep in build_deps:
|
||||||
deptype, method = deptype_and_method[context]
|
build_and_supporting_deps.update(build_dep.traverse(deptype='run'))
|
||||||
|
run_and_supporting_deps = set(
|
||||||
|
spec.traverse(root=False, deptype=('run', 'link')))
|
||||||
|
test_and_supporting_deps = set()
|
||||||
|
for test_dep in set(spec.dependencies(deptype='test')):
|
||||||
|
test_and_supporting_deps.update(test_dep.traverse(deptype='run'))
|
||||||
|
|
||||||
root = context == 'test'
|
# All dependencies that might have environment modifications to apply
|
||||||
for dspec in spec.traverse(order='post', root=root, deptype=deptype):
|
custom_mod_deps = set()
|
||||||
dpkg = dspec.package
|
if context == 'build':
|
||||||
set_module_variables_for_package(dpkg)
|
custom_mod_deps.update(build_and_supporting_deps)
|
||||||
# Allow dependencies to modify the module
|
# Tests may be performed after build
|
||||||
dpkg.setup_dependent_package(pkg.module, spec)
|
custom_mod_deps.update(test_and_supporting_deps)
|
||||||
getattr(dpkg, method)(env, spec)
|
else:
|
||||||
|
# test/run context
|
||||||
|
custom_mod_deps.update(run_and_supporting_deps)
|
||||||
|
if context == 'test':
|
||||||
|
custom_mod_deps.update(test_and_supporting_deps)
|
||||||
|
custom_mod_deps.update(link_deps)
|
||||||
|
|
||||||
|
# Determine 'exe_deps': the set of packages with binaries we want to use
|
||||||
|
if context == 'build':
|
||||||
|
exe_deps = build_and_supporting_deps | test_and_supporting_deps
|
||||||
|
elif context == 'run':
|
||||||
|
exe_deps = set(spec.traverse(deptype='run'))
|
||||||
|
elif context == 'test':
|
||||||
|
exe_deps = test_and_supporting_deps
|
||||||
|
|
||||||
|
def default_modifications_for_dep(dep):
|
||||||
|
if (dep in build_link_deps and
|
||||||
|
not is_system_path(dep.prefix) and
|
||||||
|
context == 'build'):
|
||||||
|
prefix = dep.prefix
|
||||||
|
|
||||||
|
env.prepend_path('CMAKE_PREFIX_PATH', prefix)
|
||||||
|
|
||||||
|
for directory in ('lib', 'lib64', 'share'):
|
||||||
|
pcdir = os.path.join(prefix, directory, 'pkgconfig')
|
||||||
|
if os.path.isdir(pcdir):
|
||||||
|
env.prepend_path('PKG_CONFIG_PATH', pcdir)
|
||||||
|
|
||||||
|
if dep in exe_deps and not is_system_path(dep.prefix):
|
||||||
|
_make_runnable(dep, env)
|
||||||
|
|
||||||
|
def add_modifications_for_dep(dep):
|
||||||
|
# Some callers of this function only want the custom modifications.
|
||||||
|
# For callers that want both custom and default modifications, we want
|
||||||
|
# to perform the default modifications here (this groups custom
|
||||||
|
# and default modifications together on a per-package basis).
|
||||||
|
if not custom_mods_only:
|
||||||
|
default_modifications_for_dep(dep)
|
||||||
|
|
||||||
|
# Perform custom modifications here (PrependPath actions performed in
|
||||||
|
# the custom method override the default environment modifications
|
||||||
|
# we do to help the build, namely for PATH, CMAKE_PREFIX_PATH, and
|
||||||
|
# PKG_CONFIG_PATH)
|
||||||
|
if dep in custom_mod_deps:
|
||||||
|
dpkg = dep.package
|
||||||
|
set_module_variables_for_package(dpkg)
|
||||||
|
# Allow dependencies to modify the module
|
||||||
|
dpkg.setup_dependent_package(spec.package.module, spec)
|
||||||
|
if context == 'build':
|
||||||
|
dpkg.setup_dependent_build_environment(env, spec)
|
||||||
|
else:
|
||||||
|
dpkg.setup_dependent_run_environment(env, spec)
|
||||||
|
|
||||||
|
# Note that we want to perform environment modifications in a fixed order.
|
||||||
|
# The Spec.traverse method provides this: i.e. in addition to
|
||||||
|
# the post-order semantics, it also guarantees a fixed traversal order
|
||||||
|
# among dependencies which are not constrained by post-order semantics.
|
||||||
|
for dspec in spec.traverse(root=False, order='post'):
|
||||||
|
if dspec.external:
|
||||||
|
add_modifications_for_dep(dspec)
|
||||||
|
|
||||||
|
for dspec in spec.traverse(root=False, order='post'):
|
||||||
|
# Default env modifications for non-external packages can override
|
||||||
|
# custom modifications of external packages (this can only occur
|
||||||
|
# for modifications to PATH, CMAKE_PREFIX_PATH, and PKG_CONFIG_PATH)
|
||||||
|
if not dspec.external:
|
||||||
|
add_modifications_for_dep(dspec)
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def get_cmake_prefix_path(pkg):
|
||||||
|
# Note that unlike modifications_from_dependencies, this does not include
|
||||||
|
# any edits to CMAKE_PREFIX_PATH defined in custom
|
||||||
|
# setup_dependent_build_environment implementations of dependency packages
|
||||||
|
build_deps = set(pkg.spec.dependencies(deptype=('build', 'test')))
|
||||||
|
link_deps = set(pkg.spec.traverse(root=False, deptype=('link')))
|
||||||
|
build_link_deps = build_deps | link_deps
|
||||||
|
spack_built = []
|
||||||
|
externals = []
|
||||||
|
# modifications_from_dependencies updates CMAKE_PREFIX_PATH by first
|
||||||
|
# prepending all externals and then all non-externals
|
||||||
|
for dspec in pkg.spec.traverse(root=False, order='post'):
|
||||||
|
if dspec in build_link_deps:
|
||||||
|
if dspec.external:
|
||||||
|
externals.insert(0, dspec)
|
||||||
|
else:
|
||||||
|
spack_built.insert(0, dspec)
|
||||||
|
|
||||||
|
ordered_build_link_deps = spack_built + externals
|
||||||
|
build_link_prefixes = filter_system_paths(
|
||||||
|
x.prefix for x in ordered_build_link_deps)
|
||||||
|
return build_link_prefixes
|
||||||
|
|
||||||
|
|
||||||
def _setup_pkg_and_run(serialized_pkg, function, kwargs, child_pipe,
|
def _setup_pkg_and_run(serialized_pkg, function, kwargs, child_pipe,
|
||||||
input_multiprocess_fd):
|
input_multiprocess_fd):
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ def test_package_inheritance_module_setup(config, mock_packages, working_env):
|
|||||||
assert os.environ['TEST_MODULE_VAR'] == 'test_module_variable'
|
assert os.environ['TEST_MODULE_VAR'] == 'test_module_variable'
|
||||||
|
|
||||||
|
|
||||||
def test_set_build_environment_variables(
|
def test_wrapper_variables(
|
||||||
config, mock_packages, working_env, monkeypatch,
|
config, mock_packages, working_env, monkeypatch,
|
||||||
installation_dir_with_headers
|
installation_dir_with_headers
|
||||||
):
|
):
|
||||||
@ -264,8 +264,8 @@ def test_set_build_environment_variables(
|
|||||||
try:
|
try:
|
||||||
pkg = root.package
|
pkg = root.package
|
||||||
env_mods = EnvironmentModifications()
|
env_mods = EnvironmentModifications()
|
||||||
spack.build_environment.set_build_environment_variables(
|
spack.build_environment.set_wrapper_variables(
|
||||||
pkg, env_mods, dirty=False)
|
pkg, env_mods)
|
||||||
|
|
||||||
env_mods.apply_modifications()
|
env_mods.apply_modifications()
|
||||||
|
|
||||||
@ -324,8 +324,8 @@ def _trust_me_its_a_dir(path):
|
|||||||
)
|
)
|
||||||
|
|
||||||
env_mods = EnvironmentModifications()
|
env_mods = EnvironmentModifications()
|
||||||
spack.build_environment.set_build_environment_variables(
|
spack.build_environment.set_wrapper_variables(
|
||||||
top.package, env_mods, False)
|
top.package, env_mods)
|
||||||
|
|
||||||
env_mods.apply_modifications()
|
env_mods.apply_modifications()
|
||||||
link_dir_var = os.environ['SPACK_LINK_DIRS']
|
link_dir_var = os.environ['SPACK_LINK_DIRS']
|
||||||
|
@ -245,12 +245,16 @@ def update_args(self, **kwargs):
|
|||||||
class SetEnv(NameValueModifier):
|
class SetEnv(NameValueModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("SetEnv: {0}={1}".format(self.name, str(self.value)),
|
||||||
|
level=3)
|
||||||
env[self.name] = str(self.value)
|
env[self.name] = str(self.value)
|
||||||
|
|
||||||
|
|
||||||
class AppendFlagsEnv(NameValueModifier):
|
class AppendFlagsEnv(NameValueModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("AppendFlagsEnv: {0}={1}".format(self.name, str(self.value)),
|
||||||
|
level=3)
|
||||||
if self.name in env and env[self.name]:
|
if self.name in env and env[self.name]:
|
||||||
env[self.name] += self.separator + str(self.value)
|
env[self.name] += self.separator + str(self.value)
|
||||||
else:
|
else:
|
||||||
@ -260,6 +264,7 @@ def execute(self, env):
|
|||||||
class UnsetEnv(NameModifier):
|
class UnsetEnv(NameModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("UnsetEnv: {0}".format(self.name), level=3)
|
||||||
# Avoid throwing if the variable was not set
|
# Avoid throwing if the variable was not set
|
||||||
env.pop(self.name, None)
|
env.pop(self.name, None)
|
||||||
|
|
||||||
@ -267,6 +272,8 @@ def execute(self, env):
|
|||||||
class RemoveFlagsEnv(NameValueModifier):
|
class RemoveFlagsEnv(NameValueModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("RemoveFlagsEnv: {0}-{1}".format(self.name, str(self.value)),
|
||||||
|
level=3)
|
||||||
environment_value = env.get(self.name, '')
|
environment_value = env.get(self.name, '')
|
||||||
flags = environment_value.split(
|
flags = environment_value.split(
|
||||||
self.separator) if environment_value else []
|
self.separator) if environment_value else []
|
||||||
@ -278,12 +285,15 @@ class SetPath(NameValueModifier):
|
|||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
string_path = concatenate_paths(self.value, separator=self.separator)
|
string_path = concatenate_paths(self.value, separator=self.separator)
|
||||||
|
tty.debug("SetPath: {0}={1}".format(self.name, string_path), level=3)
|
||||||
env[self.name] = string_path
|
env[self.name] = string_path
|
||||||
|
|
||||||
|
|
||||||
class AppendPath(NameValueModifier):
|
class AppendPath(NameValueModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("AppendPath: {0}+{1}".format(self.name, str(self.value)),
|
||||||
|
level=3)
|
||||||
environment_value = env.get(self.name, '')
|
environment_value = env.get(self.name, '')
|
||||||
directories = environment_value.split(
|
directories = environment_value.split(
|
||||||
self.separator) if environment_value else []
|
self.separator) if environment_value else []
|
||||||
@ -294,6 +304,8 @@ def execute(self, env):
|
|||||||
class PrependPath(NameValueModifier):
|
class PrependPath(NameValueModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("PrependPath: {0}+{1}".format(self.name, str(self.value)),
|
||||||
|
level=3)
|
||||||
environment_value = env.get(self.name, '')
|
environment_value = env.get(self.name, '')
|
||||||
directories = environment_value.split(
|
directories = environment_value.split(
|
||||||
self.separator) if environment_value else []
|
self.separator) if environment_value else []
|
||||||
@ -304,6 +316,8 @@ def execute(self, env):
|
|||||||
class RemovePath(NameValueModifier):
|
class RemovePath(NameValueModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("RemovePath: {0}-{1}".format(self.name, str(self.value)),
|
||||||
|
level=3)
|
||||||
environment_value = env.get(self.name, '')
|
environment_value = env.get(self.name, '')
|
||||||
directories = environment_value.split(
|
directories = environment_value.split(
|
||||||
self.separator) if environment_value else []
|
self.separator) if environment_value else []
|
||||||
@ -315,6 +329,7 @@ def execute(self, env):
|
|||||||
class DeprioritizeSystemPaths(NameModifier):
|
class DeprioritizeSystemPaths(NameModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("DeprioritizeSystemPaths: {0}".format(self.name), level=3)
|
||||||
environment_value = env.get(self.name, '')
|
environment_value = env.get(self.name, '')
|
||||||
directories = environment_value.split(
|
directories = environment_value.split(
|
||||||
self.separator) if environment_value else []
|
self.separator) if environment_value else []
|
||||||
@ -326,6 +341,8 @@ def execute(self, env):
|
|||||||
class PruneDuplicatePaths(NameModifier):
|
class PruneDuplicatePaths(NameModifier):
|
||||||
|
|
||||||
def execute(self, env):
|
def execute(self, env):
|
||||||
|
tty.debug("PruneDuplicatePaths: {0}".format(self.name),
|
||||||
|
level=3)
|
||||||
environment_value = env.get(self.name, '')
|
environment_value = env.get(self.name, '')
|
||||||
directories = environment_value.split(
|
directories = environment_value.split(
|
||||||
self.separator) if environment_value else []
|
self.separator) if environment_value else []
|
||||||
@ -624,6 +641,8 @@ def from_sourcing_file(filename, *arguments, **kwargs):
|
|||||||
clean (bool): in addition to removing empty entries,
|
clean (bool): in addition to removing empty entries,
|
||||||
also remove duplicate entries (default: False).
|
also remove duplicate entries (default: False).
|
||||||
"""
|
"""
|
||||||
|
tty.debug("EnvironmentModifications.from_sourcing_file: {0}"
|
||||||
|
.format(filename))
|
||||||
# Check if the file actually exists
|
# Check if the file actually exists
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
msg = 'Trying to source non-existing file: {0}'.format(filename)
|
msg = 'Trying to source non-existing file: {0}'.format(filename)
|
||||||
|
@ -95,6 +95,7 @@ def load_module(mod):
|
|||||||
load that module. It then loads the provided module. Depends on the
|
load that module. It then loads the provided module. Depends on the
|
||||||
modulecmd implementation of modules used in cray and lmod.
|
modulecmd implementation of modules used in cray and lmod.
|
||||||
"""
|
"""
|
||||||
|
tty.debug("module_cmd.load_module: {0}".format(mod))
|
||||||
# Read the module and remove any conflicting modules
|
# Read the module and remove any conflicting modules
|
||||||
# We do this without checking that they are already installed
|
# We do this without checking that they are already installed
|
||||||
# for ease of programming because unloading a module that is not
|
# for ease of programming because unloading a module that is not
|
||||||
|
Loading…
Reference in New Issue
Block a user