diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index 68a80cfd19f..0a83d64a10f 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -311,6 +311,29 @@ def __call__(self, *args): """ return AspFunction(self.name, self.args + args) + def match(self, pattern: "AspFunction"): + """Compare name and args of this ASP function to a match pattern. + + Arguments of ``pattern`` function can be strings, arbitrary objects or ``any``: + + * ``any`` matches any argument; + * ``str`` arguments are treated as regular expressions and match against the + string representation of the args of this function. + * any other object is compared with `==`. + """ + if self.name != pattern.name or len(pattern.args) > len(self.args): + return False + + for parg, arg in zip(pattern.args, self.args): + if parg is any: + continue + elif isinstance(parg, str) and not re.match(parg, str(arg)): + return False + elif parg != arg: + return False + + return True + def symbol(self, positive=True): def argify(arg): if isinstance(arg, bool): @@ -1131,6 +1154,7 @@ def __init__(self, tests=False): self.possible_oses = set() self.variant_values_from_specs = set() self.version_constraints = set() + self.synced_version_constraints = set() self.target_constraints = set() self.default_targets = [] self.compiler_version_constraints = set() @@ -1631,6 +1655,27 @@ def package_provider_rules(self, pkg): ) self.gen.newline() + def transform_my_version( + self, require: spack.spec.Spec, impose: spack.spec.Spec, funcs: List[AspFunction] + ) -> List[AspFunction]: + """Replace symbolic "my" version with reference to dependent's version.""" + result = [] + for f in funcs: + if not f.match(fn.attr("node_version_satisfies", any, r"^my\.version$")): + result.append(f) + continue + + # get Version from version(Package, Version) and generate + # node_version_satisfies(dep, Version) + dep = f.args[1] + sync = fn.attr("sync", dep, "node_version_satisfies", fn.attr("version", require.name)) + result.append(sync) + + # remember to generate version_satisfies/3 for my.version constraints + self.synced_version_constraints.add((require.name, dep)) + + return result + def package_dependencies_rules(self, pkg): """Translate 'depends_on' directives into ASP logic.""" for _, conditions in sorted(pkg.dependencies.items()): @@ -1671,7 +1716,7 @@ def dependency_holds(required, imposed, impositions): name=pkg.name, msg=msg, transform_required=[track_dependencies], - transform_imposed=[remove_node, dependency_holds], + transform_imposed=[remove_node, dependency_holds, self.transform_my_version], ) self.gen.newline() @@ -2438,6 +2483,15 @@ def generate_possible_compilers(self, specs): def define_version_constraints(self): """Define what version_satisfies(...) means in ASP logic.""" + # quadratic for now b/c we're anticipating pkg_ver being an + # expression/range/etc. right now this only does exact matches until we can + # propagate an expression from depends_on to here. + for pkg, dep in self.synced_version_constraints: + for pkg_ver in self.possible_versions[pkg]: + for dep_ver in self.possible_versions[dep]: + if dep_ver.satisfies(pkg_ver): + self.gen.fact(fn.pkg_fact(dep, fn.version_satisfies(dep_ver, pkg_ver))) + for pkg_name, versions in sorted(self.version_constraints): # generate facts for each package constraint and the version # that satisfies it diff --git a/lib/spack/spack/solver/concretize.lp b/lib/spack/spack/solver/concretize.lp index a692c072a17..2bbf66b6d5a 100644 --- a/lib/spack/spack/solver/concretize.lp +++ b/lib/spack/spack/solver/concretize.lp @@ -366,6 +366,11 @@ attr(Name, node(X, A1), A2) :- impose(ID, PackageNode), imposed_constrai attr(Name, node(X, A1), A2, A3) :- impose(ID, PackageNode), imposed_constraint(ID, Name, A1, A2, A3), imposed_nodes(ID, PackageNode, node(X, A1)), not multiple_nodes_attribute(Name). attr(Name, node(X, A1), A2, A3, A4) :- impose(ID, PackageNode), imposed_constraint(ID, Name, A1, A2, A3, A4), imposed_nodes(ID, PackageNode, node(X, A1)). +attr(DepAttrName, DepNode, Value) + :- depends_on(node(X, Package), DepNode), + attr(PackageAttrName, node(X, Package), Value), + attr("sync", DepNode, DepAttrName, attr(PackageAttrName, Package)). + % For node flag sources we need to look at the condition_set of the source, since it is the dependent % of the package on which I want to impose the constraint attr("node_flag_source", node(X, A1), A2, node(Y, A3)) diff --git a/var/spack/repos/builtin.mock/packages/version-lock-dep/package.py b/var/spack/repos/builtin.mock/packages/version-lock-dep/package.py new file mode 100644 index 00000000000..ee54a805e15 --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/version-lock-dep/package.py @@ -0,0 +1,30 @@ +# 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 VersionLockDep(Package): + """version-lock-dep is depended on by version-lock with the same version""" + + homepage = "http://example.com/version-lock-dep/" + url = "http://example.com/version-lock-dep.tar.gz" + + version("3.2.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.2.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.1.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.1.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.0.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + + version("2.2.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.2.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.1.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.1.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.0.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + + version("1.2.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.2.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.1.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.1.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.0.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") diff --git a/var/spack/repos/builtin.mock/packages/version-lock/package.py b/var/spack/repos/builtin.mock/packages/version-lock/package.py new file mode 100644 index 00000000000..818fa8e44d1 --- /dev/null +++ b/var/spack/repos/builtin.mock/packages/version-lock/package.py @@ -0,0 +1,32 @@ +# 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 VersionLock(Package): + """version-lock depends on version-lock-dep with the same version""" + + homepage = "http://example.com/version-lock/" + url = "http://example.com/version-lock.tar.gz" + + version("3.2.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.2.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.1.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.1.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("3.0.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + + version("2.2.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.2.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.1.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.1.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("2.0.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + + version("1.2.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.2.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.1.1", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.1.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + version("1.0.0", sha256="18d459400558f4ea99527bc9786c033965a3db45bf4c6a32eefdc07aa9e306a6") + + depends_on("version-lock-dep@my.version")