summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@googlemail.com>2016-12-28 21:37:02 +0100
committerTodd Gamblin <tgamblin@llnl.gov>2016-12-28 12:37:02 -0800
commit17b13b161b3ddcd691ea7ed90165cfab6dec3950 (patch)
treeb0e9360f878e8d007ab63ede06aa80df89ac4a17
parent857dac88c8c041f1384f9ffd555d8f4abc03e163 (diff)
downloadspack-17b13b161b3ddcd691ea7ed90165cfab6dec3950.tar.gz
spack-17b13b161b3ddcd691ea7ed90165cfab6dec3950.tar.bz2
spack-17b13b161b3ddcd691ea7ed90165cfab6dec3950.tar.xz
spack-17b13b161b3ddcd691ea7ed90165cfab6dec3950.zip
Directive inheritance: laziness for the win (#2623)
* inheritance of directives: using meta-classes to inject attributes coming from directives into packages + lazy directives * _dep_types -> dependency_types * using a meta-class to inject directives into packages * directives are lazy fixes #2466 * directives.py: allows for multiple inheritance. Added blank lines as suggested by @tgamblin * directives.py: added a test for simple inheritance of directives * Minor improvement requested by @tgamblin CMakePackage: importing names from spack.directives directives: wrap __new__ to respect pep8 * Refactoring requested by @tgamblin directives: removed global variables in favor of class variables. Simplified the interface for directives (they return a callable on a package or a list of them).
-rw-r--r--lib/spack/llnl/util/lang.py32
-rw-r--r--lib/spack/spack/build_systems/cmake.py3
-rw-r--r--lib/spack/spack/directives.py381
-rw-r--r--lib/spack/spack/package.py8
-rw-r--r--lib/spack/spack/spec.py2
-rw-r--r--lib/spack/spack/test/mock_packages_test.py2
-rw-r--r--lib/spack/spack/test/packages.py26
-rw-r--r--var/spack/repos/builtin.mock/packages/simple_inheritance/package.py24
-rw-r--r--var/spack/repos/builtin/packages/dealii/package.py1
-rw-r--r--var/spack/repos/builtin/packages/openjpeg/package.py2
-rw-r--r--var/spack/repos/builtin/packages/yaml-cpp/package.py2
11 files changed, 281 insertions, 202 deletions
diff --git a/lib/spack/llnl/util/lang.py b/lib/spack/llnl/util/lang.py
index 253334c416..637d18cd63 100644
--- a/lib/spack/llnl/util/lang.py
+++ b/lib/spack/llnl/util/lang.py
@@ -342,38 +342,6 @@ def match_predicate(*args):
return match
-def DictWrapper(dictionary):
- """Returns a class that wraps a dictionary and enables it to be used
- like an object."""
- class wrapper(object):
-
- def __getattr__(self, name):
- return dictionary[name]
-
- def __setattr__(self, name, value):
- dictionary[name] = value
-
- def setdefault(self, *args):
- return dictionary.setdefault(*args)
-
- def get(self, *args):
- return dictionary.get(*args)
-
- def keys(self):
- return dictionary.keys()
-
- def values(self):
- return dictionary.values()
-
- def items(self):
- return dictionary.items()
-
- def __iter__(self):
- return iter(dictionary)
-
- return wrapper()
-
-
def dedupe(sequence):
"""Yields a stable de-duplication of an hashable sequence
diff --git a/lib/spack/spack/build_systems/cmake.py b/lib/spack/spack/build_systems/cmake.py
index cb1076d7b7..e097f7e44f 100644
--- a/lib/spack/spack/build_systems/cmake.py
+++ b/lib/spack/spack/build_systems/cmake.py
@@ -30,6 +30,7 @@ import platform
import llnl.util.tty as tty
import spack.build_environment
from llnl.util.filesystem import working_dir, join_path
+from spack.directives import depends_on
from spack.package import PackageBase
@@ -49,6 +50,8 @@ class CMakePackage(PackageBase):
# build-system class we are using
build_system_class = 'CMakePackage'
+ depends_on('cmake', type='build')
+
def build_type(self):
"""Override to provide the correct build_type in case a complex
logic is needed
diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py
index 9cf00334a8..d499e96513 100644
--- a/lib/spack/spack/directives.py
+++ b/lib/spack/spack/directives.py
@@ -46,129 +46,181 @@ The available directives are:
"""
-import re
-import os.path
+import collections
import functools
+import inspect
+import os.path
+import re
-from llnl.util.lang import *
-from llnl.util.filesystem import join_path
-
+import llnl.util.lang
import spack
-import spack.spec
import spack.error
+import spack.spec
import spack.url
-from spack.version import Version
+from llnl.util.filesystem import join_path
+from spack.fetch_strategy import from_kwargs
from spack.patch import Patch
-from spack.variant import Variant
-from spack.spec import Spec, parse_anonymous_spec
from spack.resource import Resource
-from spack.fetch_strategy import from_kwargs
-
-__all__ = ['depends_on', 'extends', 'provides', 'patch', 'version', 'variant',
- 'resource']
-
-#
-# This is a list of all directives, built up as they are defined in
-# this file.
-#
-directives = {}
-
-
-def ensure_dicts(pkg):
- """Ensure that a package has all the dicts required by directives."""
- for name, d in directives.items():
- d.ensure_dicts(pkg)
-
-
-class directive(object):
- """Decorator for Spack directives.
-
- Spack directives allow you to modify a package while it is being
- defined, e.g. to add version or dependency information. Directives
- are one of the key pieces of Spack's package "language", which is
- embedded in python.
-
- Here's an example directive:
-
- @directive(dicts='versions')
- version(pkg, ...):
- ...
-
- This directive allows you write:
-
- class Foo(Package):
- version(...)
-
- The ``@directive`` decorator handles a couple things for you:
-
- 1. Adds the class scope (pkg) as an initial parameter when
- called, like a class method would. This allows you to modify
- a package from within a directive, while the package is still
- being defined.
-
- 2. It automatically adds a dictionary called "versions" to the
- package so that you can refer to pkg.versions.
+from spack.spec import Spec, parse_anonymous_spec
+from spack.variant import Variant
+from spack.version import Version
- The ``(dicts='versions')`` part ensures that ALL packages in Spack
- will have a ``versions`` attribute after they're constructed, and
- that if no directive actually modified it, it will just be an
- empty dict.
+__all__ = []
- This is just a modular way to add storage attributes to the
- Package class, and it's how Spack gets information from the
- packages to the core.
+class DirectiveMetaMixin(type):
+ """Flushes the directives that were temporarily stored in the staging
+ area into the package.
"""
- def __init__(self, dicts=None):
+ # Set of all known directives
+ _directive_names = set()
+ _directives_to_be_executed = []
+
+ def __new__(mcs, name, bases, attr_dict):
+ # Initialize the attribute containing the list of directives
+ # to be executed. Here we go reversed because we want to execute
+ # commands:
+ # 1. in the order they were defined
+ # 2. following the MRO
+ attr_dict['_directives_to_be_executed'] = []
+ for base in reversed(bases):
+ try:
+ directive_from_base = base._directives_to_be_executed
+ attr_dict['_directives_to_be_executed'].extend(
+ directive_from_base
+ )
+ except AttributeError:
+ # The base class didn't have the required attribute.
+ # Continue searching
+ pass
+ # De-duplicates directives from base classes
+ attr_dict['_directives_to_be_executed'] = [
+ x for x in llnl.util.lang.dedupe(
+ attr_dict['_directives_to_be_executed']
+ )
+ ]
+
+ # Move things to be executed from module scope (where they
+ # are collected first) to class scope
+ if DirectiveMetaMixin._directives_to_be_executed:
+ attr_dict['_directives_to_be_executed'].extend(
+ DirectiveMetaMixin._directives_to_be_executed
+ )
+ DirectiveMetaMixin._directives_to_be_executed = []
+
+ return super(DirectiveMetaMixin, mcs).__new__(
+ mcs, name, bases, attr_dict
+ )
+
+ def __init__(cls, name, bases, attr_dict):
+ # The class is being created: if it is a package we must ensure
+ # that the directives are called on the class to set it up
+ module = inspect.getmodule(cls)
+ if 'spack.pkg' in module.__name__:
+ # Package name as taken
+ # from llnl.util.lang.get_calling_module_name
+ pkg_name = module.__name__.split('.')[-1]
+ setattr(cls, 'name', pkg_name)
+ # Ensure the presence of the dictionaries associated
+ # with the directives
+ for d in DirectiveMetaMixin._directive_names:
+ setattr(cls, d, {})
+ # Lazy execution of directives
+ for command in cls._directives_to_be_executed:
+ command(cls)
+
+ super(DirectiveMetaMixin, cls).__init__(name, bases, attr_dict)
+
+ @staticmethod
+ def directive(dicts=None):
+ """Decorator for Spack directives.
+
+ Spack directives allow you to modify a package while it is being
+ defined, e.g. to add version or dependency information. Directives
+ are one of the key pieces of Spack's package "language", which is
+ embedded in python.
+
+ Here's an example directive:
+
+ @directive(dicts='versions')
+ version(pkg, ...):
+ ...
+
+ This directive allows you write:
+
+ class Foo(Package):
+ version(...)
+
+ The ``@directive`` decorator handles a couple things for you:
+
+ 1. Adds the class scope (pkg) as an initial parameter when
+ called, like a class method would. This allows you to modify
+ a package from within a directive, while the package is still
+ being defined.
+
+ 2. It automatically adds a dictionary called "versions" to the
+ package so that you can refer to pkg.versions.
+
+ The ``(dicts='versions')`` part ensures that ALL packages in Spack
+ will have a ``versions`` attribute after they're constructed, and
+ that if no directive actually modified it, it will just be an
+ empty dict.
+
+ This is just a modular way to add storage attributes to the
+ Package class, and it's how Spack gets information from the
+ packages to the core.
+
+ """
+ global __all__
+
if isinstance(dicts, basestring):
dicts = (dicts, )
- elif type(dicts) not in (list, tuple):
- raise TypeError(
- "dicts arg must be list, tuple, or string. Found %s" %
- type(dicts))
+ if not isinstance(dicts, collections.Sequence):
+ message = "dicts arg must be list, tuple, or string. Found {0}"
+ raise TypeError(message.format(type(dicts)))
+ # Add the dictionary names if not already there
+ DirectiveMetaMixin._directive_names |= set(dicts)
- self.dicts = dicts
+ # This decorator just returns the directive functions
+ def _decorator(decorated_function):
+ __all__.append(decorated_function.__name__)
- def ensure_dicts(self, pkg):
- """Ensure that a package has the dicts required by this directive."""
- for d in self.dicts:
- if not hasattr(pkg, d):
- setattr(pkg, d, {})
+ @functools.wraps(decorated_function)
+ def _wrapper(*args, **kwargs):
+ # A directive returns either something that is callable on a
+ # package or a sequence of them
+ values = decorated_function(*args, **kwargs)
- attr = getattr(pkg, d)
- if not isinstance(attr, dict):
- raise spack.error.SpackError(
- "Package %s has non-dict %s attribute!" % (pkg, d))
+ # ...so if it is not a sequence make it so
+ if not isinstance(values, collections.Sequence):
+ values = (values, )
- def __call__(self, directive_function):
- directives[directive_function.__name__] = self
+ DirectiveMetaMixin._directives_to_be_executed.extend(values)
+ return _wrapper
- @functools.wraps(directive_function)
- def wrapped(*args, **kwargs):
- pkg = DictWrapper(caller_locals())
- self.ensure_dicts(pkg)
+ return _decorator
- pkg.name = get_calling_module_name()
- return directive_function(pkg, *args, **kwargs)
- return wrapped
+directive = DirectiveMetaMixin.directive
@directive('versions')
-def version(pkg, ver, checksum=None, **kwargs):
+def version(ver, checksum=None, **kwargs):
"""Adds a version and metadata describing how to fetch it.
- Metadata is just stored as a dict in the package's versions
- dictionary. Package must turn it into a valid fetch strategy
- later.
+ Metadata is just stored as a dict in the package's versions
+ dictionary. Package must turn it into a valid fetch strategy
+ later.
"""
- # TODO: checksum vs md5 distinction is confusing -- fix this.
- # special case checksum for backward compatibility
- if checksum:
- kwargs['md5'] = checksum
+ def _execute(pkg):
+ # TODO: checksum vs md5 distinction is confusing -- fix this.
+ # special case checksum for backward compatibility
+ if checksum:
+ kwargs['md5'] = checksum
- # Store kwargs for the package to later with a fetch_strategy.
- pkg.versions[Version(ver)] = kwargs
+ # Store kwargs for the package to later with a fetch_strategy.
+ pkg.versions[Version(ver)] = kwargs
+ return _execute
def _depends_on(pkg, spec, when=None, type=None):
@@ -199,7 +251,7 @@ def _depends_on(pkg, spec, when=None, type=None):
if pkg.name == dep_spec.name:
raise CircularReferenceError('depends_on', pkg.name)
- pkg_deptypes = pkg._deptypes.setdefault(dep_spec.name, set())
+ pkg_deptypes = pkg.dependency_types.setdefault(dep_spec.name, set())
for deptype in type:
pkg_deptypes.add(deptype)
@@ -210,17 +262,19 @@ def _depends_on(pkg, spec, when=None, type=None):
conditions[when_spec] = dep_spec
-@directive(('dependencies', '_deptypes'))
-def depends_on(pkg, spec, when=None, type=None):
+@directive(('dependencies', 'dependency_types'))
+def depends_on(spec, when=None, type=None):
"""Creates a dict of deps with specs defining when they apply.
This directive is to be used inside a Package definition to declare
that the package requires other packages to be built first.
@see The section "Dependency specs" in the Spack Packaging Guide."""
- _depends_on(pkg, spec, when=when, type=type)
+ def _execute(pkg):
+ _depends_on(pkg, spec, when=when, type=type)
+ return _execute
-@directive(('extendees', 'dependencies', '_deptypes'))
-def extends(pkg, spec, **kwargs):
+@directive(('extendees', 'dependencies', 'dependency_types'))
+def extends(spec, **kwargs):
"""Same as depends_on, but dependency is symlinked into parent prefix.
This is for Python and other language modules where the module
@@ -234,63 +288,69 @@ def extends(pkg, spec, **kwargs):
mechanism.
"""
- if pkg.extendees:
- raise DirectiveError("Packages can extend at most one other package.")
+ def _execute(pkg):
+ if pkg.extendees:
+ msg = 'Packages can extend at most one other package.'
+ raise DirectiveError(msg)
- when = kwargs.pop('when', pkg.name)
- _depends_on(pkg, spec, when=when)
- pkg.extendees[spec] = (Spec(spec), kwargs)
+ when = kwargs.pop('when', pkg.name)
+ _depends_on(pkg, spec, when=when)
+ pkg.extendees[spec] = (Spec(spec), kwargs)
+ return _execute
@directive('provided')
-def provides(pkg, *specs, **kwargs):
+def provides(*specs, **kwargs):
"""Allows packages to provide a virtual dependency. If a package provides
'mpi', other packages can declare that they depend on "mpi", and spack
can use the providing package to satisfy the dependency.
"""
- spec_string = kwargs.get('when', pkg.name)
- provider_spec = parse_anonymous_spec(spec_string, pkg.name)
+ def _execute(pkg):
+ spec_string = kwargs.get('when', pkg.name)
+ provider_spec = parse_anonymous_spec(spec_string, pkg.name)
- for string in specs:
- for provided_spec in spack.spec.parse(string):
- if pkg.name == provided_spec.name:
- raise CircularReferenceError('depends_on', pkg.name)
- pkg.provided[provided_spec] = provider_spec
+ for string in specs:
+ for provided_spec in spack.spec.parse(string):
+ if pkg.name == provided_spec.name:
+ raise CircularReferenceError('depends_on', pkg.name)
+ pkg.provided[provided_spec] = provider_spec
+ return _execute
@directive('patches')
-def patch(pkg, url_or_filename, level=1, when=None, **kwargs):
+def patch(url_or_filename, level=1, when=None, **kwargs):
"""Packages can declare patches to apply to source. You can
- optionally provide a when spec to indicate that a particular
- patch should only be applied when the package's spec meets
- certain conditions (e.g. a particular version).
+ optionally provide a when spec to indicate that a particular
+ patch should only be applied when the package's spec meets
+ certain conditions (e.g. a particular version).
"""
- if when is None:
- when = pkg.name
- when_spec = parse_anonymous_spec(when, pkg.name)
- cur_patches = pkg.patches.setdefault(when_spec, [])
- # if this spec is identical to some other, then append this
- # patch to the existing list.
- cur_patches.append(Patch.create(pkg, url_or_filename, level, **kwargs))
+ def _execute(pkg):
+ constraint = pkg.name if when is None else when
+ when_spec = parse_anonymous_spec(constraint, pkg.name)
+ cur_patches = pkg.patches.setdefault(when_spec, [])
+ # if this spec is identical to some other, then append this
+ # patch to the existing list.
+ cur_patches.append(Patch.create(pkg, url_or_filename, level, **kwargs))
+ return _execute
@directive('variants')
-def variant(pkg, name, default=False, description=""):
+def variant(name, default=False, description=""):
"""Define a variant for the package. Packager can specify a default
value (on or off) as well as a text description."""
-
- default = default
description = str(description).strip()
- if not re.match(spack.spec.identifier_re, name):
- raise DirectiveError("Invalid variant name in %s: '%s'" %
- (pkg.name, name))
+ def _execute(pkg):
+ if not re.match(spack.spec.identifier_re, name):
+ msg = 'Invalid variant name in {0}: \'{1}\''
+ raise DirectiveError(msg.format(pkg.name, name))
- pkg.variants[name] = Variant(default, description)
+ pkg.variants[name] = Variant(default, description)
+ return _execute
@directive('resources')
-def resource(pkg, **kwargs):
+def resource(**kwargs):
"""Define an external resource to be fetched and staged when building the
package. Based on the keywords present in the dictionary the appropriate
FetchStrategy will be used for the resource. Resources are fetched and
@@ -306,29 +366,36 @@ def resource(pkg, **kwargs):
* 'placement' : (optional) gives the possibility to fine tune how the
resource is moved into the main package stage area.
"""
- when = kwargs.get('when', pkg.name)
- destination = kwargs.get('destination', "")
- placement = kwargs.get('placement', None)
- # Check if the path is relative
- if os.path.isabs(destination):
- message = "The destination keyword of a resource directive can't be"
- " an absolute path.\n"
- message += "\tdestination : '{dest}\n'".format(dest=destination)
- raise RuntimeError(message)
- # Check if the path falls within the main package stage area
- test_path = 'stage_folder_root'
- normalized_destination = os.path.normpath(join_path(test_path, destination)
- ) # Normalized absolute path
- if test_path not in normalized_destination:
- message = "The destination folder of a resource must fall within the"
- " main package stage directory.\n"
- message += "\tdestination : '{dest}'\n".format(dest=destination)
- raise RuntimeError(message)
- when_spec = parse_anonymous_spec(when, pkg.name)
- resources = pkg.resources.setdefault(when_spec, [])
- name = kwargs.get('name')
- fetcher = from_kwargs(**kwargs)
- resources.append(Resource(name, fetcher, destination, placement))
+ def _execute(pkg):
+ when = kwargs.get('when', pkg.name)
+ destination = kwargs.get('destination', "")
+ placement = kwargs.get('placement', None)
+
+ # Check if the path is relative
+ if os.path.isabs(destination):
+ message = 'The destination keyword of a resource directive '
+ 'can\'t be an absolute path.\n'
+ message += "\tdestination : '{dest}\n'".format(dest=destination)
+ raise RuntimeError(message)
+
+ # Check if the path falls within the main package stage area
+ test_path = 'stage_folder_root'
+ normalized_destination = os.path.normpath(
+ join_path(test_path, destination)
+ ) # Normalized absolute path
+
+ if test_path not in normalized_destination:
+ message = "The destination folder of a resource must fall "
+ "within the main package stage directory.\n"
+ message += "\tdestination : '{dest}'\n".format(dest=destination)
+ raise RuntimeError(message)
+
+ when_spec = parse_anonymous_spec(when, pkg.name)
+ resources = pkg.resources.setdefault(when_spec, [])
+ name = kwargs.get('name')
+ fetcher = from_kwargs(**kwargs)
+ resources.append(Resource(name, fetcher, destination, placement))
+ return _execute
class DirectiveError(spack.error.SpackError):
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 0c924cfcbc..f9bc1fafbc 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -134,7 +134,7 @@ class InstallPhase(object):
return other
-class PackageMeta(type):
+class PackageMeta(spack.directives.DirectiveMetaMixin):
"""Conveniently transforms attributes to permit extensible phases
Iterates over the attribute 'phases' and creates / updates private
@@ -232,10 +232,6 @@ class PackageMeta(type):
_append_checks('sanity_checks')
return super(PackageMeta, meta).__new__(meta, name, bases, attr_dict)
- def __init__(cls, name, bases, dict):
- type.__init__(cls, name, bases, dict)
- spack.directives.ensure_dicts(cls)
-
class PackageBase(object):
"""This is the superclass for all spack packages.
@@ -781,7 +777,7 @@ class PackageBase(object):
def dependencies_of_type(self, *deptypes):
"""Get subset of the dependencies with certain types."""
return dict((name, conds) for name, conds in self.dependencies.items()
- if any(d in self._deptypes[name] for d in deptypes))
+ if any(d in self.dependency_types[name] for d in deptypes))
@property
def extendee_spec(self):
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 8230142d43..5eed6c71c6 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1762,7 +1762,7 @@ class Spec(object):
for dep_name in pkg.dependencies:
# Do we depend on dep_name? If so pkg_dep is not None.
pkg_dep = self._evaluate_dependency_conditions(dep_name)
- deptypes = pkg._deptypes[dep_name]
+ deptypes = pkg.dependency_types[dep_name]
# If pkg_dep is a dependency, merge it.
if pkg_dep:
changed |= self._merge_dependency(
diff --git a/lib/spack/spack/test/mock_packages_test.py b/lib/spack/spack/test/mock_packages_test.py
index eab96165e0..69c52c2f53 100644
--- a/lib/spack/spack/test/mock_packages_test.py
+++ b/lib/spack/spack/test/mock_packages_test.py
@@ -255,7 +255,7 @@ class MockPackagesTest(unittest.TestCase):
# Change dep spec
# XXX(deptype): handle deptypes.
pkg.dependencies[spec.name] = {Spec(pkg_name): spec}
- pkg._deptypes[spec.name] = set(deptypes)
+ pkg.dependency_types[spec.name] = set(deptypes)
def cleanmock(self):
"""Restore the real packages path after any test."""
diff --git a/lib/spack/spack/test/packages.py b/lib/spack/spack/test/packages.py
index fdd079a8f7..1217568c9c 100644
--- a/lib/spack/spack/test/packages.py
+++ b/lib/spack/spack/test/packages.py
@@ -27,6 +27,7 @@ from llnl.util.filesystem import join_path
from spack.repository import Repo
from spack.test.mock_packages_test import *
from spack.util.naming import mod_to_class
+from spack.spec import *
class PackagesTest(MockPackagesTest):
@@ -89,3 +90,28 @@ class PackagesTest(MockPackagesTest):
import spack.pkg.builtin.mock # noqa
import spack.pkg.builtin.mock as m # noqa
from spack.pkg.builtin import mock # noqa
+
+ def test_inheritance_of_diretives(self):
+ p = spack.repo.get('simple_inheritance')
+
+ # Check dictionaries that should have been filled by directives
+ self.assertEqual(len(p.dependencies), 3)
+ self.assertTrue('cmake' in p.dependencies)
+ self.assertTrue('openblas' in p.dependencies)
+ self.assertTrue('mpi' in p.dependencies)
+ self.assertEqual(len(p.provided), 2)
+
+ # Check that Spec instantiation behaves as we expect
+ s = Spec('simple_inheritance')
+ s.concretize()
+ self.assertTrue('^cmake' in s)
+ self.assertTrue('^openblas' in s)
+ self.assertTrue('+openblas' in s)
+ self.assertTrue('mpi' in s)
+
+ s = Spec('simple_inheritance~openblas')
+ s.concretize()
+ self.assertTrue('^cmake' in s)
+ self.assertTrue('^openblas' not in s)
+ self.assertTrue('~openblas' in s)
+ self.assertTrue('mpi' in s)
diff --git a/var/spack/repos/builtin.mock/packages/simple_inheritance/package.py b/var/spack/repos/builtin.mock/packages/simple_inheritance/package.py
new file mode 100644
index 0000000000..3f135b002e
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/simple_inheritance/package.py
@@ -0,0 +1,24 @@
+from spack import *
+
+
+class BaseWithDirectives(Package):
+
+ depends_on('cmake', type='build')
+ depends_on('mpi')
+ variant('openblas', description='Activates openblas', default=True)
+ provides('service1')
+
+
+class SimpleInheritance(BaseWithDirectives):
+ """Simple package which acts as a build dependency"""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/simple-1.0.tar.gz"
+
+ version('1.0', '0123456789abcdef0123456789abcdef')
+
+ depends_on('openblas', when='+openblas')
+ provides('lapack', when='+openblas')
+
+ def install(self, spec, prefix):
+ pass
diff --git a/var/spack/repos/builtin/packages/dealii/package.py b/var/spack/repos/builtin/packages/dealii/package.py
index 0207f755f7..8c31b90f51 100644
--- a/var/spack/repos/builtin/packages/dealii/package.py
+++ b/var/spack/repos/builtin/packages/dealii/package.py
@@ -90,7 +90,6 @@ class Dealii(CMakePackage):
"boost@1.59.0:+thread+system+serialization+iostreams+mpi+python",
when='@8.5.0:+mpi+python')
depends_on("bzip2")
- depends_on("cmake", type='build')
depends_on("lapack")
depends_on("muparser")
depends_on("suite-sparse")
diff --git a/var/spack/repos/builtin/packages/openjpeg/package.py b/var/spack/repos/builtin/packages/openjpeg/package.py
index 988ebc155d..b22de4452a 100644
--- a/var/spack/repos/builtin/packages/openjpeg/package.py
+++ b/var/spack/repos/builtin/packages/openjpeg/package.py
@@ -43,5 +43,3 @@ class Openjpeg(CMakePackage):
version('2.0', 'cdf266530fee8af87454f15feb619609')
version('1.5.2', '545f98923430369a6b046ef3632ef95c')
version('1.5.1', 'd774e4b5a0db5f0f171c4fc0aabfa14e')
-
- depends_on('cmake', type='build')
diff --git a/var/spack/repos/builtin/packages/yaml-cpp/package.py b/var/spack/repos/builtin/packages/yaml-cpp/package.py
index a672bbca97..0ced7c89fc 100644
--- a/var/spack/repos/builtin/packages/yaml-cpp/package.py
+++ b/var/spack/repos/builtin/packages/yaml-cpp/package.py
@@ -36,8 +36,6 @@ class YamlCpp(CMakePackage):
variant('fpic', default=False,
description='Build with position independent code')
- depends_on('cmake', type='build')
-
depends_on('boost', when='@:0.5.3')
def cmake_args(self):