Concretize preserves deptypes (#2681)

Concretization preserves deptypes
This commit is contained in:
Todd Gamblin 2016-12-29 14:43:59 -08:00 committed by GitHub
parent d6390c159f
commit 5fbab1f4b5
11 changed files with 478 additions and 180 deletions

View file

@ -158,23 +158,22 @@
# for packages.
#
#-----------------------------------------------------------------------------
__all__ = ['PackageBase',
'Package',
'CMakePackage',
'AutotoolsPackage',
'MakefilePackage',
'Version',
'when',
'ver',
'alldeps',
'nolink']
from spack.package import Package, PackageBase, ExtensionConflictError
__all__ = []
from spack.package import Package
from spack.build_systems.makefile import MakefilePackage
from spack.build_systems.autotools import AutotoolsPackage
from spack.build_systems.cmake import CMakePackage
__all__ += ['Package', 'CMakePackage', 'AutotoolsPackage', 'MakefilePackage']
from spack.version import Version, ver
from spack.spec import DependencySpec, alldeps, nolink
__all__ += ['Version', 'ver']
from spack.spec import Spec, alldeps, nolink
__all__ += ['Spec', 'alldeps', 'nolink']
from spack.multimethod import when
__all__ += ['when']
import llnl.util.filesystem
from llnl.util.filesystem import *

View file

@ -84,7 +84,7 @@ def graph(parser, args):
setup_parser.parser.print_help()
return 1
deptype = nobuild
deptype = alldeps
if args.deptype:
deptype = tuple(args.deptype.split(','))
validate_deptype(deptype)

View file

@ -90,7 +90,7 @@ def topological_sort(spec, reverse=False, deptype=None):
# Work on a copy so this is nondestructive.
spec = spec.copy(deps=deptype)
nodes = spec.index()
nodes = spec.index(deptype=deptype)
topo_order = []
par = dict((name, parents(nodes[name])) for name in nodes.keys())

View file

@ -560,43 +560,47 @@ def __repr__(self):
@key_ordering
class DependencySpec(object):
"""Dependencies can be one (or more) of several types:
"""DependencySpecs connect two nodes in the DAG, and contain deptypes.
Dependencies can be one (or more) of several types:
- build: needs to be in the PATH at build time.
- link: is linked to and added to compiler flags.
- run: needs to be in the PATH for the package to run.
Fields:
- spec: the spack.spec.Spec description of a dependency.
- deptypes: strings representing the type of dependency this is.
- spec: Spec depended on by parent.
- parent: Spec that depends on `spec`.
- deptypes: list of strings, representing dependency relationships.
"""
def __init__(self, spec, deptypes, default_deptypes=False):
def __init__(self, parent, spec, deptypes):
self.parent = parent
self.spec = spec
self.deptypes = deptypes
self.default_deptypes = default_deptypes
self.deptypes = tuple(sorted(set(deptypes)))
def update_deptypes(self, deptypes):
if self.default_deptypes:
self.deptypes = deptypes
self.default_deptypes = False
return True
return False
def _cmp_key(self):
return self.spec
deptypes = tuple(sorted(set(deptypes)))
changed = self.deptypes != deptypes
self.deptypes = deptypes
return changed
def copy(self):
return DependencySpec(self.spec.copy(), self.deptype,
self.default_deptypes)
return DependencySpec(self.parent, self.spec, self.deptypes)
def _cmp_key(self):
return (self.parent.name if self.parent else None,
self.spec.name if self.spec else None,
self.deptypes)
def __str__(self):
return str(self.spec)
return "%s %s--> %s" % (self.parent.name if self.parent else None,
self.deptypes,
self.spec.name if self.spec else None)
@key_ordering
class VariantSpec(object):
"""Variants are named, build-time options for a package. Names depend
on the particular package being built, and each named variant can
be enabled or disabled.
@ -742,11 +746,11 @@ class DependencyMap(HashableMap):
The DependencyMap is keyed by name. """
@property
def concrete(self):
return all(d.spec.concrete for d in self.values())
return all((d.spec.concrete and d.deptypes)
for d in self.values())
def __str__(self):
return ''.join(
["^" + self[name].format() for name in sorted(self.keys())])
return "{deps: %s}" % ', '.join(str(d) for d in sorted(self.values()))
@key_ordering
@ -801,10 +805,21 @@ def __init__(self, spec_like, *dep_like, **kwargs):
# This allows users to construct a spec DAG with literals.
# Note that given two specs a and b, Spec(a) copies a, but
# Spec(a, b) will copy a but just add b as a dep.
deptypes = ()
for dep in dep_like:
if isinstance(dep, Spec):
spec = dep
elif isinstance(dep, (list, tuple)):
# Literals can be deptypes -- if there are tuples in the
# list, they will be used as deptypes for the following Spec.
deptypes = tuple(dep)
continue
else:
spec = Spec(dep)
spec = dep if isinstance(dep, Spec) else Spec(dep)
self._add_dependency(
spec, ('build', 'link'), default_deptypes=True)
self._add_dependency(spec, deptypes)
deptypes = ()
def __getattr__(self, item):
"""Delegate to self.package if the attribute is not in the spec"""
@ -812,7 +827,7 @@ def __getattr__(self, item):
# not present among self attributes
if item.endswith('libs'):
return getattr(self.package, item)
raise AttributeError()
raise AttributeError(item)
def get_dependency(self, name):
dep = self._dependencies.get(name)
@ -824,28 +839,25 @@ def get_dependency(self, name):
def _find_deps(self, where, deptype):
deptype = canonical_deptype(deptype)
return [dep.spec
for dep in where.values()
if deptype and any(d in deptype for d in dep.deptypes)]
return [dep for dep in where.values()
if deptype and (not dep.deptypes or
any(d in deptype for d in dep.deptypes))]
def dependencies(self, deptype=None):
return self._find_deps(self._dependencies, deptype)
return [d.spec
for d in self._find_deps(self._dependencies, deptype)]
def dependents(self, deptype=None):
return self._find_deps(self._dependents, deptype)
def _find_deps_dict(self, where, deptype):
deptype = canonical_deptype(deptype)
return dict((dep.spec.name, dep)
for dep in where.values()
if deptype and any(d in deptype for d in dep.deptypes))
return [d.parent
for d in self._find_deps(self._dependents, deptype)]
def dependencies_dict(self, deptype=None):
return self._find_deps_dict(self._dependencies, deptype)
return dict((d.spec.name, d)
for d in self._find_deps(self._dependencies, deptype))
def dependents_dict(self, deptype=None):
return self._find_deps_dict(self._dependents, deptype)
return dict((d.parent.name, d)
for d in self._find_deps(self._dependents, deptype))
#
# Private routines here are called by the parser when building a spec.
@ -914,15 +926,16 @@ def _set_compiler(self, compiler):
"Spec for '%s' cannot have two compilers." % self.name)
self.compiler = compiler
def _add_dependency(self, spec, deptypes, default_deptypes=False):
def _add_dependency(self, spec, deptypes):
"""Called by the parser to add another spec as a dependency."""
if spec.name in self._dependencies:
raise DuplicateDependencyError(
"Cannot depend on '%s' twice" % spec)
self._dependencies[spec.name] = DependencySpec(
spec, deptypes, default_deptypes)
spec._dependents[self.name] = DependencySpec(
self, deptypes, default_deptypes)
# create an edge and add to parent and child
dspec = DependencySpec(self, spec, deptypes)
self._dependencies[spec.name] = dspec
spec._dependents[self.name] = dspec
#
# Public interface
@ -947,8 +960,8 @@ def root(self):
# lead to the same place. Spack shouldn't deal with any DAGs
# with multiple roots, so something's wrong if we find one.
depiter = iter(self._dependents.values())
first_root = next(depiter).spec.root
assert(all(first_root is d.spec.root for d in depiter))
first_root = next(depiter).parent.root
assert(all(first_root is d.parent.root for d in depiter))
return first_root
@property
@ -998,18 +1011,23 @@ def concrete(self):
self._dependencies.concrete)
return self._concrete
def traverse(self, visited=None, deptype=None, **kwargs):
traversal = self.traverse_with_deptype(visited=visited,
deptype=deptype,
**kwargs)
if kwargs.get('depth', False):
return [(s[0], s[1].spec) for s in traversal]
else:
return [s.spec for s in traversal]
def traverse(self, **kwargs):
direction = kwargs.get('direction', 'children')
depth = kwargs.get('depth', False)
def traverse_with_deptype(self, visited=None, d=0, deptype=None,
deptype_query=None, _self_deptype=None,
_self_default_deptypes=False, **kwargs):
get_spec = lambda s: s.spec
if direction == 'parents':
get_spec = lambda s: s.parent
if depth:
for d, dspec in self.traverse_edges(**kwargs):
yield d, get_spec(dspec)
else:
for dspec in self.traverse_edges(**kwargs):
yield get_spec(dspec)
def traverse_edges(self, visited=None, d=0, deptype=None,
deptype_query=None, dep_spec=None, **kwargs):
"""Generic traversal of the DAG represented by this spec.
This will yield each node in the spec. Options:
@ -1061,9 +1079,7 @@ def traverse_with_deptype(self, visited=None, d=0, deptype=None,
direction = kwargs.get('direction', 'children')
order = kwargs.get('order', 'pre')
if deptype is None:
deptype = alldeps
deptype = canonical_deptype(deptype)
if deptype_query is None:
deptype_query = ('link', 'run')
@ -1084,42 +1100,49 @@ def validate(name, val, allowed_values):
if key in visited and cover == 'nodes':
return
def return_val(res):
return (d, res) if depth else res
def return_val(dspec):
if not dspec:
# make a fake dspec for the root.
if direction == 'parents':
dspec = DependencySpec(self, None, ())
else:
dspec = DependencySpec(None, self, ())
return (d, dspec) if depth else dspec
yield_me = yield_root or d > 0
# Preorder traversal yields before successors
if yield_me and order == 'pre':
yield return_val(
DependencySpec(self, _self_deptype, _self_default_deptypes))
deps = self.dependencies_dict(deptype)
yield return_val(dep_spec)
# Edge traversal yields but skips children of visited nodes
if not (key in visited and cover == 'edges'):
# This code determines direction and yields the children/parents
successors = deps
if direction == 'parents':
successors = self.dependents_dict() # TODO: deptype?
if direction == 'children':
successors = self.dependencies_dict(deptype)
succ = lambda s: s.spec
elif direction == 'parents':
successors = self.dependents_dict(deptype)
succ = lambda s: s.parent
else:
raise ValueError('Invalid traversal direction: %s' % direction)
visited.add(key)
for name in sorted(successors):
for name, dspec in sorted(successors.items()):
child = successors[name]
children = child.spec.traverse_with_deptype(
visited, d=d + 1, deptype=deptype,
children = succ(child).traverse_edges(
visited,
d=(d + 1),
deptype=deptype,
deptype_query=deptype_query,
_self_deptype=child.deptypes,
_self_default_deptypes=child.default_deptypes,
dep_spec=dspec,
**kwargs)
for elt in children:
yield elt
# Postorder traversal yields after successors
if yield_me and order == 'post':
yield return_val(
DependencySpec(self, _self_deptype, _self_default_deptypes))
yield return_val(dep_spec)
@property
def short_spec(self):
@ -1293,7 +1316,7 @@ def from_dict(data):
for dname, dhash, dtypes in Spec.read_yaml_dep_specs(yaml_deps):
# Fill in dependencies by looking them up by name in deps dict
deps[name]._dependencies[dname] = DependencySpec(
deps[dname], set(dtypes))
deps[name], deps[dname], dtypes)
return spec
@ -1367,7 +1390,7 @@ def _replace_with(self, concrete):
"""Replace this virtual spec with a concrete spec."""
assert(self.virtual)
for name, dep_spec in self._dependents.items():
dependent = dep_spec.spec
dependent = dep_spec.parent
deptypes = dep_spec.deptypes
# remove self from all dependents.
@ -1375,8 +1398,7 @@ def _replace_with(self, concrete):
# add the replacement, unless it is already a dep of dependent.
if concrete.name not in dependent._dependencies:
dependent._add_dependency(concrete, deptypes,
dep_spec.default_deptypes)
dependent._add_dependency(concrete, deptypes)
def _expand_virtual_packages(self):
"""Find virtual packages in this spec, replace them with providers,
@ -1550,13 +1572,6 @@ def concretized(self):
return clone
def flat_dependencies(self, **kwargs):
flat_deps = DependencyMap()
flat_deps_deptypes = self.flat_dependencies_with_deptype(**kwargs)
for name, depspec in flat_deps_deptypes.items():
flat_deps[name] = depspec.spec
return flat_deps
def flat_dependencies_with_deptype(self, **kwargs):
"""Return a DependencyMap containing all of this spec's
dependencies with their constraints merged.
@ -1569,30 +1584,22 @@ def flat_dependencies_with_deptype(self, **kwargs):
copy = kwargs.get('copy', True)
deptype_query = kwargs.get('deptype_query')
flat_deps = DependencyMap()
flat_deps = {}
try:
deptree = self.traverse_with_deptype(root=False,
deptype_query=deptype_query)
for depspec in deptree:
spec = depspec.spec
deptypes = depspec.deptypes
deptree = self.traverse(root=False, deptype_query=deptype_query)
for spec in deptree:
if spec.name not in flat_deps:
if copy:
dep_spec = DependencySpec(spec.copy(deps=False),
deptypes,
depspec.default_deptypes)
else:
dep_spec = DependencySpec(
spec, deptypes, depspec.default_deptypes)
flat_deps[spec.name] = dep_spec
spec = spec.copy(deps=False)
flat_deps[spec.name] = spec
else:
flat_deps[spec.name].spec.constrain(spec)
flat_deps[spec.name].constrain(spec)
if not copy:
for depspec in flat_deps.values():
depspec.spec._dependencies.clear()
depspec.spec._dependents.clear()
for spec in flat_deps.values():
spec._dependencies.clear()
spec._dependents.clear()
self._dependencies.clear()
return flat_deps
@ -1696,9 +1703,7 @@ def _merge_dependency(self, dep, deptypes, visited, spec_deps,
dep = provider
else:
index = ProviderIndex([dep], restrict=True)
for vspec in (v.spec
for v in spec_deps.values()
if v.spec.virtual):
for vspec in (v for v in spec_deps.values() if v.virtual):
if index.providers_for(vspec):
vspec._replace_with(dep)
del spec_deps[vspec.name]
@ -1708,35 +1713,37 @@ def _merge_dependency(self, dep, deptypes, visited, spec_deps,
if required:
raise UnsatisfiableProviderSpecError(required[0], dep)
provider_index.update(dep)
# If the spec isn't already in the set of dependencies, clone
# it from the package description.
if dep.name not in spec_deps:
spec_deps[dep.name] = DependencySpec(dep.copy(), deptypes)
spec_deps[dep.name] = dep.copy()
changed = True
else:
changed = spec_deps[dep.name].update_deptypes(deptypes)
if changed and dep.name in self._dependencies:
child_spec = self._dependencies[dep.name].spec
child_spec._dependents[self.name].update_deptypes(deptypes)
dspec = spec_deps[dep.name]
if self.name not in dspec._dependents:
self._add_dependency(dspec, deptypes)
else:
dependent = dspec._dependents[self.name]
changed = dependent.update_deptypes(deptypes)
# Constrain package information with spec info
try:
changed |= spec_deps[dep.name].spec.constrain(dep)
changed |= spec_deps[dep.name].constrain(dep)
except UnsatisfiableSpecError as e:
e.message = "Invalid spec: '%s'. "
e.message += "Package %s requires %s %s, but spec asked for %s"
e.message %= (spec_deps[dep.name].spec, dep.name,
e.message %= (spec_deps[dep.name], dep.name,
e.constraint_type, e.required, e.provided)
raise e
# Add merged spec to my deps and recurse
dependency = spec_deps[dep.name]
if dep.name not in self._dependencies:
self._add_dependency(
dependency.spec, dependency.deptypes,
dependency.default_deptypes)
self._add_dependency(dependency, deptypes)
changed |= dependency.spec._normalize_helper(
changed |= dependency._normalize_helper(
visited, spec_deps, provider_index)
return changed
@ -1801,17 +1808,17 @@ def normalize(self, force=False):
# Ensure first that all packages & compilers in the DAG exist.
self.validate_names()
# Get all the dependencies into one DependencyMap
spec_deps = self.flat_dependencies_with_deptype(
copy=False, deptype_query=alldeps)
spec_deps = self.flat_dependencies(copy=False, deptype_query=alldeps)
# Initialize index of virtual dependency providers if
# concretize didn't pass us one already
provider_index = ProviderIndex(
[s.spec for s in spec_deps.values()], restrict=True)
[s for s in spec_deps.values()], restrict=True)
# traverse the package DAG and fill out dependencies according
# to package files & their 'when' specs
visited = set()
any_change = self._normalize_helper(visited, spec_deps, provider_index)
# If there are deps specified but not visited, they're not
@ -1945,8 +1952,7 @@ def _constrain_dependencies(self, other):
dep_spec_copy = other.get_dependency(name)
dep_copy = dep_spec_copy.spec
deptypes = dep_spec_copy.deptypes
self._add_dependency(dep_copy.copy(), deptypes,
dep_spec_copy.default_deptypes)
self._add_dependency(dep_copy.copy(), deptypes)
changed = True
return changed
@ -2168,30 +2174,13 @@ def _dup(self, other, deps=True, cleardeps=True):
# If we copy dependencies, preserve DAG structure in the new spec
if deps:
# This copies the deps from other using _dup(deps=False)
deptypes = alldeps
deptypes = alldeps # by default copy all deptypes
# if caller restricted deptypes to be copied, adjust that here.
if isinstance(deps, (tuple, list)):
deptypes = deps
new_nodes = other.flat_dependencies(deptypes=deptypes)
new_nodes[self.name] = self
stack = [other]
while stack:
cur_spec = stack.pop(0)
new_spec = new_nodes[cur_spec.name]
for depspec in cur_spec._dependencies.values():
if not any(d in deptypes for d in depspec.deptypes):
continue
stack.append(depspec.spec)
# XXX(deptype): add any new deptypes that may have appeared
# here.
if depspec.spec.name not in new_spec._dependencies:
new_spec._add_dependency(
new_nodes[depspec.spec.name], depspec.deptypes,
depspec.default_deptypes)
self._dup_deps(other, deptypes)
# These fields are all cached results of expensive operations.
# If we preserved the original structure, we can copy them
@ -2209,6 +2198,21 @@ def _dup(self, other, deps=True, cleardeps=True):
return changed
def _dup_deps(self, other, deptypes):
new_specs = {self.name: self}
for dspec in other.traverse_edges(cover='edges', root=False):
if (dspec.deptypes and
not any(d in deptypes for d in dspec.deptypes)):
continue
if dspec.parent.name not in new_specs:
new_specs[dspec.parent.name] = dspec.parent.copy(deps=False)
if dspec.spec.name not in new_specs:
new_specs[dspec.spec.name] = dspec.spec.copy(deps=False)
new_specs[dspec.parent.name]._add_dependency(
new_specs[dspec.spec.name], dspec.deptypes)
def copy(self, deps=True):
"""Return a copy of this spec.
@ -2267,7 +2271,7 @@ def sorted_deps(self):
deps = self.flat_dependencies()
return tuple(deps[name] for name in sorted(deps))
def _eq_dag(self, other, vs, vo):
def _eq_dag(self, other, vs, vo, deptypes):
"""Recursive helper for eq_dag and ne_dag. Does the actual DAG
traversal."""
vs.add(id(self))
@ -2279,12 +2283,16 @@ def _eq_dag(self, other, vs, vo):
if len(self._dependencies) != len(other._dependencies):
return False
ssorted = [self._dependencies[name].spec
ssorted = [self._dependencies[name]
for name in sorted(self._dependencies)]
osorted = [other._dependencies[name].spec
osorted = [other._dependencies[name]
for name in sorted(other._dependencies)]
for s, o in zip(ssorted, osorted):
for s_dspec, o_dspec in zip(ssorted, osorted):
if deptypes and s_dspec.deptypes != o_dspec.deptypes:
return False
s, o = s_dspec.spec, o_dspec.spec
visited_s = id(s) in vs
visited_o = id(o) in vo
@ -2297,18 +2305,18 @@ def _eq_dag(self, other, vs, vo):
continue
# Recursive check for equality
if not s._eq_dag(o, vs, vo):
if not s._eq_dag(o, vs, vo, deptypes):
return False
return True
def eq_dag(self, other):
"""True if the full dependency DAGs of specs are equal"""
return self._eq_dag(other, set(), set())
def eq_dag(self, other, deptypes=True):
"""True if the full dependency DAGs of specs are equal."""
return self._eq_dag(other, set(), set(), deptypes)
def ne_dag(self, other):
"""True if the full dependency DAGs of specs are not equal"""
return not self.eq_dag(other)
def ne_dag(self, other, deptypes=True):
"""True if the full dependency DAGs of specs are not equal."""
return not self.eq_dag(other, set(), set(), deptypes)
def _cmp_node(self):
"""Comparison key for just *this node* and not its deps."""
@ -2600,7 +2608,7 @@ def tree(self, **kwargs):
check_kwargs(kwargs, self.tree)
out = ""
for d, dep_spec in self.traverse_with_deptype(
for d, dep_spec in self.traverse_edges(
order='pre', cover=cover, depth=True, deptypes=deptypes):
node = dep_spec.spec
@ -2716,9 +2724,10 @@ def do_parse(self):
else:
self.expect(ID)
dep = self.spec(self.token.value)
def_deptypes = ('build', 'link')
specs[-1]._add_dependency(
dep, def_deptypes, default_deptypes=True)
# command line deps get empty deptypes now.
# Real deptypes are assigned later per packages.
specs[-1]._add_dependency(dep, ())
else:
# Attempt to construct an anonymous spec, but check that

View file

@ -124,9 +124,11 @@ def get(self, spec):
def mock_fetch_log(path):
return []
specX = MockSpec('X', '1.2.0')
specY = MockSpec('Y', '2.3.8')
specX._dependencies['Y'] = spack.DependencySpec(specY, spack.alldeps)
specX._dependencies['Y'] = spack.spec.DependencySpec(
specX, specY, spack.alldeps)
pkgX = MockPackage(specX, 'logX')
pkgY = MockPackage(specY, 'logY')
specX.package = pkgX

View file

@ -94,8 +94,7 @@ def test_normalize(spec_and_expected, config, builtin_mock):
spec, expected = spec_and_expected
spec = Spec(spec)
spec.normalize()
assert spec == expected
assert spec.eq_dag(expected)
assert spec.eq_dag(expected, deptypes=False)
def test_default_variant(config, builtin_mock):

View file

@ -373,11 +373,12 @@ def test_normalize_mpileaks(self):
assert spec != expected_flat
assert not spec.eq_dag(expected_flat)
assert spec == expected_normalized
assert spec.eq_dag(expected_normalized)
# verify DAG structure without deptypes.
assert spec.eq_dag(expected_normalized, deptypes=False)
assert not spec.eq_dag(non_unique_nodes, deptypes=False)
assert spec == non_unique_nodes
assert not spec.eq_dag(non_unique_nodes)
assert not spec.eq_dag(expected_normalized, deptypes=True)
assert not spec.eq_dag(non_unique_nodes, deptypes=True)
def test_normalize_with_virtual_package(self):
spec = Spec('mpileaks ^mpi ^libelf@1.8.11 ^libdwarf')
@ -552,3 +553,140 @@ def test_hash_bits(self):
with pytest.raises(ValueError):
spack.spec.base32_prefix_bits(test_hash, 256)
def test_traversal_directions(self):
"""Make sure child and parent traversals of specs work."""
# We'll use d for a diamond dependency
d = Spec('d')
# Mock spec.
spec = Spec('a',
Spec('b',
Spec('c', d),
Spec('e')),
Spec('f',
Spec('g', d)))
assert (
['a', 'b', 'c', 'd', 'e', 'f', 'g'] ==
[s.name for s in spec.traverse(direction='children')])
assert (
['g', 'f', 'a'] ==
[s.name for s in spec['g'].traverse(direction='parents')])
assert (
['d', 'c', 'b', 'a', 'g', 'f'] ==
[s.name for s in spec['d'].traverse(direction='parents')])
def test_edge_traversals(self):
"""Make sure child and parent traversals of specs work."""
# We'll use d for a diamond dependency
d = Spec('d')
# Mock spec.
spec = Spec('a',
Spec('b',
Spec('c', d),
Spec('e')),
Spec('f',
Spec('g', d)))
assert (
['a', 'b', 'c', 'd', 'e', 'f', 'g'] ==
[s.name for s in spec.traverse(direction='children')])
assert (
['g', 'f', 'a'] ==
[s.name for s in spec['g'].traverse(direction='parents')])
assert (
['d', 'c', 'b', 'a', 'g', 'f'] ==
[s.name for s in spec['d'].traverse(direction='parents')])
def test_copy_dependencies(self):
s1 = Spec('mpileaks ^mpich2@1.1')
s2 = s1.copy()
assert '^mpich2@1.1' in s2
assert '^mpich2' in s2
def test_construct_spec_with_deptypes(self):
s = Spec('a',
Spec('b',
['build'], Spec('c')),
Spec('d',
['build', 'link'], Spec('e',
['run'], Spec('f'))))
assert s['b']._dependencies['c'].deptypes == ('build',)
assert s['d']._dependencies['e'].deptypes == ('build', 'link')
assert s['e']._dependencies['f'].deptypes == ('run',)
assert s['b']._dependencies['c'].deptypes == ('build',)
assert s['d']._dependencies['e'].deptypes == ('build', 'link')
assert s['e']._dependencies['f'].deptypes == ('run',)
assert s['c']._dependents['b'].deptypes == ('build',)
assert s['e']._dependents['d'].deptypes == ('build', 'link')
assert s['f']._dependents['e'].deptypes == ('run',)
assert s['c']._dependents['b'].deptypes == ('build',)
assert s['e']._dependents['d'].deptypes == ('build', 'link')
assert s['f']._dependents['e'].deptypes == ('run',)
def check_diamond_deptypes(self, spec):
"""Validate deptypes in dt-diamond spec."""
assert spec['dt-diamond']._dependencies[
'dt-diamond-left'].deptypes == ('build', 'link')
assert spec['dt-diamond']._dependencies[
'dt-diamond-right'].deptypes == ('build', 'link')
assert spec['dt-diamond-left']._dependencies[
'dt-diamond-bottom'].deptypes == ('build',)
assert spec['dt-diamond-right'] ._dependencies[
'dt-diamond-bottom'].deptypes == ('build', 'link', 'run')
def check_diamond_normalized_dag(self, spec):
bottom = Spec('dt-diamond-bottom')
dag = Spec('dt-diamond',
['build', 'link'], Spec('dt-diamond-left',
['build'], bottom),
['build', 'link'], Spec('dt-diamond-right',
['build', 'link', 'run'], bottom))
assert spec.eq_dag(dag)
def test_normalize_diamond_deptypes(self):
"""Ensure that dependency types are preserved even if the same thing is
depended on in two different ways."""
s = Spec('dt-diamond')
s.normalize()
self.check_diamond_deptypes(s)
self.check_diamond_normalized_dag(s)
def test_concretize_deptypes(self):
"""Ensure that dependency types are preserved after concretization."""
s = Spec('dt-diamond')
s.concretize()
self.check_diamond_deptypes(s)
def test_copy_deptypes(self):
"""Ensure that dependency types are preserved by spec copy."""
s1 = Spec('dt-diamond')
s1.normalize()
self.check_diamond_deptypes(s1)
self.check_diamond_normalized_dag(s1)
s2 = s1.copy()
self.check_diamond_normalized_dag(s2)
self.check_diamond_deptypes(s2)
s3 = Spec('dt-diamond')
s3.concretize()
self.check_diamond_deptypes(s3)
s4 = s3.copy()
self.check_diamond_deptypes(s4)

View file

@ -0,0 +1,36 @@
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
class DtDiamondBottom(Package):
"""This package has an indirect diamond dependency on dt-diamond-bottom"""
homepage = "http://www.example.com"
url = "http://www.example.com/dt-diamond-bottom-1.0.tar.gz"
version('1.0', '0123456789abcdef0123456789abcdef')
def install(self, spec, prefix):
pass

View file

@ -0,0 +1,38 @@
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
class DtDiamondLeft(Package):
"""This package has an indirect diamond dependency on dt-diamond-bottom"""
homepage = "http://www.example.com"
url = "http://www.example.com/dt-diamond-left-1.0.tar.gz"
version('1.0', '0123456789abcdef0123456789abcdef')
depends_on('dt-diamond-bottom', type='build')
def install(self, spec, prefix):
pass

View file

@ -0,0 +1,38 @@
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
class DtDiamondRight(Package):
"""This package has an indirect diamond dependency on dt-diamond-bottom"""
homepage = "http://www.example.com"
url = "http://www.example.com/dt-diamond-right-1.0.tar.gz"
version('1.0', '0123456789abcdef0123456789abcdef')
depends_on('dt-diamond-bottom', type=('build', 'link', 'run'))
def install(self, spec, prefix):
pass

View file

@ -0,0 +1,39 @@
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
from spack import *
class DtDiamond(Package):
"""This package has an indirect diamond dependency on dt-diamond-bottom"""
homepage = "http://www.example.com"
url = "http://www.example.com/dt-diamond-1.0.tar.gz"
version('1.0', '0123456789abcdef0123456789abcdef')
depends_on('dt-diamond-left')
depends_on('dt-diamond-right')
def install(self, spec, prefix):
pass