unit-tests: fix concretization and spack compiler tests

This commit is contained in:
Massimiliano Culpo 2024-11-11 12:48:56 +01:00
parent 18152b9b0f
commit 7a7556f154
No known key found for this signature in database
GPG Key ID: 3E52BB992233066C
13 changed files with 522 additions and 886 deletions

View File

@ -128,22 +128,22 @@ def test_bootstrap_disables_modulefile_generation(mutable_config):
@pytest.mark.regression("25992") @pytest.mark.regression("25992")
@pytest.mark.requires_executables("gcc") @pytest.mark.requires_executables("gcc")
def test_bootstrap_search_for_compilers_with_no_environment(no_compilers_yaml): def test_bootstrap_search_for_compilers_with_no_environment(no_packages_yaml):
assert not spack.compilers.all_compiler_specs(init_config=False) assert not spack.compilers.all_compilers(init_config=False)
with spack.bootstrap.ensure_bootstrap_configuration(): with spack.bootstrap.ensure_bootstrap_configuration():
assert spack.compilers.all_compiler_specs(init_config=False) assert spack.compilers.all_compilers(init_config=False)
assert not spack.compilers.all_compiler_specs(init_config=False) assert not spack.compilers.all_compilers(init_config=False)
@pytest.mark.regression("25992") @pytest.mark.regression("25992")
@pytest.mark.requires_executables("gcc") @pytest.mark.requires_executables("gcc")
def test_bootstrap_search_for_compilers_with_environment_active( def test_bootstrap_search_for_compilers_with_environment_active(
no_compilers_yaml, active_mock_environment no_packages_yaml, active_mock_environment
): ):
assert not spack.compilers.all_compiler_specs(init_config=False) assert not spack.compilers.all_compilers(init_config=False)
with spack.bootstrap.ensure_bootstrap_configuration(): with spack.bootstrap.ensure_bootstrap_configuration():
assert spack.compilers.all_compiler_specs(init_config=False) assert spack.compilers.all_compilers(init_config=False)
assert not spack.compilers.all_compiler_specs(init_config=False) assert not spack.compilers.all_compilers(init_config=False)
@pytest.mark.regression("26189") @pytest.mark.regression("26189")

View File

