From 6458b15bb1ff075a7c3a25c122c0bd4dde5dca0b Mon Sep 17 00:00:00 2001 From: Peter Scheibel Date: Tue, 6 Feb 2018 10:58:49 -0500 Subject: spec: support "full" hashes This hash includes the content of the package itself as well as the DAG for the package. --- lib/spack/spack/spec.py | 27 ++++++++++++++++++++++++--- lib/spack/spack/test/spec_yaml.py | 10 ++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 489e1135af..68fa632e9e 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -1085,6 +1085,8 @@ class Spec(object): self.external_path = kwargs.get('external_path', None) self.external_module = kwargs.get('external_module', None) + self._full_hash = kwargs.get('full_hash', None) + @property def external(self): return bool(self.external_path) or bool(self.external_module) @@ -1426,7 +1428,21 @@ class Spec(object): """Get the first bits of the DAG hash as an integer type.""" return base32_prefix_bits(self.dag_hash(), bits) - def to_node_dict(self): + def full_hash(self, length=None): + if not self.concrete: + raise SpecError("Spec is not concrete: " + str(self)) + + if not self._full_hash: + yaml_text = syaml.dump( + self.to_node_dict(hash_function=lambda s: s.full_hash()), + default_flow_style=True, width=maxint) + package_hash = self.package.content_hash() + sha = hashlib.sha256(yaml_text.encode('utf-8') + package_hash) + self._full_hash = base64.b32encode(sha.digest()).lower() + + return self._full_hash[:length] + + def to_node_dict(self, hash_function=None): d = syaml_dict() if self.versions: @@ -1460,10 +1476,12 @@ class Spec(object): # TODO: concretization. deps = self.dependencies_dict(deptype=('link', 'run')) if deps: + if hash_function is None: + hash_function = lambda s: s.dag_hash() d['dependencies'] = syaml_dict([ (name, syaml_dict([ - ('hash', dspec.spec.dag_hash()), + ('hash', hash_function(dspec.spec)), ('type', sorted(str(s) for s in dspec.deptypes))]) ) for name, dspec in sorted(deps.items()) ]) @@ -1491,7 +1509,7 @@ class Spec(object): name = next(iter(node)) node = node[name] - spec = Spec(name) + spec = Spec(name, full_hash=node.get('full_hash', None)) spec.namespace = node.get('namespace', None) spec._hash = node.get('hash', None) @@ -2653,11 +2671,13 @@ class Spec(object): self._cmp_key_cache = other._cmp_key_cache self._normal = other._normal self._concrete = other._concrete + self._full_hash = other._full_hash else: self._hash = None self._cmp_key_cache = None self._normal = False self._concrete = False + self._full_hash = None return changed @@ -3383,6 +3403,7 @@ class SpecParser(spack.parse.Parser): spec._package = None spec._normal = False spec._concrete = False + spec._full_hash = None # record this so that we know whether version is # unspecified or not. diff --git a/lib/spack/spack/test/spec_yaml.py b/lib/spack/spack/test/spec_yaml.py index cefb737cfb..0d1840a11c 100644 --- a/lib/spack/spack/test/spec_yaml.py +++ b/lib/spack/spack/test/spec_yaml.py @@ -195,6 +195,16 @@ def test_ordered_read_not_required_for_consistent_dag_hash( assert spec.dag_hash() == round_trip_json_spec.dag_hash() assert spec.dag_hash() == round_trip_reversed_yaml_spec.dag_hash() assert spec.dag_hash() == round_trip_reversed_json_spec.dag_hash() + # full_hashes are equal + spec.concretize() + round_trip_yaml_spec.concretize() + round_trip_json_spec.concretize() + round_trip_reversed_yaml_spec.concretize() + round_trip_reversed_json_spec.concretize() + assert spec.full_hash() == round_trip_yaml_spec.full_hash() + assert spec.full_hash() == round_trip_json_spec.full_hash() + assert spec.full_hash() == round_trip_reversed_yaml_spec.full_hash() + assert spec.full_hash() == round_trip_reversed_json_spec.full_hash() def reverse_all_dicts(data): -- cgit v1.2.3-70-g09d2