Compare commits

...

3 Commits

Author SHA1 Message Date
Harmen Stoppels
7aaf51c36e show directive properly 2025-02-10 10:17:49 +01:00
Harmen Stoppels
908095e865 group by package 2025-02-10 10:05:50 +01:00
Harmen Stoppels
f32a756800 audit.py: check for unsatisfiable when conditions of directives 2025-02-10 09:48:22 +01:00

View File

@ -61,6 +61,7 @@ def _search_duplicate_compilers(error_cls):
import spack.util.crypto import spack.util.crypto
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
import spack.variant import spack.variant
import spack.version
#: Map an audit tag to a list of callables implementing checks #: Map an audit tag to a list of callables implementing checks
CALLBACKS = {} CALLBACKS = {}
@ -1137,10 +1138,23 @@ def _ensure_variants_have_descriptions(pkgs, error_cls):
return errors return errors
def _skip_version_audit(host_arch: spack.spec.ArchSpec, pkg_cls) -> bool:
"""The version audit is skipped if a package defines skip_version_audit as True or if the
host architecture satisfies any of the archs in the list skip_version_audit."""
skip_conditions = getattr(pkg_cls, "skip_version_audit", False)
if type(skip_conditions) is bool:
return skip_conditions
for condition in skip_conditions:
if host_arch.satisfies(spack.spec.Spec(condition).architecture):
return True
return False
@package_directives @package_directives
def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls): def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls):
"""Report if version constraints used in directives are not satisfiable""" """Report if version constraints used in directives are not satisfiable"""
errors = [] errors = []
host_architecture = spack.spec.ArchSpec.default_arch()
for pkg_name in pkgs: for pkg_name in pkgs:
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name) pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
filename = spack.repo.PATH.filename_for_package_name(pkg_name) filename = spack.repo.PATH.filename_for_package_name(pkg_name)
@ -1156,20 +1170,13 @@ def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls
dependencies_to_check.append(dep.spec) dependencies_to_check.append(dep.spec)
host_architecture = spack.spec.ArchSpec.default_arch()
for s in dependencies_to_check: for s in dependencies_to_check:
dependency_pkg_cls = None dependency_pkg_cls = None
try: try:
dependency_pkg_cls = spack.repo.PATH.get_pkg_class(s.name) dependency_pkg_cls = spack.repo.PATH.get_pkg_class(s.name)
# Some packages have hacks that might cause failures on some platform # Some packages have hacks that might cause failures on some platform
# Allow to explicitly set conditions to skip version checks in that case # Allow to explicitly set conditions to skip version checks in that case
skip_conditions = getattr(dependency_pkg_cls, "skip_version_audit", []) assert _skip_version_audit(host_architecture, dependency_pkg_cls) or any(
skip_version_check = False
for condition in skip_conditions:
if host_architecture.satisfies(spack.spec.Spec(condition).architecture):
skip_version_check = True
break
assert skip_version_check or any(
v.intersects(s.versions) for v in list(dependency_pkg_cls.versions) v.intersects(s.versions) for v in list(dependency_pkg_cls.versions)
) )
except Exception: except Exception:
@ -1188,6 +1195,64 @@ def _version_constraints_are_satisfiable_by_some_version_in_repo(pkgs, error_cls
return errors return errors
@package_directives
def _when_conditions_are_satisfiable_by_some_version(pkgs, error_cls):
"""Report if versions in when conditions used in directives are not satisfiable"""
errors = []
attrs = {
"conflicts": "conflicts",
"dependencies": "depends_on",
"licenses": "license",
"patches": "patch",
"provided_together": "provides",
"provided": "provides",
"requirements": "requires",
"resources": "resource",
"splice_specs": "can_splice",
"variants": "variant",
}
host_architecture = spack.spec.ArchSpec.default_arch()
for pkg_name in pkgs:
pkg_cls = spack.repo.PATH.get_pkg_class(pkg_name)
filename = spack.repo.PATH.filename_for_package_name(pkg_name)
if _skip_version_audit(host_architecture, pkg_cls):
continue
if not pkg_cls.versions:
continue
range = spack.version.VersionRange(
min(pkg_cls.versions), spack.version.StandardVersion.typemax()
)
details = []
for attr, directive in attrs.items():
if attr == "patches":
# Patches should strictly apply to some known version
unsatisfiable = [
when
for when in getattr(pkg_cls, attr)
if not any(y.intersects(when.versions) for y in pkg_cls.versions)
]
else:
# Other directives should just be in the range of min known version to infinity.
# The reason being sometimes people add depends_on("foo@1.1", when="@1.1") because
# it's copied, even though only 1.0 and 1.2 are registered in Spack. It's too
# annoying to complain about that.
unsatisfiable = [
when for when in getattr(pkg_cls, attr) if not range.intersects(when.versions)
]
details.extend(f'{directive}(..., when="{x}")' for x in unsatisfiable)
if details:
errors.append(
error_cls(
summary=f"{filename}: when conditions are not satisfiable by "
f"any known version of {pkg_cls.name}",
details=details,
)
)
return errors
def _analyze_variants_in_directive(pkg, constraint, directive, error_cls): def _analyze_variants_in_directive(pkg, constraint, directive, error_cls):
errors = [] errors = []
variant_names = pkg.variant_names() variant_names = pkg.variant_names()