@ -4,7 +4,6 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os import os
import shutil import shutil
import sys
import pytest import pytest
@ -70,7 +69,7 @@ def compilers_dir(mock_executable):
@pytest.mark.not_on_windows("Cannot execute bash script on Windows") @pytest.mark.not_on_windows("Cannot execute bash script on Windows")
@pytest.mark.regression("11678,13138") @pytest.mark.regression("11678,13138")
def test_compiler_find_without_paths(no_compilers_yaml, working_env, mock_executable): def test_compiler_find_without_paths(no_packages_yaml, working_env, mock_executable):
"""Tests that 'spack compiler find' looks into PATH by default, if no specific path """Tests that 'spack compiler find' looks into PATH by default, if no specific path
is given. is given.
""" """
@ -85,22 +84,26 @@ def test_compiler_find_without_paths(no_compilers_yaml, working_env, mock_execut
@pytest.mark.regression("37996") @pytest.mark.regression("37996")
def test_compiler_remove(mutable_config, mock_packages): def test_compiler_remove(mutable_config, mock_packages):
"""Tests that we can remove a compiler from configuration.""" """Tests that we can remove a compiler from configuration."""
assert spack.spec.CompilerSpec("gcc@=9.4.0") in spack.compilers.all_compiler_specs() assert any(compiler.satisfies("gcc@=9.4.0") for compiler in spack.compilers.all_compilers())
args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@9.4.0", add_paths=[], scope=None) args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@9.4.0", add_paths=[], scope=None)
spack.cmd.compiler.compiler_remove(args) spack.cmd.compiler.compiler_remove(args)
assert spack.spec.CompilerSpec("gcc@=9.4.0") not in spack.compilers.all_compiler_specs() assert not any(
compiler.satisfies("gcc@=9.4.0") for compiler in spack.compilers.all_compilers()
)
@pytest.mark.regression("37996") @pytest.mark.regression("37996")
def test_removing_compilers_from_multiple_scopes(mutable_config, mock_packages): def test_removing_compilers_from_multiple_scopes(mutable_config, mock_packages):
# Duplicate "site" scope into "user" scope # Duplicate "site" scope into "user" scope
site_config = spack.config.get("compilers", scope="site") site_config = spack.config.get("packages", scope="site")
spack.config.set("compilers", site_config, scope="user") spack.config.set("packages", site_config, scope="user")
assert spack.spec.CompilerSpec("gcc@=9.4.0") in spack.compilers.all_compiler_specs() assert any(compiler.satisfies("gcc@=9.4.0") for compiler in spack.compilers.all_compilers())
args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@9.4.0", add_paths=[], scope=None) args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@9.4.0", add_paths=[], scope=None)
spack.cmd.compiler.compiler_remove(args) spack.cmd.compiler.compiler_remove(args)
assert spack.spec.CompilerSpec("gcc@=9.4.0") not in spack.compilers.all_compiler_specs() assert not any(
compiler.satisfies("gcc@=9.4.0") for compiler in spack.compilers.all_compilers()
)
@pytest.mark.not_on_windows("Cannot execute bash script on Windows") @pytest.mark.not_on_windows("Cannot execute bash script on Windows")
@ -120,7 +123,7 @@ def test_compiler_add(mutable_config, mock_executable):
bin_dir = gcc_path.parent bin_dir = gcc_path.parent
root_dir = bin_dir.parent root_dir = bin_dir.parent
compilers_before_find = set(spack.compilers.all_compiler_specs()) compilers_before_find = set(spack.compilers.all_compilers())
args = spack.util.pattern.Bunch( args = spack.util.pattern.Bunch(
all=None, all=None,
compiler_spec=None, compiler_spec=None,
@ -130,7 +133,7 @@ def test_compiler_add(mutable_config, mock_executable):
jobs=1, jobs=1,
) )
spack.cmd.compiler.compiler_find(args) spack.cmd.compiler.compiler_find(args)
compilers_after_find = set(spack.compilers.all_compiler_specs()) compilers_after_find = set(spack.compilers.all_compilers())
compilers_added_by_find = compilers_after_find - compilers_before_find compilers_added_by_find = compilers_after_find - compilers_before_find
assert len(compilers_added_by_find) == 1 assert len(compilers_added_by_find) == 1
@ -140,45 +143,7 @@ def test_compiler_add(mutable_config, mock_executable):
@pytest.mark.not_on_windows("Cannot execute bash script on Windows") @pytest.mark.not_on_windows("Cannot execute bash script on Windows")
@pytest.mark.regression("17590") @pytest.mark.regression("17590")
@pytest.mark.parametrize("mixed_toolchain", [True, False]) def test_compiler_find_prefer_no_suffix(no_packages_yaml, working_env, compilers_dir):
def test_compiler_find_mixed_suffixes(
mixed_toolchain, no_compilers_yaml, working_env, compilers_dir
):
"""Ensure that we'll mix compilers with different suffixes when necessary."""
os.environ["PATH"] = str(compilers_dir)
output = compiler(
"find", "--scope=site", "--mixed-toolchain" if mixed_toolchain else "--no-mixed-toolchain"
)
assert "clang@11.0.0" in output
assert "gcc@8.4.0" in output
config = spack.compilers.get_compiler_config(
no_compilers_yaml, scope="site", init_config=False
)
clang = next(c["compiler"] for c in config if c["compiler"]["spec"] == "clang@=11.0.0")
gcc = next(c["compiler"] for c in config if c["compiler"]["spec"] == "gcc@=8.4.0")
gfortran_path = str(compilers_dir / "gfortran-8")
assert clang["paths"] == {
"cc": str(compilers_dir / "clang"),
"cxx": str(compilers_dir / "clang++"),
"f77": gfortran_path if mixed_toolchain else None,
"fc": gfortran_path if mixed_toolchain else None,
}
assert gcc["paths"] == {
"cc": str(compilers_dir / "gcc-8"),
"cxx": str(compilers_dir / "g++-8"),
"f77": gfortran_path,
"fc": gfortran_path,
}
@pytest.mark.not_on_windows("Cannot execute bash script on Windows")
@pytest.mark.regression("17590")
def test_compiler_find_prefer_no_suffix(no_compilers_yaml, working_env, compilers_dir):
"""Ensure that we'll pick 'clang' over 'clang-gpu' when there is a choice.""" """Ensure that we'll pick 'clang' over 'clang-gpu' when there is a choice."""
clang_path = compilers_dir / "clang" clang_path = compilers_dir / "clang"
shutil.copy(clang_path, clang_path.parent / "clang-gpu") shutil.copy(clang_path, clang_path.parent / "clang-gpu")
@ -187,20 +152,19 @@ def test_compiler_find_prefer_no_suffix(no_compilers_yaml, working_env, compiler
os.environ["PATH"] = str(compilers_dir) os.environ["PATH"] = str(compilers_dir)
output = compiler("find", "--scope=site") output = compiler("find", "--scope=site")
assert "clang@11.0.0" in output assert "llvm@11.0.0" in output
assert "gcc@8.4.0" in output assert "gcc@8.4.0" in output
config = spack.compilers.get_compiler_config( compilers = spack.compilers.all_compilers_from(no_packages_yaml, scope="site")
no_compilers_yaml, scope="site", init_config=False clang = [x for x in compilers if x.satisfies("llvm@11")]
)
clang = next(c["compiler"] for c in config if c["compiler"]["spec"] == "clang@=11.0.0")
assert clang["paths"]["cc"] == str(compilers_dir / "clang") assert len(clang) == 1
assert clang["paths"]["cxx"] == str(compilers_dir / "clang++") assert clang[0].extra_attributes["compilers"]["c"] == str(compilers_dir / "clang")
assert clang[0].extra_attributes["compilers"]["cxx"] == str(compilers_dir / "clang++")
@pytest.mark.not_on_windows("Cannot execute bash script on Windows") @pytest.mark.not_on_windows("Cannot execute bash script on Windows")
def test_compiler_find_path_order(no_compilers_yaml, working_env, compilers_dir): def test_compiler_find_path_order(no_packages_yaml, working_env, compilers_dir):
"""Ensure that we look for compilers in the same order as PATH, when there are duplicates""" """Ensure that we look for compilers in the same order as PATH, when there are duplicates"""
new_dir = compilers_dir / "first_in_path" new_dir = compilers_dir / "first_in_path"
new_dir.mkdir() new_dir.mkdir()
@ -211,19 +175,19 @@ def test_compiler_find_path_order(no_compilers_yaml, working_env, compilers_dir)
compiler("find", "--scope=site") compiler("find", "--scope=site")
config = spack.compilers.get_compiler_config( compilers = spack.compilers.all_compilers(scope="site")
no_compilers_yaml, scope="site", init_config=False gcc = [x for x in compilers if x.satisfies("gcc@8.4")]
)
gcc = next(c["compiler"] for c in config if c["compiler"]["spec"] == "gcc@=8.4.0") # Ensure we found both duplicates
assert gcc["paths"] == { assert len(gcc) == 2
"cc": str(new_dir / "gcc-8"), assert gcc[0].extra_attributes["compilers"] == {
"c": str(new_dir / "gcc-8"),
"cxx": str(new_dir / "g++-8"), "cxx": str(new_dir / "g++-8"),
"f77": str(new_dir / "gfortran-8"), "fortran": str(new_dir / "gfortran-8"),
"fc": str(new_dir / "gfortran-8"),
} }
def test_compiler_list_empty(no_compilers_yaml, working_env, compilers_dir): def test_compiler_list_empty(no_packages_yaml, working_env, compilers_dir):
"""Spack should not automatically search for compilers when listing them and none are """Spack should not automatically search for compilers when listing them and none are
available. And when stdout is not a tty like in tests, there should be no output and available. And when stdout is not a tty like in tests, there should be no output and
no error exit code. no error exit code.
@ -251,10 +215,10 @@ def test_compiler_list_empty(no_compilers_yaml, working_env, compilers_dir):
"flags": {"fflags": "-ffree-form"}, "flags": {"fflags": "-ffree-form"},
}, },
}, },
"""gcc@7.7.7: """gcc@7.7.7 languages=c,cxx,fortran os=foobar target=x86_64:
\tpaths: paths:
\t\tcc = /path/to/fake/gcc cc = /path/to/fake/gcc
\t\tcxx = /path/to/fake/g++ cxx = /path/to/fake/g++
\t\tf77 = /path/to/fake/gfortran \t\tf77 = /path/to/fake/gfortran
\t\tfc = /path/to/fake/gfortran \t\tfc = /path/to/fake/gfortran
\tflags: \tflags:
@ -266,7 +230,7 @@ def test_compiler_list_empty(no_compilers_yaml, working_env, compilers_dir):
], ],
) )
def test_compilers_shows_packages_yaml( def test_compilers_shows_packages_yaml(
external, expected, no_compilers_yaml, working_env, compilers_dir external, expected, no_packages_yaml, working_env, compilers_dir
): ):
"""Spack should see a single compiler defined from packages.yaml""" """Spack should see a single compiler defined from packages.yaml"""
external["prefix"] = external["prefix"].format(prefix=os.path.dirname(compilers_dir)) external["prefix"] = external["prefix"].format(prefix=os.path.dirname(compilers_dir))
@ -276,12 +240,5 @@ def test_compilers_shows_packages_yaml(
packages["gcc"] = gcc_entry packages["gcc"] = gcc_entry
spack.config.set("packages", packages) spack.config.set("packages", packages)
out = compiler("list") out = compiler("list", fail_on_error=True)
assert out.count("gcc@7.7.7") == 1 assert out.count("gcc@7.7.7") == 1
out = compiler("info", "gcc@7.7.7")
assert out == expected.format(
compilers_dir=str(compilers_dir),
sep=os.sep,
suffix=".bat" if sys.platform == "win32" else "",
)

View File

@ -18,31 +18,32 @@
import spack.spec import spack.spec
import spack.util.module_cmd import spack.util.module_cmd
from spack.compiler import Compiler from spack.compiler import Compiler
from spack.util.executable import Executable, ProcessError from spack.util.executable import Executable
from spack.util.file_cache import FileCache from spack.util.file_cache import FileCache
def test_multiple_conflicting_compiler_definitions(mutable_config): # FIXME (compiler as nodes): revisit this test
compiler_def = { # def test_multiple_conflicting_compiler_definitions(mutable_config):
"compiler": { # compiler_def = {
"flags": {}, # "compiler": {
"modules": [], # "flags": {},
"paths": {"cc": "cc", "cxx": "cxx", "f77": "null", "fc": "null"}, # "modules": [],
"extra_rpaths": [], # "paths": {"cc": "cc", "cxx": "cxx", "f77": "null", "fc": "null"},
"operating_system": "test", # "extra_rpaths": [],
"target": "test", # "operating_system": "test",
"environment": {}, # "target": "test",
"spec": "clang@0.0.0", # "environment": {},
} # "spec": "clang@0.0.0",
} # }
# }
compiler_config = [compiler_def, compiler_def] #
compiler_config[0]["compiler"]["paths"]["f77"] = "f77" # compiler_config = [compiler_def, compiler_def]
mutable_config.update_config("compilers", compiler_config) # compiler_config[0]["compiler"]["paths"]["f77"] = "f77"
# mutable_config.update_config("compilers", compiler_config)
arch_spec = spack.spec.ArchSpec(("test", "test", "test")) #
cmp = spack.compilers.compiler_for_spec("clang@=0.0.0", arch_spec) # arch_spec = spack.spec.ArchSpec(("test", "test", "test"))
assert cmp.f77 == "f77" # cmp = spack.compilers.compiler_for_spec("clang@=0.0.0", arch_spec)
# assert cmp.f77 == "f77"
def test_compiler_flags_from_config_are_grouped(): def test_compiler_flags_from_config_are_grouped():
@ -579,240 +580,77 @@ def test_xl_r_flags():
) )
@pytest.mark.parametrize( # FIXME (compiler as nodes): revisit this test
"compiler_spec,expected_result", # @pytest.mark.regression("14798,13733")
[("gcc@4.7.2", False), ("clang@3.3", False), ("clang@8.0.0", True)], # def test_raising_if_compiler_target_is_over_specific(config):
) # # Compiler entry with an overly specific target
@pytest.mark.not_on_windows("GCC and LLVM currently not supported on the platform") # compilers = [
def test_detecting_mixed_toolchains( # {
compiler_spec, expected_result, mutable_config, compiler_factory # "compiler": {
): # "spec": "gcc@9.0.1",
mixed_c = compiler_factory(spec="clang@8.0.0", operating_system="debian6") # "paths": {
mixed_c["compiler"]["paths"] = { # "cc": "/usr/bin/gcc-9",
"cc": "/path/to/clang-8", # "cxx": "/usr/bin/g++-9",
"cxx": "/path/to/clang++-8", # "f77": "/usr/bin/gfortran-9",
"f77": "/path/to/gfortran-9", # "fc": "/usr/bin/gfortran-9",
"fc": "/path/to/gfortran-9", # },
} # "flags": {},
mutable_config.set( # "operating_system": "ubuntu18.04",
"compilers", # "target": "haswell",
[ # "modules": [],
compiler_factory(spec="gcc@4.7.2", operating_system="debian6"), # "environment": {},
compiler_factory(spec="clang@3.3", operating_system="debian6"), # "extra_rpaths": [],
mixed_c, # }
], # }
) # ]
# arch_spec = spack.spec.ArchSpec(("linux", "ubuntu18.04", "haswell"))
# with spack.config.override("compilers", compilers):
# cfg = spack.compilers.get_compiler_config(config)
# with pytest.raises(ValueError):
# spack.compilers.get_compilers(cfg, spack.spec.CompilerSpec("gcc@9.0.1"), arch_spec)
compiler = spack.compilers.compilers_for_spec(compiler_spec).pop() # FIXME (compiler as nodes): revisit this test
assert spack.compilers.is_mixed_toolchain(compiler) is expected_result # @pytest.mark.regression("42679")
# def test_get_compilers(config):
# """Tests that we can select compilers whose versions differ only for a suffix."""
@pytest.mark.regression("14798,13733") # common = {
def test_raising_if_compiler_target_is_over_specific(config): # "flags": {},
# Compiler entry with an overly specific target # "operating_system": "ubuntu23.10",
compilers = [ # "target": "x86_64",
{ # "modules": [],
"compiler": { # "environment": {},
"spec": "gcc@9.0.1", # "extra_rpaths": [],
"paths": { # }
"cc": "/usr/bin/gcc-9", # with_suffix = {
"cxx": "/usr/bin/g++-9", # "spec": "gcc@13.2.0-suffix",
"f77": "/usr/bin/gfortran-9", # "paths": {
"fc": "/usr/bin/gfortran-9", # "cc": "/usr/bin/gcc-13.2.0-suffix",
}, # "cxx": "/usr/bin/g++-13.2.0-suffix",
"flags": {}, # "f77": "/usr/bin/gfortran-13.2.0-suffix",
"operating_system": "ubuntu18.04", # "fc": "/usr/bin/gfortran-13.2.0-suffix",
"target": "haswell", # },
"modules": [], # **common,
"environment": {}, # }
"extra_rpaths": [], # without_suffix = {
} # "spec": "gcc@13.2.0",
} # "paths": {
] # "cc": "/usr/bin/gcc-13.2.0",
arch_spec = spack.spec.ArchSpec(("linux", "ubuntu18.04", "haswell")) # "cxx": "/usr/bin/g++-13.2.0",
with spack.config.override("compilers", compilers): # "f77": "/usr/bin/gfortran-13.2.0",
cfg = spack.compilers.get_compiler_config(config) # "fc": "/usr/bin/gfortran-13.2.0",
with pytest.raises(ValueError): # },
spack.compilers.get_compilers(cfg, spack.spec.CompilerSpec("gcc@9.0.1"), arch_spec) # **common,
# }
#
@pytest.mark.not_on_windows("Not supported on Windows (yet)") # compilers = [{"compiler": without_suffix}, {"compiler": with_suffix}]
@pytest.mark.enable_compiler_execution #
def test_compiler_get_real_version(working_env, monkeypatch, tmpdir): # assert spack.compilers.get_compilers(
# Test variables # compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0-suffix")
test_version = "2.2.2" # ) == [spack.compilers._compiler_from_config_entry(with_suffix)]
#
# Create compiler # assert spack.compilers.get_compilers(
gcc = str(tmpdir.join("gcc")) # compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0")
with open(gcc, "w") as f: # ) == [spack.compilers._compiler_from_config_entry(without_suffix)]
f.write(
"""#!/bin/sh
if [ "$CMP_ON" = "1" ]; then
echo "$CMP_VER"
fi
"""
)
fs.set_executable(gcc)
# Add compiler to config
compiler_info = {
"spec": "gcc@foo",
"paths": {"cc": gcc, "cxx": None, "f77": None, "fc": None},
"flags": {},
"operating_system": "fake",
"target": "fake",
"modules": ["turn_on"],
"environment": {"set": {"CMP_VER": test_version}},
"extra_rpaths": [],
}
compiler_dict = {"compiler": compiler_info}
# Set module load to turn compiler on
def module(*args):
if args[0] == "show":
return ""
elif args[0] == "load":
os.environ["CMP_ON"] = "1"
monkeypatch.setattr(spack.util.module_cmd, "module", module)
# Run and confirm output
compilers = spack.compilers.get_compilers([compiler_dict])
assert len(compilers) == 1
compiler = compilers[0]
version = compiler.get_real_version()
assert version == test_version
@pytest.mark.regression("42679")
def test_get_compilers(config):
"""Tests that we can select compilers whose versions differ only for a suffix."""
common = {
"flags": {},
"operating_system": "ubuntu23.10",
"target": "x86_64",
"modules": [],
"environment": {},
"extra_rpaths": [],
}
with_suffix = {
"spec": "gcc@13.2.0-suffix",
"paths": {
"cc": "/usr/bin/gcc-13.2.0-suffix",
"cxx": "/usr/bin/g++-13.2.0-suffix",
"f77": "/usr/bin/gfortran-13.2.0-suffix",
"fc": "/usr/bin/gfortran-13.2.0-suffix",
},
**common,
}
without_suffix = {
"spec": "gcc@13.2.0",
"paths": {
"cc": "/usr/bin/gcc-13.2.0",
"cxx": "/usr/bin/g++-13.2.0",
"f77": "/usr/bin/gfortran-13.2.0",
"fc": "/usr/bin/gfortran-13.2.0",
},
**common,
}
compilers = [{"compiler": without_suffix}, {"compiler": with_suffix}]
assert spack.compilers.get_compilers(
compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0-suffix")
) == [spack.compilers._compiler_from_config_entry(with_suffix)]
assert spack.compilers.get_compilers(
compilers, cspec=spack.spec.CompilerSpec("gcc@=13.2.0")
) == [spack.compilers._compiler_from_config_entry(without_suffix)]
@pytest.mark.enable_compiler_execution
def test_compiler_get_real_version_fails(working_env, monkeypatch, tmpdir):
# Test variables
test_version = "2.2.2"
# Create compiler
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f:
f.write(
"""#!/bin/sh
if [ "$CMP_ON" = "1" ]; then
echo "$CMP_VER"
fi
"""
)
fs.set_executable(gcc)
# Add compiler to config
compiler_info = {
"spec": "gcc@foo",
"paths": {"cc": gcc, "cxx": None, "f77": None, "fc": None},
"flags": {},
"operating_system": "fake",
"target": "fake",
"modules": ["turn_on"],
"environment": {"set": {"CMP_VER": test_version}},
"extra_rpaths": [],
}
compiler_dict = {"compiler": compiler_info}
# Set module load to turn compiler on
def module(*args):
if args[0] == "show":
return ""
elif args[0] == "load":
os.environ["SPACK_TEST_CMP_ON"] = "1"
monkeypatch.setattr(spack.util.module_cmd, "module", module)
# Make compiler fail when getting implicit rpaths
def _call(*args, **kwargs):
raise ProcessError("Failed intentionally")
monkeypatch.setattr(Executable, "__call__", _call)
# Run and no change to environment
compilers = spack.compilers.get_compilers([compiler_dict])
assert len(compilers) == 1
compiler = compilers[0]
assert compiler.get_real_version() == "unknown"
# Confirm environment does not change after failed call
assert "SPACK_TEST_CMP_ON" not in os.environ
@pytest.mark.not_on_windows("Bash scripting unsupported on Windows (for now)")
@pytest.mark.enable_compiler_execution
def test_compiler_flags_use_real_version(working_env, monkeypatch, tmpdir):
# Create compiler
gcc = str(tmpdir.join("gcc"))
with open(gcc, "w") as f:
f.write(
"""#!/bin/sh
echo "4.4.4"
"""
) # Version for which c++11 flag is -std=c++0x
fs.set_executable(gcc)
# Add compiler to config
compiler_info = {
"spec": "gcc@foo",
"paths": {"cc": gcc, "cxx": None, "f77": None, "fc": None},
"flags": {},
"operating_system": "fake",
"target": "fake",
"modules": ["turn_on"],
"environment": {},
"extra_rpaths": [],
}
compiler_dict = {"compiler": compiler_info}
# Run and confirm output
compilers = spack.compilers.get_compilers([compiler_dict])
assert len(compilers) == 1
compiler = compilers[0]
flag = compiler.cxx11_flag
assert flag == "-std=c++0x"
@pytest.mark.enable_compiler_verification @pytest.mark.enable_compiler_verification
@ -869,7 +707,7 @@ def test_detection_requires_c_compiler(compilers_extra_attributes, expected_leng
] ]
} }
} }
result = spack.compilers.CompilerConfigFactory.from_packages_yaml(packages_yaml) result = spack.compilers.CompilerFactory.from_packages_yaml(packages_yaml)
assert len(result) == expected_length assert len(result) == expected_length

