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:
		| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Harmen Stoppels
					Harmen Stoppels