From 1db73eb1f222048fbdaf43835000ac17c12694bf Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Wed, 9 Aug 2023 16:35:01 +0200 Subject: Add vendors directive For the time being this directive prevents the vendored package to be in the same DAG as the one vendoring it. --- lib/spack/spack/directives.py | 24 +++++++++++++++ lib/spack/spack/solver/asp.py | 12 ++++++++ lib/spack/spack/solver/concretize.lp | 10 +++++++ lib/spack/spack/test/env.py | 35 ++++++++++++++++++++++ .../builtin.mock/packages/vendorsb/package.py | 18 +++++++++++ .../repos/builtin/packages/memkind/package.py | 2 +- var/spack/repos/builtin/packages/palace/package.py | 6 ++-- var/spack/repos/builtin/packages/scotch/package.py | 4 +-- var/spack/repos/builtin/packages/votca/package.py | 7 +++-- 9 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 var/spack/repos/builtin.mock/packages/vendorsb/package.py diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py index 2b7aefca55..03f2f38189 100644 --- a/lib/spack/spack/directives.py +++ b/lib/spack/spack/directives.py @@ -69,6 +69,7 @@ __all__ = [ "resource", "build_system", "requires", + "vendors", ] #: These are variant names used by Spack internally; packages can't use them @@ -916,6 +917,29 @@ def requires(*requirement_specs, policy="one_of", when=None, msg=None): return _execute_requires +@directive("vendors") +def vendors(spec, when=None): + """Declares that a package has an internal copy of another package. + + Currently, the effect is to forbid having the two packages in the same + "unification set". + + Args: + spec: spec being vendored + when: optional constraint that triggers vendoring + """ + + def _execute_vendors(pkg): + when_spec = make_when_spec(when) + if not when_spec: + return + + when_spec_list = pkg.vendors.setdefault(spec, []) + when_spec_list.append(when_spec) + + return _execute_vendors + + class DirectiveError(spack.error.SpackError): """This is raised when something is wrong with a package directive.""" diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index e9141d071d..71aa57e1bf 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -1040,6 +1040,15 @@ class SpackSolverSetup: ) self.gen.newline() + def vendor_rules(self, pkg): + """Facts about vendored packages.""" + for vendored_spec_str, constraints in pkg.vendors.items(): + vendored_spec = spack.spec.Spec(vendored_spec_str) + for constraint in constraints: + constraint_id = self.condition(constraint, name=pkg.name) + self.gen.fact(fn.pkg_fact(pkg.name, fn.vendors(constraint_id, vendored_spec.name))) + self.gen.newline() + def compiler_facts(self): """Facts about available compilers.""" @@ -1189,6 +1198,9 @@ class SpackSolverSetup: # conflicts self.conflict_rules(pkg) + # vendoring + self.vendor_rules(pkg) + # default compilers for this package self.package_compiler_defaults(pkg) diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index eb691225b8..e41539a366 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -449,6 +449,16 @@ error(1, Msg) not external(node(ID, Package)), % ignore conflicts for externals not attr("hash", node(ID, Package), _). % ignore conflicts for installed packages +%----------------------------------------------------------------------------- +% Vendoring +%----------------------------------------------------------------------------- +error(1, "{0} vendors an internal copy of {1}, so it cannot be in the same unification set as {1}", Package, VendoredPackage) + :- pkg_fact(Package, vendors(ConditionID, VendoredPackage)), + attr("node", node(ID, Package)), + condition_holds(ConditionID, node(ID, Package)), + unification_set(X, node(ID, Package)), + unification_set(X, node(_, VendoredPackage)). + %----------------------------------------------------------------------------- % Virtual dependencies %----------------------------------------------------------------------------- diff --git a/lib/spack/spack/test/env.py b/lib/spack/spack/test/env.py index bcb459f100..23c429de16 100644 --- a/lib/spack/spack/test/env.py +++ b/lib/spack/spack/test/env.py @@ -551,3 +551,38 @@ spack: with spack.config.override("concretizer:unify", unify_in_config): with ev.Environment(manifest.parent) as e: assert e.unify == unify_in_config + + +@pytest.mark.parametrize( + "spec_str,expected_raise,expected_spec", + [ + # vendorsb vendors "b" only when @=1.1 + ("vendorsb", False, "vendorsb@=1.0"), + ("vendorsb@=1.1", True, None), + ], +) +def test_vendors_directive( + spec_str, expected_raise, expected_spec, tmp_path, mock_packages, config +): + """Tests that we cannot concretize two specs together, if one vendors the other.""" + if spack.config.get("config:concretizer") == "original": + pytest.xfail("Known failure of the original concretizer") + + manifest = tmp_path / "spack.yaml" + manifest.write_text( + f"""\ +spack: + specs: + - {spec_str} + - b + concretizer: + unify: true +""" + ) + with ev.Environment(manifest.parent) as e: + if expected_raise: + with pytest.raises(spack.solver.asp.UnsatisfiableSpecError): + e.concretize() + else: + e.concretize() + assert any(s.satisfies(expected_spec) for s in e.concrete_roots()) diff --git a/var/spack/repos/builtin.mock/packages/vendorsb/package.py b/var/spack/repos/builtin.mock/packages/vendorsb/package.py new file mode 100644 index 0000000000..73fe9ad1cf --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/vendorsb/package.py @@ -0,0 +1,18 @@ +# Copyright 2013-2023 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) + +from spack.package import * + + +class Vendorsb(Package): + """A package that vendors another""" + + homepage = "http://www.example.com" + url = "http://www.example.com/b-1.0.tar.gz" + + version("1.1", md5="0123456789abcdef0123456789abcdef") + version("1.0", md5="0123456789abcdef0123456789abcdef") + + vendors("b", when="@=1.1") diff --git a/var/spack/repos/builtin/packages/memkind/package.py b/var/spack/repos/builtin/packages/memkind/package.py index f51740f48e..12a0e18fd6 100644 --- a/var/spack/repos/builtin/packages/memkind/package.py +++ b/var/spack/repos/builtin/packages/memkind/package.py @@ -40,7 +40,7 @@ class Memkind(AutotoolsPackage): # memkind includes a copy of jemalloc; see # . - conflicts("jemalloc") + vendors("jemalloc") # https://github.com/spack/spack/issues/37292 parallel = False diff --git a/var/spack/repos/builtin/packages/palace/package.py b/var/spack/repos/builtin/packages/palace/package.py index fd06706db1..6eb8d87936 100644 --- a/var/spack/repos/builtin/packages/palace/package.py +++ b/var/spack/repos/builtin/packages/palace/package.py @@ -94,9 +94,9 @@ class Palace(CMakePackage): depends_on("arpack-ng+shared", when="+shared") depends_on("arpack-ng~shared", when="~shared") - # Conflicts: Palace always builds its own internal MFEM, GSLIB - conflicts("^mfem", msg="Palace builds its own internal MFEM") - conflicts("^gslib", msg="Palace builds its own internal GSLIB") + # Palace always builds its own internal MFEM, GSLIB + vendors("mfem") + vendors("gslib") # More dependency variant conflicts conflicts("^hypre+int64", msg="Palace uses HYPRE's mixedint option for 64 bit integers") diff --git a/var/spack/repos/builtin/packages/scotch/package.py b/var/spack/repos/builtin/packages/scotch/package.py index 12e81d7b96..3a1e03cbbd 100644 --- a/var/spack/repos/builtin/packages/scotch/package.py +++ b/var/spack/repos/builtin/packages/scotch/package.py @@ -70,8 +70,8 @@ class Scotch(CMakePackage, MakefilePackage): # Vendored dependency of METIS/ParMETIS conflicts with standard # installations - conflicts("^metis", when="+metis") - conflicts("^parmetis", when="+metis") + vendors("metis", when="+metis") + vendors("parmetis", when="+metis") parallel = False diff --git a/var/spack/repos/builtin/packages/votca/package.py b/var/spack/repos/builtin/packages/votca/package.py index aa5a5d0b3f..629c9eb0ad 100644 --- a/var/spack/repos/builtin/packages/votca/package.py +++ b/var/spack/repos/builtin/packages/votca/package.py @@ -28,9 +28,10 @@ class Votca(CMakePackage): "new-gmx", default=False, description="Build against gromacs>2019 - no tabulated kernels" ) variant("xtp", default=True, description="Build xtp parts of votca") - conflicts("votca-tools") - conflicts("votca-csg") - conflicts("votca-xtp") + + vendors("votca-tools") + vendors("votca-csg") + vendors("votca-xtp") depends_on("cmake@3.13:", type="build") depends_on("expat") -- cgit v1.2.3-60-g2f50