From 8f5fcc6e95745dd18c978396d79f028d83a62a20 Mon Sep 17 00:00:00 2001 From: Tom Scogland Date: Wed, 16 Feb 2022 13:23:12 -0800 Subject: extensions: allow multiple "extends" directives (#28853) * extensions: allow multiple "extends" directives This will allow multiple extends directives in a package as long as only one of them is selected as a dependency in the concrete spec. * document the option to have multiple extends --- lib/spack/docs/packaging_guide.rst | 18 ++++++++++++++++++ lib/spack/spack/package.py | 15 ++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst index 293e83a118..bdf557a346 100644 --- a/lib/spack/docs/packaging_guide.rst +++ b/lib/spack/docs/packaging_guide.rst @@ -2470,6 +2470,24 @@ Now, the ``py-numpy`` package can be used as an argument to ``spack activate``. When it is activated, all the files in its prefix will be symbolically linked into the prefix of the python package. +A package can only extend one other package at a time. To support packages +that may extend one of a list of other packages, Spack supports multiple +``extends`` directives as long as at most one of them is selected as +a dependency during concretization. For example, a lua package could extend +either lua or luajit, but not both: + +.. code-block:: python + + class LuaLpeg(Package): + ... + variant('use_lua', default=True) + extends('lua', when='+use_lua') + extends('lua-luajit', when='~use_lua') + ... + +Now, a user can install, and activate, the ``lua-lpeg`` package for either +lua or luajit. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Adding additional constraints ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index d3e1bdc002..447e7a9d46 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -1186,22 +1186,27 @@ class PackageBase(six.with_metaclass(PackageMeta, PackageViewMixin, object)): if not self.extendees: return None - # TODO: allow more than one extendee. - name = next(iter(self.extendees)) + deps = [] # If the extendee is in the spec's deps already, return that. for dep in self.spec.traverse(deptypes=('link', 'run')): - if name == dep.name: - return dep + if dep.name in self.extendees: + deps.append(dep) + + # TODO: allow more than one active extendee. + if deps: + assert len(deps) == 1 + return deps[0] # if the spec is concrete already, then it extends something # that is an *optional* dependency, and the dep isn't there. if self.spec._concrete: return None else: + # TODO: do something sane here with more than one extendee # If it's not concrete, then return the spec from the # extends() directive since that is all we know so far. - spec, kwargs = self.extendees[name] + spec, kwargs = next(iter(self.extendees.items())) return spec @property -- cgit v1.2.3-60-g2f50