diff --git a/lib/spack/spack/caches.py b/lib/spack/spack/caches.py index 58594059a58..4496bd90e2c 100644 --- a/lib/spack/spack/caches.py +++ b/lib/spack/spack/caches.py @@ -74,6 +74,6 @@ def store(self, fetcher, relative_dest): #: Spack's local cache for downloaded source archives -FETCH_CACHE: Union[spack.fetch_strategy.FsCache, llnl.util.lang.Singleton] = ( +FETCH_CACHE: Union["spack.fetch_strategy.FsCache", llnl.util.lang.Singleton] = ( llnl.util.lang.Singleton(_fetch_cache) ) diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py index 54e8a80b8af..f08316bc9d2 100644 --- a/lib/spack/spack/fetch_strategy.py +++ b/lib/spack/spack/fetch_strategy.py @@ -49,6 +49,7 @@ import spack.util.archive import spack.util.crypto as crypto import spack.util.git +import spack.util.spack_yaml as syaml import spack.util.url as url_util import spack.util.web as web_util import spack.version @@ -110,6 +111,20 @@ def __init__(self, **kwargs): self.package = None + def spec_attrs(self): + """Create a dictionary of attributes that describe this fetch strategy for a Spec. + + This is included in the serialized Spec format to store provenance (like hashes). + """ + attrs = syaml.syaml_dict() + if self.url_attr: + attrs["type"] = "archive" if self.url_attr == "url" else self.url_attr + for attr in self.optional_attrs: + value = getattr(self, attr, None) + if value: + attrs[attr] = value + return attrs + def set_package(self, package): self.package = package @@ -254,6 +269,16 @@ def __init__(self, *, url: str, checksum: Optional[str] = None, **kwargs) -> Non self.extension: Optional[str] = kwargs.get("extension", None) self._effective_url: Optional[str] = None + def spec_attrs(self): + attrs = super(URLFetchStrategy, self).spec_attrs() + if self.digest: + try: + hash_type = spack.util.crypto.hash_algo_for_digest(self.digest) + except ValueError: + hash_type = "digest" + attrs[hash_type] = self.digest + return attrs + @property def curl(self) -> Executable: if not self._curl: diff --git a/lib/spack/spack/hash_types.py b/lib/spack/spack/hash_types.py index 4b1bf627d64..04e4235add0 100644 --- a/lib/spack/spack/hash_types.py +++ b/lib/spack/spack/hash_types.py @@ -5,7 +5,6 @@ """Definitions that control how Spack creates Spec hashes.""" import spack.deptypes as dt -import spack.repo hashes = [] @@ -13,20 +12,17 @@ class SpecHashDescriptor: """This class defines how hashes are generated on Spec objects. - Spec hashes in Spack are generated from a serialized (e.g., with - YAML) representation of the Spec graph. The representation may only - include certain dependency types, and it may optionally include a - canonicalized hash of the package.py for each node in the graph. + Spec hashes in Spack are generated from a serialized JSON representation of the DAG. + The representation may only include certain dependency types, and it may optionally + include a canonicalized hash of the ``package.py`` for each node in the graph. - We currently use different hashes for different use cases.""" + """ - def __init__(self, depflag: dt.DepFlag, package_hash, name, override=None): + def __init__(self, depflag: dt.DepFlag, package_hash, name): self.depflag = depflag self.package_hash = package_hash self.name = name hashes.append(self) - # Allow spec hashes to have an alternate computation method - self.override = override @property def attr(self): @@ -54,18 +50,6 @@ def __repr__(self): ) -def _content_hash_override(spec): - pkg_cls = spack.repo.PATH.get_pkg_class(spec.name) - pkg = pkg_cls(spec) - return pkg.content_hash() - - -#: Package hash used as part of dag hash -package_hash = SpecHashDescriptor( - depflag=0, package_hash=True, name="package_hash", override=_content_hash_override -) - - # Deprecated hash types, no longer used, but needed to understand old serialized # spec formats diff --git a/lib/spack/spack/package_base.py b/lib/spack/spack/package_base.py index ef2f27cca63..2e7dabbeb31 100644 --- a/lib/spack/spack/package_base.py +++ b/lib/spack/spack/package_base.py @@ -9,12 +9,10 @@ packages. """ -import base64 import collections import copy import functools import glob -import hashlib import importlib import io import os @@ -50,6 +48,7 @@ import spack.url import spack.util.environment import spack.util.path +import spack.util.spack_yaml as syaml import spack.util.web from spack.error import InstallError, NoURLError, PackageError from spack.filesystem_view import YamlFilesystemView @@ -1754,7 +1753,7 @@ def all_patches(cls): return patches - def content_hash(self, content=None): + def artifact_hashes(self, content=None): """Create a hash based on the artifacts and patches used to build this package. This includes: @@ -1767,52 +1766,47 @@ def content_hash(self, content=None): determinable) portions of the hash will be included. """ - # list of components to make up the hash - hash_content = [] + hashes = syaml.syaml_dict() # source artifacts/repositories # TODO: resources if self.spec.versions.concrete: + sources = [] try: - source_id = fs.for_package_version(self).source_id() + fetcher = fs.for_package_version(self) + sources.append(fetcher.spec_attrs()) + except (fs.ExtrapolationError, fs.InvalidArgsError): # ExtrapolationError happens if the package has no fetchers defined. # InvalidArgsError happens when there are version directives with args, # but none of them identifies an actual fetcher. - source_id = None - if not source_id: - # TODO? in cases where a digest or source_id isn't available, - # should this attempt to download the source and set one? This - # probably only happens for source repositories which are - # referenced by branch name rather than tag or commit ID. + # if this is a develop spec, say so from_local_sources = "dev_path" in self.spec.variants + + # don't bother setting a source id if none is available, but warn if + # it seems like there should be one. if self.has_code and not self.spec.external and not from_local_sources: message = "Missing a source id for {s.name}@{s.version}" tty.debug(message.format(s=self)) - hash_content.append("".encode("utf-8")) - else: - hash_content.append(source_id.encode("utf-8")) + + for resource in self._get_needed_resources(): + sources.append(resource.fetcher.spec_attrs()) + + if sources: + hashes["sources"] = sources # patch sha256's # Only include these if they've been assigned by the concretizer. # We check spec._patches_assigned instead of spec.concrete because # we have to call package_hash *before* marking specs concrete if self.spec._patches_assigned(): - hash_content.extend( - ":".join((p.sha256, str(p.level))).encode("utf-8") for p in self.spec.patches - ) + hashes["patches"] = [p.sha256 for p in self.spec.patches] # package.py contents - hash_content.append(package_hash(self.spec, source=content).encode("utf-8")) + hashes["package_hash"] = package_hash(self.spec, source=content) - # put it all together and encode as base32 - b32_hash = base64.b32encode( - hashlib.sha256(bytes().join(sorted(hash_content))).digest() - ).lower() - b32_hash = b32_hash.decode("utf-8") - - return b32_hash + return hashes @property def cmake_prefix_paths(self): diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index d64507a9a17..56446e4b1c3 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -1478,6 +1478,12 @@ def __init__( for h in ht.hashes: setattr(self, h.attr, None) + # dictionary of source artifact hashes, set at concretization time + self._package_hash = None + + # dictionary of source artifact hashes, set at concretization time + self._artifact_hashes = None + # Python __hash__ is handled separately from the cached spec hashes self._dunder_hash = None @@ -1968,10 +1974,6 @@ def spec_hash(self, hash): Arguments: hash (spack.hash_types.SpecHashDescriptor): type of hash to generate. """ - # TODO: currently we strip build dependencies by default. Rethink - # this when we move to using package hashing on all specs. - if hash.override is not None: - return hash.override(self) node_dict = self.to_node_dict(hash=hash) json_text = sjson.dump(node_dict) # This implements "frankenhashes", preserving the last 7 characters of the @@ -1981,7 +1983,7 @@ def spec_hash(self, hash): return out[:-7] + self.build_spec.spec_hash(hash)[-7:] return out - def _cached_hash(self, hash, length=None, force=False): + def _cached_hash(self, hash, length=None): """Helper function for storing a cached hash on the spec. This will run spec_hash() with the deptype and package_hash @@ -1991,7 +1993,6 @@ def _cached_hash(self, hash, length=None, force=False): Arguments: hash (spack.hash_types.SpecHashDescriptor): type of hash to generate. length (int): length of hash prefix to return (default is full hash string) - force (bool): cache the hash even if spec is not concrete (default False) """ if not hash.attr: return self.spec_hash(hash)[:length] @@ -2001,7 +2002,7 @@ def _cached_hash(self, hash, length=None, force=False): return hash_string[:length] else: hash_string = self.spec_hash(hash) - if force or self.concrete: + if self.concrete: setattr(self, hash.attr, hash_string) return hash_string[:length] @@ -2172,7 +2173,10 @@ def to_node_dict(self, hash=ht.dag_hash): if self.namespace: d["namespace"] = self.namespace - params = syaml.syaml_dict(sorted(v.yaml_entry() for _, v in self.variants.items())) + # Get all variants *except* for patches. Patches are included in "artifacts" below. + params = syaml.syaml_dict( + sorted(v.yaml_entry() for _, v in self.variants.items() if v.name != "patches") + ) # Only need the string compiler flag for yaml file params.update( @@ -2197,28 +2201,23 @@ def to_node_dict(self, hash=ht.dag_hash): if not self._concrete: d["concrete"] = False - if "patches" in self.variants: - variant = self.variants["patches"] - if hasattr(variant, "_patches_in_order_of_appearance"): - d["patches"] = variant._patches_in_order_of_appearance + if self._concrete and hash.package_hash: + # We use the attribute here instead of `self.package_hash()` because this should + # *always* be assigned at concretization time. We don't want to try to compute a + # package hash for concrete spec where a) the package might not exist, or b) the + # `dag_hash` didn't include the package hash when the spec was concretized. + if hasattr(self, "_package_hash") and self._package_hash: + d["package_hash"] = self._package_hash - if ( - self._concrete - and hash.package_hash - and hasattr(self, "_package_hash") - and self._package_hash - ): - # We use the attribute here instead of `self.package_hash()` because this - # should *always* be assignhed at concretization time. We don't want to try - # to compute a package hash for concrete spec where a) the package might not - # exist, or b) the `dag_hash` didn't include the package hash when the spec - # was concretized. - package_hash = self._package_hash + if self._artifact_hashes: + for key, source_list in sorted(self._artifact_hashes.items()): + # sources may be dictionaries (for archives/resources) + def order(source): + if isinstance(source, dict): + return syaml.syaml_dict(sorted(source.items())) + return source - # Full hashes are in bytes - if not isinstance(package_hash, str) and isinstance(package_hash, bytes): - package_hash = package_hash.decode("utf-8") - d["package_hash"] = package_hash + d[key] = [order(source) for source in source_list] # Note: Relies on sorting dict by keys later in algorithm. deps = self._dependencies_dict(depflag=hash.depflag) @@ -2917,12 +2916,16 @@ def _finalize_concretization(self): # We only assign package hash to not-yet-concrete specs, for which we know # we can compute the hash. if not spec.concrete: - # we need force=True here because package hash assignment has to happen - # before we mark concrete, so that we know what was *already* concrete. - spec._cached_hash(ht.package_hash, force=True) + # package hash assignment has to happen before we mark concrete, so that + # we know what was *already* concrete. + # can't use self.package here b/c not concrete yet + pkg_cls = spack.repo.PATH.get_pkg_class(spec.name) + pkg = pkg_cls(spec) - # keep this check here to ensure package hash is saved - assert getattr(spec, ht.package_hash.attr) + # TODO: make artifact hashes a static method + artifact_hashes = pkg.artifact_hashes() + spec._package_hash = artifact_hashes.pop("package_hash") + spec._artifact_hashes = artifact_hashes # Mark everything in the spec as concrete self._mark_concrete() @@ -3558,6 +3561,8 @@ def _dup(self, other, deps: Union[bool, dt.DepTypes, dt.DepFlag] = True, clearde self._normal = other._normal for h in ht.hashes: setattr(self, h.attr, getattr(other, h.attr, None)) + self._package_hash = getattr(other, "_package_hash", None) + self._artifact_hashes = getattr(other, "_artifact_hashes", None) else: self._dunder_hash = None # Note, we could use other._normal if we are copying all deps, but @@ -3565,6 +3570,8 @@ def _dup(self, other, deps: Union[bool, dt.DepTypes, dt.DepFlag] = True, clearde self._normal = False for h in ht.hashes: setattr(self, h.attr, None) + self._package_hash = None + self._artifact_hashes = None return changed @@ -4427,6 +4434,8 @@ def clear_cached_hashes(self, ignore=()): if h.attr not in ignore: if hasattr(self, h.attr): setattr(self, h.attr, None) + self._package_hash = None + self._artifact_hashes = None self._dunder_hash = None def __hash__(self): @@ -4702,6 +4711,14 @@ def from_node_dict(cls, node): for h in ht.hashes: setattr(spec, h.attr, node.get(h.name, None)) + # old and new-style package hash + if "package_hash" in node: + spec._package_hash = node["package_hash"] + + # all source artifact hashes + if "sources" in node: + spec._artifact_hashes = syaml.syaml_dict([("sources", node["sources"])]) + spec.name = name spec.namespace = node.get("namespace", None) diff --git a/lib/spack/spack/test/util/package_hash.py b/lib/spack/spack/test/util/package_hash.py index cfadae40be3..1838c6e1ccb 100644 --- a/lib/spack/spack/test/util/package_hash.py +++ b/lib/spack/spack/test/util/package_hash.py @@ -19,29 +19,27 @@ datadir = os.path.join(spack.paths.test_path, "data", "unparse") -def compare_sans_name(eq, spec1, spec2): +def canonical_source_equal_sans_name(spec1, spec2): content1 = ph.canonical_source(spec1) content1 = content1.replace(spack.repo.PATH.get_pkg_class(spec1.name).__name__, "TestPackage") content2 = ph.canonical_source(spec2) content2 = content2.replace(spack.repo.PATH.get_pkg_class(spec2.name).__name__, "TestPackage") - if eq: - assert content1 == content2 - else: - assert content1 != content2 + + return content1 == content2 -def compare_hash_sans_name(eq, spec1, spec2): +def package_hash_equal_sans_name(spec1, spec2): content1 = ph.canonical_source(spec1) pkg_cls1 = spack.repo.PATH.get_pkg_class(spec1.name) content1 = content1.replace(pkg_cls1.__name__, "TestPackage") - hash1 = pkg_cls1(spec1).content_hash(content=content1) + hash1 = ph.package_hash(spec1, source=content1) content2 = ph.canonical_source(spec2) pkg_cls2 = spack.repo.PATH.get_pkg_class(spec2.name) content2 = content2.replace(pkg_cls2.__name__, "TestPackage") - hash2 = pkg_cls2(spec2).content_hash(content=content2) + hash2 = ph.package_hash(spec2, source=content2) - assert (hash1 == hash2) == eq + return hash1 == hash2 def test_hash(mock_packages, config): @@ -57,11 +55,11 @@ def test_different_variants(mock_packages, config): def test_all_same_but_name(mock_packages, config): spec1 = Spec("hash-test1@=1.2") spec2 = Spec("hash-test2@=1.2") - compare_sans_name(True, spec1, spec2) + assert canonical_source_equal_sans_name(spec1, spec2) spec1 = Spec("hash-test1@=1.2 +varianty") spec2 = Spec("hash-test2@=1.2 +varianty") - compare_sans_name(True, spec1, spec2) + assert canonical_source_equal_sans_name(spec1, spec2) def test_all_same_but_archive_hash(mock_packages, config): @@ -70,60 +68,63 @@ def test_all_same_but_archive_hash(mock_packages, config): """ spec1 = Spec("hash-test1@=1.3") spec2 = Spec("hash-test2@=1.3") - compare_sans_name(True, spec1, spec2) + assert canonical_source_equal_sans_name(spec1, spec2) def test_all_same_but_patch_contents(mock_packages, config): spec1 = Spec("hash-test1@=1.1") spec2 = Spec("hash-test2@=1.1") - compare_sans_name(True, spec1, spec2) + assert canonical_source_equal_sans_name(spec1, spec2) def test_all_same_but_patches_to_apply(mock_packages, config): spec1 = Spec("hash-test1@=1.4") spec2 = Spec("hash-test2@=1.4") - compare_sans_name(True, spec1, spec2) + assert canonical_source_equal_sans_name(spec1, spec2) def test_all_same_but_install(mock_packages, config): spec1 = Spec("hash-test1@=1.5") spec2 = Spec("hash-test2@=1.5") - compare_sans_name(False, spec1, spec2) + assert not canonical_source_equal_sans_name(spec1, spec2) -def test_content_hash_all_same_but_patch_contents(mock_packages, config): +def test_package_hash_all_same_but_patch_contents_different(mock_packages, config): spec1 = Spec("hash-test1@1.1").concretized() spec2 = Spec("hash-test2@1.1").concretized() - compare_hash_sans_name(False, spec1, spec2) + + assert package_hash_equal_sans_name(spec1, spec2) + assert spec1.dag_hash() != spec2.dag_hash() + assert spec1.to_node_dict()["patches"] != spec2.to_node_dict()["patches"] -def test_content_hash_not_concretized(mock_packages, config): - """Check that Package.content_hash() works on abstract specs.""" - # these are different due to the package hash +def test_package_hash_not_concretized(mock_packages, config): + """Check that ``package_hash()`` works on abstract specs.""" + # these are different due to patches but not package hash spec1 = Spec("hash-test1@=1.1") spec2 = Spec("hash-test2@=1.3") - compare_hash_sans_name(False, spec1, spec2) + assert package_hash_equal_sans_name(spec1, spec2) # at v1.1 these are actually the same package when @when's are removed # and the name isn't considered spec1 = Spec("hash-test1@=1.1") spec2 = Spec("hash-test2@=1.1") - compare_hash_sans_name(True, spec1, spec2) + assert package_hash_equal_sans_name(spec1, spec2) - # these end up being different b/c we can't eliminate much of the package.py - # without a version. + # these end up being different b/c without a version, we can't eliminate much of the + # package.py when canonicalizing source. spec1 = Spec("hash-test1") spec2 = Spec("hash-test2") - compare_hash_sans_name(False, spec1, spec2) + assert not package_hash_equal_sans_name(spec1, spec2) -def test_content_hash_different_variants(mock_packages, config): +def test_package_hash_different_variants(mock_packages, config): spec1 = Spec("hash-test1@1.2 +variantx").concretized() spec2 = Spec("hash-test2@1.2 ~variantx").concretized() - compare_hash_sans_name(True, spec1, spec2) + assert package_hash_equal_sans_name(spec1, spec2) -def test_content_hash_cannot_get_details_from_ast(mock_packages, config): +def test_package_hash_cannot_get_details_from_ast(mock_packages, config): """Packages hash-test1 and hash-test3 would be considered the same except that hash-test3 conditionally executes a phase based on a "when" directive that Spack cannot evaluate by examining the @@ -135,18 +136,38 @@ def test_content_hash_cannot_get_details_from_ast(mock_packages, config): """ spec3 = Spec("hash-test1@1.7").concretized() spec4 = Spec("hash-test3@1.7").concretized() - compare_hash_sans_name(False, spec3, spec4) + assert not package_hash_equal_sans_name(spec3, spec4) -def test_content_hash_all_same_but_archive_hash(mock_packages, config): +def test_package_hash_all_same_but_archive_hash(mock_packages, config): spec1 = Spec("hash-test1@1.3").concretized() spec2 = Spec("hash-test2@1.3").concretized() - compare_hash_sans_name(False, spec1, spec2) + + assert package_hash_equal_sans_name(spec1, spec2) + + # the sources for these two packages will not be the same b/c their archive hashes differ + assert spec1.to_node_dict()["sources"] != spec2.to_node_dict()["sources"] + + assert spec1.dag_hash() != spec2.dag_hash() -def test_content_hash_parse_dynamic_function_call(mock_packages, config): +def test_package_hash_all_same_but_resources(mock_packages, config): + spec1 = Spec("hash-test1@1.7").concretized() + spec2 = Spec("hash-test1@1.8").concretized() + + # these should be the same + assert canonical_source_equal_sans_name(spec1, spec2) + assert package_hash_equal_sans_name(spec1, spec2) + + # but 1.7 has a resource that affects the hash + assert spec1.to_node_dict()["sources"] != spec2.to_node_dict()["sources"] + + assert spec1.dag_hash() != spec2.dag_hash() + + +def test_package_hash_parse_dynamic_function_call(mock_packages, config): spec = Spec("hash-test4").concretized() - spec.package.content_hash() + ph.package_hash(spec) many_strings = '''\ diff --git a/var/spack/repos/builtin.mock/packages/hash-test1/package.py b/var/spack/repos/builtin.mock/packages/hash-test1/package.py index 403ad591886..ce01d597cb1 100644 --- a/var/spack/repos/builtin.mock/packages/hash-test1/package.py +++ b/var/spack/repos/builtin.mock/packages/hash-test1/package.py @@ -14,13 +14,14 @@ class HashTest1(Package): homepage = "http://www.hashtest1.org" url = "http://www.hashtest1.org/downloads/hashtest1-1.1.tar.bz2" - version("1.1", md5="a" * 32) - version("1.2", md5="b" * 32) - version("1.3", md5="c" * 32) - version("1.4", md5="d" * 32) - version("1.5", md5="d" * 32) - version("1.6", md5="e" * 32) - version("1.7", md5="f" * 32) + version("1.1", sha256="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + version("1.2", sha256="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + version("1.3", sha256="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc") + version("1.4", sha256="dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd") + version("1.5", sha256="dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd") + version("1.6", sha256="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") + version("1.7", sha256="ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + version("1.8", sha256="1111111111111111111111111111111111111111111111111111111111111111") patch("patch1.patch", when="@1.1") patch("patch2.patch", when="@1.4") @@ -28,6 +29,12 @@ class HashTest1(Package): variant("variantx", default=False, description="Test variant X") variant("varianty", default=False, description="Test variant Y") + resource( + url="http://www.example.com/example-1.0-resource.tar.gz", + sha256="abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + when="@1.8", + ) + def setup_dependent_build_environment(self, env, dependent_spec): pass diff --git a/var/spack/repos/builtin.mock/packages/hash-test2/package.py b/var/spack/repos/builtin.mock/packages/hash-test2/package.py index 773370e5a19..214dad17e13 100644 --- a/var/spack/repos/builtin.mock/packages/hash-test2/package.py +++ b/var/spack/repos/builtin.mock/packages/hash-test2/package.py @@ -14,10 +14,13 @@ class HashTest2(Package): homepage = "http://www.hashtest2.org" url = "http://www.hashtest1.org/downloads/hashtest2-1.1.tar.bz2" - version("1.1", md5="a" * 32) - version("1.2", md5="b" * 32) - version("1.3", md5="c" * 31 + "x") # Source hash differs from hash-test1@1.3 - version("1.4", md5="d" * 32) + version("1.1", sha256="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + version("1.2", sha256="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + + # Source hash differs from hash-test1@1.3 + version("1.3", sha256="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccf") + + version("1.4", sha256="dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd") patch("patch1.patch", when="@1.1") diff --git a/var/spack/repos/builtin.mock/packages/hash-test3/package.py b/var/spack/repos/builtin.mock/packages/hash-test3/package.py index 3e7b79419af..38cafdd6b12 100644 --- a/var/spack/repos/builtin.mock/packages/hash-test3/package.py +++ b/var/spack/repos/builtin.mock/packages/hash-test3/package.py @@ -14,11 +14,11 @@ class HashTest3(Package): homepage = "http://www.hashtest3.org" url = "http://www.hashtest1.org/downloads/hashtest3-1.1.tar.bz2" - version("1.2", md5="b" * 32) - version("1.3", md5="c" * 32) - version("1.5", md5="d" * 32) - version("1.6", md5="e" * 32) - version("1.7", md5="f" * 32) + version("1.2", sha256="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + version("1.3", sha256="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc") + version("1.5", sha256="dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd") + version("1.6", sha256="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") + version("1.7", sha256="ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") variant("variantx", default=False, description="Test variant X") variant("varianty", default=False, description="Test variant Y")