summaryrefslogtreecommitdiff
path: root/lib/spack/spack/spec.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/spec.py')
-rw-r--r--lib/spack/spack/spec.py84
1 files changed, 63 insertions, 21 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 227652168c..941656a964 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -959,7 +959,7 @@ class Spec(object):
def __init__(self, spec_like=None,
normal=False, concrete=False, external_path=None,
- external_module=None, full_hash=None):
+ external_modules=None, full_hash=None):
"""Create a new Spec.
Arguments:
@@ -988,8 +988,6 @@ class Spec(object):
self.variants = vt.VariantMap(self)
self.architecture = None
self.compiler = None
- self.external_path = None
- self.external_module = None
self.compiler_flags = FlagMap(self)
self._dependents = DependencyMap()
self._dependencies = DependencyMap()
@@ -1010,9 +1008,13 @@ class Spec(object):
self._normal = normal
self._concrete = concrete
self.external_path = external_path
- self.external_module = external_module
+ self.external_modules = external_modules
self._full_hash = full_hash
+ # This attribute is used to store custom information for
+ # external specs. None signal that it was not set yet.
+ self.extra_attributes = None
+
if isinstance(spec_like, six.string_types):
spec_list = SpecParser(self).parse(spec_like)
if len(spec_list) > 1:
@@ -1025,7 +1027,7 @@ class Spec(object):
@property
def external(self):
- return bool(self.external_path) or bool(self.external_module)
+ return bool(self.external_path) or bool(self.external_modules)
def get_dependency(self, name):
dep = self._dependencies.get(name)
@@ -1526,7 +1528,8 @@ class Spec(object):
if self.external:
d['external'] = syaml.syaml_dict([
('path', self.external_path),
- ('module', self.external_module),
+ ('module', self.external_modules),
+ ('extra_attributes', self.extra_attributes)
])
if not self._concrete:
@@ -1695,21 +1698,21 @@ class Spec(object):
for name in FlagMap.valid_compiler_flags():
spec.compiler_flags[name] = []
+ spec.external_path = None
+ spec.external_modules = None
if 'external' in node:
- spec.external_path = None
- spec.external_module = None
# This conditional is needed because sometimes this function is
# called with a node already constructed that contains a 'versions'
# and 'external' field. Related to virtual packages provider
# indexes.
if node['external']:
spec.external_path = node['external']['path']
- spec.external_module = node['external']['module']
- if spec.external_module is False:
- spec.external_module = None
- else:
- spec.external_path = None
- spec.external_module = None
+ spec.external_modules = node['external']['module']
+ if spec.external_modules is False:
+ spec.external_modules = None
+ spec.extra_attributes = node['external'].get(
+ 'extra_attributes', syaml.syaml_dict()
+ )
# specs read in are concrete unless marked abstract
spec._concrete = node.get('concrete', True)
@@ -1970,6 +1973,44 @@ class Spec(object):
tty.debug(e)
raise sjson.SpackJSONError("error parsing JSON spec:", str(e))
+ @staticmethod
+ def from_detection(spec_str, extra_attributes=None):
+ """Construct a spec from a spec string determined during external
+ detection and attach extra attributes to it.
+
+ Args:
+ spec_str (str): spec string
+ extra_attributes (dict): dictionary containing extra attributes
+
+ Returns:
+ spack.spec.Spec: external spec
+ """
+ s = Spec(spec_str)
+ extra_attributes = syaml.sorted_dict(extra_attributes or {})
+ # This is needed to be able to validate multi-valued variants,
+ # otherwise they'll still be abstract in the context of detection.
+ vt.substitute_abstract_variants(s)
+ s.extra_attributes = extra_attributes
+ return s
+
+ def validate_detection(self):
+ """Validate the detection of an external spec.
+
+ This method is used as part of Spack's detection protocol, and is
+ not meant for client code use.
+ """
+ # Assert that _extra_attributes is a Mapping and not None,
+ # which likely means the spec was created with Spec.from_detection
+ msg = ('cannot validate "{0}" since it was not created '
+ 'using Spec.from_detection'.format(self))
+ assert isinstance(self.extra_attributes, collections.Mapping), msg
+
+ # Validate the spec calling a package specific method
+ validate_fn = getattr(
+ self.package, 'validate_detected_spec', lambda x, y: None
+ )
+ validate_fn(self, self.extra_attributes)
+
def _concretize_helper(self, concretizer, presets=None, visited=None):
"""Recursive helper function for concretize().
This concretizes everything bottom-up. As things are
@@ -2115,8 +2156,8 @@ class Spec(object):
feq(replacement.variants, spec.variants) and
feq(replacement.external_path,
spec.external_path) and
- feq(replacement.external_module,
- spec.external_module)):
+ feq(replacement.external_modules,
+ spec.external_modules)):
continue
# Refine this spec to the candidate. This uses
# replace_with AND dup so that it can work in
@@ -2250,7 +2291,7 @@ class Spec(object):
t[-1] for t in ordered_hashes)
for s in self.traverse():
- if s.external_module and not s.external_path:
+ if s.external_modules and not s.external_path:
compiler = spack.compilers.compiler_for_spec(
s.compiler, s.architecture)
for mod in compiler.modules:
@@ -2259,8 +2300,8 @@ class Spec(object):
# get the path from the module
# the package can override the default
s.external_path = getattr(s.package, 'external_prefix',
- md.get_path_from_module(
- s.external_module))
+ md.path_from_modules(
+ s.external_modules))
# Mark everything in the spec as concrete, as well.
self._mark_concrete()
@@ -3046,7 +3087,7 @@ class Spec(object):
self._normal != other._normal and
self.concrete != other.concrete and
self.external_path != other.external_path and
- self.external_module != other.external_module and
+ self.external_modules != other.external_modules and
self.compiler_flags != other.compiler_flags)
self._package = None
@@ -3074,7 +3115,8 @@ class Spec(object):
self.variants.spec = self
self.external_path = other.external_path
- self.external_module = other.external_module
+ self.external_modules = other.external_modules
+ self.extra_attributes = other.extra_attributes
self.namespace = other.namespace
# Cached fields are results of expensive operations.