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):
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)

View File

@ -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 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):
"""Test if a particular compiler is supported."""
if isinstance(compiler_spec, spack.spec.Compiler):
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")
if not isinstance(compiler_spec, spack.spec.Compiler):
compiler_spec = spack.spec.Compiler(compiler_spec)
return compiler_spec.name in supported_compilers()
@memoized

View File

@ -89,15 +89,11 @@ def concretize_architecture(self, spec):
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 @@ def concretize_compiler(self, spec):
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):

View File

@ -196,12 +196,20 @@ def _add_version(self, 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):
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 @@ def root(self):
"""
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 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
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 @@ def preorder_traversal(self, visited=None, d=0, **kwargs):
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 @@ def normalized(self):
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 @@ def validate_names(self):
# 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 @@ def __init__(self, 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."""
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):

View File

@ -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 @@ def test_my_dep_depends_on_provider_of_my_virtual_dep(self):
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'))