[!19] Introduce variant dependency

Merge branch 'feature/variant-dependencies' into 'master'

ref:spack/dune-spack\> The previous approach had the big downside of bypassing
the specified variants through the specified dependencies of variants. E.g.
dune+pdelab~functions would silently ignore the ~functions aspect, because
+pdelab enforces the installation of dune-functions. This introduces even more
subtle bugs as more Dune modules are added to the package as any
Dune-module-dependent logic in the package needs to be duplicated for each
Dune module that depends on the module that originally introduced that logic
(yuck!).

There is no direct mechanism in Spack to specify such dependencies in a way
that enforces the correct variants on dependent modules. I have now added
conflicts between variants that reflect the Dune module dependencies. In that
case dune+pdelab~functions would throw an error.

This comes at the cost that a command such as

    spack install dune+pdelab

is not possible anymore. Instead the user needs to specify the dependent
modules as well:

    spack install dune+pdelab+functions+typetree

Note that in order to remove some burden from users, I removed the variants
for the core modules - they are always installed.

See merge request [spack/dune-spack!19]

  [spack/dune-spack!19]: gitlab.dune-project.org/spack/dune-spack/merge_requests/19
This commit is contained in:
Dominic Kempf 2020-05-20 07:32:17 +00:00
commit ce341af06e
1 changed files with 71 additions and 83 deletions

View File

