diff options
author | Peter Scheibel <scheibel1@llnl.gov> | 2018-02-06 10:58:49 -0500 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2018-03-20 00:29:54 -0700 |
commit | 6458b15bb1ff075a7c3a25c122c0bd4dde5dca0b (patch) | |
tree | fca5f85e44b85552b5055b0a16c59cb643b89b40 | |
parent | 3501f2f4b5db871d8f2f63f05b29147b264c0920 (diff) | |
download | spack-6458b15bb1ff075a7c3a25c122c0bd4dde5dca0b.tar.gz spack-6458b15bb1ff075a7c3a25c122c0bd4dde5dca0b.tar.bz2 spack-6458b15bb1ff075a7c3a25c122c0bd4dde5dca0b.tar.xz spack-6458b15bb1ff075a7c3a25c122c0bd4dde5dca0b.zip |
spec: support "full" hashes
This hash includes the content of the package itself as well as the DAG
for the package.
-rw-r--r-- | lib/spack/spack/spec.py | 27 | ||||
-rw-r--r-- | lib/spack/spack/test/spec_yaml.py | 10 |
2 files changed, 34 insertions, 3 deletions
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> 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): |