environment_modifications_for_specs: do not mutate spec.prefix (#41737)
Sometimes env variables computed in `setup_run_environment` depend on tests w.r.t. files in `spec.prefix`, but Spack temporarily projects `spec.prefix` to the view. This is problematic for two reasons: 1. Some packages iterate over `<prefix>/bin`: they expect only the current package's executables, but find all linked in the view, leading to false positives. 2. Some packages test for `os.path.islink(...)`, which is always true in a view `gcc` is an example that does both. This PR lets Spack compute the environment modifications using the original prefix, and projects to the view afterwards
This commit is contained in:
parent
494d3f9002
commit
ec2729706b
@ -3,18 +3,14 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from llnl.util.lang import nullcontext
|
|
||||||
|
|
||||||
import spack.build_environment
|
import spack.build_environment
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.error
|
import spack.error
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.util.environment as environment
|
import spack.util.environment as environment
|
||||||
import spack.util.prefix as prefix
|
|
||||||
from spack import traverse
|
from spack import traverse
|
||||||
from spack.context import Context
|
from spack.context import Context
|
||||||
|
|
||||||
@ -70,22 +66,6 @@ def unconditional_environment_modifications(view):
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def projected_prefix(*specs: spack.spec.Spec, projection: Callable[[spack.spec.Spec], str]):
|
|
||||||
"""Temporarily replace every Spec's prefix with projection(s)"""
|
|
||||||
prefixes = dict()
|
|
||||||
for s in traverse.traverse_nodes(specs, key=lambda s: s.dag_hash()):
|
|
||||||
if s.external:
|
|
||||||
continue
|
|
||||||
prefixes[s.dag_hash()] = s.prefix
|
|
||||||
s.prefix = prefix.Prefix(projection(s))
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
for s in traverse.traverse_nodes(specs, key=lambda s: s.dag_hash()):
|
|
||||||
s.prefix = prefixes.get(s.dag_hash(), s.prefix)
|
|
||||||
|
|
||||||
|
|
||||||
def environment_modifications_for_specs(
|
def environment_modifications_for_specs(
|
||||||
*specs: spack.spec.Spec, view=None, set_package_py_globals: bool = True
|
*specs: spack.spec.Spec, view=None, set_package_py_globals: bool = True
|
||||||
):
|
):
|
||||||
@ -102,26 +82,36 @@ def environment_modifications_for_specs(
|
|||||||
been built on a different but compatible OS)
|
been built on a different but compatible OS)
|
||||||
"""
|
"""
|
||||||
env = environment.EnvironmentModifications()
|
env = environment.EnvironmentModifications()
|
||||||
topo_ordered = traverse.traverse_nodes(specs, root=True, deptype=("run", "link"), order="topo")
|
topo_ordered = list(
|
||||||
|
traverse.traverse_nodes(specs, root=True, deptype=("run", "link"), order="topo")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Static environment changes (prefix inspections)
|
||||||
|
for s in reversed(topo_ordered):
|
||||||
|
static = environment.inspect_path(
|
||||||
|
s.prefix, prefix_inspections(s.platform), exclude=environment.is_system_path
|
||||||
|
)
|
||||||
|
env.extend(static)
|
||||||
|
|
||||||
|
# Dynamic environment changes (setup_run_environment etc)
|
||||||
|
setup_context = spack.build_environment.SetupContext(*specs, context=Context.RUN)
|
||||||
|
if set_package_py_globals:
|
||||||
|
setup_context.set_all_package_py_globals()
|
||||||
|
env.extend(setup_context.get_env_modifications())
|
||||||
|
|
||||||
|
# Apply view projections if any.
|
||||||
if view:
|
if view:
|
||||||
maybe_projected = projected_prefix(*specs, projection=view.get_projection_for_spec)
|
prefix_to_prefix = {
|
||||||
else:
|
s.prefix: view.get_projection_for_spec(s)
|
||||||
maybe_projected = nullcontext()
|
for s in reversed(topo_ordered)
|
||||||
|
if not s.external
|
||||||
with maybe_projected:
|
}
|
||||||
# Static environment changes (prefix inspections)
|
# Avoid empty regex if all external
|
||||||
for s in reversed(list(topo_ordered)):
|
if not prefix_to_prefix:
|
||||||
static = environment.inspect_path(
|
return env
|
||||||
s.prefix, prefix_inspections(s.platform), exclude=environment.is_system_path
|
prefix_regex = re.compile("|".join(re.escape(p) for p in prefix_to_prefix.keys()))
|
||||||
)
|
for mod in env.env_modifications:
|
||||||
env.extend(static)
|
if isinstance(mod, environment.NameValueModifier):
|
||||||
|
mod.value = prefix_regex.sub(lambda m: prefix_to_prefix[m.group(0)], mod.value)
|
||||||
# Dynamic environment changes (setup_run_environment etc)
|
|
||||||
setup_context = spack.build_environment.SetupContext(*specs, context=Context.RUN)
|
|
||||||
if set_package_py_globals:
|
|
||||||
setup_context.set_all_package_py_globals()
|
|
||||||
dynamic = setup_context.get_env_modifications()
|
|
||||||
env.extend(dynamic)
|
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
Loading…
Reference in New Issue
Block a user