summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarmen Stoppels <me@harmenstoppels.nl>2023-12-19 23:33:16 +0100
committerGitHub <noreply@github.com>2023-12-19 23:33:16 +0100
commitec2729706b235f96fe481bf6333fbcc7732e28c1 (patch)
tree06a5d78fc9846898bb777eeab5d231b1947453b9
parent494d3f9002454eba8da7f07e272937c8398172f6 (diff)
downloadspack-ec2729706b235f96fe481bf6333fbcc7732e28c1.tar.gz
spack-ec2729706b235f96fe481bf6333fbcc7732e28c1.tar.bz2
spack-ec2729706b235f96fe481bf6333fbcc7732e28c1.tar.xz
spack-ec2729706b235f96fe481bf6333fbcc7732e28c1.zip
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
-rw-r--r--lib/spack/spack/user_environment.py72
1 files changed, 31 insertions, 41 deletions
diff --git a/lib/spack/spack/user_environment.py b/lib/spack/spack/user_environment.py
index 6e1c798e51..895e7b1203 100644
--- a/lib/spack/spack/user_environment.py
+++ b/lib/spack/spack/user_environment.py
@@ -3,18 +3,14 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
+import re
import sys
-from contextlib import contextmanager
-from typing import Callable
-
-from llnl.util.lang import nullcontext
import spack.build_environment
import spack.config
import spack.error
import spack.spec
import spack.util.environment as environment
-import spack.util.prefix as prefix
from spack import traverse
from spack.context import Context
@@ -70,22 +66,6 @@ def unconditional_environment_modifications(view):
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(
*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)
"""
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:
- maybe_projected = projected_prefix(*specs, projection=view.get_projection_for_spec)
- else:
- maybe_projected = nullcontext()
-
- with maybe_projected:
- # Static environment changes (prefix inspections)
- for s in reversed(list(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()
- dynamic = setup_context.get_env_modifications()
- env.extend(dynamic)
+ prefix_to_prefix = {
+ s.prefix: view.get_projection_for_spec(s)
+ for s in reversed(topo_ordered)
+ if not s.external
+ }
+ # Avoid empty regex if all external
+ if not prefix_to_prefix:
+ return env
+ prefix_regex = re.compile("|".join(re.escape(p) for p in prefix_to_prefix.keys()))
+ for mod in env.env_modifications:
+ if isinstance(mod, environment.NameValueModifier):
+ mod.value = prefix_regex.sub(lambda m: prefix_to_prefix[m.group(0)], mod.value)
return env