Implemented compiler concretization policy.
This commit is contained in:
parent
c6956bc8e7
commit
45baf73c34
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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'))
|
||||||
|
Loading…
Reference in New Issue
Block a user