Introduce variant dependency

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.

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.
This commit is contained in:
Dominic Kempf 2020-05-19 16:11:46 +02:00
parent d74cbb85a8
commit c930ddd5f0
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.