From 45baf73c349465cce9c82db48777f2b8a853caa5 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Wed, 7 May 2014 20:01:12 -0700 Subject: Implemented compiler concretization policy. --- lib/spack/spack/cmd/compilers.py | 2 +- lib/spack/spack/compilers/__init__.py | 29 +++++++++++-------- lib/spack/spack/concretize.py | 34 +++++++++++++--------- lib/spack/spack/spec.py | 53 +++++++++++++++++++++++++---------- lib/spack/spack/test/concretize.py | 14 ++++++++- 5 files changed, 91 insertions(+), 41 deletions(-) diff --git a/lib/spack/spack/cmd/compilers.py b/lib/spack/spack/cmd/compilers.py index 5658263c26..8267ecbd6b 100644 --- a/lib/spack/spack/cmd/compilers.py +++ b/lib/spack/spack/cmd/compilers.py @@ -34,7 +34,7 @@ description = "List available compilers" def compilers(parser, args): tty.msg("Available compilers") - index = index_by(spack.compilers.supported_compilers(), 'name') + index = index_by(spack.compilers.available_compilers(), 'name') for name, compilers in index.items(): tty.hline(name, char='-', color=spack.spec.compiler_color) colify(compilers, indent=4) diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py index 4bfb855d94..2b97cd1036 100644 --- a/lib/spack/spack/compilers/__init__.py +++ b/lib/spack/spack/compilers/__init__.py @@ -27,27 +27,34 @@ # from llnl.util.lang import memoized, list_modules +import spack import spack.spec from spack.util.executable import which - - @memoized def supported_compilers(): - """Return a list of compiler types supported by Spack.""" + """Return a list of names of compilers supported by Spack. + + See available_compilers() to get a list of all the available + versions of supported compilers. + """ return sorted(c for c in list_modules(spack.compilers_path)) -def supported(compiler_spec): - """Test if a particular compiler is supported.""" - if isinstance(compiler_spec, spack.spec.Compiler): - return compiler_spec.name in supported_compilers() +def available_compilers(): + """Return a list of specs for all the compiler versions currently + available to build with. These are instances of + spack.spec.Compiler. + """ + return [spack.spec.Compiler(c) + for c in list_modules(spack.compiler_version_path)] - elif isinstance(compiler_spec, basestring): - return compiler_spec in supported_compilers() - else: - raise TypeError("compiler_spec must be string or spack.spec.Compiler") +def supported(compiler_spec): + """Test if a particular compiler is supported.""" + if not isinstance(compiler_spec, spack.spec.Compiler): + compiler_spec = spack.spec.Compiler(compiler_spec) + return compiler_spec.name in supported_compilers() @memoized diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py index fc360d59ba..8679d3e13e 100644 --- a/lib/spack/spack/concretize.py +++ b/lib/spack/spack/concretize.py @@ -89,15 +89,11 @@ class DefaultConcretizer(object): def concretize_compiler(self, spec): - """Currently just sets the compiler to gcc or throws an exception - if the compiler is set to something else. - - TODO: implement below description. - - If the spec already has a compiler, we're done. If not, then - take the compiler used for the nearest ancestor with a concrete - compiler, or use the system default if there is no ancestor - with a compiler. + """If the spec already has a compiler, we're done. If not, then take + the compiler used for the nearest ancestor with a compiler + spec and use that. If the ancestor's compiler is not + concrete, then give it a valid version. If there is no + ancestor with a compiler, use the system default compiler. Intuition: Use the system default if no package that depends on this one has a strict compiler requirement. Otherwise, try to @@ -105,10 +101,22 @@ class DefaultConcretizer(object): link to this one, to maximize compatibility. """ if spec.compiler and spec.compiler.concrete: - if spec.compiler != spack.compilers.default_compiler(): - raise spack.spec.UnknownCompilerError(str(spec.compiler)) - else: - spec.compiler = spack.compilers.default_compiler() + return + + try: + nearest = next(p for p in spec.preorder_traversal(direction='parents') + if p.compiler is not None).compiler + + if not nearest.concrete: + matches = [c for c in spack.compilers.available_compilers() + if c.name == nearest.name] + nearest.versions = sorted(matches)[-1].versions.copy() + assert(nearest.concrete) + + spec.compiler = nearest.copy() + + except StopIteration: + spec.compiler = spack.compilers.default_compiler().copy() def choose_provider(self, spec, providers): diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index bb8965ccc8..d0b08cca00 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -196,12 +196,20 @@ class Compiler(object): self.versions.add(version) + def _autospec(self, compiler_spec_like): + if not isinstance(compiler_spec_like, Compiler): + return Compiler(compiler_spec_like) + return compiler_spec_like + + def satisfies(self, other): + other = self._autospec(other) return (self.name == other.name and self.versions.overlaps(other.versions)) def constrain(self, other): + other = self._autospec(other) if not self.satisfies(other): raise UnsatisfiableCompilerSpecError(self, other) @@ -374,14 +382,14 @@ class Spec(object): """ if not self.dependents: return self - else: - # If the spec has multiple dependents, ensure that they all - # 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).root - assert(all(first_root is d.root for d in depiter)) - return first_root + + # If the spec has multiple dependents, ensure that they all + # 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).root + assert(all(first_root is d.root for d in depiter)) + return first_root @property @@ -441,17 +449,28 @@ class Spec(object): root [=True] If false, this won't yield the root node, just its descendents. + + direction [=children|parents] + If 'children', does a traversal of this spec's children. If + 'parents', traverses upwards in the DAG towards the root. + """ depth = kwargs.get('depth', False) key_fun = kwargs.get('key', id) yield_root = kwargs.get('root', True) cover = kwargs.get('cover', 'nodes') + direction = kwargs.get('direction', 'children') cover_values = ('nodes', 'edges', 'paths') if cover not in cover_values: raise ValueError("Invalid value for cover: %s. Choices are %s" % (cover, ",".join(cover_values))) + direction_values = ('children', 'parents') + if direction not in direction_values: + raise ValueError("Invalid value for direction: %s. Choices are %s" + % (direction, ",".join(direction_values))) + if visited is None: visited = set() @@ -465,9 +484,13 @@ class Spec(object): else: if yield_root or d > 0: yield result + successors = self.dependencies + if direction == 'parents': + successors = self.dependents + visited.add(key) - for name in sorted(self.dependencies): - child = self.dependencies[name] + for name in sorted(successors): + child = successors[name] for elt in child.preorder_traversal(visited, d+1, **kwargs): yield elt @@ -776,7 +799,7 @@ class Spec(object): def validate_names(self): """This checks that names of packages and compilers in this spec are real. If they're not, it will raise either UnknownPackageError or - UnknownCompilerError. + UnsupportedCompilerError. """ for spec in self.preorder_traversal(): # Don't get a package for a virtual name. @@ -786,7 +809,7 @@ class Spec(object): # validate compiler in addition to the package name. if spec.compiler: if not spack.compilers.supported(spec.compiler): - raise UnknownCompilerError(spec.compiler) + raise UnsupportedCompilerError(spec.compiler.name) def constrain(self, other, **kwargs): @@ -1385,11 +1408,11 @@ class DuplicateCompilerError(SpecError): super(DuplicateCompilerError, self).__init__(message) -class UnknownCompilerError(SpecError): +class UnsupportedCompilerError(SpecError): """Raised when the user asks for a compiler spack doesn't know about.""" def __init__(self, compiler_name): - super(UnknownCompilerError, self).__init__( - "Unknown compiler: %s" % compiler_name) + super(UnsupportedCompilerError, self).__init__( + "The '%s' compiler is not yet supported." % compiler_name) class DuplicateArchitectureError(SpecError): diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 62e2732749..2a989f6766 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -25,7 +25,7 @@ import unittest import spack -from spack.spec import Spec +from spack.spec import Spec, Compiler from spack.test.mock_packages_test import * class ConcretizeTest(MockPackagesTest): @@ -163,3 +163,15 @@ class ConcretizeTest(MockPackagesTest): spec = Spec('indirect_mpich') spec.normalize() spec.concretize() + + + def test_compiler_inheritance(self): + spec = Spec('mpileaks') + spec.normalize() + + spec['dyninst'].compiler = Compiler('clang') + spec.concretize() + + # TODO: not exactly the syntax I would like. + self.assertTrue(spec['libdwarf'].compiler.satisfies('clang')) + self.assertTrue(spec['libelf'].compiler.satisfies('clang')) -- cgit v1.2.3-60-g2f50