From c930ddd5f0d1e1f595cac025190d9b786f67f6d6 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Tue, 19 May 2020 16:11:46 +0200 Subject: [PATCH] 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. --- packages/dune/package.py | 154 ++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 83 deletions(-) diff --git a/packages/dune/package.py b/packages/dune/package.py index 8c12511..88b913d 100644 --- a/packages/dune/package.py +++ b/packages/dune/package.py @@ -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.