Turn the compiler wrapper into a package

Remove the compiler wrappers from core Spack, and move
the relevant code into the "compiler-wrapper" package.
This commit is contained in:
Massimiliano Culpo 2024-12-19 22:47:04 +01:00
parent e5f8049f3d
commit 95115d4290
No known key found for this signature in database
GPG Key ID: 3E52BB992233066C
97 changed files with 693 additions and 551 deletions

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cpp

View File

@ -1 +0,0 @@
../fc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

1
lib/spack/env/c++ vendored
View File

@ -1 +0,0 @@
cc

1
lib/spack/env/c89 vendored
View File

@ -1 +0,0 @@
cc

1
lib/spack/env/c99 vendored
View File

@ -1 +0,0 @@
cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../../cc

View File

@ -1 +0,0 @@
../../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

1
lib/spack/env/cpp vendored
View File

@ -1 +0,0 @@
cc

1
lib/spack/env/f77 vendored
View File

@ -1 +0,0 @@
cc

1
lib/spack/env/f90 vendored
View File

@ -1 +0,0 @@
cc

1
lib/spack/env/f95 vendored
View File

@ -1 +0,0 @@
cc

1
lib/spack/env/fc vendored
View File

@ -1 +0,0 @@
cc

View File

@ -1 +0,0 @@
../../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

1
lib/spack/env/ftn vendored
View File

@ -1 +0,0 @@
cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

1
lib/spack/env/ld vendored
View File

@ -1 +0,0 @@
cc

View File

@ -1 +0,0 @@
cc

View File

@ -1 +0,0 @@
cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cpp

View File

@ -1 +0,0 @@
../fc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -1 +0,0 @@
../cc

View File

@ -21,6 +21,7 @@
import spack.platforms
import spack.spec
import spack.traverse
import spack.version
from .config import spec_for_current_python
@ -126,6 +127,10 @@ def concretize(self) -> "spack.spec.Spec":
if node.name == "gcc-runtime":
node.versions = self.host_compiler.versions
# Can't use re2c@3.1 with Python 3.6
if self.host_python.satisfies("@3.6"):
s["re2c"].versions.versions = [spack.version.from_string("=2.2")]
for edge in spack.traverse.traverse_edges([s], cover="edges"):
if edge.spec.name == "python":
edge.spec = self.host_python

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -584,23 +584,6 @@ def set_package_py_globals(pkg, context: Context = Context.BUILD):
# Don't use which for this; we want to find it in the current dir.
module.configure = Executable("./configure")
# Put spack compiler paths in module scope. (Some packages use it
# in setup_run_environment etc, so don't put it context == build)
link_dir = spack.paths.build_env_path
# Set spack_cc, etc. for backward compatibility. This might change if the compiler wrapper
# is modeled as a package.
global_names = {
"c": ("spack_cc",),
"cxx": ("spack_cxx",),
"fortran": ("spack_fc", "spack_f77"),
}
for language in ("c", "cxx", "fortran"):
spec = pkg.spec.dependencies(virtuals=[language])
value = None if not spec else os.path.join(link_dir, spec[0].package.link_paths[language])
for name in global_names[language]:
setattr(module, name, value)
# Useful directories within the prefix are encapsulated in
# a Prefix object.
module.prefix = pkg.prefix

View File

