Implemented compiler concretization policy.

This commit is contained in:
Todd Gamblin 2014-05-07 20:01:12 -07:00
parent c6956bc8e7
commit 45baf73c34
5 changed files with 91 additions and 41 deletions

View File

@ -34,7 +34,7 @@
def compilers(parser, args): def compilers(parser, args):
tty.msg("Available compilers") 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(): for name, compilers in index.items():
tty.hline(name, char='-', color=spack.spec.compiler_color) tty.hline(name, char='-', color=spack.spec.compiler_color)
colify(compilers, indent=4) colify(compilers, indent=4)

View File

@ -27,27 +27,34 @@
# #
from llnl.util.lang import memoized, list_modules from llnl.util.lang import memoized, list_modules
import spack
import spack.spec import spack.spec
from spack.util.executable import which from spack.util.executable import which
@memoized @memoized
def supported_compilers(): 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)) return sorted(c for c in list_modules(spack.compilers_path))
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)]
def supported(compiler_spec): def supported(compiler_spec):
"""Test if a particular compiler is supported.""" """Test if a particular compiler is supported."""
if isinstance(compiler_spec, spack.spec.Compiler): if not isinstance(compiler_spec, spack.spec.Compiler):
return compiler_spec.name in supported_compilers() compiler_spec = spack.spec.Compiler(compiler_spec)
return compiler_spec.name in supported_compilers()
elif isinstance(compiler_spec, basestring):
return compiler_spec in supported_compilers()
else:
raise TypeError("compiler_spec must be string or spack.spec.Compiler")
@memoized @memoized

View File

@ -89,15 +89,11 @@ def concretize_architecture(self, spec):
def concretize_compiler(self, spec): def concretize_compiler(self, spec):
"""Currently just sets the compiler to gcc or throws an exception """If the spec already has a compiler, we're done. If not, then take
if the compiler is set to something else. the compiler used for the nearest ancestor with a compiler
spec and use that. If the ancestor's compiler is not
TODO: implement below description. concrete, then give it a valid version. If there is no
ancestor with a compiler, use the system default compiler.
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.
Intuition: Use the system default if no package that depends on Intuition: Use the system default if no package that depends on
this one has a strict compiler requirement. Otherwise, try to this one has a strict compiler requirement. Otherwise, try to
@ -105,10 +101,22 @@ def concretize_compiler(self, spec):
link to this one, to maximize compatibility. link to this one, to maximize compatibility.
""" """
if spec.compiler and spec.compiler.concrete: if spec.compiler and spec.compiler.concrete:
if spec.compiler != spack.compilers.default_compiler(): return
raise spack.spec.UnknownCompilerError(str(spec.compiler))
else: try:
spec.compiler = spack.compilers.default_compiler() 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): def choose_provider(self, spec, providers):

View File

@ -196,12 +196,20 @@ def _add_version(self, version):
self.versions.add(version) 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): def satisfies(self, other):
other = self._autospec(other)
return (self.name == other.name and return (self.name == other.name and
self.versions.overlaps(other.versions)) self.versions.overlaps(other.versions))
def constrain(self, other): def constrain(self, other):
other = self._autospec(other)
if not self.satisfies(other): if not self.satisfies(other):
raise UnsatisfiableCompilerSpecError(self, other) raise UnsatisfiableCompilerSpecError(self, other)
@ -374,14 +382,14 @@ def root(self):
""" """
if not self.dependents: if not self.dependents:
return self return self
else:
# If the spec has multiple dependents, ensure that they all # If the spec has multiple dependents, ensure that they all
# lead to the same place. Spack shouldn't deal with any DAGs # lead to the same place. Spack shouldn't deal with any DAGs
# with multiple roots, so something's wrong if we find one. # with multiple roots, so something's wrong if we find one.
depiter = iter(self.dependents.values()) depiter = iter(self.dependents.values())
first_root = next(depiter).root first_root = next(depiter).root
assert(all(first_root is d.root for d in depiter)) assert(all(first_root is d.root for d in depiter))
return first_root return first_root
@property @property
@ -441,17 +449,28 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
root [=True] root [=True]
If false, this won't yield the root node, just its descendents. 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) depth = kwargs.get('depth', False)
key_fun = kwargs.get('key', id) key_fun = kwargs.get('key', id)
yield_root = kwargs.get('root', True) yield_root = kwargs.get('root', True)
cover = kwargs.get('cover', 'nodes') cover = kwargs.get('cover', 'nodes')
direction = kwargs.get('direction', 'children')
cover_values = ('nodes', 'edges', 'paths') cover_values = ('nodes', 'edges', 'paths')
if cover not in cover_values: if cover not in cover_values:
raise ValueError("Invalid value for cover: %s. Choices are %s" raise ValueError("Invalid value for cover: %s. Choices are %s"
% (cover, ",".join(cover_values))) % (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: if visited is None:
visited = set() visited = set()
@ -465,9 +484,13 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
else: else:
if yield_root or d > 0: yield result if yield_root or d > 0: yield result
successors = self.dependencies
if direction == 'parents':
successors = self.dependents
visited.add(key) visited.add(key)
for name in sorted(self.dependencies): for name in sorted(successors):
child = self.dependencies[name] child = successors[name]
for elt in child.preorder_traversal(visited, d+1, **kwargs): for elt in child.preorder_traversal(visited, d+1, **kwargs):
yield elt yield elt
@ -776,7 +799,7 @@ def normalized(self):
def validate_names(self): def validate_names(self):
"""This checks that names of packages and compilers in this spec are real. """This checks that names of packages and compilers in this spec are real.
If they're not, it will raise either UnknownPackageError or If they're not, it will raise either UnknownPackageError or
UnknownCompilerError. UnsupportedCompilerError.
""" """
for spec in self.preorder_traversal(): for spec in self.preorder_traversal():
# Don't get a package for a virtual name. # Don't get a package for a virtual name.
@ -786,7 +809,7 @@ def validate_names(self):
# validate compiler in addition to the package name. # validate compiler in addition to the package name.
if spec.compiler: if spec.compiler:
if not spack.compilers.supported(spec.compiler): if not spack.compilers.supported(spec.compiler):
raise UnknownCompilerError(spec.compiler) raise UnsupportedCompilerError(spec.compiler.name)
def constrain(self, other, **kwargs): def constrain(self, other, **kwargs):
@ -1385,11 +1408,11 @@ def __init__(self, message):
super(DuplicateCompilerError, self).__init__(message) super(DuplicateCompilerError, self).__init__(message)
class UnknownCompilerError(SpecError): class UnsupportedCompilerError(SpecError):
"""Raised when the user asks for a compiler spack doesn't know about.""" """Raised when the user asks for a compiler spack doesn't know about."""
def __init__(self, compiler_name): def __init__(self, compiler_name):
super(UnknownCompilerError, self).__init__( super(UnsupportedCompilerError, self).__init__(
"Unknown compiler: %s" % compiler_name) "The '%s' compiler is not yet supported." % compiler_name)
class DuplicateArchitectureError(SpecError): class DuplicateArchitectureError(SpecError):

View File

@ -25,7 +25,7 @@
import unittest import unittest
import spack import spack
from spack.spec import Spec from spack.spec import Spec, Compiler
from spack.test.mock_packages_test import * from spack.test.mock_packages_test import *
class ConcretizeTest(MockPackagesTest): class ConcretizeTest(MockPackagesTest):
@ -163,3 +163,15 @@ def test_my_dep_depends_on_provider_of_my_virtual_dep(self):
spec = Spec('indirect_mpich') spec = Spec('indirect_mpich')
spec.normalize() spec.normalize()
spec.concretize() 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'))