Add support for externals with compiler specified

Compiler annotation is taken into account when
selecting externals as dependencies.
This commit is contained in:
Massimiliano Culpo 2025-02-12 22:02:30 +01:00
parent 745a0fac8a
commit 476f2a63e2
No known key found for this signature in database
GPG Key ID: 3E52BB992233066C
3 changed files with 79 additions and 1 deletions

View File

@ -2286,8 +2286,10 @@ def external_requirement(input_spec, requirements):
def external_imposition(input_spec, requirements):
result = []
for asp_fn in requirements:
if asp_fn.args[0] in ("depends_on", "build_requirement"):
if asp_fn.args[0] == "depends_on":
continue
elif asp_fn.args[0] == "build_requirement":
asp_fn.args = "external_build_requirement", *asp_fn.args[1:]
if asp_fn.args[1] != input_spec.name:
continue
result.append(asp_fn)

View File

@ -831,6 +831,16 @@ attr("external_spec_selected", node(ID, Package), LocalIndex) :-
attr("node", node(ID, Package)),
not attr("hash", node(ID, Package), _).
% Account for compiler annotation on externals
:- not attr("root", ExternalNode),
attr("external_build_requirement", ExternalNode, build_requirement("node", Compiler)),
not node_compiler(_, node(_, Compiler)).
1 { attr("node_version_satisfies", node(X, Compiler), Constraint) : node_compiler(_, node(X, Compiler)) }
:- not attr("root", ExternalNode),
attr("external_build_requirement", ExternalNode, build_requirement("node", Compiler)),
attr("external_build_requirement", ExternalNode, build_requirement("node_version_satisfies", Compiler, Constraint)).
% it cannot happen that a spec is external, but none of the external specs
% conditions hold.
error(100, "Attempted to use external for '{0}' which does not satisfy any configured external spec", Package)

View File

@ -3247,3 +3247,69 @@ def test_compiler_can_be_built_with_other_compilers(config, mock_packages):
c_compiler = s.dependencies(virtuals=("c",))
assert len(c_compiler) == 1 and c_compiler[0].satisfies("gcc@10")
@pytest.mark.parametrize(
"spec_str,expected",
[
# Only one compiler is in the DAG, so pick the external associated with it
("dyninst %clang", "clang"),
("dyninst %gcc", "gcc"),
# Both compilers are in the DAG, so pick the best external according to other criteria
("dyninst %clang ^libdwarf%gcc", "clang"),
("dyninst %gcc ^libdwarf%clang", "clang"),
],
)
def test_compiler_match_for_externals_is_taken_into_account(
spec_str, expected, mutable_config, mock_packages, tmp_path
):
"""Tests that compiler annotation for externals are somehow taken into account for a match"""
packages_yaml = syaml.load_config(
f"""
packages:
libelf:
externals:
- spec: "libelf@0.8.12 %gcc"
prefix: {tmp_path / 'gcc'}
- spec: "libelf@0.8.13 %clang"
prefix: {tmp_path / 'clang'}
"""
)
mutable_config.set("packages", packages_yaml["packages"])
s = spack.concretize.concretize_one(spec_str)
libelf = s["libelf"]
assert libelf.external and libelf.external_path == str(tmp_path / expected)
@pytest.mark.parametrize(
"spec_str,expected",
[
# Only one compiler is in the DAG, so pick the external associated with it
("dyninst %gcc@10", "libelf-gcc10"),
("dyninst %gcc@9", "libelf-gcc9"),
# Both compilers are in the DAG, so pick the best external according to other criteria
("dyninst %gcc@10 ^libdwarf%gcc@9", "libelf-gcc9"),
],
)
def test_compiler_match_for_externals_with_versions(
spec_str, expected, mutable_config, mock_packages, tmp_path
):
"""Tests that version constraints are taken into account for compiler annotations
on externals
"""
packages_yaml = syaml.load_config(
f"""
packages:
libelf:
buildable: false
externals:
- spec: "libelf@0.8.12 %gcc@10"
prefix: {tmp_path / 'libelf-gcc10'}
- spec: "libelf@0.8.13 %gcc@9"
prefix: {tmp_path / 'libelf-gcc9'}
"""
)
mutable_config.set("packages", packages_yaml["packages"])
s = spack.concretize.concretize_one(spec_str)
libelf = s["libelf"]
assert libelf.external and libelf.external_path == str(tmp_path / expected)