spack/lib/spack/spack/test/flag_mixing.py

252 lines
7.4 KiB
Python

# 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)
import pathlib
import pytest
import spack.config
import spack.environment as ev
import spack.repo
import spack.util.spack_yaml as syaml
from spack.spec import Spec
"""
These tests include the following package DAGs:
Firstly, w, x, y where w and x apply cflags to y.
w
|\
x |
|/
y
Secondly, v, y which where v does not apply cflags to y - this is for testing
mixing with compiler flag propagation in the absence of compiler flags applied
by dependents.
v
|
y
Finally, a diamond dag to check that the topological order is resolved into
a total order:
t
|\
u x
|/
y
"""
@pytest.fixture
def test_repo(mutable_config, monkeypatch, mock_stage):
repo_dir = pathlib.Path(spack.paths.repos_path) / "flags.test"
with spack.repo.use_repositories(str(repo_dir)) as mock_repo_path:
yield mock_repo_path
def update_concretize_scope(conf_str, section):
conf = syaml.load_config(conf_str)
spack.config.set(section, conf[section], scope="concretize")
def test_mix_spec_and_requirements(concretize_scope, test_repo):
conf_str = """\
packages:
y:
require: cflags="-c"
"""
update_concretize_scope(conf_str, "packages")
s1 = Spec('y cflags="-a"').concretized()
assert s1.satisfies('cflags="-a -c"')
def test_mix_spec_and_dependent(concretize_scope, test_repo):
s1 = Spec('x ^y cflags="-a"').concretized()
assert s1["y"].satisfies('cflags="-a -d1"')
def _compiler_cfg_one_entry_with_cflags(cflags):
return f"""\
compilers::
- compiler:
spec: gcc@12.100.100
paths:
cc: /usr/bin/fake-gcc
cxx: /usr/bin/fake-g++
f77: null
fc: null
flags:
cflags: {cflags}
operating_system: debian6
modules: []
"""
def test_mix_spec_and_compiler_cfg(concretize_scope, test_repo):
conf_str = _compiler_cfg_one_entry_with_cflags("-Wall")
update_concretize_scope(conf_str, "compilers")
s1 = Spec('y %gcc@12.100.100 cflags="-O2"').concretized()
assert s1.satisfies('cflags="-Wall -O2"')
@pytest.mark.parametrize(
"cmd_flags,req_flags,cmp_flags,dflags,expected_order",
[
("-a -b", "-c", None, False, "-c -a -b"),
("-x7 -x4", "-x5 -x6", None, False, "-x5 -x6 -x7 -x4"),
("-x7 -x4", "-x5 -x6", "-x3 -x8", False, "-x3 -x8 -x5 -x6 -x7 -x4"),
("-x7 -x4", "-x5 -x6", "-x3 -x8", True, "-x3 -x8 -d1 -d2 -x5 -x6 -x7 -x4"),
("-x7 -x4", None, "-x3 -x8", False, "-x3 -x8 -x7 -x4"),
("-x7 -x4", None, "-x3 -x8", True, "-x3 -x8 -d1 -d2 -x7 -x4"),
# The remaining test cover cases of intersection
("-a -b", "-a -c", None, False, "-c -a -b"),
("-a -b", None, "-a -c", False, "-c -a -b"),
("-a -b", "-a -c", "-a -d", False, "-d -c -a -b"),
("-a -d2 -d1", "-d2 -c", "-d1 -b", True, "-b -c -a -d2 -d1"),
("-a", "-d0 -d2 -c", "-d1 -b", True, "-b -d1 -d0 -d2 -c -a"),
],
)
def test_flag_order_and_grouping(
concretize_scope, test_repo, cmd_flags, req_flags, cmp_flags, dflags, expected_order
):
"""Check consistent flag ordering and grouping on a package "y"
with flags introduced from a variety of sources.
The ordering rules are explained in ``asp.SpecBuilder.reorder_flags``.
"""
if req_flags:
conf_str = f"""\
packages:
y:
require: cflags="{req_flags}"
"""
update_concretize_scope(conf_str, "packages")
if cmp_flags:
conf_str = _compiler_cfg_one_entry_with_cflags(cmp_flags)
update_concretize_scope(conf_str, "compilers")
compiler_spec = ""
if cmp_flags:
compiler_spec = "%gcc@12.100.100"
if dflags:
spec_str = f"x+activatemultiflag {compiler_spec} ^y"
expected_dflags = "-d1 -d2"
else:
spec_str = f"y {compiler_spec}"
expected_dflags = None
if cmd_flags:
spec_str += f' cflags="{cmd_flags}"'
root_spec = Spec(spec_str).concretized()
spec = root_spec["y"]
satisfy_flags = " ".join(x for x in [cmd_flags, req_flags, cmp_flags, expected_dflags] if x)
assert spec.satisfies(f'cflags="{satisfy_flags}"')
assert spec.compiler_flags["cflags"] == expected_order.split()
def test_two_dependents_flag_mixing(concretize_scope, test_repo):
root_spec1 = Spec("w~moveflaglater").concretized()
spec1 = root_spec1["y"]
assert spec1.compiler_flags["cflags"] == "-d0 -d1 -d2".split()
root_spec2 = Spec("w+moveflaglater").concretized()
spec2 = root_spec2["y"]
assert spec2.compiler_flags["cflags"] == "-d3 -d1 -d2".split()
def test_propagate_and_compiler_cfg(concretize_scope, test_repo):
conf_str = _compiler_cfg_one_entry_with_cflags("-f2")
update_concretize_scope(conf_str, "compilers")
root_spec = Spec("v %gcc@12.100.100 cflags=='-f1'").concretized()
assert root_spec["y"].satisfies("cflags='-f1 -f2'")
# Note: setting flags on a dependency overrides propagation, which
# is tested in test/concretize.py:test_compiler_flag_propagation
def test_propagate_and_pkg_dep(concretize_scope, test_repo):
root_spec1 = Spec("x ~activatemultiflag cflags=='-f1'").concretized()
assert root_spec1["y"].satisfies("cflags='-f1 -d1'")
def test_propagate_and_require(concretize_scope, test_repo):
conf_str = """\
packages:
y:
require: cflags="-f2"
"""
update_concretize_scope(conf_str, "packages")
root_spec1 = Spec("v cflags=='-f1'").concretized()
assert root_spec1["y"].satisfies("cflags='-f1 -f2'")
# Next, check that a requirement does not "undo" a request for
# propagation from the command-line spec
conf_str = """\
packages:
v:
require: cflags="-f1"
"""
update_concretize_scope(conf_str, "packages")
root_spec2 = Spec("v cflags=='-f1'").concretized()
assert root_spec2["y"].satisfies("cflags='-f1'")
# Note: requirements cannot enforce propagation: any attempt to do
# so will generate a concretization error; this likely relates to
# the note about #37180 in concretize.lp
def test_dev_mix_flags(tmp_path, concretize_scope, mutable_mock_env_path, test_repo):
src_dir = tmp_path / "x-src"
env_content = f"""\
spack:
specs:
- y %gcc@12.100.100 cflags=='-fsanitize=address'
develop:
y:
spec: y cflags=='-fsanitize=address'
path: {src_dir}
"""
conf_str = _compiler_cfg_one_entry_with_cflags("-f1")
update_concretize_scope(conf_str, "compilers")
manifest_file = tmp_path / ev.manifest_name
manifest_file.write_text(env_content)
e = ev.create("test", manifest_file)
with e:
e.concretize()
e.write()
(result,) = list(j for i, j in e.concretized_specs() if j.name == "y")
assert result["y"].satisfies("cflags='-fsanitize=address -f1'")
def test_diamond_dep_flag_mixing(concretize_scope, test_repo):
"""A diamond where each dependent applies flags to the bottom
dependency. The goal is to ensure that the flag ordering is
(a) topological and (b) repeatable for elements not subject to
this partial ordering (i.e. the flags for the left and right
nodes of the diamond always appear in the same order).
`Spec.traverse` is responsible for handling both of these needs.
"""
root_spec1 = Spec("t").concretized()
spec1 = root_spec1["y"]
assert spec1.satisfies('cflags="-c1 -c2 -d1 -d2 -e1 -e2"')
assert spec1.compiler_flags["cflags"] == "-c1 -c2 -e1 -e2 -d1 -d2".split()