diff options
Diffstat (limited to 'lib/spack/spack/spec.py')
-rw-r--r-- | lib/spack/spack/spec.py | 84 |
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. |