View File

@ -103,7 +103,7 @@ def test_external_nodes_do_not_have_runtimes(runtime_repo, mutable_config, tmp_p
"pkg-a%gcc@10.2.1", "pkg-a%gcc@10.2.1",
"pkg-b%gcc@9.4.0 target=x86_64", "pkg-b%gcc@9.4.0 target=x86_64",
{ {
"pkg-a": "gcc-runtime@10.2.1 target=x86_64", "pkg-a": "gcc-runtime@10.2.1 target=core2",
"pkg-b": "gcc-runtime@9.4.0 target=x86_64", "pkg-b": "gcc-runtime@9.4.0 target=x86_64",
}, },
2, 2,
@ -123,7 +123,7 @@ def test_reusing_specs_with_gcc_runtime(root_str, reused_str, expected, nruntime
root, reused_spec = _concretize_with_reuse(root_str=root_str, reused_str=reused_str) root, reused_spec = _concretize_with_reuse(root_str=root_str, reused_str=reused_str)
runtime_a = root.dependencies("gcc-runtime")[0] runtime_a = root.dependencies("gcc-runtime")[0]
assert runtime_a.satisfies(expected["pkg-a"]) assert runtime_a.satisfies(expected["pkg-a"]), runtime_a.tree()
runtime_b = root["pkg-b"].dependencies("gcc-runtime")[0] runtime_b = root["pkg-b"].dependencies("gcc-runtime")[0]
assert runtime_b.satisfies(expected["pkg-b"]) assert runtime_b.satisfies(expected["pkg-b"])

View File

@ -2,7 +2,6 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import copy
import os import os
import sys import sys
@ -32,9 +31,10 @@
import spack.spec import spack.spec
import spack.store import spack.store
import spack.util.file_cache import spack.util.file_cache
import spack.util.spack_yaml as syaml
import spack.variant as vt import spack.variant as vt
from spack.installer import PackageInstaller from spack.installer import PackageInstaller
from spack.spec import CompilerSpec, Spec from spack.spec import Spec
from spack.version import Version, VersionList, ver from spack.version import Version, VersionList, ver
@ -60,9 +60,6 @@ def check_spec(abstract, concrete):
for flag in concrete.compiler_flags.valid_compiler_flags(): for flag in concrete.compiler_flags.valid_compiler_flags():
assert flag in concrete.compiler_flags assert flag in concrete.compiler_flags
if abstract.compiler and abstract.compiler.concrete:
assert abstract.compiler == concrete.compiler
if abstract.architecture and abstract.architecture.concrete: if abstract.architecture and abstract.architecture.concrete:
assert abstract.architecture == concrete.architecture assert abstract.architecture == concrete.architecture
@ -91,7 +88,6 @@ def binary_compatibility(monkeypatch, request):
return return
monkeypatch.setattr(spack.solver.asp, "using_libc_compatibility", lambda: True) monkeypatch.setattr(spack.solver.asp, "using_libc_compatibility", lambda: True)
monkeypatch.setattr(spack.compiler.Compiler, "default_libc", Spec("glibc@=2.28"))
@pytest.fixture( @pytest.fixture(
@ -277,15 +273,15 @@ def change(self, changes=None):
@pytest.fixture() @pytest.fixture()
def clang12_with_flags(compiler_factory): def clang12_with_flags(compiler_factory):
c = compiler_factory(spec="clang@12.2.0", operating_system="redhat6") c = compiler_factory(spec="llvm@12.2.0 os=redhat6")
c["compiler"]["flags"] = {"cflags": "-O3", "cxxflags": "-O3"} c["extra_attributes"]["flags"] = {"cflags": "-O3", "cxxflags": "-O3"}
return c return c
@pytest.fixture() @pytest.fixture()
def gcc11_with_flags(compiler_factory): def gcc11_with_flags(compiler_factory):
c = compiler_factory(spec="gcc@11.1.0", operating_system="redhat6") c = compiler_factory(spec="gcc@11.1.0 os=redhat6")
c["compiler"]["flags"] = {"cflags": "-O0 -g", "cxxflags": "-O0 -g", "fflags": "-O0 -g"} c["extra_attributes"]["flags"] = {"cflags": "-O0 -g", "cxxflags": "-O0 -g", "fflags": "-O0 -g"}
return c return c
@ -347,16 +343,6 @@ def test_concretize_with_restricted_virtual(self):
concrete = check_concretize("mpileaks ^mpich2@1.3.1:1.4") concrete = check_concretize("mpileaks ^mpich2@1.3.1:1.4")
assert concrete["mpich2"].satisfies("mpich2@1.3.1:1.4") assert concrete["mpich2"].satisfies("mpich2@1.3.1:1.4")
def test_concretize_enable_disable_compiler_existence_check(self):
with spack.concretize.enable_compiler_existence_check():
with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
check_concretize("dttop %gcc@=100.100")
with spack.concretize.disable_compiler_existence_check():
spec = check_concretize("dttop %gcc@=100.100")
assert spec.satisfies("%gcc@100.100")
assert spec["dtlink3"].satisfies("%gcc@100.100")
def test_concretize_with_provides_when(self): def test_concretize_with_provides_when(self):
"""Make sure insufficient versions of MPI are not in providers list when """Make sure insufficient versions of MPI are not in providers list when
we ask for some advanced version. we ask for some advanced version.
@ -385,7 +371,13 @@ def test_different_compilers_get_different_flags(
self, mutable_config, clang12_with_flags, gcc11_with_flags self, mutable_config, clang12_with_flags, gcc11_with_flags
): ):
"""Tests that nodes get the flags of the associated compiler.""" """Tests that nodes get the flags of the associated compiler."""
mutable_config.set("compilers", [clang12_with_flags, gcc11_with_flags]) mutable_config.set(
"packages",
{
"llvm": {"externals": [clang12_with_flags]},
"gcc": {"externals": [gcc11_with_flags]},
},
)
client = Spec( client = Spec(
"cmake-client %gcc@11.1.0 platform=test os=fe target=fe" "cmake-client %gcc@11.1.0 platform=test os=fe target=fe"
" ^cmake %clang@12.2.0 platform=test os=fe target=fe" " ^cmake %clang@12.2.0 platform=test os=fe target=fe"
@ -401,7 +393,7 @@ def test_spec_flags_maintain_order(self, mutable_config, gcc11_with_flags):
"""Tests that Spack assembles flags in a consistent way (i.e. with the same ordering), """Tests that Spack assembles flags in a consistent way (i.e. with the same ordering),
for successive concretizations. for successive concretizations.
""" """
mutable_config.set("compilers", [gcc11_with_flags]) mutable_config.set("packages", {"gcc": {"externals": [gcc11_with_flags]}})
spec_str = "libelf %gcc@11.1.0 os=redhat6" spec_str = "libelf %gcc@11.1.0 os=redhat6"
for _ in range(3): for _ in range(3):
s = Spec(spec_str).concretized() s = Spec(spec_str).concretized()
@ -409,22 +401,23 @@ def test_spec_flags_maintain_order(self, mutable_config, gcc11_with_flags):
s.compiler_flags[x] == ["-O0", "-g"] for x in ("cflags", "cxxflags", "fflags") s.compiler_flags[x] == ["-O0", "-g"] for x in ("cflags", "cxxflags", "fflags")
) )
def test_compiler_flags_differ_identical_compilers(self, mutable_config, clang12_with_flags): # FIXME (compiler as nodes): revisit this test
mutable_config.set("compilers", [clang12_with_flags]) # def test_compiler_flags_differ_identical_compilers(self, mutable_config, clang12_with_flags):
# Correct arch to use test compiler that has flags # mutable_config.set("compilers", [clang12_with_flags])
spec = Spec("pkg-a %clang@12.2.0 platform=test os=fe target=fe") # # Correct arch to use test compiler that has flags
# spec = Spec("pkg-a %clang@12.2.0 platform=test os=fe target=fe")
# Get the compiler that matches the spec ( #
compiler = spack.compilers.compiler_for_spec("clang@=12.2.0", spec.architecture) # # Get the compiler that matches the spec (
# compiler = spack.compilers.compiler_for_spec("clang@=12.2.0", spec.architecture)
# Configure spack to have two identical compilers with different flags #
default_dict = spack.compilers._to_dict(compiler) # # Configure spack to have two identical compilers with different flags
different_dict = copy.deepcopy(default_dict) # default_dict = spack.compilers._to_dict(compiler)
different_dict["compiler"]["flags"] = {"cflags": "-O2"} # different_dict = copy.deepcopy(default_dict)
# different_dict["compiler"]["flags"] = {"cflags": "-O2"}
with spack.config.override("compilers", [different_dict]): #
spec.concretize() # with spack.config.override("compilers", [different_dict]):
assert spec.satisfies("cflags=-O2") # spec.concretize()
# assert spec.satisfies("cflags=-O2")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"spec_str,expected,not_expected", "spec_str,expected,not_expected",
@ -467,25 +460,34 @@ def test_compiler_flag_propagation(self, spec_str, expected, not_expected):
assert not root.satisfies(constraint) assert not root.satisfies(constraint)
def test_mixing_compilers_only_affects_subdag(self): def test_mixing_compilers_only_affects_subdag(self):
spack.config.set("packages:all:compiler", ["clang", "gcc"]) """Tests that, when we mix compilers, the one with lower penalty is used for nodes
spec = Spec("dt-diamond%gcc ^dt-diamond-bottom%clang").concretized() where the compiler is not forced.
for dep in spec.traverse(): """
assert ("%clang" in dep) == (dep.name == "dt-diamond-bottom") spec = Spec("dt-diamond%clang ^dt-diamond-bottom%gcc").concretized()
for x in spec.traverse(deptype=("link", "run")):
if "c" not in x or not x.name.startswith("dt-diamond"):
continue
expected_gcc = x.name != "dt-diamond"
assert bool(x.dependencies(name="llvm", deptype="build")) is not expected_gcc
assert bool(x.dependencies(name="gcc", deptype="build")) is expected_gcc
assert x.satisfies("%clang") is not expected_gcc
# FIXME (compiler as nodes): satisfies semantic should be only for direct build deps
# assert x.satisfies("%gcc") is expected_gcc
def test_compiler_inherited_upwards(self): def test_compiler_inherited_upwards(self):
spec = Spec("dt-diamond ^dt-diamond-bottom%clang").concretized() spec = Spec("dt-diamond ^dt-diamond-bottom%clang").concretized()
for dep in spec.traverse(): for x in spec.traverse(deptype=("link", "run")):
assert "%clang" in dep if "c" not in x:
continue
assert x.satisfies("%clang")
def test_architecture_deep_inheritance(self, mock_targets, compiler_factory): def test_architecture_deep_inheritance(self, mock_targets, compiler_factory):
"""Make sure that indirect dependencies receive architecture """Make sure that indirect dependencies receive architecture
information from the root even when partial architecture information information from the root even when partial architecture information
is provided by an intermediate dependency. is provided by an intermediate dependency.
""" """
cnl_compiler = compiler_factory(spec="gcc@4.5.0", operating_system="CNL") cnl_compiler = compiler_factory(spec="gcc@4.5.0 os=CNL target=nocona")
# CNL compiler has no target attribute, and this is essential to make detection pass with spack.config.override("packages", {"gcc": {"externals": [cnl_compiler]}}):
del cnl_compiler["compiler"]["target"]
with spack.config.override("compilers", [cnl_compiler]):
spec_str = "mpileaks %gcc@4.5.0 os=CNL target=nocona ^dyninst os=CNL ^callpath os=CNL" spec_str = "mpileaks %gcc@4.5.0 os=CNL target=nocona ^dyninst os=CNL ^callpath os=CNL"
spec = Spec(spec_str).concretized() spec = Spec(spec_str).concretized()
for s in spec.traverse(root=False): for s in spec.traverse(root=False):
@ -720,11 +722,9 @@ def test_concretize_propagate_variant_second_level_dep_not_in_source(self):
assert not spec.satisfies("parent-foo-bar +fee") assert not spec.satisfies("parent-foo-bar +fee")
def test_no_matching_compiler_specs(self, mock_low_high_config): def test_no_matching_compiler_specs(self, mock_low_high_config):
# only relevant when not building compilers as needed s = Spec("pkg-a %gcc@0.0.0")
with spack.concretize.enable_compiler_existence_check(): with pytest.raises(spack.solver.asp.UnsatisfiableSpecError):
s = Spec("pkg-a %gcc@=0.0.0") s.concretize()
with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
s.concretize()
def test_no_compilers_for_arch(self): def test_no_compilers_for_arch(self):
s = Spec("pkg-a arch=linux-rhel0-x86_64") s = Spec("pkg-a arch=linux-rhel0-x86_64")
@ -764,21 +764,20 @@ def test_virtual_is_fully_expanded_for_mpileaks(self):
assert all(not d.dependencies(name="mpi") for d in spec.traverse()) assert all(not d.dependencies(name="mpi") for d in spec.traverse())
assert all(x in spec for x in ("zmpi", "mpi")) assert all(x in spec for x in ("zmpi", "mpi"))
@pytest.mark.parametrize("compiler_str", ["clang", "gcc", "gcc@10.2.1", "clang@:15.0.0"]) @pytest.mark.parametrize("compiler_str", ["%clang", "%gcc", "%gcc@10.2.1", "%clang@:15.0.0"])
def test_compiler_inheritance(self, compiler_str): def test_compiler_inheritance(self, compiler_str):
spec_str = "mpileaks %{0}".format(compiler_str) spec_str = f"mpileaks {compiler_str}"
spec = Spec(spec_str).concretized() spec = Spec(spec_str).concretized()
assert spec["libdwarf"].compiler.satisfies(compiler_str) assert spec["libdwarf"].satisfies(compiler_str)
assert spec["libelf"].compiler.satisfies(compiler_str) assert spec["libelf"].satisfies(compiler_str)
def test_external_package(self): def test_external_package(self):
spec = Spec("externaltool%gcc") """Tests that an external is preferred, if present, and that it does not
spec.concretize() have dependencies.
assert spec["externaltool"].external_path == os.path.sep + os.path.join( """
"path", "to", "external_tool" spec = Spec("externaltool").concretized()
) assert spec.external_path == os.path.sep + os.path.join("path", "to", "external_tool")
assert "externalprereq" not in spec assert not spec.dependencies()
assert spec["externaltool"].compiler.satisfies("gcc")
def test_nobuild_package(self): def test_nobuild_package(self):
"""Test that a non-buildable package raise an error if no specs """Test that a non-buildable package raise an error if no specs
@ -790,16 +789,14 @@ def test_nobuild_package(self):
def test_external_and_virtual(self, mutable_config): def test_external_and_virtual(self, mutable_config):
mutable_config.set("packages:stuff", {"buildable": False}) mutable_config.set("packages:stuff", {"buildable": False})
spec = Spec("externaltest") spec = Spec("externaltest").concretized()
spec.concretize()
assert spec["externaltool"].external_path == os.path.sep + os.path.join( assert spec["externaltool"].external_path == os.path.sep + os.path.join(
"path", "to", "external_tool" "path", "to", "external_tool"
) )
# "stuff" is a virtual provided by externalvirtual
assert spec["stuff"].external_path == os.path.sep + os.path.join( assert spec["stuff"].external_path == os.path.sep + os.path.join(
"path", "to", "external_virtual_gcc" "path", "to", "external_virtual_clang"
) )
assert spec["externaltool"].compiler.satisfies("gcc")
assert spec["stuff"].compiler.satisfies("gcc")
def test_compiler_child(self): def test_compiler_child(self):
s = Spec("mpileaks%clang target=x86_64 ^dyninst%gcc") s = Spec("mpileaks%clang target=x86_64 ^dyninst%gcc")
@ -824,7 +821,7 @@ def test_conflict_in_all_directives_true(self):
with pytest.raises(spack.error.SpackError): with pytest.raises(spack.error.SpackError):
s.concretize() s.concretize()
@pytest.mark.parametrize("spec_str", ["conflict@10.0%clang+foo"]) @pytest.mark.parametrize("spec_str", ["unsat-provider@1.0+foo"])
def test_no_conflict_in_external_specs(self, spec_str): def test_no_conflict_in_external_specs(self, spec_str):
# Modify the configuration to have the spec with conflict # Modify the configuration to have the spec with conflict
# registered as an external # registered as an external
@ -933,35 +930,45 @@ def test_noversion_pkg(self, spec):
Spec(spec).concretized() Spec(spec).concretized()
@pytest.mark.not_on_windows("Not supported on Windows (yet)") @pytest.mark.not_on_windows("Not supported on Windows (yet)")
# Include targets to prevent regression on 20537
@pytest.mark.parametrize( @pytest.mark.parametrize(
"spec, best_achievable", "spec,compiler_spec,best_achievable",
[ [
("mpileaks%gcc@=4.4.7 ^dyninst@=10.2.1 target=x86_64:", "core2"), (
("mpileaks%gcc@=4.8 target=x86_64:", "haswell"), "mpileaks%gcc@=4.4.7 ^dyninst@=10.2.1 target=x86_64:",
("mpileaks%gcc@=5.3.0 target=x86_64:", "broadwell"), "gcc@4.4.7 languages=c,cxx,fortran",
("mpileaks%apple-clang@=5.1.0 target=x86_64:", "x86_64"), "core2",
),
("mpileaks%gcc@=4.8 target=x86_64:", "gcc@4.8 languages=c,cxx,fortran", "haswell"),
(
"mpileaks%gcc@=5.3.0 target=x86_64:",
"gcc@5.3.0 languages=c,cxx,fortran",
"broadwell",
),
], ],
) )
@pytest.mark.regression("13361", "20537") @pytest.mark.regression("13361", "20537")
@pytest.mark.usefixtures("mock_targets")
def test_adjusting_default_target_based_on_compiler( def test_adjusting_default_target_based_on_compiler(
self, spec, best_achievable, current_host, mock_targets self, spec, compiler_spec, best_achievable, current_host, compiler_factory, mutable_config
): ):
best_achievable = archspec.cpu.TARGETS[best_achievable] best_achievable = archspec.cpu.TARGETS[best_achievable]
expected = best_achievable if best_achievable < current_host else current_host expected = best_achievable if best_achievable < current_host else current_host
with spack.concretize.disable_compiler_existence_check(): mutable_config.set(
s = Spec(spec).concretized() "packages", {"gcc": {"externals": [compiler_factory(spec=f"{compiler_spec}")]}}
assert str(s.architecture.target) == str(expected) )
s = Spec(spec).concretized()
assert str(s.architecture.target) == str(expected)
def test_compiler_version_matches_any_entry_in_compilers_yaml(self): @pytest.mark.parametrize(
"constraint,expected", [("%gcc@10.2", "@=10.2.1"), ("%gcc@10.2:", "@=10.2.1")]
)
def test_compiler_version_matches_any_entry_in_packages_yaml(self, constraint, expected):
# The behavior here has changed since #8735 / #14730. Now %gcc@10.2 is an abstract # The behavior here has changed since #8735 / #14730. Now %gcc@10.2 is an abstract
# compiler spec, and it should first find a matching compiler gcc@=10.2.1 # compiler spec, and it should first find a matching compiler gcc@=10.2.1
assert Spec("mpileaks %gcc@10.2").concretized().compiler == CompilerSpec("gcc@=10.2.1") s = Spec(f"mpileaks {constraint}").concretized()
assert Spec("mpileaks %gcc@10.2:").concretized().compiler == CompilerSpec("gcc@=10.2.1") gcc_deps = s.dependencies(name="gcc", deptype="build")
assert len(gcc_deps) == 1
# This compiler does not exist assert gcc_deps[0].satisfies(expected)
with pytest.raises(spack.concretize.UnavailableCompilerVersionError):
Spec("mpileaks %gcc@=10.2").concretized()
def test_concretize_anonymous(self): def test_concretize_anonymous(self):
with pytest.raises(spack.error.SpackError): with pytest.raises(spack.error.SpackError):
@ -981,14 +988,13 @@ def test_concretize_anonymous_dep(self, spec_str):
("bowtie@1.4.0", "%gcc@10.2.1"), ("bowtie@1.4.0", "%gcc@10.2.1"),
# Version with conflicts and no valid gcc select another compiler # Version with conflicts and no valid gcc select another compiler
("bowtie@1.3.0", "%clang@15.0.0"), ("bowtie@1.3.0", "%clang@15.0.0"),
# If a higher gcc is available still prefer that # FIXME (compiler as nodes): does this make sense?
("bowtie@1.2.2 os=redhat6", "%gcc@11.1.0"), # If a higher gcc is available, with a worse os, still prefer that
("bowtie@1.2.2", "%gcc@11.1.0"),
], ],
) )
def test_compiler_conflicts_in_package_py( def test_compiler_conflicts_in_package_py(self, spec_str, expected_str, gcc11_with_flags):
self, spec_str, expected_str, clang12_with_flags, gcc11_with_flags with spack.config.override("packages", {"gcc": {"externals": [gcc11_with_flags]}}):
):
with spack.config.override("compilers", [clang12_with_flags, gcc11_with_flags]):
s = Spec(spec_str).concretized() s = Spec(spec_str).concretized()
assert s.satisfies(expected_str) assert s.satisfies(expected_str)
@ -1109,26 +1115,6 @@ def test_working_around_conflicting_defaults(self, spec_str, expected):
for constraint in expected: for constraint in expected:
assert s.satisfies(constraint) assert s.satisfies(constraint)
@pytest.mark.regression("4635")
@pytest.mark.parametrize(
"spec_str,expected",
[("cmake", ["%clang"]), ("cmake %gcc", ["%gcc"]), ("cmake %clang", ["%clang"])],
)
def test_external_package_and_compiler_preferences(self, spec_str, expected, mutable_config):
packages_yaml = {
"all": {"compiler": ["clang", "gcc"]},
"cmake": {
"externals": [{"spec": "cmake@3.4.3", "prefix": "/usr"}],
"buildable": False,
},
}
mutable_config.set("packages", packages_yaml)
s = Spec(spec_str).concretized()
assert s.external
for condition in expected:
assert s.satisfies(condition)
@pytest.mark.regression("5651") @pytest.mark.regression("5651")
def test_package_with_constraint_not_met_by_external(self): def test_package_with_constraint_not_met_by_external(self):
"""Check that if we have an external package A at version X.Y in """Check that if we have an external package A at version X.Y in
@ -1167,56 +1153,21 @@ def test_dependency_conditional_on_another_dependency_state(self):
assert s.concrete assert s.concrete
assert not s.satisfies("^variant-on-dependency-condition-b") assert not s.satisfies("^variant-on-dependency-condition-b")
@pytest.mark.regression("8082") # FIXME (compiler as nodes): revisit this test
@pytest.mark.parametrize( # @pytest.mark.regression("8082")
"spec_str,expected", [("cmake %gcc", "%gcc"), ("cmake %clang", "%clang")] # @pytest.mark.parametrize(
) # "spec_str,expected", [("cmake %gcc", "%gcc"), ("cmake %clang", "%clang")]
def test_compiler_constraint_with_external_package(self, spec_str, expected): # )
packages_yaml = { # def test_compiler_constraint_with_external_package(self, spec_str, expected):
"cmake": {"externals": [{"spec": "cmake@3.4.3", "prefix": "/usr"}], "buildable": False} # packages_yaml = {
} # "cmake": {"externals": [{"spec": "cmake@3.4.3", "prefix": "/usr"}],
spack.config.set("packages", packages_yaml) # "buildable": False}
# }
s = Spec(spec_str).concretized() # spack.config.set("packages", packages_yaml)
assert s.external #
assert s.satisfies(expected) # s = Spec(spec_str).concretized()
# assert s.external
@pytest.mark.regression("20976") # assert s.satisfies(expected)
@pytest.mark.parametrize(
"compiler,spec_str,expected,xfailold",
[
(
"gcc",
"external-common-python %clang",
"%clang ^external-common-openssl%gcc ^external-common-gdbm%clang",
False,
),
(
"clang",
"external-common-python",
"%clang ^external-common-openssl%clang ^external-common-gdbm%clang",
True,
),
],
)
def test_compiler_in_nonbuildable_external_package(
self, compiler, spec_str, expected, xfailold
):
"""Check that the compiler of a non-buildable external package does not
spread to other dependencies, unless no other commpiler is specified."""
packages_yaml = {
"external-common-openssl": {
"externals": [
{"spec": "external-common-openssl@1.1.1i%" + compiler, "prefix": "/usr"}
],
"buildable": False,
}
}
spack.config.set("packages", packages_yaml)
s = Spec(spec_str).concretized()
assert s.satisfies(expected)
assert "external-common-perl" not in [d.name for d in s.dependencies()]
def test_external_that_would_require_a_virtual_dependency(self): def test_external_that_would_require_a_virtual_dependency(self):
s = Spec("requires-virtual").concretized() s = Spec("requires-virtual").concretized()
@ -1278,7 +1229,7 @@ def test_compiler_match_is_preferred_to_newer_version(self, compiler_factory):
# that an old version of openblas is selected, rather than # that an old version of openblas is selected, rather than
# a different compiler for just that node. # a different compiler for just that node.
with spack.config.override( with spack.config.override(
"compilers", [compiler_factory(spec="gcc@10.1.0", operating_system="redhat6")] "packages", {"gcc": {"externals": [compiler_factory(spec="gcc@10.1.0 os=redhat6")]}}
): ):
spec_str = "simple-inheritance+openblas %gcc@10.1.0 os=redhat6" spec_str = "simple-inheritance+openblas %gcc@10.1.0 os=redhat6"
s = Spec(spec_str).concretized() s = Spec(spec_str).concretized()
@ -1305,15 +1256,6 @@ def test_variant_not_default(self):
d = s["dep-with-variants"] d = s["dep-with-variants"]
assert "+foo+bar+baz" in d assert "+foo+bar+baz" in d
@pytest.mark.regression("20055")
def test_custom_compiler_version(self, mutable_config, compiler_factory, monkeypatch):
mutable_config.set(
"compilers", [compiler_factory(spec="gcc@10foo", operating_system="redhat6")]
)
monkeypatch.setattr(spack.compiler.Compiler, "real_version", "10.2.1")
s = Spec("pkg-a %gcc@10foo os=redhat6").concretized()
assert "%gcc@10foo" in s
def test_all_patches_applied(self): def test_all_patches_applied(self):
uuidpatch = ( uuidpatch = (
"a60a42b73e03f207433c5579de207c6ed61d58e4d12dd3b5142eb525728d89ea" "a60a42b73e03f207433c5579de207c6ed61d58e4d12dd3b5142eb525728d89ea"
@ -1454,10 +1396,9 @@ def test_reuse_with_flags(self, mutable_database, mutable_config):
spack.config.set("concretizer:reuse", True) spack.config.set("concretizer:reuse", True)
spec = Spec("pkg-a cflags=-g cxxflags=-g").concretized() spec = Spec("pkg-a cflags=-g cxxflags=-g").concretized()
PackageInstaller([spec.package], fake=True, explicit=True).install() PackageInstaller([spec.package], fake=True, explicit=True).install()
testspec = Spec("pkg-a cflags=-g") testspec = Spec("pkg-a cflags=-g")
testspec.concretize() testspec.concretize()
assert testspec == spec assert testspec == spec, testspec.tree()
@pytest.mark.regression("20784") @pytest.mark.regression("20784")
def test_concretization_of_test_dependencies(self): def test_concretization_of_test_dependencies(self):
@ -1514,29 +1455,30 @@ def test_external_with_non_default_variant_as_dependency(self):
assert "~bar" in s["external-non-default-variant"] assert "~bar" in s["external-non-default-variant"]
assert s["external-non-default-variant"].external assert s["external-non-default-variant"].external
@pytest.mark.regression("22871") # FIXME (compiler as nodes): revisit this test
@pytest.mark.parametrize( # @pytest.mark.regression("22871")
"spec_str,expected_os", # @pytest.mark.parametrize(
[ # "spec_str,expected_os",
("mpileaks", "os=debian6"), # [
# To trigger the bug in 22871 we need to have the same compiler # ("mpileaks", "os=debian6"),
# spec available on both operating systems # # To trigger the bug in 22871 we need to have the same compiler
("mpileaks%gcc@10.2.1 platform=test os=debian6", "os=debian6"), # # spec available on both operating systems
("mpileaks%gcc@10.2.1 platform=test os=redhat6", "os=redhat6"), # ("mpileaks%gcc@10.2.1 platform=test os=debian6", "os=debian6"),
], # ("mpileaks%gcc@10.2.1 platform=test os=redhat6", "os=redhat6"),
) # ],
def test_os_selection_when_multiple_choices_are_possible( # )
self, spec_str, expected_os, compiler_factory # def test_os_selection_when_multiple_choices_are_possible(
): # self, spec_str, expected_os, compiler_factory
# GCC 10.2.1 is defined both for debian and for redhat # ):
with spack.config.override( # # GCC 10.2.1 is defined both for debian and for redhat
"compilers", [compiler_factory(spec="gcc@10.2.1", operating_system="redhat6")] # with spack.config.override(
): # "packages", {"gcc": {"externals": [compiler_factory(spec="gcc@10.2.1 os=redhat6")]}}
s = Spec(spec_str).concretized() # ):
for node in s.traverse(): # s = Spec(spec_str).concretized()
if node.name == "glibc": # for node in s.traverse():
continue # if node.name == "glibc":
assert node.satisfies(expected_os) # continue
# assert node.satisfies(expected_os)
@pytest.mark.regression("22718") @pytest.mark.regression("22718")
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1547,6 +1489,8 @@ def test_compiler_is_unique(self, spec_str, expected_compiler):
s = Spec(spec_str).concretized() s = Spec(spec_str).concretized()
for node in s.traverse(): for node in s.traverse():
if not node.satisfies("^ c"):
continue
assert node.satisfies(expected_compiler) assert node.satisfies(expected_compiler)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1827,20 +1771,21 @@ def test_reuse_with_unknown_package_dont_raise(self, tmpdir, temporary_store, mo
assert s.namespace == "builtin.mock" assert s.namespace == "builtin.mock"
@pytest.mark.parametrize( @pytest.mark.parametrize(
"specs,expected,libc_offset", "specs,checks",
[ [
(["libelf", "libelf@0.8.10"], 1, 1), (["libelf", "libelf@0.8.10"], {"libelf": 1}),
(["libdwarf%gcc", "libelf%clang"], 2, 1), (["libdwarf%gcc", "libelf%clang"], {"libdwarf": 1, "libelf": 1}),
(["libdwarf%gcc", "libdwarf%clang"], 3, 1), (["libdwarf%gcc", "libdwarf%clang"], {"libdwarf": 2, "libelf": 1}),
(["libdwarf^libelf@0.8.12", "libdwarf^libelf@0.8.13"], 4, 1), (["libdwarf^libelf@0.8.12", "libdwarf^libelf@0.8.13"], {"libdwarf": 2, "libelf": 2}),
(["hdf5", "zmpi"], 3, 1), # FIXME (compiler as nodes): fix these
(["hdf5", "mpich"], 2, 1), # (["hdf5", "zmpi"], 3, 1),
(["hdf5^zmpi", "mpich"], 4, 1), # (["hdf5", "mpich"], 2, 1),
(["mpi", "zmpi"], 2, 1), # (["hdf5^zmpi", "mpich"], 4, 1),
(["mpi", "mpich"], 1, 1), # (["mpi", "zmpi"], 2, 1),
# (["mpi", "mpich"], 1, 1),
], ],
) )
def test_best_effort_coconcretize(self, specs, expected, libc_offset): def test_best_effort_coconcretize(self, specs, checks):
specs = [Spec(s) for s in specs] specs = [Spec(s) for s in specs]
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
solver.reuse = False solver.reuse = False
@ -1849,10 +1794,9 @@ def test_best_effort_coconcretize(self, specs, expected, libc_offset):
for s in result.specs: for s in result.specs:
concrete_specs.update(s.traverse()) concrete_specs.update(s.traverse())
if not spack.solver.asp.using_libc_compatibility(): for matching_spec, expected_count in checks.items():
libc_offset = 0 matches = [x for x in concrete_specs if x.satisfies(matching_spec)]
assert len(matches) == expected_count, matching_spec
assert len(concrete_specs) == expected + libc_offset
@pytest.mark.parametrize( @pytest.mark.parametrize(
"specs,expected_spec,occurances", "specs,expected_spec,occurances",
@ -1965,17 +1909,9 @@ def test_version_weight_and_provenance(self):
# #
# Depending on the target, it may also use gnuconfig # Depending on the target, it may also use gnuconfig
result_spec = result.specs[0] result_spec = result.specs[0]
num_specs = len(list(result_spec.traverse())) assert (2, 0, "version badness (non roots)") in result.criteria
libc_offset = 1 if spack.solver.asp.using_libc_compatibility() else 0
criteria = [
(num_specs - 1 - libc_offset, None, "number of packages to build (vs. reuse)"),
(2, 0, "version badness (non roots)"),
]
for criterion in criteria:
assert criterion in result.criteria, criterion
assert result_spec.satisfies("^pkg-b@1.0") assert result_spec.satisfies("^pkg-b@1.0")
assert result_spec["pkg-b"].dag_hash() == reusable_specs[1].dag_hash()
def test_reuse_succeeds_with_config_compatible_os(self): def test_reuse_succeeds_with_config_compatible_os(self):
root_spec = Spec("pkg-b") root_spec = Spec("pkg-b")
@ -2276,95 +2212,25 @@ def test_unsolved_specs_raises_error(self, monkeypatch, mock_packages):
@pytest.mark.regression("43141") @pytest.mark.regression("43141")
def test_clear_error_when_unknown_compiler_requested(self, mock_packages): def test_clear_error_when_unknown_compiler_requested(self, mock_packages):
"""Tests that the solver can report a case where the compiler cannot be set""" """Tests that the solver can report a case where the compiler cannot be set"""
with pytest.raises( with pytest.raises(spack.error.UnsatisfiableSpecError, match="since 'foo' does not exist"):
spack.error.UnsatisfiableSpecError, match="Cannot set the required compiler: pkg-a%foo"
):
Spec("pkg-a %foo").concretized() Spec("pkg-a %foo").concretized()
@pytest.mark.regression("36339") @pytest.mark.regression("36339")
def test_compiler_match_constraints_when_selected(self): @pytest.mark.parametrize(
"compiler_str,expected",
[
("gcc@:9", "@=9.4.0"),
("gcc@:10", "@=10.2.1"),
("gcc@10", "@=10.2.1"),
("gcc@10:", "@=10.2.1"),
],
)
def test_compiler_match_constraints_when_selected(self, compiler_str, expected):
"""Test that, when multiple compilers with the same name are in the configuration """Test that, when multiple compilers with the same name are in the configuration
we ensure that the selected one matches all the required constraints. we ensure that the selected one matches all the required constraints.
""" """
compiler_configuration = [ s = Spec(f"pkg-a %{compiler_str}").concretized()
{ assert s["gcc"].satisfies(expected)
"compiler": {
"spec": "gcc@11.1.0",
"paths": {
"cc": "/usr/bin/gcc",
"cxx": "/usr/bin/g++",
"f77": "/usr/bin/gfortran",
"fc": "/usr/bin/gfortran",
},
"operating_system": "debian6",
"modules": [],
}
},
{
"compiler": {
"spec": "gcc@12.1.0",
"paths": {
"cc": "/usr/bin/gcc",
"cxx": "/usr/bin/g++",
"f77": "/usr/bin/gfortran",
"fc": "/usr/bin/gfortran",
},
"operating_system": "debian6",
"modules": [],
}
},
]
spack.config.set("compilers", compiler_configuration)
s = Spec("pkg-a %gcc@:11").concretized()
assert s.compiler.version == ver("=11.1.0"), s
@pytest.mark.regression("36339")
@pytest.mark.not_on_windows("Not supported on Windows")
@pytest.mark.enable_compiler_execution
def test_compiler_with_custom_non_numeric_version(self, mock_executable):
"""Test that, when a compiler has a completely made up version, we can use its
'real version' to detect targets and don't raise during concretization.
"""
gcc_path = mock_executable("gcc", output="echo 9")
compiler_configuration = [
{
"compiler": {
"spec": "gcc@foo",
"paths": {"cc": str(gcc_path), "cxx": str(gcc_path), "f77": None, "fc": None},
"operating_system": "debian6",
"modules": [],
}
}
]
spack.config.set("compilers", compiler_configuration)
s = Spec("pkg-a %gcc@foo").concretized()
assert s.compiler.version == ver("=foo")
@pytest.mark.regression("36628")
def test_concretization_with_compilers_supporting_target_any(self):
"""Tests that a compiler with 'target: any' can satisfy any target, and is a viable
candidate for concretization.
"""
compiler_configuration = [
{
"compiler": {
"spec": "gcc@12.1.0",
"paths": {
"cc": "/some/path/gcc",
"cxx": "/some/path/g++",
"f77": None,
"fc": None,
},
"operating_system": "debian6",
"target": "any",
"modules": [],
}
}
]
with spack.config.override("compilers", compiler_configuration):
s = Spec("pkg-a").concretized()
assert s.satisfies("%gcc@12.1.0")
@pytest.mark.parametrize("spec_str", ["mpileaks", "mpileaks ^mpich"]) @pytest.mark.parametrize("spec_str", ["mpileaks", "mpileaks ^mpich"])
def test_virtuals_are_annotated_on_edges(self, spec_str): def test_virtuals_are_annotated_on_edges(self, spec_str):
@ -2493,25 +2359,23 @@ def test_select_lower_priority_package_from_repository_stack(
def test_reuse_specs_from_non_available_compilers(self, mutable_config, mutable_database): def test_reuse_specs_from_non_available_compilers(self, mutable_config, mutable_database):
"""Tests that we can reuse specs with compilers that are not configured locally.""" """Tests that we can reuse specs with compilers that are not configured locally."""
# All the specs in the mutable DB have been compiled with %gcc@=10.2.1 # All the specs in the mutable DB have been compiled with %gcc@10.2.1
specs = mutable_database.query_local() mpileaks = [s for s in mutable_database.query_local() if s.name == "mpileaks"]
assert all(s.satisfies("%gcc@=10.2.1") for s in specs)
spack.compilers.remove_compiler_from_config("gcc@=10.2.1") # Remove gcc@10.2.1
assert not spack.compilers.compilers_for_spec("gcc@=10.2.1") remover = spack.compilers.CompilerRemover(mutable_config)
remover.mark_compilers(match="gcc@=10.2.1")
remover.flush()
mutable_config.set("concretizer:reuse", True) mutable_config.set("concretizer:reuse", True)
# mpileaks is in the database, it will be reused with gcc@=10.2.1 # mpileaks is in the database, it will be reused with gcc@=10.2.1
root = Spec("mpileaks").concretized() root = Spec("mpileaks").concretized()
for s in root.traverse(): assert root.satisfies("%gcc@10.2.1")
assert s.satisfies("%gcc@10.2.1") assert any(root.dag_hash() == x.dag_hash() for x in mpileaks)
# fftw is not in the database, therefore the root will be compiled with gcc@=9.4.0, # fftw is not in the database, therefore it will be compiled with gcc@=9.4.0
# while the mpi is reused from the database and is compiled with gcc@=10.2.1 root = Spec("fftw~mpi").concretized()
root = Spec("fftw").concretized() assert root.satisfies("%gcc@9.4.0")
assert root.satisfies("%gcc@=9.4.0")
for s in root.traverse(root=False):
assert s.satisfies("%gcc@10.2.1")
@pytest.mark.regression("43406") @pytest.mark.regression("43406")
def test_externals_with_platform_explicitly_set(self, tmp_path): def test_externals_with_platform_explicitly_set(self, tmp_path):
@ -2542,25 +2406,36 @@ def test_spec_with_build_dep_from_json(self, tmp_path):
@pytest.mark.regression("44040") @pytest.mark.regression("44040")
def test_exclude_specs_from_reuse(self, monkeypatch): def test_exclude_specs_from_reuse(self, monkeypatch):
"""Tests that we can exclude a spec from reuse when concretizing, and that the spec r"""Tests that we can exclude a spec from reuse when concretizing, and that the spec
is not added back to the solve as a dependency of another reusable spec. is not added back to the solve as a dependency of another reusable spec.
The expected spec is: The expected spec is:
o callpath@1.0 o callpath@1.0
|\ |\
| |\ o | mpich@3.0.4
o | | mpich@3.0.4 |\ \
|/ / | |\ \
| o dyninst@8.2 | | | o dyninst@8.2
|/| | |_|/|
| |\ |/| |/|
| | o libdwarf@20130729 | |/|/|
| |/| | | | |\
|/|/ | | | | o libdwarf@20130729
| o libelf@0.8.13 | |_|_|/|
|/ |/| |_|/|
o glibc@2.31 | |/| |/|
| | |/|/
| | | o libelf@0.8.13
| |_|/|
|/| |/|
| |/|/
| o | gcc-runtime@10.5.0
|/| |
| |/
o | glibc@2.31
/
o gcc@10.5.0
""" """
# Prepare a mock mirror that returns an old version of dyninst # Prepare a mock mirror that returns an old version of dyninst
request_str = "callpath ^mpich" request_str = "callpath ^mpich"
@ -2627,11 +2502,11 @@ def test_can_reuse_concrete_externals_for_dependents(self, mutable_config, tmp_p
preferred to concretizing another external from packages.yaml preferred to concretizing another external from packages.yaml
""" """
packages_yaml = { packages_yaml = {
"externaltool": {"externals": [{"spec": "externaltool@2.0", "prefix": "/fake/path"}]} "externaltool": {"externals": [{"spec": "externaltool@0.9", "prefix": "/fake/path"}]}
} }
mutable_config.set("packages", packages_yaml) mutable_config.set("packages", packages_yaml)
# Concretize with gcc@9 to get a suboptimal spec, since we have gcc@10 available # Concretize with v0.9 to get a suboptimal spec, since we have gcc@10 available
external_spec = Spec("externaltool@2 %gcc@9").concretized() external_spec = Spec("externaltool@0.9").concretized()
assert external_spec.external assert external_spec.external
root_specs = [Spec("sombrero")] root_specs = [Spec("sombrero")]
@ -2664,7 +2539,7 @@ def test_cannot_reuse_host_incompatible_libc(self):
setup = spack.solver.asp.SpackSolverSetup() setup = spack.solver.asp.SpackSolverSetup()
result, _, _ = solver.driver.solve(setup, [Spec("pkg-b")], reuse=[fst, snd]) result, _, _ = solver.driver.solve(setup, [Spec("pkg-b")], reuse=[fst, snd])
assert len(result.specs) == 1 assert len(result.specs) == 1
assert result.specs[0] == snd assert result.specs[0] == snd, result.specs[0].tree()
@pytest.mark.regression("45321") @pytest.mark.regression("45321")
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -2695,24 +2570,24 @@ def test_correct_external_is_selected_from_packages_yaml(self, mutable_config):
reconstruct the prefix, and other external attributes. reconstruct the prefix, and other external attributes.
""" """
packages_yaml = { packages_yaml = {
"cmake": { "mpileaks": {
"externals": [ "externals": [
{"spec": "cmake@3.23.1 %gcc", "prefix": "/tmp/prefix1"}, {"spec": "mpileaks@2.3 +opt", "prefix": "/tmp/prefix1"},
{"spec": "cmake@3.23.1 %clang", "prefix": "/tmp/prefix2"}, {"spec": "mpileaks@2.3 ~opt", "prefix": "/tmp/prefix2"},
] ]
} }
} }
concretizer_yaml = { concretizer_yaml = {
"reuse": {"roots": True, "from": [{"type": "external", "exclude": ["%gcc"]}]} "reuse": {"roots": True, "from": [{"type": "external", "exclude": ["+opt"]}]}
} }
mutable_config.set("packages", packages_yaml) mutable_config.set("packages", packages_yaml)
mutable_config.set("concretizer", concretizer_yaml) mutable_config.set("concretizer", concretizer_yaml)
s = Spec("cmake").concretized() s = Spec("mpileaks").concretized()
# Check that we got the properties from the right external # Check that we got the properties from the right external
assert s.external assert s.external
assert s.satisfies("%clang") assert s.satisfies("~opt")
assert s.prefix == "/tmp/prefix2" assert s.prefix == "/tmp/prefix2"
@ -3135,7 +3010,7 @@ def test_filtering_reused_specs(
@pytest.mark.usefixtures("mutable_database", "mock_store") @pytest.mark.usefixtures("mutable_database", "mock_store")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"reuse_yaml,expected_length", "reuse_yaml,expected_length",
[({"from": [{"type": "local"}]}, 17), ({"from": [{"type": "buildcache"}]}, 0)], [({"from": [{"type": "local"}]}, 19), ({"from": [{"type": "buildcache"}]}, 0)],
) )
@pytest.mark.not_on_windows("Expected length is different on Windows") @pytest.mark.not_on_windows("Expected length is different on Windows")
def test_selecting_reused_sources(reuse_yaml, expected_length, mutable_config, monkeypatch): def test_selecting_reused_sources(reuse_yaml, expected_length, mutable_config, monkeypatch):
@ -3219,3 +3094,51 @@ def test_spec_unification(unify, mutable_config, mock_packages):
maybe_fails = pytest.raises if unify is True else llnl.util.lang.nullcontext maybe_fails = pytest.raises if unify is True else llnl.util.lang.nullcontext
with maybe_fails(spack.solver.asp.UnsatisfiableSpecError): with maybe_fails(spack.solver.asp.UnsatisfiableSpecError):
_ = spack.cmd.parse_specs([a_restricted, b], concretize=True) _ = spack.cmd.parse_specs([a_restricted, b], concretize=True)
@pytest.mark.regression("42679")
@pytest.mark.parametrize("compiler_str", ["gcc@=9.4.0", "gcc@=9.4.0-foo"])
def test_selecting_compiler_with_suffix(mutable_config, mock_packages, compiler_str):
"""Tests that we can select compilers whose versions differ only for a suffix."""
packages_yaml = syaml.load_config(
"""
packages:
gcc:
externals:
- spec: "gcc@9.4.0-foo languages='c,c++'"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/gcc
cxx: /path/bin/g++
"""
)
mutable_config.set("packages", packages_yaml["packages"])
s = Spec(f"libelf %{compiler_str}").concretized()
assert s["c"].satisfies(compiler_str)
def test_duplicate_compiler_in_externals(mutable_config, mock_packages):
"""Tests that having duplicate compilers in packages.yaml do not raise and error."""
packages_yaml = syaml.load_config(
"""
packages:
gcc:
externals:
- spec: "gcc@9.4.0 languages='c,c++'"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/gcc
cxx: /path/bin/g++
- spec: "gcc@9.4.0 languages='c,c++'"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/gcc
cxx: /path/bin/g++
"""
)
mutable_config.set("packages", packages_yaml["packages"])
s = Spec("libelf %gcc@9.4").concretized()
assert s["c"].satisfies("gcc@9.4.0")

View File

@ -74,24 +74,23 @@ def test_mix_spec_and_dependent(concretize_scope, test_repo):
def _compiler_cfg_one_entry_with_cflags(cflags): def _compiler_cfg_one_entry_with_cflags(cflags):
return f"""\ return f"""\
compilers:: packages:
- compiler: gcc:
spec: gcc@12.100.100 externals:
paths: - spec: gcc@12.100.100
cc: /usr/bin/fake-gcc prefix: /fake
cxx: /usr/bin/fake-g++ extra_attributes:
f77: null compilers:
fc: null c: /fake/bin/gcc
flags: cxx: /fake/bin/g++
cflags: {cflags} flags:
operating_system: debian6 cflags: {cflags}
modules: []
""" """
def test_mix_spec_and_compiler_cfg(concretize_scope, test_repo): def test_mix_spec_and_compiler_cfg(concretize_scope, test_repo):
conf_str = _compiler_cfg_one_entry_with_cflags("-Wall") conf_str = _compiler_cfg_one_entry_with_cflags("-Wall")
update_concretize_scope(conf_str, "compilers") update_concretize_scope(conf_str, "packages")
s1 = Spec('y %gcc@12.100.100 cflags="-O2"').concretized() s1 = Spec('y %gcc@12.100.100 cflags="-O2"').concretized()
assert s1.satisfies('cflags="-Wall -O2"') assert s1.satisfies('cflags="-Wall -O2"')
@ -122,17 +121,20 @@ def test_flag_order_and_grouping(
The ordering rules are explained in ``asp.SpecBuilder.reorder_flags``. The ordering rules are explained in ``asp.SpecBuilder.reorder_flags``.
""" """
conf_str = """
packages:
"""
if cmp_flags:
conf_str = _compiler_cfg_one_entry_with_cflags(cmp_flags)
if req_flags: if req_flags:
conf_str = f"""\ conf_str = f"""\
packages: {conf_str}
y: y:
require: cflags="{req_flags}" require: cflags="{req_flags}"
""" """
update_concretize_scope(conf_str, "packages")
if cmp_flags: update_concretize_scope(conf_str, "packages")
conf_str = _compiler_cfg_one_entry_with_cflags(cmp_flags)
update_concretize_scope(conf_str, "compilers")
compiler_spec = "" compiler_spec = ""
if cmp_flags: if cmp_flags:
@ -167,7 +169,7 @@ def test_two_dependents_flag_mixing(concretize_scope, test_repo):
def test_propagate_and_compiler_cfg(concretize_scope, test_repo): def test_propagate_and_compiler_cfg(concretize_scope, test_repo):
conf_str = _compiler_cfg_one_entry_with_cflags("-f2") conf_str = _compiler_cfg_one_entry_with_cflags("-f2")
update_concretize_scope(conf_str, "compilers") update_concretize_scope(conf_str, "packages")
root_spec = Spec("v %gcc@12.100.100 cflags=='-f1'").concretized() root_spec = Spec("v %gcc@12.100.100 cflags=='-f1'").concretized()
assert root_spec["y"].satisfies("cflags='-f1 -f2'") assert root_spec["y"].satisfies("cflags='-f1 -f2'")
@ -224,7 +226,7 @@ def test_dev_mix_flags(tmp_path, concretize_scope, mutable_mock_env_path, test_r
""" """
conf_str = _compiler_cfg_one_entry_with_cflags("-f1") conf_str = _compiler_cfg_one_entry_with_cflags("-f1")
update_concretize_scope(conf_str, "compilers") update_concretize_scope(conf_str, "packages")
manifest_file = tmp_path / ev.manifest_name manifest_file = tmp_path / ev.manifest_name
manifest_file.write_text(env_content) manifest_file.write_text(env_content)

View File

@ -15,7 +15,7 @@
import spack.util.module_cmd import spack.util.module_cmd
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
from spack.error import ConfigError from spack.error import ConfigError
from spack.spec import CompilerSpec, Spec from spack.spec import Spec
from spack.version import Version from spack.version import Version
@ -105,16 +105,6 @@ def test_preferred_variants_from_wildcard(self):
update_packages("multivalue-variant", "variants", "foo=bar") update_packages("multivalue-variant", "variants", "foo=bar")
assert_variant_values("multivalue-variant foo=*", foo=("bar",)) assert_variant_values("multivalue-variant foo=*", foo=("bar",))
@pytest.mark.parametrize(
"compiler_str,spec_str",
[("gcc@=9.4.0", "mpileaks"), ("clang@=15.0.0", "mpileaks"), ("gcc@=9.4.0", "openmpi")],
)
def test_preferred_compilers(self, compiler_str, spec_str):
"""Test preferred compilers are applied correctly"""
update_packages("all", "compiler", [compiler_str])
spec = spack.spec.Spec(spec_str).concretized()
assert spec.compiler == CompilerSpec(compiler_str)
def test_preferred_target(self, mutable_mock_repo): def test_preferred_target(self, mutable_mock_repo):
"""Test preferred targets are applied correctly""" """Test preferred targets are applied correctly"""
spec = concretize("mpich") spec = concretize("mpich")
@ -127,12 +117,12 @@ def test_preferred_target(self, mutable_mock_repo):
spec = concretize("mpileaks") spec = concretize("mpileaks")
assert str(spec["mpileaks"].target) == preferred assert str(spec["mpileaks"].target) == preferred
assert str(spec["mpich"].target) == preferred assert str(spec["mpi"].target) == preferred
update_packages("all", "target", [default]) update_packages("all", "target", [default])
spec = concretize("mpileaks") spec = concretize("mpileaks")
assert str(spec["mpileaks"].target) == default assert str(spec["mpileaks"].target) == default
assert str(spec["mpich"].target) == default assert str(spec["mpi"].target) == default
def test_preferred_versions(self): def test_preferred_versions(self):
"""Test preferred package versions are applied correctly""" """Test preferred package versions are applied correctly"""

View File

@ -492,8 +492,10 @@ def test_default_requirements_with_all(spec_str, requirement_str, concretize_sco
update_packages_config(conf_str) update_packages_config(conf_str)
spec = Spec(spec_str).concretized() spec = Spec(spec_str).concretized()
assert "c" in spec
for s in spec.traverse(): for s in spec.traverse():
assert s.satisfies(requirement_str) if "c" in s:
assert s.satisfies(requirement_str)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -520,8 +522,7 @@ def test_default_and_package_specific_requirements(
spec = Spec("x").concretized() spec = Spec("x").concretized()
assert spec.satisfies(specific_exp) assert spec.satisfies(specific_exp)
for s in spec.traverse(root=False): assert spec["y"].satisfies(generic_exp)
assert s.satisfies(generic_exp)
@pytest.mark.parametrize("mpi_requirement", ["mpich", "mpich2", "zmpi"]) @pytest.mark.parametrize("mpi_requirement", ["mpich", "mpich2", "zmpi"])
@ -761,33 +762,22 @@ def test_skip_requirement_when_default_requirement_condition_cannot_be_met(
assert "shared" not in s["callpath"].variants assert "shared" not in s["callpath"].variants
def test_requires_directive(concretize_scope, mock_packages): def test_requires_directive(mock_packages, config):
compilers_yaml = pathlib.Path(concretize_scope) / "compilers.yaml"
# NOTE: target is omitted here so that the test works on aarch64, as well.
compilers_yaml.write_text(
"""
compilers::
- compiler:
spec: gcc@12.0.0
paths:
cc: /usr/bin/clang-12
cxx: /usr/bin/clang++-12
f77: null
fc: null
operating_system: debian6
modules: []
"""
)
spack.config.CONFIG.clear_caches()
# This package requires either clang or gcc # This package requires either clang or gcc
s = Spec("requires_clang_or_gcc").concretized() s = Spec("requires_clang_or_gcc").concretized()
assert s.satisfies("%gcc@12.0.0") assert s.satisfies("%gcc")
s = Spec("requires_clang_or_gcc %gcc").concretized()
assert s.satisfies("%gcc")
s = Spec("requires_clang_or_gcc %clang").concretized()
assert s.satisfies("%llvm")
# This package can only be compiled with clang # This package can only be compiled with clang
s = Spec("requires_clang").concretized()
assert s.satisfies("%llvm")
s = Spec("requires_clang %clang").concretized()
assert s.satisfies("%llvm")
with pytest.raises(spack.error.SpackError, match="can only be compiled with Clang"): with pytest.raises(spack.error.SpackError, match="can only be compiled with Clang"):
Spec("requires_clang").concretized() Spec("requires_clang %gcc").concretized()
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -953,11 +943,10 @@ def test_requiring_package_on_multiple_virtuals(concretize_scope, mock_packages)
all: all:
prefer: prefer:
- "%clang" - "%clang"
compiler: [gcc]
""", """,
"multivalue-variant", "multivalue-variant",
["%clang"], ["llvm"],
["%gcc"], ["gcc"],
), ),
( (
""" """
@ -967,8 +956,8 @@ def test_requiring_package_on_multiple_virtuals(concretize_scope, mock_packages)
- "%clang" - "%clang"
""", """,
"multivalue-variant %gcc", "multivalue-variant %gcc",
["%gcc"], ["gcc"],
["%clang"], ["llvm"],
), ),
# Test parsing objects instead of strings # Test parsing objects instead of strings
( (
@ -977,26 +966,25 @@ def test_requiring_package_on_multiple_virtuals(concretize_scope, mock_packages)
all: all:
prefer: prefer:
- spec: "%clang" - spec: "%clang"
compiler: [gcc]
""", """,
"multivalue-variant", "multivalue-variant",
["%clang"], ["llvm"],
["%gcc"], ["gcc"],
), ),
], ],
) )
def test_strong_preferences_packages_yaml( def test_compiler_strong_preferences_packages_yaml(
packages_yaml, spec_str, expected, not_expected, concretize_scope, mock_packages packages_yaml, spec_str, expected, not_expected, concretize_scope, mock_packages
): ):
"""Tests that "preferred" specs are stronger than usual preferences, but can be overridden.""" """Tests that strong preferences are taken into account for compilers."""
update_packages_config(packages_yaml) update_packages_config(packages_yaml)
s = Spec(spec_str).concretized() s = Spec(spec_str).concretized()
for constraint in expected: for constraint in expected:
assert s.satisfies(constraint), constraint assert s.dependencies(deptype="build", name=constraint)
for constraint in not_expected: for constraint in not_expected:
assert not s.satisfies(constraint), constraint assert not s.dependencies(deptype="build", name=constraint)
@pytest.mark.parametrize( @pytest.mark.parametrize(

View File

@ -47,6 +47,7 @@
import spack.platforms import spack.platforms
import spack.repo import spack.repo
import spack.solver.asp import spack.solver.asp
import spack.solver.libc
import spack.spec import spack.spec
import spack.stage import spack.stage
import spack.store import spack.store
@ -360,18 +361,6 @@ def no_chdir():
assert os.getcwd() == original_wd assert os.getcwd() == original_wd
@pytest.fixture(scope="function", autouse=True)
def reset_compiler_cache():
"""Ensure that the compiler cache is not shared across Spack tests
This cache can cause later tests to fail if left in a state incompatible
with the new configuration. Since tests can make almost unlimited changes
to their setup, default to not use the compiler cache across tests."""
spack.compilers._compiler_cache = {}
yield
spack.compilers._compiler_cache = {}
def onerror(func, path, error_info): def onerror(func, path, error_info):
# Python on Windows is unable to remvove paths without # Python on Windows is unable to remvove paths without
# write (IWUSR) permissions (such as those generated by Git on Windows) # write (IWUSR) permissions (such as those generated by Git on Windows)
@ -715,8 +704,8 @@ def configuration_dir(tmpdir_factory, linux_os):
config.write(config_template.read_text().format(install_tree_root, locks)) config.write(config_template.read_text().format(install_tree_root, locks))
target = str(archspec.cpu.host().family) target = str(archspec.cpu.host().family)
compilers = tmpdir.join("site", "compilers.yaml") compilers = tmpdir.join("site", "packages.yaml")
compilers_template = test_config / "compilers.yaml" compilers_template = test_config / "packages.yaml"
compilers.write(compilers_template.read_text().format(linux_os=linux_os, target=target)) compilers.write(compilers_template.read_text().format(linux_os=linux_os, target=target))
modules = tmpdir.join("site", "modules.yaml") modules = tmpdir.join("site", "modules.yaml")
@ -810,12 +799,12 @@ def concretize_scope(mutable_config, tmpdir):
@pytest.fixture @pytest.fixture
def no_compilers_yaml(mutable_config): def no_packages_yaml(mutable_config):
"""Creates a temporary configuration without compilers.yaml""" """Creates a temporary configuration without compilers.yaml"""
for local_config in mutable_config.scopes.values(): for local_config in mutable_config.scopes.values():
if not isinstance(local_config, spack.config.DirectoryConfigScope): if not isinstance(local_config, spack.config.DirectoryConfigScope):
continue continue
compilers_yaml = local_config.get_section_filename("compilers") compilers_yaml = local_config.get_section_filename("packages")
if os.path.exists(compilers_yaml): if os.path.exists(compilers_yaml):
os.remove(compilers_yaml) os.remove(compilers_yaml)
return mutable_config return mutable_config
@ -2089,15 +2078,11 @@ def create_test_repo(tmpdir, pkg_name_content_tuples):
def compiler_factory(): def compiler_factory():
"""Factory for a compiler dict, taking a spec and an OS as arguments.""" """Factory for a compiler dict, taking a spec and an OS as arguments."""
def _factory(*, spec, operating_system): def _factory(*, spec):
return { return {
"compiler": { "spec": f"{spec}",
"spec": spec, "prefix": "/path",
"operating_system": operating_system, "extra_attributes": {"compilers": {"c": "/path/bin/cc", "cxx": "/path/bin/cxx"}},
"paths": {"cc": "/path/to/cc", "cxx": "/path/to/cxx", "f77": None, "fc": None},
"modules": [],
"target": str(archspec.cpu.host().family),
}
} }
return _factory return _factory
@ -2113,6 +2098,10 @@ def _true(x):
return True return True
def _libc_from_python(self):
return spack.spec.Spec("glibc@=2.28")
@pytest.fixture() @pytest.fixture()
def do_not_check_runtimes_on_reuse(monkeypatch): def do_not_check_runtimes_on_reuse(monkeypatch):
monkeypatch.setattr(spack.solver.asp, "_has_runtime_dependencies", _true) monkeypatch.setattr(spack.solver.asp, "_has_runtime_dependencies", _true)
@ -2122,8 +2111,11 @@ def do_not_check_runtimes_on_reuse(monkeypatch):
def _c_compiler_always_exists(): def _c_compiler_always_exists():
fn = spack.solver.asp.c_compiler_runs fn = spack.solver.asp.c_compiler_runs
spack.solver.asp.c_compiler_runs = _true spack.solver.asp.c_compiler_runs = _true
mthd = spack.solver.libc.CompilerPropertyDetector.default_libc
spack.solver.libc.CompilerPropertyDetector.default_libc = _libc_from_python
yield yield
spack.solver.asp.c_compiler_runs = fn spack.solver.asp.c_compiler_runs = fn
spack.solver.libc.CompilerPropertyDetector.default_libc = mthd
@pytest.fixture(scope="session") @pytest.fixture(scope="session")

View File

@ -367,20 +367,20 @@ def test_read_cray_manifest_add_compiler_failure(
"""Check that cray manifest can be read even if some compilers cannot """Check that cray manifest can be read even if some compilers cannot
be added. be added.
""" """
orig_add_compilers_to_config = spack.compilers.add_compilers_to_config orig_add_compiler_to_config = spack.compilers.add_compiler_to_config
class fail_for_clang: class fail_for_clang:
def __init__(self): def __init__(self):
self.called_with_clang = False self.called_with_clang = False
def __call__(self, compilers, **kwargs): def __call__(self, compiler, **kwargs):
if any(x.name == "clang" for x in compilers): if compiler.name == "clang":
self.called_with_clang = True self.called_with_clang = True
raise Exception() raise Exception()
return orig_add_compilers_to_config(compilers, **kwargs) return orig_add_compiler_to_config(compiler, **kwargs)
checker = fail_for_clang() checker = fail_for_clang()
monkeypatch.setattr(spack.compilers, "add_compilers_to_config", checker) monkeypatch.setattr(spack.compilers, "add_compiler_to_config", checker)
with tmpdir.as_cwd(): with tmpdir.as_cwd():
test_db_fname = "external-db.json" test_db_fname = "external-db.json"

View File

@ -1,41 +0,0 @@
compilers:
- compiler:
spec: gcc@=9.4.0
operating_system: {linux_os.name}{linux_os.version}
paths:
cc: /path/to/gcc
cxx: /path/to/g++
f77: None
fc: None
modules: []
target: {target}
- compiler:
spec: gcc@=9.4.0
operating_system: redhat6
paths:
cc: /path/to/gcc
cxx: /path/to/g++
f77: None
fc: None
modules: []
target: {target}
- compiler:
spec: clang@=15.0.0
operating_system: {linux_os.name}{linux_os.version}
paths:
cc: /path/to/clang
cxx: /path/to/clang++
f77: None
fc: None
modules: []
target: {target}
- compiler:
spec: gcc@=10.2.1
operating_system: {linux_os.name}{linux_os.version}
paths:
cc: /path/to/gcc
cxx: /path/to/g++
f77: None
fc: None
modules: []
target: {target}

View File

@ -1,30 +1,35 @@
packages: packages:
all: all:
compiler: [gcc, clang]
providers: providers:
mpi: [openmpi, mpich, zmpi] c: [gcc, llvm]
cxx: [gcc, llvm]
fortran: [gcc]
fortran-rt: [gcc-runtime]
libc: [glibc]
libgfortran: [gcc-runtime]
mpi: [mpich, zmpi]
lapack: [openblas-with-lapack] lapack: [openblas-with-lapack]
blas: [openblas] blas: [openblas]
externaltool: externaltool:
buildable: False buildable: False
externals: externals:
- spec: externaltool@1.0%gcc@10.2.1 - spec: externaltool@1.0
prefix: /path/to/external_tool prefix: /path/to/external_tool
- spec: externaltool@0.9%gcc@10.2.1 - spec: externaltool@0.9
prefix: /usr prefix: /usr
- spec: externaltool@0_8%gcc@10.2.1 - spec: externaltool@0_8
prefix: /usr prefix: /usr
externalvirtual: externalvirtual:
buildable: False buildable: False
externals: externals:
- spec: externalvirtual@2.0%clang@15.0.0 - spec: externalvirtual@2.0
prefix: /path/to/external_virtual_clang prefix: /path/to/external_virtual_clang
- spec: externalvirtual@1.0%gcc@10.2.1 - spec: externalvirtual@1.0
prefix: /path/to/external_virtual_gcc prefix: /path/to/external_virtual_gcc
externalmodule: externalmodule:
buildable: False buildable: False
externals: externals:
- spec: externalmodule@1.0%gcc@4.5.0 - spec: externalmodule@1.0
modules: modules:
- external-module - external-module
'requires-virtual': 'requires-virtual':
@ -51,3 +56,35 @@ packages:
prefix: /usr prefix: /usr
version-test-dependency-preferred: version-test-dependency-preferred:
version: ['5.2.5'] version: ['5.2.5']
# Compilers
gcc:
externals:
- spec: "gcc@9.4.0 languages='c,c++' os={linux_os.name}{linux_os.version} target={target}"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/gcc
cxx: /path/bin/g++
- spec: "gcc@9.4.0 languages='c,c++' os=redhat6 target={target}"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/gcc
cxx: /path/bin/g++
- spec: "gcc@10.2.1 languages='c,c++,fortran' os={linux_os.name}{linux_os.version} target={target}"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/gcc-10
cxx: /path/bin/g++-10
fortran: /path/bin/gfortran-10
llvm:
buildable: false
externals:
- spec: "llvm@15.0.0 +clang os={linux_os.name}{linux_os.version} target={target}"
prefix: /path
extra_attributes:
compilers:
c: /path/bin/clang
cxx: /path/bin/clang++

View File

@ -1,50 +0,0 @@
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Unit tests for objects turning configuration into an intermediate format used by the solver."""
import pytest
import spack.compilers
import spack.spec
from spack.concretize import UnavailableCompilerVersionError
from spack.solver import asp
class TestCompilerParser:
def test_expected_order_mock_config(self, config):
"""Tests the expected preference order in the mock compiler configuration"""
parser = asp.CompilerParser(config)
expected_order = ["gcc@=10.2.1", "gcc@=9.4.0", "gcc@=9.4.0", "clang@=15.0.0"]
for c, expected in zip(parser.possible_compilers(), expected_order):
assert c.spec.satisfies(expected)
@pytest.mark.parametrize("spec_str", ["a %gcc@=13.2.0", "a ^b %gcc@=13.2.0"])
def test_compiler_from_input_raise(self, spec_str, config):
"""Tests that having an unknown compiler in the input spec raises an exception, if we
don't allow bootstrapping missing compilers.
"""
spec = spack.spec.Spec(spec_str)
with pytest.raises(UnavailableCompilerVersionError):
asp.CompilerParser(config).with_input_specs([spec])
def test_compilers_inferred_from_concrete_specs(self, mutable_config, mutable_database):
"""Test that compilers inferred from concrete specs, that are not in the local
configuration too, are last in the preference order.
"""
spack.compilers.remove_compiler_from_config("gcc@=10.2.1")
assert not spack.compilers.compilers_for_spec("gcc@=10.2.1")
parser = asp.CompilerParser(mutable_config)
for reuse_spec in mutable_database.query():
parser.add_compiler_from_concrete_spec(reuse_spec)
expected_order = [
("gcc@=9.4.0", True),
("gcc@=9.4.0", True),
("clang@=15.0.0", True),
("gcc@=10.2.1", False),
]
for c, (expected, available) in zip(parser.possible_compilers(), expected_order):
assert c.spec.satisfies(expected)
assert c.available is available