@ -4,22 +4,16 @@
import itertools
import os
import pathlib
import platform
import re
import sys
from typing import Dict, List, Optional, Sequence, Tuple, Union
import archspec.cpu
import llnl.util.tty as tty
from llnl.util.lang import classproperty, memoized
import spack
import spack.compilers.error
import spack.compilers.libraries
import spack.config
import spack.package_base
import spack.paths
import spack.util.executable
# Local "type" for type hints
@ -109,7 +103,7 @@ def determine_version(cls, exe: Path) -> str:
@classmethod
def compiler_bindir(cls, prefix: Path) -> Path:
"""Overridable method for the location of the compiler bindir within the preifx"""
"""Overridable method for the location of the compiler bindir within the prefix"""
return os.path.join(prefix, "bin")
@classmethod
@ -183,109 +177,6 @@ def standard_flag(self, *, language: str, standard: str) -> str:
def _standard_flag(self, *, language: str, standard: str) -> str:
raise NotImplementedError("Must be implemented by derived classes")
@property
def disable_new_dtags(self) -> str:
if platform.system() == "Darwin":
return ""
return "--disable-new-dtags"
@property
def enable_new_dtags(self) -> str:
if platform.system() == "Darwin":
return ""
return "--enable-new-dtags"
def setup_dependent_build_environment(self, env, dependent_spec):
# FIXME (compiler as nodes): check if this is good enough or should be made more general
# The package is not used as a compiler, so skip this setup
if not any(
lang in dependent_spec and dependent_spec[lang].name == self.spec.name
for lang in ("c", "cxx", "fortran")
):
return
# Populate an object with the list of environment modifications and return it
link_dir = pathlib.Path(spack.paths.build_env_path)
env_paths = []
for language, attr_name, wrapper_var_name, spack_var_name in [
("c", "cc", "CC", "SPACK_CC"),
("cxx", "cxx", "CXX", "SPACK_CXX"),
("fortran", "fortran", "F77", "SPACK_F77"),
("fortran", "fortran", "FC", "SPACK_FC"),
]:
if language not in dependent_spec or dependent_spec[language].name != self.spec.name:
continue
if not hasattr(self, attr_name):
continue
compiler = getattr(self, attr_name)
env.set(spack_var_name, compiler)
if language not in self.link_paths:
continue
wrapper_path = link_dir / self.link_paths.get(language)
env.set(wrapper_var_name, str(wrapper_path))
env.set(f"SPACK_{wrapper_var_name}_RPATH_ARG", self.rpath_arg)
uarch = dependent_spec.architecture.target
version_number, _ = archspec.cpu.version_components(
self.spec.version.dotted_numeric_string
)
try:
isa_arg = uarch.optimization_flags(self.archspec_name(), version_number)
except (ValueError, archspec.cpu.UnsupportedMicroarchitecture):
isa_arg = ""
if isa_arg:
env.set(f"SPACK_TARGET_ARGS_{attr_name.upper()}", isa_arg)
# Add spack build environment path with compiler wrappers first in
# the path. We add the compiler wrapper path, which includes default
# wrappers (cc, c++, f77, f90), AND a subdirectory containing
# compiler-specific symlinks. The latter ensures that builds that
# are sensitive to the *name* of the compiler see the right name when
# we're building with the wrappers.
#
# Conflicts on case-insensitive systems (like "CC" and "cc") are
# handled by putting one in the <build_env_path>/case-insensitive
# directory. Add that to the path too.
compiler_specific = os.path.join(
spack.paths.build_env_path, os.path.dirname(self.link_paths[language])
)
for item in [spack.paths.build_env_path, compiler_specific]:
env_paths.append(item)
ci = os.path.join(item, "case-insensitive")
if os.path.isdir(ci):
env_paths.append(ci)
# FIXME (compiler as nodes): make these paths language specific
env.set("SPACK_LINKER_ARG", self.linker_arg)
paths = _implicit_rpaths(pkg=self)
if paths:
env.set("SPACK_COMPILER_IMPLICIT_RPATHS", ":".join(paths))
# Check whether we want to force RPATH or RUNPATH
if spack.config.CONFIG.get("config:shared_linking:type") == "rpath":
env.set("SPACK_DTAGS_TO_STRIP", self.enable_new_dtags)
env.set("SPACK_DTAGS_TO_ADD", self.disable_new_dtags)
else:
env.set("SPACK_DTAGS_TO_STRIP", self.disable_new_dtags)
env.set("SPACK_DTAGS_TO_ADD", self.enable_new_dtags)
spec = self.spec
if spec.extra_attributes:
extra_rpaths = spec.extra_attributes.get("extra_rpaths")
if extra_rpaths:
env.append_path("SPACK_COMPILER_EXTRA_RPATHS", ":".join(extra_rpaths))
for item in env_paths:
env.prepend_path("SPACK_ENV_PATH", item)
def archspec_name(self) -> str:
"""Name that archspec uses to refer to this compiler"""
return self.spec.name
@ -324,12 +215,6 @@ def _fortran_path(self) -> Optional[str]:
return None
def _implicit_rpaths(pkg: spack.package_base.PackageBase) -> List[str]:
detector = spack.compilers.libraries.CompilerPropertyDetector(pkg.spec)
paths = detector.implicit_rpaths()
return paths
@memoized
def _compiler_output(
compiler_path: Path, *, version_argument: str, ignore_errors: Tuple[int, ...] = ()

View File

@ -38,7 +38,6 @@
r"^lib/spack/spack/.*\.sh$",
r"^lib/spack/spack/.*\.lp$",
r"^lib/spack/llnl/.*\.py$",
r"^lib/spack/env/cc$",
# special case some test data files that have license headers
r"^lib/spack/spack/test/data/style/broken.dummy",
r"^lib/spack/spack/test/data/unparse/.*\.txt",

View File

@ -31,7 +31,6 @@
# spack directory hierarchy
lib_path = os.path.join(prefix, "lib", "spack")
external_path = os.path.join(lib_path, "external")
build_env_path = os.path.join(lib_path, "env")
module_path = os.path.join(lib_path, "spack")
command_path = os.path.join(module_path, "cmd")
analyzers_path = os.path.join(module_path, "analyzers")

View File

@ -3149,6 +3149,16 @@ def define_runtime_constraints(self):
# Inject default flags for compilers
recorder("*").default_flags(compiler)
# FIXME (compiler as nodes): think of using isinstance(compiler_cls, WrappedCompiler)
# Add a dependency on the compiler wrapper
if sys.platform != "win32":
recorder("*").depends_on(
"compiler-wrapper",
when=f"%{compiler.name}@{compiler.versions}",
type="build",
description=f"Add the compiler wrapper when using {compiler}",
)
if not using_libc_compatibility():
continue

View File

@ -2,8 +2,8 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import platform
import posixpath
import sys
import pytest
@ -13,19 +13,16 @@
from llnl.util.filesystem import HeaderList, LibraryList
import spack.build_environment
import spack.build_systems.compiler
import spack.concretize
import spack.config
import spack.deptypes as dt
import spack.package_base
import spack.paths
import spack.spec
import spack.util.environment
import spack.util.spack_yaml as syaml
from spack.build_environment import UseMode, _static_to_shared_library, dso_suffix
from spack.context import Context
from spack.installer import PackageInstaller
from spack.paths import build_env_path
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
@ -42,55 +39,41 @@ def prep_and_join(path, *pths):
@pytest.fixture
def build_environment(working_env):
cc = Executable(os.path.join(build_env_path, "cc"))
cxx = Executable(os.path.join(build_env_path, "c++"))
fc = Executable(os.path.join(build_env_path, "fc"))
def build_environment(monkeypatch, wrapper_dir, tmp_path):
realcc = "/bin/mycc"
prefix = "/spack-test-prefix"
prefix = str(tmp_path)
os.environ["SPACK_CC"] = realcc
os.environ["SPACK_CXX"] = realcc
os.environ["SPACK_FC"] = realcc
monkeypatch.setenv("SPACK_CC", realcc)
monkeypatch.setenv("SPACK_CXX", realcc)
monkeypatch.setenv("SPACK_FC", realcc)
os.environ["SPACK_PREFIX"] = prefix
os.environ["SPACK_ENV_PATH"] = "test"
os.environ["SPACK_DEBUG_LOG_DIR"] = "."
os.environ["SPACK_DEBUG_LOG_ID"] = "foo-hashabc"
os.environ["SPACK_SHORT_SPEC"] = "foo@1.2 arch=linux-rhel6-x86_64 /hashabc"
monkeypatch.setenv("SPACK_PREFIX", prefix)
monkeypatch.setenv("SPACK_ENV_PATH", "test")
monkeypatch.setenv("SPACK_DEBUG_LOG_DIR", ".")
monkeypatch.setenv("SPACK_DEBUG_LOG_ID", "foo-hashabc")
monkeypatch.setenv("SPACK_SHORT_SPEC", "foo@1.2 arch=linux-rhel6-x86_64 /hashabc")
os.environ["SPACK_CC_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_CXX_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_F77_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_FC_RPATH_ARG"] = "-Wl,-rpath,"
os.environ["SPACK_LINKER_ARG"] = "-Wl,"
os.environ["SPACK_DTAGS_TO_ADD"] = "--disable-new-dtags"
os.environ["SPACK_DTAGS_TO_STRIP"] = "--enable-new-dtags"
os.environ["SPACK_SYSTEM_DIRS"] = "/usr/include|/usr/lib"
os.environ["SPACK_MANAGED_DIRS"] = f"{prefix}/opt/spack"
os.environ["SPACK_TARGET_ARGS"] = ""
monkeypatch.setenv("SPACK_CC_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_CXX_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_F77_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_FC_RPATH_ARG", "-Wl,-rpath,")
monkeypatch.setenv("SPACK_CC_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_CXX_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_FC_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_F77_LINKER_ARG", "-Wl,")
monkeypatch.setenv("SPACK_DTAGS_TO_ADD", "--disable-new-dtags")
monkeypatch.setenv("SPACK_DTAGS_TO_STRIP", "--enable-new-dtags")
monkeypatch.setenv("SPACK_SYSTEM_DIRS", "/usr/include|/usr/lib")
monkeypatch.setenv("SPACK_MANAGED_DIRS", f"{prefix}/opt/spack")
monkeypatch.setenv("SPACK_TARGET_ARGS", "")
if "SPACK_DEPENDENCIES" in os.environ:
del os.environ["SPACK_DEPENDENCIES"]
monkeypatch.delenv("SPACK_DEPENDENCIES", raising=False)
yield {"cc": cc, "cxx": cxx, "fc": fc}
cc = Executable(str(wrapper_dir / "cc"))
cxx = Executable(str(wrapper_dir / "c++"))
fc = Executable(str(wrapper_dir / "fc"))
for name in (
"SPACK_CC",
"SPACK_CXX",
"SPACK_FC",
"SPACK_PREFIX",
"SPACK_ENV_PATH",
"SPACK_DEBUG_LOG_DIR",
"SPACK_SHORT_SPEC",
"SPACK_CC_RPATH_ARG",
"SPACK_CXX_RPATH_ARG",
"SPACK_F77_RPATH_ARG",
"SPACK_FC_RPATH_ARG",
"SPACK_TARGET_ARGS",
):
del os.environ[name]
return {"cc": cc, "cxx": cxx, "fc": fc}
@pytest.fixture
@ -322,14 +305,14 @@ def test_external_config_env(mock_packages, mutable_config, working_env):
@pytest.mark.regression("9107")
@pytest.mark.not_on_windows("Windows does not support module files")
def test_spack_paths_before_module_paths(
mutable_config, mock_packages, compiler_factory, monkeypatch, working_env
mutable_config, mock_packages, compiler_factory, monkeypatch, working_env, wrapper_dir
):
gcc_entry = compiler_factory(spec="gcc@14.0.1 languages=c,c++")
gcc_entry["modules"] = ["some_module"]
mutable_config.set("packages", {"gcc": {"externals": [gcc_entry]}})
module_path = os.path.join("path", "to", "module")
spack_path = os.path.join(spack.paths.prefix, os.path.join("lib", "spack", "env"))
monkeypatch.setenv("SPACK_ENV_PATH", wrapper_dir)
def _set_wrong_cc(x):
os.environ["PATH"] = module_path + os.pathsep + os.environ["PATH"]
@ -341,7 +324,7 @@ def _set_wrong_cc(x):
spack.build_environment.setup_package(s.package, dirty=False)
paths = os.environ["PATH"].split(os.pathsep)
assert paths.index(spack_path) < paths.index(module_path)
assert paths.index(str(wrapper_dir)) < paths.index(module_path)
def test_package_inheritance_module_setup(config, mock_packages, working_env):
@ -484,11 +467,9 @@ def test_parallel_false_is_not_propagating(default_mock_concretization):
@pytest.mark.parametrize(
"config_setting,expected_flag",
[
("runpath", "" if platform.system() == "Darwin" else "--enable-new-dtags"),
("rpath", "" if platform.system() == "Darwin" else "--disable-new-dtags"),
],
[("runpath", "--enable-new-dtags"), ("rpath", "--disable-new-dtags")],
)
@pytest.mark.skipif(sys.platform != "linux", reason="dtags make sense only on linux")
def test_setting_dtags_based_on_config(
config_setting, expected_flag, config, mock_packages, working_env
):
@ -787,12 +768,11 @@ def test_optimization_flags_are_using_node_target(default_mock_concretization, m
"""Tests that we are using the target on the node to be compiled to retrieve the uarch
specific flags, and not the target of the compiler.
"""
monkeypatch.setattr(spack.build_systems.compiler, "_implicit_rpaths", lambda pkg: [])
gcc = default_mock_concretization("gcc target=core2")
compiler_wrapper_pkg = default_mock_concretization("compiler-wrapper target=core2").package
mpileaks = default_mock_concretization("mpileaks target=x86_64")
env = EnvironmentModifications()
gcc.package.setup_dependent_build_environment(env, mpileaks)
compiler_wrapper_pkg.setup_dependent_build_environment(env, mpileaks)
actions = env.group_by_name()["SPACK_TARGET_ARGS_CC"]
assert len(actions) == 1 and isinstance(actions[0], spack.util.environment.SetEnv)

View File

@ -12,7 +12,6 @@
import spack.build_environment
import spack.config
from spack.paths import build_env_path
from spack.util.environment import SYSTEM_DIR_CASE_ENTRY, set_env
from spack.util.executable import Executable, ProcessError
@ -110,12 +109,6 @@
#: The prefix of the package being mock installed
pkg_prefix = "/spack-test-prefix"
# Compilers to use during tests
cc = Executable(os.path.join(build_env_path, "cc"))
ld = Executable(os.path.join(build_env_path, "ld"))
cpp = Executable(os.path.join(build_env_path, "cpp"))
cxx = Executable(os.path.join(build_env_path, "c++"))
fc = Executable(os.path.join(build_env_path, "fc"))
#: the "real" compiler the wrapper is expected to invoke
real_cc = "/bin/mycc"
@ -169,7 +162,10 @@ def wrapper_environment(working_env):
SPACK_TARGET_ARGS_CC="-march=znver2 -mtune=znver2",
SPACK_TARGET_ARGS_CXX="-march=znver2 -mtune=znver2",
SPACK_TARGET_ARGS_FORTRAN="-march=znver4 -mtune=znver4",
SPACK_LINKER_ARG="-Wl,",
SPACK_CC_LINKER_ARG="-Wl,",
SPACK_CXX_LINKER_ARG="-Wl,",
SPACK_FC_LINKER_ARG="-Wl,",
SPACK_F77_LINKER_ARG="-Wl,",
SPACK_DTAGS_TO_ADD="--disable-new-dtags",
SPACK_DTAGS_TO_STRIP="--enable-new-dtags",
SPACK_COMPILER_FLAGS_KEEP="",
@ -198,6 +194,7 @@ def check_args(cc, args, expected):
per line, so that we see whether arguments that should (or shouldn't)
contain spaces are parsed correctly.
"""
cc = Executable(str(cc))
with set_env(SPACK_TEST_COMMAND="dump-args"):
cc_modified_args = cc(*args, output=str).strip().split("\n")
assert cc_modified_args == expected
@ -210,9 +207,9 @@ def check_args_contents(cc, args, must_contain, must_not_contain):
per line, so that we see whether arguments that should (or shouldn't)
contain spaces are parsed correctly.
"""
cc = Executable(str(cc))
with set_env(SPACK_TEST_COMMAND="dump-args"):
cc_modified_args = cc(*args, output=str).strip().split("\n")
print(cc_modified_args)
for a in must_contain:
assert a in cc_modified_args
for a in must_not_contain:
@ -225,6 +222,7 @@ def check_env_var(executable, var, expected):
This assumes that cc will print debug output when it's environment
contains SPACK_TEST_COMMAND=dump-env-<variable-to-debug>
"""
executable = Executable(str(executable))
with set_env(SPACK_TEST_COMMAND="dump-env-" + var):
output = executable(*test_args, output=str).strip()
assert executable.path + ": " + var + ": " + expected == output
@ -232,17 +230,25 @@ def check_env_var(executable, var, expected):
def dump_mode(cc, args):
"""Make cc dump the mode it detects, and return it."""
cc = Executable(str(cc))
with set_env(SPACK_TEST_COMMAND="dump-mode"):
return cc(*args, output=str).strip()
def test_no_wrapper_environment():
def test_no_wrapper_environment(wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
with pytest.raises(ProcessError):
output = cc(output=str)
assert "Spack compiler must be run from Spack" in output
def test_vcheck_mode(wrapper_environment):
def test_modes(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
cxx = wrapper_dir / "c++"
cpp = wrapper_dir / "cpp"
ld = wrapper_dir / "ld"
# vcheck
assert dump_mode(cc, ["-I/include", "--version"]) == "vcheck"
assert dump_mode(cc, ["-I/include", "-V"]) == "vcheck"
assert dump_mode(cc, ["-I/include", "-v"]) == "vcheck"
@ -250,38 +256,39 @@ def test_vcheck_mode(wrapper_environment):
assert dump_mode(cc, ["-I/include", "--version", "-c"]) == "vcheck"
assert dump_mode(cc, ["-I/include", "-V", "-o", "output"]) == "vcheck"
def test_cpp_mode(wrapper_environment):
# cpp
assert dump_mode(cc, ["-E"]) == "cpp"
assert dump_mode(cxx, ["-E"]) == "cpp"
assert dump_mode(cpp, []) == "cpp"
def test_as_mode(wrapper_environment):
# as
assert dump_mode(cc, ["-S"]) == "as"
def test_ccld_mode(wrapper_environment):
# ccld
assert dump_mode(cc, []) == "ccld"
assert dump_mode(cc, ["foo.c", "-o", "foo"]) == "ccld"
assert dump_mode(cc, ["foo.c", "-o", "foo", "-Wl,-rpath,foo"]) == "ccld"
assert dump_mode(cc, ["foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath,foo"]) == "ccld"
def test_ld_mode(wrapper_environment):
# ld
assert dump_mode(ld, []) == "ld"
assert dump_mode(ld, ["foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath,foo"]) == "ld"
def test_ld_unterminated_rpath(wrapper_environment):
@pytest.mark.regression("37179")
def test_expected_args(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
fc = wrapper_dir / "fc"
ld = wrapper_dir / "ld"
# ld_unterminated_rpath
check_args(
ld,
["foo.o", "bar.o", "baz.o", "-o", "foo", "-rpath"],
["ld", "--disable-new-dtags", "foo.o", "bar.o", "baz.o", "-o", "foo", "-rpath"],
)
def test_xlinker_unterminated_rpath(wrapper_environment):
# xlinker_unterminated_rpath
check_args(
cc,
["foo.o", "bar.o", "baz.o", "-o", "foo", "-Xlinker", "-rpath"],
@ -299,8 +306,7 @@ def test_xlinker_unterminated_rpath(wrapper_environment):
],
)
def test_wl_unterminated_rpath(wrapper_environment):
# wl_unterminated_rpath
check_args(
cc,
["foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath"],
@ -309,99 +315,7 @@ def test_wl_unterminated_rpath(wrapper_environment):
+ ["-Wl,--disable-new-dtags", "foo.o", "bar.o", "baz.o", "-o", "foo", "-Wl,-rpath"],
)
def test_ld_flags(wrapper_environment, wrapper_flags):
check_args(
ld,
test_args,
["ld"]
+ test_include_paths
+ test_library_paths
+ ["--disable-new-dtags"]
+ test_rpaths
+ test_args_without_paths
+ spack_ldlibs,
)
def test_cpp_flags(wrapper_environment, wrapper_flags):
check_args(
cpp,
test_args,
["cpp"]
+ test_include_paths
+ test_library_paths
+ test_args_without_paths
+ spack_cppflags,
)
def test_cc_flags(wrapper_environment, wrapper_flags):
check_args(
cc,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ spack_cflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
def test_cxx_flags(wrapper_environment, wrapper_flags):
check_args(
cxx,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
def test_fc_flags(wrapper_environment, wrapper_flags):
check_args(
fc,
test_args,
[real_cc]
+ target_args_fc
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_fflags
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
def test_always_cflags(wrapper_environment, wrapper_flags):
with set_env(SPACK_ALWAYS_CFLAGS="-always1 -always2"):
check_args(
cc,
["-v", "--cmd-line-v-opt"],
[real_cc] + ["-always1", "-always2"] + ["-v", "--cmd-line-v-opt"],
)
def test_Wl_parsing(wrapper_environment):
# Wl_parsing
check_args(
cc,
["-Wl,-rpath,/a,--enable-new-dtags,-rpath=/b,--rpath", "-Wl,/c"],
@ -410,26 +324,22 @@ def test_Wl_parsing(wrapper_environment):
+ ["-Wl,--disable-new-dtags", "-Wl,-rpath,/a", "-Wl,-rpath,/b", "-Wl,-rpath,/c"],
)
@pytest.mark.regression("37179")
def test_Wl_parsing_with_missing_value(wrapper_environment):
# Wl_parsing_with_missing_value
check_args(
cc,
["-Wl,-rpath=/a,-rpath=", "-Wl,--rpath="],
[real_cc] + target_args + ["-Wl,--disable-new-dtags", "-Wl,-rpath,/a"],
)
@pytest.mark.regression("37179")
def test_Wl_parsing_NAG_is_ignored(wrapper_environment):
# Wl_parsing_NAG_is_ignored
check_args(
fc,
["-Wl,-Wl,,x,,y,,z"],
[real_cc] + target_args_fc + ["-Wl,--disable-new-dtags", "-Wl,-Wl,,x,,y,,z"],
)
def test_Xlinker_parsing(wrapper_environment):
# Xlinker_parsing
#
# -Xlinker <x> ... -Xlinker <y> may have compiler flags inbetween, like -O3 in this
# example. Also check that a trailing -Xlinker (which is a compiler error) is not
# dropped or given an empty argument.
@ -460,8 +370,8 @@ def test_Xlinker_parsing(wrapper_environment):
],
)
def test_rpath_without_value(wrapper_environment):
# rpath_without_value
#
# cc -Wl,-rpath without a value shouldn't drop -Wl,-rpath;
# same for -Xlinker
check_args(
@ -475,14 +385,10 @@ def test_rpath_without_value(wrapper_environment):
[real_cc] + target_args + ["-Wl,--disable-new-dtags", "-O3", "-g", "-Xlinker", "-rpath"],
)
def test_dep_rpath(wrapper_environment):
"""Ensure RPATHs for root package are added."""
# dep_rapth
check_args(cc, test_args, [real_cc] + target_args + common_compile_args)
def test_dep_include(wrapper_environment):
"""Ensure a single dependency include directory is added."""
# dep_include
with set_env(SPACK_INCLUDE_DIRS="x"):
check_args(
cc,
@ -497,29 +403,9 @@ def test_dep_include(wrapper_environment):
+ test_args_without_paths,
)
def test_system_path_cleanup(wrapper_environment):
"""Ensure SPACK_ENV_PATH is removed from PATH, even with trailing /
The compiler wrapper has to ensure that it is not called nested
like it would happen when gcc's collect2 looks in PATH for ld.
To prevent nested calls, the compiler wrapper removes the elements
of SPACK_ENV_PATH from PATH. Autotest's generated testsuite appends
a / to each element of PATH when adding AUTOTEST_PATH.
Thus, ensure that PATH cleanup works even with trailing /.
"""
system_path = "/bin:/usr/bin:/usr/local/bin"
cc_dir = os.path.dirname(cc.path)
with set_env(SPACK_ENV_PATH=cc_dir, SPACK_CC="true"):
with set_env(PATH=cc_dir + ":" + system_path):
check_env_var(cc, "PATH", system_path)
with set_env(PATH=cc_dir + "/:" + system_path):
check_env_var(cc, "PATH", system_path)
def test_dep_lib(wrapper_environment):
"""Ensure a single dependency RPATH is added."""
# dep_lib
#
# Ensure a single dependency RPATH is added
with set_env(SPACK_LINK_DIRS="x", SPACK_RPATH_DIRS="x"):
check_args(
cc,
@ -535,9 +421,9 @@ def test_dep_lib(wrapper_environment):
+ test_args_without_paths,
)
def test_dep_lib_no_rpath(wrapper_environment):
"""Ensure a single dependency link flag is added with no dep RPATH."""
# dep_lib_no_rpath
#
# Ensure a single dependency link flag is added with no dep RPATH
with set_env(SPACK_LINK_DIRS="x"):
check_args(
cc,
@ -552,9 +438,8 @@ def test_dep_lib_no_rpath(wrapper_environment):
+ test_args_without_paths,
)
def test_dep_lib_no_lib(wrapper_environment):
"""Ensure a single dependency RPATH is added with no -L."""
# dep_lib_no_lib
# Ensure a single dependency RPATH is added with no -L
with set_env(SPACK_RPATH_DIRS="x"):
check_args(
cc,
@ -569,9 +454,8 @@ def test_dep_lib_no_lib(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_deps(wrapper_environment):
"""Ensure all flags are added in ccld mode."""
# ccld_deps
# Ensure all flags are added in ccld mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@ -592,13 +476,13 @@ def test_ccld_deps(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_deps_isystem(wrapper_environment):
"""Ensure all flags are added in ccld mode.
When a build uses -isystem, Spack should inject it's
include paths using -isystem. Spack will insert these
after any provided -isystem includes, but before any
system directories included using -isystem"""
# ccld_deps_isystem
#
# Ensure all flags are added in ccld mode.
# When a build uses -isystem, Spack should inject it's
# include paths using -isystem. Spack will insert these
# after any provided -isystem includes, but before any
# system directories included using -isystem
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@ -620,9 +504,8 @@ def test_ccld_deps_isystem(wrapper_environment):
+ test_args_without_paths,
)
def test_cc_deps(wrapper_environment):
"""Ensure -L and RPATHs are not added in cc mode."""
# cc_deps
# Ensure -L and RPATHs are not added in cc mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@ -640,9 +523,8 @@ def test_cc_deps(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_with_system_dirs(wrapper_environment):
"""Ensure all flags are added in ccld mode."""
# ccld_with_system_dirs
# Ensure all flags are added in ccld mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@ -673,12 +555,11 @@ def test_ccld_with_system_dirs(wrapper_environment):
+ test_args_without_paths,
)
def test_ccld_with_system_dirs_isystem(wrapper_environment):
"""Ensure all flags are added in ccld mode.
Ensure that includes are in the proper
place when a build uses -isystem, and uses
system directories in the include paths"""
# ccld_with_system_dirs_isystem
# Ensure all flags are added in ccld mode.
# Ensure that includes are in the proper
# place when a build uses -isystem, and uses
# system directories in the include paths
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@ -711,9 +592,8 @@ def test_ccld_with_system_dirs_isystem(wrapper_environment):
+ test_args_without_paths,
)
def test_ld_deps(wrapper_environment):
"""Ensure no (extra) -I args or -Wl, are passed in ld mode."""
# ld_deps
# Ensure no (extra) -I args or -Wl, are passed in ld mode
with set_env(
SPACK_INCLUDE_DIRS="xinc:yinc:zinc",
SPACK_RPATH_DIRS="xlib:ylib:zlib",
@ -732,9 +612,8 @@ def test_ld_deps(wrapper_environment):
+ test_args_without_paths,
)
def test_ld_deps_no_rpath(wrapper_environment):
"""Ensure SPACK_LINK_DEPS controls -L for ld."""
# ld_deps_no_rpath
# Ensure SPACK_LINK_DEPS controls -L for ld
with set_env(SPACK_INCLUDE_DIRS="xinc:yinc:zinc", SPACK_LINK_DIRS="xlib:ylib:zlib"):
check_args(
ld,
@ -748,9 +627,8 @@ def test_ld_deps_no_rpath(wrapper_environment):
+ test_args_without_paths,
)
def test_ld_deps_no_link(wrapper_environment):
"""Ensure SPACK_RPATH_DEPS controls -rpath for ld."""
# ld_deps_no_link
# Ensure SPACK_RPATH_DEPS controls -rpath for ld
with set_env(SPACK_INCLUDE_DIRS="xinc:yinc:zinc", SPACK_RPATH_DIRS="xlib:ylib:zlib"):
check_args(
ld,
@ -765,10 +643,124 @@ def test_ld_deps_no_link(wrapper_environment):
)
def test_ld_deps_partial(wrapper_environment):
def test_expected_args_with_flags(wrapper_environment, wrapper_flags, wrapper_dir):
cc = wrapper_dir / "cc"
cxx = wrapper_dir / "c++"
cpp = wrapper_dir / "cpp"
fc = wrapper_dir / "fc"
ld = wrapper_dir / "ld"
# ld_flags
check_args(
ld,
test_args,
["ld"]
+ test_include_paths
+ test_library_paths
+ ["--disable-new-dtags"]
+ test_rpaths
+ test_args_without_paths
+ spack_ldlibs,
)
# cpp_flags
check_args(
cpp,
test_args,
["cpp"]
+ test_include_paths
+ test_library_paths
+ test_args_without_paths
+ spack_cppflags,
)
# cc_flags
check_args(
cc,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ spack_cflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
# cxx_flags
check_args(
cxx,
test_args,
[real_cc]
+ target_args
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
# fc_flags
check_args(
fc,
test_args,
[real_cc]
+ target_args_fc
+ test_include_paths
+ ["-Lfoo"]
+ test_library_paths
+ ["-Wl,--disable-new-dtags"]
+ test_wl_rpaths
+ test_args_without_paths
+ spack_fflags
+ spack_cppflags
+ ["-Wl,--gc-sections"]
+ spack_ldlibs,
)
# always_cflags
with set_env(SPACK_ALWAYS_CFLAGS="-always1 -always2"):
check_args(
cc,
["-v", "--cmd-line-v-opt"],
[real_cc] + ["-always1", "-always2"] + ["-v", "--cmd-line-v-opt"],
)
def test_system_path_cleanup(wrapper_environment, wrapper_dir):
"""Ensure SPACK_ENV_PATH is removed from PATH, even with trailing /
The compiler wrapper has to ensure that it is not called nested
like it would happen when gcc's collect2 looks in PATH for ld.
To prevent nested calls, the compiler wrapper removes the elements
of SPACK_ENV_PATH from PATH. Autotest's generated testsuite appends
a / to each element of PATH when adding AUTOTEST_PATH.
Thus, ensure that PATH cleanup works even with trailing /.
"""
cc = wrapper_dir / "cc"
system_path = "/bin:/usr/bin:/usr/local/bin"
with set_env(SPACK_ENV_PATH=str(wrapper_dir), SPACK_CC="true"):
with set_env(PATH=str(wrapper_dir) + ":" + system_path):
check_env_var(cc, "PATH", system_path)
with set_env(PATH=str(wrapper_dir) + "/:" + system_path):
check_env_var(cc, "PATH", system_path)
def test_ld_deps_partial(wrapper_environment, wrapper_dir):
"""Make sure ld -r (partial link) is handled correctly on OS's where it
doesn't accept rpaths.
"""
ld = wrapper_dir / "ld"
with set_env(SPACK_INCLUDE_DIRS="xinc", SPACK_RPATH_DIRS="xlib", SPACK_LINK_DIRS="xlib"):
# TODO: do we need to add RPATHs on other platforms like Linux?
# TODO: Can't we treat them the same?
@ -805,7 +797,8 @@ def test_ld_deps_partial(wrapper_environment):
)
def test_ccache_prepend_for_cc(wrapper_environment):
def test_ccache_prepend_for_cc(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
with set_env(SPACK_CCACHE_BINARY="ccache"):
os.environ["SPACK_SHORT_SPEC"] = "foo@1.2=linux-x86_64"
check_args(
@ -828,7 +821,8 @@ def test_ccache_prepend_for_cc(wrapper_environment):
)
def test_no_ccache_prepend_for_fc(wrapper_environment):
def test_no_ccache_prepend_for_fc(wrapper_environment, wrapper_dir):
fc = wrapper_dir / "fc"
os.environ["SPACK_SHORT_SPEC"] = "foo@1.2=linux-x86_64"
check_args(
fc,
@ -845,7 +839,8 @@ def test_no_ccache_prepend_for_fc(wrapper_environment):
)
def test_keep_and_replace(wrapper_environment):
def test_keep_and_replace(wrapper_environment, wrapper_dir):
cc = wrapper_dir / "cc"
werror_specific = ["-Werror=meh"]
werror = ["-Werror"]
werror_all = werror_specific + werror
@ -906,7 +901,8 @@ def test_keep_and_replace(wrapper_environment):
],
)
@pytest.mark.usefixtures("wrapper_environment", "mutable_config")
def test_flag_modification(cfg_override, initial, expected, must_be_gone):
def test_flag_modification(cfg_override, initial, expected, must_be_gone, wrapper_dir):
cc = wrapper_dir / "cc"
spack.config.add(cfg_override)
env = spack.build_environment.clean_environment()
@ -917,7 +913,9 @@ def test_flag_modification(cfg_override, initial, expected, must_be_gone):
@pytest.mark.regression("9160")
def test_disable_new_dtags(wrapper_environment, wrapper_flags):
def test_disable_new_dtags(wrapper_environment, wrapper_flags, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
ld = Executable(str(wrapper_dir / "ld"))
with set_env(SPACK_TEST_COMMAND="dump-args"):
result = ld(*test_args, output=str).strip().split("\n")
assert "--disable-new-dtags" in result
@ -926,7 +924,9 @@ def test_disable_new_dtags(wrapper_environment, wrapper_flags):
@pytest.mark.regression("9160")
def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags):
def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
ld = Executable(str(wrapper_dir / "ld"))
with set_env(SPACK_TEST_COMMAND="dump-args"):
result = ld(*(test_args + ["--enable-new-dtags"]), output=str)
result = result.strip().split("\n")
@ -938,7 +938,9 @@ def test_filter_enable_new_dtags(wrapper_environment, wrapper_flags):
@pytest.mark.regression("22643")
def test_linker_strips_loopopt(wrapper_environment, wrapper_flags):
def test_linker_strips_loopopt(wrapper_environment, wrapper_flags, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
ld = Executable(str(wrapper_dir / "ld"))
with set_env(SPACK_TEST_COMMAND="dump-args"):
# ensure that -loopopt=0 is not present in ld mode
result = ld(*(test_args + ["-loopopt=0"]), output=str)
@ -958,7 +960,9 @@ def test_linker_strips_loopopt(wrapper_environment, wrapper_flags):
assert "-loopopt=0" in result
def test_spack_managed_dirs_are_prioritized(wrapper_environment):
def test_spack_managed_dirs_are_prioritized(wrapper_environment, wrapper_dir):
cc = Executable(str(wrapper_dir / "cc"))
# We have two different stores with 5 packages divided over them
pkg1 = "/path/to/spack-1/opt/spack/linux-ubuntu22.04-zen2/gcc-13.2.0/pkg-1.0-abcdef"
pkg2 = "/path/to/spack-1/opt/spack/linux-ubuntu22.04-zen2/gcc-13.2.0/pkg-2.0-abcdef"

View File

@ -344,10 +344,20 @@ def test_get_spec_filter_list(mutable_mock_env_path, mutable_mock_repo):
"libelf",
"gcc",
"gcc-runtime",
"compiler-wrapper",
}
depth_2_set = {"mpich", "callpath", "dyninst", "libdwarf", "libelf", "gcc", "gcc-runtime"}
depth_1_set = {"dyninst", "libdwarf", "libelf", "gcc", "gcc-runtime"}
depth_0_set = {"libdwarf", "libelf", "gcc", "gcc-runtime"}
depth_2_set = {
"mpich",
"callpath",
"dyninst",
"libdwarf",
"libelf",
"gcc",
"gcc-runtime",
"compiler-wrapper",
}
depth_1_set = {"dyninst", "libdwarf", "libelf", "gcc", "gcc-runtime", "compiler-wrapper"}
depth_0_set = {"libdwarf", "libelf", "gcc", "gcc-runtime", "compiler-wrapper"}
expectations = {
None: full_set,

View File

@ -223,7 +223,7 @@ def test_load_first(install_mockery, mock_fetch, mock_archive, mock_packages):
for dep in ("mpileaks", "callpath", "dyninst", "libelf", "libdwarf", "mpich")
)
assert all(
len([diff for diff in result["intersect"] if diff[0] == attr]) == 7
len([diff for diff in result["intersect"] if diff[0] == attr]) == 8
for attr in (
"version",
"node_target",

View File

@ -170,7 +170,7 @@ def _check_json_output(spec_list):
def _check_json_output_deps(spec_list):
assert len(spec_list) == 15
assert len(spec_list) == 16
names = [spec["name"] for spec in spec_list]
assert names.count("mpileaks") == 3
@ -272,6 +272,7 @@ def test_find_format_deps(database, config):
dyninst-8.2
libdwarf-20130729
libelf-0.8.13
compiler-wrapper-1.0
gcc-10.2.1
gcc-runtime-10.2.1
zmpi-1.0
@ -293,6 +294,7 @@ def test_find_format_deps_paths(database, config):
dyninst-8.2 {mpileaks['dyninst'].prefix}
libdwarf-20130729 {mpileaks['libdwarf'].prefix}
libelf-0.8.13 {mpileaks['libelf'].prefix}
compiler-wrapper-1.0 {mpileaks['compiler-wrapper'].prefix}
gcc-10.2.1 {mpileaks['gcc'].prefix}
gcc-runtime-10.2.1 {mpileaks['gcc-runtime'].prefix}
zmpi-1.0 {mpileaks['zmpi'].prefix}

View File

@ -61,7 +61,7 @@ def test_install_package_and_dependency(
assert filename in files
content = filename.open().read()
assert 'tests="3"' in content
assert 'tests="4"' in content
assert 'failures="0"' in content
assert 'errors="0"' in content
@ -106,12 +106,12 @@ def test_install_package_already_installed(
content = filename.open().read()
print(content)
assert 'tests="4"' in content
assert 'tests="5"' in content
assert 'failures="0"' in content
assert 'errors="0"' in content
skipped = [line for line in content.split("\n") if "skipped" in line]
assert len(skipped) == 4
assert len(skipped) == 5
@pytest.mark.parametrize(
@ -448,16 +448,16 @@ def just_throw(*args, **kwargs):
# Only libelf error is reported (through libdwarf root spec). libdwarf
# install is skipped and it is not an error.
assert 'tests="1"' in content
assert 'tests="0"' not in content
assert 'failures="0"' in content
assert 'errors="1"' in content
assert 'errors="0"' not in content
# Nothing should have succeeded
assert 'errors="0"' not in content
# We want to have both stdout and stderr
assert "<system-out>" in content
assert 'error message="{0}"'.format(msg) in content
assert f'error message="{msg}"' in content
@pytest.mark.usefixtures("noop_install", "mock_packages", "config")

View File

@ -30,7 +30,7 @@ def test_mark_all_explicit(mutable_database):
mark("-e", "-a")
gc("-y")
all_specs = spack.store.STORE.layout.all_specs()
assert len(all_specs) == 16
assert len(all_specs) == 17
@pytest.mark.db
@ -64,4 +64,4 @@ def test_mark_all_implicit_then_explicit(mutable_database):
mark("-e", "-a")
gc("-y")
all_specs = spack.store.STORE.layout.all_specs()
assert len(all_specs) == 16
assert len(all_specs) == 17

View File

@ -90,7 +90,7 @@ def test_recursive_uninstall(mutable_database):
@pytest.mark.db
@pytest.mark.regression("3690")
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 9), ("libelf", 7)])
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 10), ("libelf", 8)])
def test_uninstall_spec_with_multiple_roots(
constraint, expected_number_of_specs, mutable_database
):
@ -100,7 +100,7 @@ def test_uninstall_spec_with_multiple_roots(
@pytest.mark.db
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 15), ("libelf", 15)])
@pytest.mark.parametrize("constraint,expected_number_of_specs", [("dyninst", 16), ("libelf", 16)])
def test_force_uninstall_spec_with_ref_count_not_zero(
constraint, expected_number_of_specs, mutable_database
):
@ -170,7 +170,7 @@ def db_specs():
all_specs, mpileaks_specs, callpath_specs, mpi_specs = db_specs()
total_specs = len(all_specs)
assert total_specs == 15
assert total_specs == 16
assert len(mpileaks_specs) == 3
assert len(callpath_specs) == 2
assert len(mpi_specs) == 3

View File

@ -466,7 +466,7 @@ def test_architecture_deep_inheritance(self, mock_targets, compiler_factory):
with spack.config.override("packages", {"gcc": {"externals": [cnl_compiler]}}):
spec_str = "mpileaks %gcc@4.5.0 os=CNL target=nocona ^dyninst os=CNL ^callpath os=CNL"
spec = spack.concretize.concretize_one(spec_str)
for s in spec.traverse(root=False):
for s in spec.traverse(root=False, deptype=("link", "run")):
assert s.architecture.target == spec.architecture.target
def test_compiler_flags_from_user_are_grouped(self):
@ -1991,17 +1991,17 @@ def test_installed_specs_disregard_conflicts(self, mutable_database, monkeypatch
assert s.satisfies("~debug"), s
@pytest.mark.regression("32471")
def test_require_targets_are_allowed(self, mutable_database):
def test_require_targets_are_allowed(self, mutable_config, mutable_database):
"""Test that users can set target constraints under the require attribute."""
# Configuration to be added to packages.yaml
required_target = archspec.cpu.TARGETS[spack.platforms.test.Test.default].family
external_conf = {"all": {"require": f"target={required_target}"}}
spack.config.set("packages", external_conf)
mutable_config.set("packages", external_conf)
with spack.config.override("concretizer:reuse", False):
spec = spack.concretize.concretize_one("mpich")
for s in spec.traverse():
for s in spec.traverse(deptype=("link", "run")):
assert s.satisfies(f"target={required_target}")
def test_external_python_extensions_have_dependency(self):
@ -2994,7 +2994,7 @@ def test_filtering_reused_specs(
@pytest.mark.usefixtures("mutable_database", "mock_store")
@pytest.mark.parametrize(
"reuse_yaml,expected_length",
[({"from": [{"type": "local"}]}, 19), ({"from": [{"type": "buildcache"}]}, 0)],
[({"from": [{"type": "local"}]}, 20), ({"from": [{"type": "buildcache"}]}, 0)],
)
@pytest.mark.not_on_windows("Expected length is different on Windows")
def test_selecting_reused_sources(

View File

@ -2154,3 +2154,12 @@ def info(self):
@pytest.fixture()
def mock_runtimes(config, mock_packages):
return mock_packages.packages_with_tags("runtime")
@pytest.fixture()
def wrapper_dir(install_mockery):
"""Installs the compiler wrapper and returns the prefix where the script is installed."""
wrapper = spack.spec.Spec("compiler-wrapper").concretized()
wrapper_pkg = wrapper.package
PackageInstaller([wrapper_pkg], explicit=True).install()
return wrapper_pkg.bin_dir()

View File

@ -95,11 +95,11 @@ def upstream_and_downstream_db(tmpdir, gen_mock_layout):
@pytest.mark.parametrize(
"install_tree,result",
[
("all", ["pkg-b", "pkg-c", "gcc-runtime", "gcc"]),
("all", ["pkg-b", "pkg-c", "gcc-runtime", "gcc", "compiler-wrapper"]),
("upstream", ["pkg-c"]),
("local", ["pkg-b", "gcc-runtime", "gcc"]),
("local", ["pkg-b", "gcc-runtime", "gcc", "compiler-wrapper"]),
("{u}", ["pkg-c"]),
("{d}", ["pkg-b", "gcc-runtime", "gcc"]),
("{d}", ["pkg-b", "gcc-runtime", "gcc", "compiler-wrapper"]),
],
ids=["all", "upstream", "local", "upstream_path", "downstream_path"],
)
@ -491,7 +491,7 @@ def test_005_db_exists(database):
def test_010_all_install_sanity(database):
"""Ensure that the install layout reflects what we think it does."""
all_specs = spack.store.STORE.layout.all_specs()
assert len(all_specs) == 16
assert len(all_specs) == 17
# Query specs with multiple configurations
mpileaks_specs = [s for s in all_specs if s.satisfies("mpileaks")]
@ -608,7 +608,7 @@ def test_050_basic_query(database):
"""Ensure querying database is consistent with what is installed."""
# query everything
total_specs = len(spack.store.STORE.db.query())
assert total_specs == 19
assert total_specs == 20
# query specs with multiple configurations
mpileaks_specs = database.query("mpileaks")
@ -827,11 +827,11 @@ def check_unused(roots, deptype, expected):
assert set(u.name for u in unused) == set(expected)
default_dt = dt.LINK | dt.RUN
check_unused(None, default_dt, ["cmake", "gcc"])
check_unused(None, default_dt, ["cmake", "gcc", "compiler-wrapper"])
check_unused(
[si, ml_mpich, ml_mpich2, ml_zmpi, externaltest],
default_dt,
["trivial-smoke-test", "cmake", "gcc"],
["trivial-smoke-test", "cmake", "gcc", "compiler-wrapper"],
)
check_unused(
[si, ml_mpich, ml_mpich2, ml_zmpi, externaltest],
@ -846,7 +846,15 @@ def check_unused(roots, deptype, expected):
check_unused(
[si, ml_mpich, ml_mpich2, ml_zmpi],
default_dt,
["trivial-smoke-test", "cmake", "externaltest", "externaltool", "externalvirtual", "gcc"],
[
"trivial-smoke-test",
"cmake",
"externaltest",
"externaltool",
"externalvirtual",
"gcc",
"compiler-wrapper",
],
)
@ -1080,7 +1088,7 @@ def test_check_parents(spec_str, parent_name, expected_nparents, database):
def test_db_all_hashes(database):
# ensure we get the right number of hashes without a read transaction
hashes = database.all_hashes()
assert len(hashes) == 19
assert len(hashes) == 20
# and make sure the hashes match
with database.read_transaction():

View File

@ -54,25 +54,34 @@ def test_ascii_graph_mpileaks(config, mock_packages, monkeypatch):
|\
| |\
| | |\
| | | o callpath
| |_|/|
|/| |/|
| |/|/|
o | | | mpich
|\| | |
| |/ /
|/| |
| | o dyninst
| |/|
|/|/|
| | |\
| | | o libdwarf
| | | |\
| | | | o callpath
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/|
| | | | o dyninst
| | |_|/|
| |/| |/|
| | |/|/|
| | | | |\
o | | | | | mpich
|\| | | | |
|\ \ \ \ \ \
| |_|/ / / /
|/| | | | |
| |/ / / /
| | | | o libdwarf
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/
| | | o libelf
| |_|/|
|/| |/|
| |/|/
| | o libelf
| |/|
|/|/
| o | compiler-wrapper
| /
| o gcc-runtime
|/
o gcc

View File

@ -1022,7 +1022,7 @@ def test_install_fail_fast_on_detect(install_mockery, monkeypatch, capsys):
b, c = spack.concretize.concretize_one("pkg-b"), spack.concretize.concretize_one("pkg-c")
b_id, c_id = inst.package_id(b), inst.package_id(c)
installer = create_installer([b, c], {"fail_fast": True})
installer = create_installer([c, b], {"fail_fast": True})
# Make sure all packages are identified as failed
# This will prevent b from installing, which will cause the build of c to be skipped.
@ -1031,7 +1031,7 @@ def test_install_fail_fast_on_detect(install_mockery, monkeypatch, capsys):
with pytest.raises(spack.error.InstallError, match="after first install failure"):
installer.install()
assert c_id in installer.failed, "Expected b to be marked as failed"
assert c_id in installer.failed
assert b_id not in installer.failed, "Expected no attempt to install pkg-c"
assert f"{c_id} failed to install" in capsys.readouterr().err

View File

@ -15,12 +15,15 @@
import pytest
import spack
import spack.binary_distribution
import spack.database
import spack.deptypes as dt
import spack.environment as ev
import spack.error
import spack.oci.opener
import spack.spec
import spack.traverse
from spack.main import SpackCommand
from spack.oci.image import Digest, ImageReference, default_config, default_manifest
from spack.oci.oci import blob_exists, get_manifest_and_config, upload_blob, upload_manifest
@ -82,7 +85,13 @@ def test_buildcache_tag(install_mockery, mock_fetch, mutable_mock_env_path):
name = ImageReference.from_string("example.com/image:full_env")
with ev.read("test") as e:
specs = [x for x in e.all_specs() if not x.external]
specs = [
x
for x in spack.traverse.traverse_nodes(
e.concrete_roots(), deptype=dt.LINK | dt.RUN
)
if not x.external
]
manifest, config = get_manifest_and_config(name)
@ -99,7 +108,9 @@ def test_buildcache_tag(install_mockery, mock_fetch, mutable_mock_env_path):
name = ImageReference.from_string("example.com/image:single_spec")
manifest, config = get_manifest_and_config(name)
assert len(manifest["layers"]) == len([x for x in libelf.traverse() if not x.external])
assert len(manifest["layers"]) == len(
[x for x in libelf.traverse(deptype=dt.LINK | dt.RUN) if not x.external]
)
def test_buildcache_push_with_base_image_command(mutable_database, tmpdir):

View File

@ -184,6 +184,7 @@ def test_conflicting_package_constraints(self, set_dependency):
[
(0, "mpileaks"),
(1, "callpath"),
(2, "compiler-wrapper"),
(2, "dyninst"),
(3, "gcc"),
(3, "gcc-runtime"),
@ -199,23 +200,29 @@ def test_conflicting_package_constraints(self, set_dependency):
[
(0, "mpileaks"),
(1, "callpath"),
(2, "compiler-wrapper"),
(2, "dyninst"),
(3, "compiler-wrapper"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "gcc"),
(3, "libdwarf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(4, "libelf"),
(5, "compiler-wrapper"),
(5, "gcc"),
(5, "gcc-runtime"),
(3, "libelf"),
(2, "gcc"),
(2, "gcc-runtime"),
(2, "zmpi"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(3, "gcc-runtime"),
(1, "compiler-wrapper"),
(1, "gcc"),
(1, "gcc-runtime"),
(1, "zmpi"),
@ -227,19 +234,24 @@ def test_conflicting_package_constraints(self, set_dependency):
[
(0, "mpileaks"),
(1, "callpath"),
(2, "compiler-wrapper"),
(2, "dyninst"),
(3, "compiler-wrapper"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "gcc"),
(3, "libdwarf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(5, "gcc"),
(4, "libelf"),
(5, "compiler-wrapper"),
(5, "gcc"),
(5, "gcc-runtime"),
(6, "gcc"),
(3, "libelf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(5, "gcc"),
@ -247,14 +259,17 @@ def test_conflicting_package_constraints(self, set_dependency):
(2, "gcc-runtime"),
(3, "gcc"),
(2, "zmpi"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "gcc"),
(1, "compiler-wrapper"),
(1, "gcc"),
(1, "gcc-runtime"),
(2, "gcc"),
(1, "zmpi"),
(2, "compiler-wrapper"),
(2, "fake"),
(2, "gcc"),
(2, "gcc-runtime"),
@ -265,6 +280,7 @@ def test_conflicting_package_constraints(self, set_dependency):
# Postorder node traversal
(
[
(2, "compiler-wrapper"),
(3, "gcc"),
(3, "gcc-runtime"),
(4, "libelf"),
@ -280,11 +296,15 @@ def test_conflicting_package_constraints(self, set_dependency):
# Postorder edge traversal
(
[
(2, "compiler-wrapper"),
(3, "compiler-wrapper"),
(3, "gcc"),
(4, "gcc"),
(3, "gcc-runtime"),
(4, "compiler-wrapper"),
(4, "gcc"),
(4, "gcc-runtime"),
(5, "compiler-wrapper"),
(5, "gcc"),
(5, "gcc-runtime"),
(4, "libelf"),
@ -293,11 +313,13 @@ def test_conflicting_package_constraints(self, set_dependency):
(2, "dyninst"),
(2, "gcc"),
(2, "gcc-runtime"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(3, "gcc-runtime"),
(2, "zmpi"),
(1, "callpath"),
(1, "compiler-wrapper"),
(1, "gcc"),
(1, "gcc-runtime"),
(1, "zmpi"),
@ -308,17 +330,22 @@ def test_conflicting_package_constraints(self, set_dependency):
# Postorder path traversal
(
[
(2, "compiler-wrapper"),
(3, "compiler-wrapper"),
(3, "gcc"),
(4, "gcc"),
(3, "gcc-runtime"),
(4, "compiler-wrapper"),
(4, "gcc"),
(5, "gcc"),
(4, "gcc-runtime"),
(5, "compiler-wrapper"),
(5, "gcc"),
(6, "gcc"),
(5, "gcc-runtime"),
(4, "libelf"),
(3, "libdwarf"),
(4, "compiler-wrapper"),
(4, "gcc"),
(5, "gcc"),
(4, "gcc-runtime"),
@ -327,15 +354,18 @@ def test_conflicting_package_constraints(self, set_dependency):
(2, "gcc"),
(3, "gcc"),
(2, "gcc-runtime"),
(3, "compiler-wrapper"),
(3, "fake"),
(3, "gcc"),
(4, "gcc"),
(3, "gcc-runtime"),
(2, "zmpi"),
(1, "callpath"),
(1, "compiler-wrapper"),
(1, "gcc"),
(2, "gcc"),
(1, "gcc-runtime"),
(2, "compiler-wrapper"),
(2, "fake"),
(2, "gcc"),
(3, "gcc"),
@ -350,47 +380,53 @@ def test_conflicting_package_constraints(self, set_dependency):
def test_traversal(self, pairs, traverse_kwargs, default_mock_concretization):
r"""Tests different traversals of the following graph
o mpileaks
o mpileaks@2.3/3qeg7jx
|\
| |\
| | |\
| | | |\
| | | | o callpath
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/|
o | | | | zmpi
|\| | | |
|\ \ \ \ \
| |_|/ / /
|/| | | |
| |\ \ \ \
| | |_|/ /
| |/| | |
| | o | | fake
| | / /
| | | o dyninst
| |_|/|
|/| |/|
| |/|/|
| | | |\
| | | | o libdwarf
| | | | |\
| | | | | o callpath@1.0/4gilijr
| |_|_|_|/|
|/| |_|_|/|
| |/| |_|/|
| | |/| |/|
| | | |/|/|
| | | | | o dyninst@8.2/u4oymb3
| | |_|_|/|
| |/| |_|/|
| | |/| |/|
| | | |/|/|
| | | | | |\
o | | | | | | mpich@3.0.4/g734fu6
|\| | | | | |
|\ \ \ \ \ \ \
| |_|/ / / / /
|/| | | | | |
| |\ \ \ \ \ \
| | |_|/ / / /
| |/| | | | |
| | |/ / / /
| | | | | o libdwarf@20130729/q5r7l2r
| |_|_|_|/|
|/| |_|_|/|
| |/| |_|/|
| | |/| |/|
| | | |/|/
| | | | o libelf@0.8.13/i2x6pya
| |_|_|/|
|/| |_|/|
| |/| |/|
| | |/|/
| | | o libel
| |_|/|
|/| |/|
| |/|/
o | | gcc-runtime
| | o | compiler-wrapper@1.0/njdili2
| | /
o | | gcc-runtime@10.5.0/iyytqeo
|\| |
| |/
|/|
o | glibc
/
o gcc
| o gcc@10.5.0/ljeisd4
|
o glibc@2.31/tbyn33w
"""
dag = default_mock_concretization("mpileaks ^zmpi")
names = [x for _, x in pairs]
@ -843,10 +879,10 @@ def test_spec_tree_respect_deptypes(self):
"query,expected_length,expected_satisfies",
[
({"virtuals": ["mpi"]}, 1, ["mpich", "mpi"]),
({"depflag": dt.BUILD}, 3, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD}, 4, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD, "virtuals": ["mpi"]}, 1, ["mpich", "mpi"]),
({"depflag": dt.LINK}, 3, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD | dt.LINK}, 4, ["mpich", "mpi", "callpath"]),
({"depflag": dt.BUILD | dt.LINK}, 5, ["mpich", "mpi", "callpath"]),
({"virtuals": ["lapack"]}, 0, []),
],
)
@ -928,7 +964,7 @@ def test_synthetic_construction_of_split_dependencies_from_same_package(mock_pac
root.add_dependency_edge(build_spec, depflag=dt.BUILD, virtuals=())
# Check dependencies from the perspective of root
assert len(root.dependencies()) == 4
assert len(root.dependencies()) == 5
assert len([x for x in root.dependencies() if x.name == "pkg-c"]) == 2
assert "@2.0" in root.dependencies(name="pkg-c", deptype=dt.BUILD)[0]

View File

@ -1697,6 +1697,7 @@ def test_spec_trim(mock_packages, config):
top.trim("dt-diamond-left")
remaining = {x.name for x in top.traverse()}
assert {
"compiler-wrapper",
"dt-diamond",
"dt-diamond-right",
"dt-diamond-bottom",
@ -1706,7 +1707,7 @@ def test_spec_trim(mock_packages, config):
top.trim("dt-diamond-right")
remaining = {x.name for x in top.traverse()}
assert {"dt-diamond", "gcc-runtime", "gcc"} == remaining
assert {"compiler-wrapper", "dt-diamond", "gcc-runtime", "gcc"} == remaining
@pytest.mark.regression("30861")

View File

@ -30,7 +30,6 @@ if [[ "$COVERAGE" == "true" ]]; then
bashcov=$(realpath ${QA_DIR}/bashcov)
# instrument scripts requiring shell coverage
sed -i "s@#\!/bin/bash@#\!${bashcov}@" "$SPACK_ROOT/lib/spack/env/cc"
if [ "$(uname -o)" != "Darwin" ]; then
# On darwin, #! interpreters must be binaries, so no sbang for bashcov
sed -i "s@#\!/bin/sh@#\!${bashcov}@" "$SPACK_ROOT/bin/sbang"

View File

@ -0,0 +1 @@
../../builtin/packages/compiler-wrapper/

View File

@ -39,7 +39,6 @@ readonly params="\
SPACK_ENV_PATH
SPACK_DEBUG_LOG_DIR
SPACK_DEBUG_LOG_ID
SPACK_LINKER_ARG
SPACK_SHORT_SPEC
SPACK_SYSTEM_DIRS
SPACK_MANAGED_DIRS"
@ -397,7 +396,9 @@ fi
#
dtags_to_add="${SPACK_DTAGS_TO_ADD}"
dtags_to_strip="${SPACK_DTAGS_TO_STRIP}"
linker_arg="${SPACK_LINKER_ARG}"
linker_arg="ERROR: LINKER ARG WAS NOT SET, MAYBE THE PACKAGE DOES NOT DEPEND ON ${comp}?"
eval "linker_arg=\${SPACK_${comp}_LINKER_ARG:?${linker_arg}}"
# Set up rpath variable according to language.
rpath="ERROR: RPATH ARG WAS NOT SET, MAYBE THE PACKAGE DOES NOT DEPEND ON ${comp}?"

View File

@ -0,0 +1,245 @@
# Copyright Spack Project Developers. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import pathlib
import shutil
from typing import List
import archspec.cpu
from llnl.util import lang
import spack.compilers.libraries
import spack.config
import spack.package_base
from spack.package import *
class CompilerWrapper(Package):
"""Spack compiler wrapper script.
Compiler commands go through this compiler wrapper in Spack builds.
The compiler wrapper is a thin layer around the standard compilers.
It enables several key pieces of functionality:
1. It allows Spack to swap compilers into and out of builds easily.
2. It adds several options to the compile line so that spack
packages can find their dependencies at build time and run time:
-I and/or -isystem arguments for dependency /include directories.
-L arguments for dependency /lib directories.
-Wl,-rpath arguments for dependency /lib directories.
"""
homepage = "https://github.com/spack/spack"
url = f"file:///{pathlib.PurePath(__file__).parent}/cc.sh"
# FIXME (compiler as nodes): use a different tag, since this is only to exclude
# this node from auto-generated rules
tags = ["runtime"]
license("Apache-2.0 OR MIT")
version(
"1.0",
sha256="92924570efbc0f388bbbeb87188e05537008bc25069529f7b519b4e48d7ddfb6",
expand=False,
)
conflicts("platform=windows")
def bin_dir(self) -> pathlib.Path:
# This adds an extra "spack" subdir, so that the script and symlinks don't get
# their way to the default view /bin directory in environment
return pathlib.Path(str(self.prefix)) / "spack" / "bin"
def install(self, spec, prefix):
cc_script = pathlib.Path(self.stage.source_path) / "cc.sh"
bin_dir = self.bin_dir()
# Copy the script
bin_dir.mkdir(parents=True)
installed_script = bin_dir / "cc"
shutil.copy(cc_script, str(installed_script))
set_executable(installed_script)
# Create links to use the script under different names
for name in (
"ld.lld",
"ld.gold",
"ld",
"ftn",
"fc",
"f95",
"f90",
"f77",
"cpp",
"c99",
"c89",
"c++",
):
(bin_dir / name).symlink_to(installed_script)
for subdir, name in (
("aocc", "clang"),
("aocc", "clang++"),
("aocc", "flang"),
("arm", "armclang"),
("arm", "armclang++"),
("arm", "armflang"),
("case-insensitive", "CC"),
("cce", "cc"),
("cce", "craycc"),
("cce", "crayftn"),
("cce", "ftn"),
("clang", "clang"),
("clang", "clang++"),
("clang", "flang"),
("fj", "fcc"),
("fj", "frt"),
("gcc", "gcc"),
("gcc", "g++"),
("gcc", "gfortran"),
("intel", "icc"),
("intel", "icpc"),
("intel", "ifort"),
("nag", "nagfor"),
("nvhpc", "nvc"),
("nvhpc", "nvc++"),
("nvhpc", "nvfortran"),
("oneapi", "icx"),
("oneapi", "icpx"),
("oneapi", "ifx"),
("rocmcc", "amdclang"),
("rocmcc", "amdclang++"),
("rocmcc", "amdflang"),
("xl", "xlc"),
("xl", "xlc++"),
("xl", "xlf"),
("xl", "xlf90"),
("xl_r", "xlc_r"),
("xl_r", "xlc++_r"),
("xl_r", "xlf_r"),
("xl_r", "xlf90_r"),
):
(bin_dir / subdir).mkdir(exist_ok=True)
(bin_dir / subdir / name).symlink_to(installed_script)
def setup_dependent_build_environment(self, env, dependent_spec):
_var_list = []
if dependent_spec.dependencies(virtuals=("c",)):
_var_list.append(("c", "cc", "CC", "SPACK_CC"))
if dependent_spec.dependencies(virtuals=("cxx",)):
_var_list.append(("cxx", "cxx", "CXX", "SPACK_CXX"))
if dependent_spec.dependencies(virtuals=("fortran",)):
_var_list.append(("fortran", "fortran", "F77", "SPACK_F77"))
_var_list.append(("fortran", "fortran", "FC", "SPACK_FC"))
# The package is not used as a compiler, so skip this setup
if not _var_list:
return
bin_dir = self.bin_dir()
implicit_rpaths, env_paths = [], []
for language, attr_name, wrapper_var_name, spack_var_name in _var_list:
compiler_pkg = dependent_spec[language].package
if not hasattr(compiler_pkg, attr_name):
continue
compiler = getattr(compiler_pkg, attr_name)
env.set(spack_var_name, compiler)
if language not in compiler_pkg.link_paths:
continue
wrapper_path = bin_dir / compiler_pkg.link_paths.get(language)
env.set(wrapper_var_name, str(wrapper_path))
env.set(f"SPACK_{wrapper_var_name}_RPATH_ARG", compiler_pkg.rpath_arg)
uarch = dependent_spec.architecture.target
version_number, _ = archspec.cpu.version_components(
compiler_pkg.spec.version.dotted_numeric_string
)
try:
isa_arg = uarch.optimization_flags(compiler_pkg.archspec_name(), version_number)
except (ValueError, archspec.cpu.UnsupportedMicroarchitecture):
isa_arg = ""
if isa_arg:
env.set(f"SPACK_TARGET_ARGS_{attr_name.upper()}", isa_arg)
# Add spack build environment path with compiler wrappers first in
# the path. We add the compiler wrapper path, which includes default
# wrappers (cc, c++, f77, f90), AND a subdirectory containing
# compiler-specific symlinks. The latter ensures that builds that
# are sensitive to the *name* of the compiler see the right name when
# we're building with the wrappers.
#
# Conflicts on case-insensitive systems (like "CC" and "cc") are
# handled by putting one in the <bin_dir>/case-insensitive
# directory. Add that to the path too.
compiler_specific_dir = (bin_dir / compiler_pkg.link_paths[language]).parent
for item in [bin_dir, compiler_specific_dir]:
env_paths.append(item)
ci = item / "case-insensitive"
if ci.is_dir():
env_paths.append(ci)
env.set(f"SPACK_{wrapper_var_name}_LINKER_ARG", compiler_pkg.linker_arg)
# Check if this compiler has implicit rpaths
implicit_rpaths.extend(_implicit_rpaths(pkg=compiler_pkg))
if implicit_rpaths:
# Implicit rpaths are accumulated across all compilers so, whenever they are mixed,
# the compiler used in ccld mode will account for rpaths from other compilers too.
implicit_rpaths = lang.dedupe(implicit_rpaths)
env.set("SPACK_COMPILER_IMPLICIT_RPATHS", ":".join(implicit_rpaths))
# Check whether we want to force RPATH or RUNPATH
if spack.config.CONFIG.get("config:shared_linking:type") == "rpath":
env.set("SPACK_DTAGS_TO_STRIP", self.enable_new_dtags)
env.set("SPACK_DTAGS_TO_ADD", self.disable_new_dtags)
else:
env.set("SPACK_DTAGS_TO_STRIP", self.disable_new_dtags)
env.set("SPACK_DTAGS_TO_ADD", self.enable_new_dtags)
for item in env_paths:
env.prepend_path("SPACK_ENV_PATH", item)
def setup_dependent_package(self, module, dependent_spec):
bin_dir = self.bin_dir()
if dependent_spec.dependencies(virtuals=("c",)):
compiler_pkg = dependent_spec["c"].package
setattr(module, "spack_cc", str(bin_dir / compiler_pkg.link_paths["c"]))
if dependent_spec.dependencies(virtuals=("cxx",)):
compiler_pkg = dependent_spec["cxx"].package
setattr(module, "spack_cxx", str(bin_dir / compiler_pkg.link_paths["cxx"]))
if dependent_spec.dependencies(virtuals=("fortran",)):
compiler_pkg = dependent_spec["fortran"].package
setattr(module, "spack_fc", str(bin_dir / compiler_pkg.link_paths["fortran"]))
setattr(module, "spack_f77", str(bin_dir / compiler_pkg.link_paths["fortran"]))
@property
def disable_new_dtags(self) -> str:
if self.spec.satisfies("platform=darwin"):
return ""
return "--disable-new-dtags"
@property
def enable_new_dtags(self) -> str:
if self.spec.satisfies("platform=darwin"):
return ""
return "--enable-new-dtags"
def _implicit_rpaths(pkg: spack.package_base.PackageBase) -> List[str]:
detector = spack.compilers.libraries.CompilerPropertyDetector(pkg.spec)
paths = detector.implicit_rpaths()
return paths

View File

@ -32,7 +32,6 @@ def install(self, spec, prefix):
compiler_version_argument = ""
compiler_version_regex = r"([1-9][0-9]*\.[0-9]*\.[0-9]*)"
# Named wrapper links within build_env_path
# Due to the challenges of supporting compiler wrappers
# in Windows, we leave these blank, and dynamically compute
# based on proper versions of MSVC from there

View File

@ -14,7 +14,6 @@
from llnl.util.lang import dedupe
import spack.paths
from spack.build_environment import dso_suffix, stat_suffix
from spack.package import *
@ -1269,6 +1268,9 @@ def setup_dependent_build_environment(self, env, dependent_spec):
"""Set PYTHONPATH to include the site-packages directory for the
extension and any other python extensions it depends on.
"""
if sys.platform == "win32":
return
# 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
@ -1295,15 +1297,16 @@ def setup_dependent_build_environment(self, env, dependent_spec):
if not dependent_spec.dependencies(virtuals=(language,)):
continue
compiler_wrapper_pkg = dependent_spec["compiler-wrapper"].package
compiler_pkg = dependent_spec[language].package
# First, we get the values from the sysconfigdata:
config_compile = self.config_vars[compile_var]
config_link = self.config_vars[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[language].package.link_paths[language]
)
new_compile = str(compiler_wrapper_pkg.bin_dir() / compiler_pkg.link_paths[language])
# Normally, the link command starts with the compilation command:
if config_link.startswith(config_compile):

View File

@ -0,0 +1 @@
../../builtin/packages/compiler-wrapper/

View File

@ -0,0 +1 @@
../../builtin/packages/compiler-wrapper/

View File

@ -0,0 +1 @@
../../builtin.mock/packages/compiler-wrapper/