@ -41,6 +41,58 @@ class Dune(CMakePackage):
("master" , "master"),
]
# This defines the mapping of the variant names for Dune modules and the
# resource names that we assign later on.
dune_variants_to_resources = {
'alugrid' : 'dune-alugrid',
'codegen' : 'dune-codegen',
'fem' : 'dune-fem',
'foamgrid' : 'dune-foamgrid',
'functions': 'dune-functions',
'gridglue' : 'dune-grid-glue',
'multidomaingrid' : 'dune-multidomaingrid',
'pdelab' : 'dune-pdelab',
'polygongrid' : 'dune-polygongrid',
'spgrid' : 'dune-spgrid',
'testtools' : 'dune-testtools',
'typetree' : 'dune-typetree',
'uggrid' : 'dune-uggrid',
}
# Define the inverse mapping
dune_resources_to_variants = {v: k for k, v in dune_variants_to_resources.items()}
# Dependencies between modules - not necessarily the full set
# as the closure of module dependencies is built later on.
# dune-common does not need to be named.
module_dependencies = {
'dune-alugrid': ['dune-grid'],
'dune-codegen': ['dune-pdelab', 'dune-testtools', 'dune-alugrid'],
'dune-fem': ['dune-grid'],
'dune-fempy': ['dune-fem'],
'dune-foamgrid': ['dune-grid'],
'dune-functions': ["dune-grid", "dune-typetree", "dune-localfunctions", "dune-istl"],
'dune-grid': ['dune-geometry'],
'dune-grid-glue': ['dune-grid'],
'dune-localfunctions': ['dune-geometry'],
'dune-multidomaingrid': ['dune-grid', 'dune-typetree'],
'dune-pdelab': ['dune-istl', 'dune-functions'],
'dune-polygongrid': ['dune-grid'],
}
# Build the closure of above module dependency list
for module in module_dependencies:
closure = set(module_dependencies[module])
old_closure = set()
while (len(closure) > len(old_closure)):
old_closure = closure.copy()
for res in old_closure:
for mod in module_dependencies.get(res, []):
closure.add(mod)
module_dependencies[module] = list(closure)
# Variants for the general build process
variant('shared', default=True, description='Enables the build of shared libraries.')
@ -68,25 +120,16 @@ class Dune(CMakePackage):
# Some variants that were here that are on my todo list
# variant('jupyter', default=False, description='Build with Jupyter support')
# Define one variant for each Dune module that we have. Only core modules
# are activated by default.
variant('alugrid', default=False, description='Build with dune-alugrid module')
variant('codegen', default=False, description='Build with dune-codegen module')
variant('fem', default=False, description='Build with dune-fem(py) module')
variant('foamgrid', default=False, description='Build with dune-foamgrid module')
variant('functions', default=False, description='Build with dune-functions module')
variant('geometry', default=True, description='Build with dune-geometry module')
variant('grid', default=True, description='Build with dune-grid module')
variant('gridglue', default=False, description='Build with dune-grid-glue module')
variant('istl', default=True, description='Build with dune-istl module')
variant('localfunctions', default=True, description='Build with dune-localfunctions module')
variant('multidomaingrid', default=False, description='Build with dune-multidomaingrid module')
variant('pdelab', default=False, description='Build with dune-pdelab module')
variant('polygongrid', default=False, description='Build with dune-polygongrid module')
variant('spgrid', default=False, description='Build with dune-spgrid module')
variant('testtools', default=False, description='Build with dune-testtools module')
variant('typetree', default=False, description='Build with dune-typetree module')
variant('uggrid', default=False, description='Build with dune-uggrid module')
# Define one variant for each non-core Dune module that we have.
for var, res in dune_variants_to_resources.items():
variant(var, default=False, description='Build with the %s module' % res)
# Define conflicts between Dune module variants. These conflicts are of the following type:
# conflicts('dune~functions', when='+pdelab') -> dune-pdelab cannot be built without dune-functions
for var, res in dune_variants_to_resources.items():
for dep in module_dependencies.get(res, []):
if dep in dune_resources_to_variants:
conflicts('dune~%s' % dune_resources_to_variants[dep], when='+%s' % var)
# Iterate over all available Dune versions and define resources for all Dune modules
# If a Dune module behaves differently for different versions (e.g. dune-python got
@ -98,28 +141,28 @@ class Dune(CMakePackage):
name='dune-geometry',
git='https://gitlab.dune-project.org/core/dune-geometry.git',
branch=branch,
when='@%s+geometry' % vers,
when='@%s' % vers,
)
resource(
name='dune-grid',
git='https://gitlab.dune-project.org/core/dune-grid.git',
branch=branch,
when='@%s+grid' % vers,
when='@%s' % vers,
)
resource(
name='dune-istl',
git='https://gitlab.dune-project.org/core/dune-istl.git',
branch=branch,
when='@%s+istl' % vers,
when='@%s' % vers,
)
resource(
name='dune-localfunctions',
git='https://gitlab.dune-project.org/core/dune-localfunctions.git',
branch=branch,
when='@%s+localfunctions' % vers,
when='@%s' % vers,
)
resource(
@ -232,29 +275,7 @@ class Dune(CMakePackage):
submodules=True,
)
# Dependencies between modules - not necessarily the full set
# as the closure of module dependencies is built later on.
module_dependencies = {}
module_dependencies["dune-alugrid"] = ["dune-grid", "dune-geometry", "dune-common"]
module_dependencies["dune-codegen"] = ["dune-pdelab", "dune-testtools", "dune-alugrid"]
module_dependencies["dune-common"] = []
module_dependencies["dune-fem"] = ["dune-grid"]
module_dependencies["fune-fempy"] = ["dune-fem"]
module_dependencies["dune-foamgrid"] = ["dune-grid"]
module_dependencies["dune-functions"] = ["dune-grid", "dune-typetree", "dune-localfunctions", "dune-istl"]
module_dependencies["dune-geometry"] = ["dune-common"]
module_dependencies["dune-grid"] = ["dune-common", "dune-geometry"]
module_dependencies["dune-grid-glue"] = ["dune-grid"]
module_dependencies["dune-istl"] = ["dune-common"]
module_dependencies["dune-localfunctions"] = ["dune-common", "dune-geometry"]
module_dependencies["dune-multidomaingrid"] = ["dune-grid", "dune-typetree"]
module_dependencies["dune-pdelab"] = ["dune-common", "dune-grid", "dune-istl", "dune-functions"]
module_dependencies["dune-polygongrid"] = ["dune-grid"]
module_dependencies["dune-python"] = ["dune-common"]
module_dependencies["dune-testtools"] = ["dune-common"]
module_dependencies["dune-typetree"] = ["dune-common"]
module_dependencies["dune-uggrid"] = ["dune-common"]
# Make sure that Python components integrate well into Spack
extends('python')
python_components = [ 'dune' ]
@ -280,13 +301,9 @@ class Dune(CMakePackage):
depends_on('python@3.0:', type=('build', 'run'))
depends_on('py-setuptools', type='build', when='+python')
depends_on('py-numpy', type=('build', 'run'), when='+python')
depends_on('py-pip', type=('build', 'run'), when='+codegen')
depends_on('py-pip', type=('build', 'run'), when='+python')
depends_on('py-pip', type=('build', 'run'), when='+testtools')
depends_on('py-pip', type=('build', 'run'))
depends_on('py-sphinx', type=('build', 'run'), when='+doc')
depends_on('py-wheel', type='build', when='+codegen')
depends_on('py-wheel', type='build', when='+python')
depends_on('py-wheel', type='build', when='+testtools')
depends_on('py-wheel', type='build')
depends_on('scotch+mpi', when='+ptscotch')
depends_on('sionlib', when='+sionlib')
depends_on('suite-sparse', when='+suitesparse')
@ -297,9 +314,8 @@ class Dune(CMakePackage):
# Apply patches
patch('virtualenv_from_envvariable.patch', when='+testtools')
patch('virtualenv_from_envvariable.patch', when='+codegen')
# Some conflicts
# Some additional variant conflicts of Dune modules and upstream dependencies
conflicts('dune~superlu', when='+codegen')
def setup_build_environment(self, env):
@ -356,7 +372,7 @@ class Dune(CMakePackage):
'-DCMAKE_DISABLE_FIND_PACKAGE_ZOLTAN:BOOL=%s' % nvariant_bool('+zoltan'),
]
if '+testtools' in spec or '+codegen' in spec:
if '+testtools' in spec:
cmake_args.append('-DDUNE_PYTHON_VIRTUALENV_SETUP:BOOL=ON')
cmake_args.append('-DDUNE_PYTHON_ALLOW_GET_PIP:BOOL=ON')
cmake_args.append('-DDUNE_PYTHON_VIRTUALENV_PATH:STRING="%s"' % join_path(Path.home(), '.cache', 'dune-python-env', self.spec.dag_hash()))
@ -370,34 +386,6 @@ class Dune(CMakePackage):
return cmake_args
def module_dependency_closure(self, when, reslist):
# Get a list of all modules that share the version requirement
all_resources = []
for w, rl in self.resources.items():
if w.version == when.version:
all_resources.extend(rl)
# And build the closure of modules from the given reslist
closure = set(reslist)
old_closure = set()
while (len(closure) > len(old_closure)):
old_closure = closure.copy()
for res in old_closure:
for mod in self.module_dependencies.get(res.name, []):
for r in all_resources:
if r.name == mod:
closure.add(r)
return list(closure)
def _get_needed_resources(self):
# Modify the list of resources by building a transitive closure of Dune module dependencies.
self.resources = {when: self.module_dependency_closure(when, reslist) for when, reslist in self.resources.items()}
# and call the original implementation
return CMakePackage._get_needed_resources(self)
def cmake(self, spec, prefix):
# dune-codegen delivers its own set of patches for its submodules
# that we can apply with a script delivered by dune-codegen.