summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/dependency.py48
-rw-r--r--lib/spack/spack/directives.py123
-rw-r--r--lib/spack/spack/package.py78
-rw-r--r--lib/spack/spack/patch.py93
-rw-r--r--lib/spack/spack/spec.py124
-rw-r--r--lib/spack/spack/test/cmd/dependents.py6
-rw-r--r--lib/spack/spack/test/conftest.py5
-rw-r--r--lib/spack/spack/test/data/patch/bar.txt1
-rw-r--r--lib/spack/spack/test/data/patch/foo.patch7
-rw-r--r--lib/spack/spack/test/data/patch/foo.tgzbin116 -> 229 bytes
-rw-r--r--lib/spack/spack/test/patch.py198
-rw-r--r--lib/spack/spack/test/spec_dag.py4
-rw-r--r--lib/spack/spack/variant.py23
-rw-r--r--var/spack/repos/builtin.mock/packages/patch-a-dependency/foo.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch-a-dependency/package.py39
-rw-r--r--var/spack/repos/builtin.mock/packages/patch-several-dependencies/bar.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch-several-dependencies/baz.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch-several-dependencies/foo.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch-several-dependencies/package.py60
-rw-r--r--var/spack/repos/builtin.mock/packages/patch/bar.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch/baz.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch/foo.patch1
-rw-r--r--var/spack/repos/builtin.mock/packages/patch/package.py41
-rw-r--r--var/spack/repos/builtin/packages/nauty/package.py28
-rw-r--r--var/spack/repos/builtin/packages/nwchem/package.py44
-rw-r--r--var/spack/repos/builtin/packages/tcsh/package.py22
26 files changed, 754 insertions, 197 deletions
diff --git a/lib/spack/spack/dependency.py b/lib/spack/spack/dependency.py
index 996dfa20c4..40494476bf 100644
--- a/lib/spack/spack/dependency.py
+++ b/lib/spack/spack/dependency.py
@@ -26,6 +26,9 @@
"""
from six import string_types
+import spack
+
+
#: The types of dependency relationships that Spack understands.
all_deptypes = ('build', 'link', 'run', 'test')
@@ -78,13 +81,52 @@ class Dependency(object):
e.g. whether it is required for building the package, whether it
needs to be linked to, or whether it is needed at runtime so that
Spack can call commands from it.
+
+ A package can also depend on another package with *patches*. This is
+ for cases where the maintainers of one package also maintain special
+ patches for their dependencies. If one package depends on another
+ with patches, a special version of that dependency with patches
+ applied will be built for use by the dependent package. The patches
+ are included in the new version's spec hash to differentiate it from
+ unpatched versions of the same package, so that unpatched versions of
+ the dependency package can coexist with the patched version.
+
"""
- def __init__(self, spec, type=default_deptype):
+ def __init__(self, pkg, spec, type=default_deptype):
"""Create a new Dependency.
Args:
+ pkg (type): Package that has this dependency
spec (Spec): Spec indicating dependency requirements
type (sequence): strings describing dependency relationship
"""
- self.spec = spec
- self.type = set(type)
+ assert isinstance(spec, spack.spec.Spec)
+
+ self.pkg = pkg
+ self.spec = spec.copy()
+
+ # This dict maps condition specs to lists of Patch objects, just
+ # as the patches dict on packages does.
+ self.patches = {}
+
+ if type is None:
+ self.type = set(default_deptype)
+ else:
+ self.type = set(type)
+
+ @property
+ def name(self):
+ """Get the name of the dependency package."""
+ return self.spec.name
+
+ def merge(self, other):
+ """Merge constraints, deptypes, and patches of other into self."""
+ self.spec.constrain(other.spec)
+ self.type |= other.type
+
+ # concatenate patch lists, or just copy them in
+ for cond, p in other.patches.items():
+ if cond in self.patches:
+ self.patches[cond].extend(other.patches[cond])
+ else:
+ self.patches[cond] = other.patches[cond]
diff --git a/lib/spack/spack/directives.py b/lib/spack/spack/directives.py
index 57063c7f63..cf12b16dd6 100644
--- a/lib/spack/spack/directives.py
+++ b/lib/spack/spack/directives.py
@@ -94,30 +94,26 @@ class DirectiveMetaMixin(type):
try:
directive_from_base = base._directives_to_be_executed
attr_dict['_directives_to_be_executed'].extend(
- directive_from_base
- )
+ directive_from_base)
except AttributeError:
# The base class didn't have the required attribute.
# Continue searching
pass
+
# De-duplicates directives from base classes
attr_dict['_directives_to_be_executed'] = [
x for x in llnl.util.lang.dedupe(
- attr_dict['_directives_to_be_executed']
- )
- ]
+ attr_dict['_directives_to_be_executed'])]
# Move things to be executed from module scope (where they
# are collected first) to class scope
if DirectiveMetaMixin._directives_to_be_executed:
attr_dict['_directives_to_be_executed'].extend(
- DirectiveMetaMixin._directives_to_be_executed
- )
+ DirectiveMetaMixin._directives_to_be_executed)
DirectiveMetaMixin._directives_to_be_executed = []
return super(DirectiveMetaMixin, mcs).__new__(
- mcs, name, bases, attr_dict
- )
+ mcs, name, bases, attr_dict)
def __init__(cls, name, bases, attr_dict):
# The class is being created: if it is a package we must ensure
@@ -128,14 +124,20 @@ class DirectiveMetaMixin(type):
# from llnl.util.lang.get_calling_module_name
pkg_name = module.__name__.split('.')[-1]
setattr(cls, 'name', pkg_name)
+
# Ensure the presence of the dictionaries associated
# with the directives
for d in DirectiveMetaMixin._directive_names:
setattr(cls, d, {})
- # Lazy execution of directives
+
+ # Lazily execute directives
for directive in cls._directives_to_be_executed:
directive(cls)
+ # Ignore any directives executed *within* top-level
+ # directives by clearing out the queue they're appended to
+ DirectiveMetaMixin._directives_to_be_executed = []
+
super(DirectiveMetaMixin, cls).__init__(name, bases, attr_dict)
@staticmethod
@@ -194,15 +196,44 @@ class DirectiveMetaMixin(type):
@functools.wraps(decorated_function)
def _wrapper(*args, **kwargs):
+ # If any of the arguments are executors returned by a
+ # directive passed as an argument, don't execute them
+ # lazily. Instead, let the called directive handle them.
+ # This allows nested directive calls in packages. The
+ # caller can return the directive if it should be queued.
+ def remove_directives(arg):
+ directives = DirectiveMetaMixin._directives_to_be_executed
+ if isinstance(arg, (list, tuple)):
+ # Descend into args that are lists or tuples
+ for a in arg:
+ remove_directives(a)
+ else:
+ # Remove directives args from the exec queue
+ remove = next(
+ (d for d in directives if d is arg), None)
+ if remove is not None:
+ directives.remove(remove)
+
+ # Nasty, but it's the best way I can think of to avoid
+ # side effects if directive results are passed as args
+ remove_directives(args)
+ remove_directives(kwargs.values())
+
# A directive returns either something that is callable on a
# package or a sequence of them
- values = decorated_function(*args, **kwargs)
+ result = decorated_function(*args, **kwargs)
# ...so if it is not a sequence make it so
+ values = result
if not isinstance(values, collections.Sequence):
values = (values, )
DirectiveMetaMixin._directives_to_be_executed.extend(values)
+
+ # wrapped function returns same result as original so
+ # that we can nest directives
+ return result
+
return _wrapper
return _decorator
@@ -229,7 +260,7 @@ def version(ver, checksum=None, **kwargs):
return _execute_version
-def _depends_on(pkg, spec, when=None, type=default_deptype):
+def _depends_on(pkg, spec, when=None, type=default_deptype, patches=None):
# If when is False do nothing
if when is False:
return
@@ -245,13 +276,36 @@ def _depends_on(pkg, spec, when=None, type=default_deptype):
type = canonical_deptype(type)
conditions = pkg.dependencies.setdefault(dep_spec.name, {})
+
+ # call this patches here for clarity -- we want patch to be a list,
+ # but the caller doesn't have to make it one.
+ if patches and dep_spec.virtual:
+ raise DependencyPatchError("Cannot patch a virtual dependency.")
+
+ # ensure patches is a list
+ if patches is None:
+ patches = []
+ elif not isinstance(patches, (list, tuple)):
+ patches = [patches]
+
+ # auto-call patch() directive on any strings in patch list
+ patches = [patch(p) if isinstance(p, string_types)
+ else p for p in patches]
+ assert all(callable(p) for p in patches)
+
+ # this is where we actually add the dependency to this package
if when_spec not in conditions:
- conditions[when_spec] = Dependency(dep_spec, type)
+ dependency = Dependency(pkg, dep_spec, type=type)
+ conditions[when_spec] = dependency
else:
dependency = conditions[when_spec]
dependency.spec.constrain(dep_spec, deps=False)
dependency.type |= set(type)
+ # apply patches to the dependency
+ for execute_patch in patches:
+ execute_patch(dependency)
+
@directive('conflicts')
def conflicts(conflict_spec, when=None, msg=None):
@@ -285,13 +339,24 @@ def conflicts(conflict_spec, when=None, msg=None):
@directive(('dependencies'))
-def depends_on(spec, when=None, type=default_deptype):
+def depends_on(spec, when=None, type=default_deptype, patches=None):
"""Creates a dict of deps with specs defining when they apply.
+
+ Args:
+ spec (Spec or str): the package and constraints depended on
+ when (Spec or str): when the dependent satisfies this, it has
+ the dependency represented by ``spec``
+ type (str or tuple of str): str or tuple of legal Spack deptypes
+ patches (obj or list): single result of ``patch()`` directive, a
+ ``str`` to be passed to ``patch``, or a list of these
+
This directive is to be used inside a Package definition to declare
that the package requires other packages to be built first.
- @see The section "Dependency specs" in the Spack Packaging Guide."""
+ @see The section "Dependency specs" in the Spack Packaging Guide.
+
+ """
def _execute_depends_on(pkg):
- _depends_on(pkg, spec, when=when, type=type)
+ _depends_on(pkg, spec, when=when, type=type, patches=patches)
return _execute_depends_on
@@ -356,20 +421,23 @@ def patch(url_or_filename, level=1, when=None, **kwargs):
level (int): patch level (as in the patch shell command)
when (Spec): optional anonymous spec that specifies when to apply
the patch
- **kwargs: the following list of keywords is supported
- - md5 (str): md5 sum of the patch (used to verify the file
- if it comes from a url)
+ Keyword Args:
+ sha256 (str): sha256 sum of the patch, used to verify the patch
+ (only required for URL patches)
+ archive_sha256 (str): sha256 sum of the *archive*, if the patch
+ is compressed (only required for compressed URL patches)
"""
- def _execute_patch(pkg):
- constraint = pkg.name if when is None else when
- when_spec = parse_anonymous_spec(constraint, pkg.name)
+ def _execute_patch(pkg_or_dep):
+ constraint = pkg_or_dep.name if when is None else when
+ when_spec = parse_anonymous_spec(constraint, pkg_or_dep.name)
# if this spec is identical to some other, then append this
# patch to the existing list.
- cur_patches = pkg.patches.setdefault(when_spec, [])
- cur_patches.append(Patch.create(pkg, url_or_filename, level, **kwargs))
+ cur_patches = pkg_or_dep.patches.setdefault(when_spec, [])
+ cur_patches.append(
+ Patch.create(pkg_or_dep, url_or_filename, level, **kwargs))
return _execute_patch
@@ -381,8 +449,7 @@ def variant(
description='',
values=None,
multi=False,
- validator=None
-):
+ validator=None):
"""Define a variant for the package. Packager can specify a default
value as well as a text description.
@@ -484,3 +551,7 @@ class DirectiveError(spack.error.SpackError):
class CircularReferenceError(DirectiveError):
"""This is raised when something depends on itself."""
+
+
+class DependencyPatchError(DirectiveError):
+ """Raised for errors with patching dependencies."""
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 1c722082e8..57653d7d8b 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -542,9 +542,12 @@ class PackageBase(with_metaclass(PackageMeta, object)):
#: Defaults to the empty string.
license_url = ''
- # Verbosity level, preserved across installs.
+ #: Verbosity level, preserved across installs.
_verbose = None
+ #: index of patches by sha256 sum, built lazily
+ _patches_by_hash = None
+
#: List of strings which contains GitHub usernames of package maintainers.
#: Do not include @ here in order not to unnecessarily ping the users.
maintainers = []
@@ -642,7 +645,7 @@ class PackageBase(with_metaclass(PackageMeta, object)):
@property
def package_dir(self):
"""Return the directory where the package.py file lives."""
- return os.path.dirname(self.module.__file__)
+ return os.path.abspath(os.path.dirname(self.module.__file__))
@property
def global_license_dir(self):
@@ -990,9 +993,42 @@ class PackageBase(with_metaclass(PackageMeta, object)):
self.stage.expand_archive()
self.stage.chdir_to_source()
+ @classmethod
+ def lookup_patch(cls, sha256):
+ """Look up a patch associated with this package by its sha256 sum.
+
+ Args:
+ sha256 (str): sha256 sum of the patch to look up
+
+ Returns:
+ (Patch): ``Patch`` object with the given hash, or ``None`` if
+ not found.
+
+ To do the lookup, we build an index lazily. This allows us to
+ avoid computing a sha256 for *every* patch and on every package
+ load. With lazy hashing, we only compute hashes on lookup, which
+ usually happens at build time.
+
+ """
+ if cls._patches_by_hash is None:
+ cls._patches_by_hash = {}
+
+ # Add patches from the class
+ for cond, patch_list in cls.patches.items():
+ for patch in patch_list:
+ cls._patches_by_hash[patch.sha256] = patch
+
+ # and patches on dependencies
+ for name, conditions in cls.dependencies.items():
+ for cond, dependency in conditions.items():
+ for pcond, patch_list in dependency.patches.items():
+ for patch in patch_list:
+ cls._patches_by_hash[patch.sha256] = patch
+
+ return cls._patches_by_hash.get(sha256, None)
+
def do_patch(self):
- """Calls do_stage(), then applied patches to the expanded tarball if they
- haven't been applied already."""
+ """Applies patches if they haven't been applied already."""
if not self.spec.concrete:
raise ValueError("Can only patch concrete packages.")
@@ -1002,8 +1038,11 @@ class PackageBase(with_metaclass(PackageMeta, object)):
# Package can add its own patch function.
has_patch_fun = hasattr(self, 'patch') and callable(self.patch)
+ # Get the patches from the spec (this is a shortcut for the MV-variant)
+ patches = self.spec.patches
+
# If there are no patches, note it.
- if not self.patches and not has_patch_fun:
+ if not patches and not has_patch_fun:
tty.msg("No patches needed for %s" % self.name)
return
@@ -1032,18 +1071,16 @@ class PackageBase(with_metaclass(PackageMeta, object)):
# Apply all the patches for specs that match this one
patched = False
- for spec, patch_list in self.patches.items():
- if self.spec.satisfies(spec):
- for patch in patch_list:
- try:
- patch.apply(self.stage)
- tty.msg('Applied patch %s' % patch.path_or_url)
- patched = True
- except:
- # Touch bad file if anything goes wrong.
- tty.msg('Patch %s failed.' % patch.path_or_url)
- touch(bad_file)
- raise
+ for patch in patches:
+ try:
+ patch.apply(self.stage)
+ tty.msg('Applied patch %s' % patch.path_or_url)
+ patched = True
+ except:
+ # Touch bad file if anything goes wrong.
+ tty.msg('Patch %s failed.' % patch.path_or_url)
+ touch(bad_file)
+ raise
if has_patch_fun:
try:
@@ -1054,9 +1091,10 @@ class PackageBase(with_metaclass(PackageMeta, object)):
# We are running a multimethod without a default case.
# If there's no default it means we don't need to patch.
if not patched:
- # if we didn't apply a patch, AND the patch function
- # didn't apply, say no patches are needed.
- # Otherwise, we already said we applied each patch.
+ # if we didn't apply a patch from a patch()
+ # directive, AND the patch function didn't apply, say
+ # no patches are needed. Otherwise, we already
+ # printed a message for each patch.
tty.msg("No patches needed for %s" % self.name)
except:
tty.msg("patch() function failed for %s" % self.name)
diff --git a/lib/spack/spack/patch.py b/lib/spack/spack/patch.py
index 3f0596b23a..2f52fbdc9f 100644
--- a/lib/spack/spack/patch.py
+++ b/lib/spack/spack/patch.py
@@ -25,13 +25,16 @@
import os
import os.path
import inspect
+import hashlib
import spack
import spack.error
import spack.fetch_strategy as fs
import spack.stage
+from spack.util.crypto import checksum, Checker
from llnl.util.filesystem import working_dir
from spack.util.executable import which
+from spack.util.compression import allowed_archive
def absolute_path_for_package(pkg):
@@ -39,8 +42,10 @@ def absolute_path_for_package(pkg):
the recipe for the package passed as argument.
Args:
- pkg: a valid package object
+ pkg: a valid package object, or a Dependency object.
"""
+ if isinstance(pkg, spack.dependency.Dependency):
+ pkg = pkg.pkg
m = inspect.getmodule(pkg)
return os.path.abspath(m.__file__)
@@ -51,7 +56,7 @@ class Patch(object):
"""
@staticmethod
- def create(pkg, path_or_url, level, **kwargs):
+ def create(pkg, path_or_url, level=1, **kwargs):
"""
Factory method that creates an instance of some class derived from
Patch
@@ -59,18 +64,18 @@ class Patch(object):
Args:
pkg: package that needs to be patched
path_or_url: path or url where the patch is found
- level: patch level
+ level: patch level (default 1)
Returns:
instance of some Patch class
"""
# Check if we are dealing with a URL
if '://' in path_or_url:
- return UrlPatch(pkg, path_or_url, level, **kwargs)
+ return UrlPatch(path_or_url, level, **kwargs)
# Assume patches are stored in the repository
return FilePatch(pkg, path_or_url, level)
- def __init__(self, pkg, path_or_url, level):
+ def __init__(self, path_or_url, level):
# Check on level (must be an integer > 0)
if not isinstance(level, int) or not level >= 0:
raise ValueError("Patch level needs to be a non-negative integer.")
@@ -100,20 +105,39 @@ class Patch(object):
class FilePatch(Patch):
"""Describes a patch that is retrieved from a file in the repository"""
def __init__(self, pkg, path_or_url, level):
- super(FilePatch, self).__init__(pkg, path_or_url, level)
+ super(FilePatch, self).__init__(path_or_url, level)
pkg_dir = os.path.dirname(absolute_path_for_package(pkg))
self.path = os.path.join(pkg_dir, path_or_url)
if not os.path.isfile(self.path):
- raise NoSuchPatchFileError(pkg.name, self.path)
+ raise NoSuchPatchError(
+ "No such patch for package %s: %s" % (pkg.name, self.path))
+ self._sha256 = None
+
+ @property
+ def sha256(self):
+ if self._sha256 is None:
+ self._sha256 = checksum(hashlib.sha256, self.path)
+ return self._sha256
class UrlPatch(Patch):
"""Describes a patch that is retrieved from a URL"""
- def __init__(self, pkg, path_or_url, level, **kwargs):
- super(UrlPatch, self).__init__(pkg, path_or_url, level)
+ def __init__(self, path_or_url, level, **kwargs):
+ super(UrlPatch, self).__init__(path_or_url, level)
self.url = path_or_url
- self.md5 = kwargs.get('md5')
+
+ self.archive_sha256 = None
+ if allowed_archive(self.url):
+ if 'archive_sha256' not in kwargs:
+ raise PatchDirectiveError(
+ "Compressed patches require 'archive_sha256' "
+ "and patch 'sha256' attributes: %s" % self.url)
+ self.archive_sha256 = kwargs.get('archive_sha256')
+
+ if 'sha256' not in kwargs:
+ raise PatchDirectiveError("URL patches require a sha256 checksum")
+ self.sha256 = kwargs.get('sha256')
def apply(self, stage):
"""Retrieve the patch in a temporary stage, computes
@@ -122,7 +146,12 @@ class UrlPatch(Patch):
Args:
stage: stage for the package that needs to be patched
"""
- fetcher = fs.URLFetchStrategy(self.url, digest=self.md5)
+ # use archive digest for compressed archives
+ fetch_digest = self.sha256
+ if self.archive_sha256:
+ fetch_digest = self.archive_sha256
+
+ fetcher = fs.URLFetchStrategy(self.url, digest=fetch_digest)
mirror = os.path.join(
os.path.dirname(stage.mirror_path),
os.path.basename(self.url))
@@ -132,20 +161,40 @@ class UrlPatch(Patch):
patch_stage.check()
patch_stage.cache_local()
- if spack.util.compression.allowed_archive(self.url):
+ root = patch_stage.path
+ if self.archive_sha256:
patch_stage.expand_archive()
-
- self.path = os.path.abspath(
- os.listdir(patch_stage.path).pop())
+ root = patch_stage.source_path
+
+ files = os.listdir(root)
+ if not files:
+ if self.archive_sha256:
+ raise NoSuchPatchError(
+ "Archive was empty: %s" % self.url)
+ else:
+ raise NoSuchPatchError(
+ "Patch failed to download: %s" % self.url)
+
+ self.path = os.path.join(root, files.pop())
+
+ if not os.path.isfile(self.path):
+ raise NoSuchPatchError(
+ "Archive %s contains no patch file!" % self.url)
+
+ # for a compressed archive, Need to check the patch sha256 again
+ # and the patch is in a directory, not in the same place
+ if self.archive_sha256:
+ if not Checker(self.sha256).check(self.path):
+ raise fs.ChecksumError(
+ "sha256 checksum failed for %s" % self.path,
+ "Expected %s but got %s" % (self.sha256, checker.sum))
super(UrlPatch, self).apply(stage)
-class NoSuchPatchFileError(spack.error.SpackError):
- """Raised when user specifies a patch file that doesn't exist."""
+class NoSuchPatchError(spack.error.SpackError):
+ """Raised when a patch file doesn't exist."""
+
- def __init__(self, package, path):
- super(NoSuchPatchFileError, self).__init__(
- "No such patch file for package %s: %s" % (package, path))
- self.package = package
- self.path = path
+class PatchDirectiveError(spack.error.SpackError):
+ """Raised when the wrong arguments are suppled to the patch directive."""
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index dc2042a7f4..d919aeecb7 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1488,8 +1488,7 @@ class Spec(object):
spec.compiler_flags[name] = value
else:
spec.variants[name] = MultiValuedVariant.from_node_dict(
- name, value
- )
+ name, value)
elif 'variants' in node:
for name, value in node['variants'].items():
spec.variants[name] = MultiValuedVariant.from_node_dict(
@@ -1806,6 +1805,43 @@ class Spec(object):
if s.namespace is None:
s.namespace = spack.repo.repo_for_pkg(s.name).namespace
+ # Add any patches from the package to the spec.
+ patches = []
+ for cond, patch_list in s.package_class.patches.items():
+ if s.satisfies(cond):
+ for patch in patch_list:
+ patches.append(patch.sha256)
+ if patches:
+ # Special-case: keeps variant values unique but ordered.
+ s.variants['patches'] = MultiValuedVariant('patches', ())
+ mvar = s.variants['patches']
+ mvar._value = mvar._original_value = tuple(dedupe(patches))
+
+ # Apply patches required on dependencies by depends_on(..., patch=...)
+ for dspec in self.traverse_edges(deptype=all,
+ cover='edges', root=False):
+ pkg_deps = dspec.parent.package_class.dependencies
+ if dspec.spec.name not in pkg_deps:
+ continue
+
+ patches = []
+ for cond, dependency in pkg_deps[dspec.spec.name].items():
+ if dspec.parent.satisfies(cond):
+ for pcond, patch_list in dependency.patches.items():
+ if dspec.spec.satisfies(pcond):
+ for patch in patch_list:
+ patches.append(patch.sha256)
+ if patches:
+ # note that we use a special multi-valued variant and
+ # keep the patches ordered.
+ if 'patches' not in dspec.spec.variants:
+ mvar = MultiValuedVariant('patches', ())
+ dspec.spec.variants['patches'] = mvar
+ else:
+ mvar = dspec.spec.variants['patches']
+ mvar._value = mvar._original_value = tuple(
+ dedupe(list(mvar._value) + patches))
+
for s in self.traverse():
if s.external_module:
compiler = spack.compilers.compiler_for_spec(
@@ -1908,7 +1944,7 @@ class Spec(object):
name (str): name of dependency to evaluate conditions on.
Returns:
- (tuple): tuple of ``Spec`` and tuple of ``deptypes``.
+ (Dependency): new Dependency object combining all constraints.
If the package depends on <name> in the current spec
configuration, return the constrained dependency and
@@ -1922,21 +1958,19 @@ class Spec(object):
substitute_abstract_variants(self)
# evaluate when specs to figure out constraints on the dependency.
- dep, deptypes = None, None
+ dep = None
for when_spec, dependency in conditions.items():
if self.satisfies(when_spec, strict=True):
if dep is None:
- dep = Spec(name)
- deptypes = set()
+ dep = Dependency(self.name, Spec(name), type=())
try:
- dep.constrain(dependency.spec)
- deptypes |= dependency.type
+ dep.merge(dependency)
except UnsatisfiableSpecError as e:
e.message = ("Conflicting conditional dependencies on"
"package %s for spec %s" % (self.name, self))
raise e
- return dep, deptypes
+ return dep
def _find_provider(self, vdep, provider_index):
"""Find provider for a virtual spec in the provider index.
@@ -1971,15 +2005,26 @@ class Spec(object):
elif required:
raise UnsatisfiableProviderSpecError(required[0], vdep)
- def _merge_dependency(self, dep, deptypes, visited, spec_deps,
- provider_index):
- """Merge the dependency into this spec.
+ def _merge_dependency(
+ self, dependency, visited, spec_deps, provider_index):
+ """Merge dependency information from a Package into this Spec.
- Caller should assume that this routine can owns the dep parameter
- (i.e. it needs to be a copy of any internal structures like
- dependencies on Package class objects).
-
- This is the core of normalize(). There are some basic steps:
+ Args:
+ dependency (Dependency): dependency metadata from a package;
+ this is typically the result of merging *all* matching
+ dependency constraints from the package.
+ visited (set): set of dependency nodes already visited by
+ ``normalize()``.
+ spec_deps (dict): ``dict`` of all dependencies from the spec
+ being normalized.
+ provider_index (dict): ``provider_index`` of virtual dep
+ providers in the ``Spec`` as normalized so far.
+
+ NOTE: Caller should assume that this routine owns the
+ ``dependency`` parameter, i.e., it needs to be a copy of any
+ internal structures.
+
+ This is the core of ``normalize()``. There are some basic steps:
* If dep is virtual, evaluate whether it corresponds to an
existing concrete dependency, and merge if so.
@@ -1994,6 +2039,7 @@ class Spec(object):
"""
changed = False
+ dep = dependency.spec
# If it's a virtual dependency, try to find an existing
# provider in the spec, and merge that.
@@ -2045,11 +2091,11 @@ class Spec(object):
raise
# Add merged spec to my deps and recurse
- dependency = spec_deps[dep.name]
+ spec_dependency = spec_deps[dep.name]
if dep.name not in self._dependencies:
- self._add_dependency(dependency, deptypes)
+ self._add_dependency(spec_dependency, dependency.type)
- changed |= dependency._normalize_helper(
+ changed |= spec_dependency._normalize_helper(
visited, spec_deps, provider_index)
return changed
@@ -2074,12 +2120,12 @@ class Spec(object):
changed = False
for dep_name in pkg.dependencies:
# Do we depend on dep_name? If so pkg_dep is not None.
- dep, deptypes = self._evaluate_dependency_conditions(dep_name)
+ dep = self._evaluate_dependency_conditions(dep_name)
# If dep is a needed dependency, merge it.
if dep and (spack.package_testing.check(self.name) or
- set(deptypes) - set(['test'])):
+ set(dep.type) - set(['test'])):
changed |= self._merge_dependency(
- dep, deptypes, visited, spec_deps, provider_index)
+ dep, visited, spec_deps, provider_index)
any_change |= changed
return any_change
@@ -2463,6 +2509,35 @@ class Spec(object):
"""Return list of any virtual deps in this spec."""
return [spec for spec in self.traverse() if spec.virtual]
+ @property
+ def patches(self):
+ """Return patch objects for any patch sha256 sums on this Spec.
+
+ This is for use after concretization to iterate over any patches
+ associated with this spec.
+
+ TODO: this only checks in the package; it doesn't resurrect old
+ patches from install directories, but it probably should.
+ """
+ if 'patches' not in self.variants:
+ return []
+
+ patches = []
+ for sha256 in self.variants['patches'].value:
+ patch = self.package.lookup_patch(sha256)
+ if patch:
+ patches.append(patch)
+ continue
+
+ # if not found in this package, check immediate dependents
+ # for dependency patches
+ for dep in self._dependents:
+ patch = dep.parent.package.lookup_patch(sha256)
+ if patch:
+ patches.append(patch)
+
+ return patches
+
def _dup(self, other, deps=True, cleardeps=True, caches=None):
"""Copy the spec other into self. This is an overwriting
copy. It does not copy any dependents (parents), but by default
@@ -2549,7 +2624,8 @@ class Spec(object):
def _dup_deps(self, other, deptypes, caches):
new_specs = {self.name: self}
- for dspec in other.traverse_edges(cover='edges', root=False):
+ for dspec in other.traverse_edges(cover='edges',
+ root=False):
if (dspec.deptypes and
not any(d in deptypes for d in dspec.deptypes)):
continue
diff --git a/lib/spack/spack/test/cmd/dependents.py b/lib/spack/spack/test/cmd/dependents.py
index c43270a2af..00a8f8168d 100644
--- a/lib/spack/spack/test/cmd/dependents.py
+++ b/lib/spack/spack/test/cmd/dependents.py
@@ -35,7 +35,8 @@ dependents = SpackCommand('dependents')
def test_immediate_dependents(builtin_mock):
out = dependents('libelf')
actual = set(re.split(r'\s+', out.strip()))
- assert actual == set(['dyninst', 'libdwarf'])
+ assert actual == set(['dyninst', 'libdwarf',
+ 'patch-a-dependency', 'patch-several-dependencies'])
def test_transitive_dependents(builtin_mock):
@@ -43,7 +44,8 @@ def test_transitive_dependents(builtin_mock):
actual = set(re.split(r'\s+', out.strip()))
assert actual == set(
['callpath', 'dyninst', 'libdwarf', 'mpileaks', 'multivalue_variant',
- 'singlevalue-variant-dependent'])
+ 'singlevalue-variant-dependent',
+ 'patch-a-dependency', 'patch-several-dependencies'])
def test_immediate_installed_dependents(builtin_mock, database):
diff --git a/lib/spack/spack/test/conftest.py b/lib/spack/spack/test/conftest.py
index 16ac5dfe86..80e93e8944 100644
--- a/lib/spack/spack/test/conftest.py
+++ b/lib/spack/spack/test/conftest.py
@@ -572,7 +572,7 @@ class MockPackage(object):
assert len(dependencies) == len(dependency_types)
for dep, dtype in zip(dependencies, dependency_types):
- d = Dependency(Spec(dep.name), type=dtype)
+ d = Dependency(self, Spec(dep.name), type=dtype)
if not conditions or dep.name not in conditions:
self.dependencies[dep.name] = {Spec(name): d}
else:
@@ -587,12 +587,15 @@ class MockPackage(object):
self.variants = {}
self.provided = {}
self.conflicts = {}
+ self.patches = {}
class MockPackageMultiRepo(object):
def __init__(self, packages):
self.spec_to_pkg = dict((x.name, x) for x in packages)
+ self.spec_to_pkg.update(
+ dict(('mockrepo.' + x.name, x) for x in packages))
def get(self, spec):
if not isinstance(spec, spack.spec.Spec):
diff --git a/lib/spack/spack/test/data/patch/bar.txt b/lib/spack/spack/test/data/patch/bar.txt
deleted file mode 100644
index ba578e48b1..0000000000
--- a/lib/spack/spack/test/data/patch/bar.txt
+++ /dev/null
@@ -1 +0,0 @@
-BAR
diff --git a/lib/spack/spack/test/data/patch/foo.patch b/lib/spack/spack/test/data/patch/foo.patch
new file mode 100644
index 0000000000..ff59bd4c54
--- /dev/null
+++ b/lib/spack/spack/test/data/patch/foo.patch
@@ -0,0 +1,7 @@
+--- a/foo.txt 2017-09-25 21:24:33.000000000 -0700
++++ b/foo.txt 2017-09-25 14:31:17.000000000 -0700
+@@ -1,2 +1,3 @@
++zeroth line
+ first line
+-second line
++third line
diff --git a/lib/spack/spack/test/data/patch/foo.tgz b/lib/spack/spack/test/data/patch/foo.tgz
index 73f598ac25..11ec586256 100644
--- a/lib/spack/spack/test/data/patch/foo.tgz
+++ b/lib/spack/spack/test/data/patch/foo.tgz
Binary files differ
diff --git a/lib/spack/spack/test/patch.py b/lib/spack/spack/test/patch.py
index 7976956748..0a91a0847c 100644
--- a/lib/spack/spack/test/patch.py
+++ b/lib/spack/spack/test/patch.py
@@ -22,63 +22,171 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
-
-import os.path
-
-import pytest
+import os
import sys
+import filecmp
+import pytest
+
+from llnl.util.filesystem import working_dir, mkdirp
import spack
import spack.util.compression
-import spack.stage
-
-
-@pytest.fixture()
-def mock_apply(monkeypatch):
- """Monkeypatches ``Patch.apply`` to test only the additional behavior of
- derived classes.
- """
-
- m = sys.modules['spack.patch']
-
- def check_expand(self, *args, **kwargs):
- # Check tarball expansion
- if spack.util.compression.allowed_archive(self.url):
- file = os.path.join(self.path, 'foo.txt')
- assert os.path.exists(file)
-
- # Check tarball fetching
- dirname = os.path.dirname(self.path)
- basename = os.path.basename(self.url)
- tarball = os.path.join(dirname, basename)
- assert os.path.exists(tarball)
-
- monkeypatch.setattr(m.Patch, 'apply', check_expand)
+from spack.stage import Stage
+from spack.spec import Spec
@pytest.fixture()
def mock_stage(tmpdir, monkeypatch):
-
- monkeypatch.setattr(spack, 'stage_path', str(tmpdir))
-
- class MockStage(object):
- def __init__(self):
- self.mirror_path = str(tmpdir)
-
- return MockStage()
+ # don't disrupt the spack install directory with tests.
+ mock_path = str(tmpdir)
+ monkeypatch.setattr(spack, 'stage_path', mock_path)
+ return mock_path
data_path = os.path.join(spack.test_path, 'data', 'patch')
-@pytest.mark.usefixtures('mock_apply')
-@pytest.mark.parametrize('filename,md5', [
- (os.path.join(data_path, 'foo.tgz'), 'bff717ca9cbbb293bdf188e44c540758'),
- (os.path.join(data_path, 'bar.txt'), 'f98bf6f12e995a053b7647b10d937912')
+@pytest.mark.parametrize('filename, sha256, archive_sha256', [
+ # compressed patch -- needs sha256 and archive_256
+ (os.path.join(data_path, 'foo.tgz'),
+ '252c0af58be3d90e5dc5e0d16658434c9efa5d20a5df6c10bf72c2d77f780866',
+ '4e8092a161ec6c3a1b5253176fcf33ce7ba23ee2ff27c75dbced589dabacd06e'),
+ # uncompressed patch -- needs only sha256
+ (os.path.join(data_path, 'foo.patch'),
+ '252c0af58be3d90e5dc5e0d16658434c9efa5d20a5df6c10bf72c2d77f780866',
+ None)
])
-def test_url_patch_expansion(mock_stage, filename, md5):
-
- m = sys.modules['spack.patch']
+def test_url_patch(mock_stage, filename, sha256, archive_sha256):
+ # Make a patch object
url = 'file://' + filename
- patch = m.Patch.create(None, url, 0, md5=md5)
- patch.apply(mock_stage)
+ m = sys.modules['spack.patch']
+ patch = m.Patch.create(
+ None, url, sha256=sha256, archive_sha256=archive_sha256)
+
+ # make a stage
+ with Stage(url) as stage: # TODO: url isn't used; maybe refactor Stage
+ # TODO: there is probably a better way to mock this.
+ stage.mirror_path = mock_stage # don't disrupt the spack install
+
+ # fake a source path
+ with working_dir(stage.path):
+ mkdirp('spack-expanded-archive')
+
+ with working_dir(stage.source_path):
+ # write a file to be patched
+ with open('foo.txt', 'w') as f:
+ f.write("""\
+first line
+second line
+""")
+ # write the expected result of patching.
+ with open('foo-expected.txt', 'w') as f:
+ f.write("""\
+zeroth line
+first line
+third line
+""")
+ # apply the patch and compare files
+ patch.apply(stage)
+
+ with working_dir(stage.source_path):
+ assert filecmp.cmp('foo.txt', 'foo-expected.txt')
+
+
+def test_patch_in_spec(builtin_mock, config):
+ """Test whether patches in a package appear in the spec."""
+ spec = Spec('patch')
+ spec.concretize()
+ assert 'patches' in list(spec.variants.keys())
+
+ # foo, bar, baz
+ assert (('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c',
+ '7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730',
+ 'bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c') ==
+ spec.variants['patches'].value)
+
+
+def test_patched_dependency(builtin_mock, config):
+ """Test whether patched dependencies work."""
+ spec = Spec('patch-a-dependency')
+ spec.concretize()
+ assert 'patches' in list(spec['libelf'].variants.keys())
+
+ # foo
+ assert (('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c',) ==
+ spec['libelf'].variants['patches'].value)
+
+
+def test_multiple_patched_dependencies(builtin_mock, config):
+ """Test whether multiple patched dependencies work."""
+ spec = Spec('patch-several-dependencies')
+ spec.concretize()
+
+ # basic patch on libelf
+ assert 'patches' in list(spec['libelf'].variants.keys())
+ # foo
+ assert (('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c',) ==
+ spec['libelf'].variants['patches'].value)
+
+ # URL patches
+ assert 'patches' in list(spec['fake'].variants.keys())
+ # urlpatch.patch, urlpatch.patch.gz
+ assert (('abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234',
+ '1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd') ==
+ spec['fake'].variants['patches'].value)
+
+
+def test_conditional_patched_dependencies(builtin_mock, config):
+ """Test whether conditional patched dependencies work."""
+ spec = Spec('patch-several-dependencies @1.0')
+ spec.concretize()
+
+ # basic patch on libelf
+ assert 'patches' in list(spec['libelf'].variants.keys())
+ # foo
+ assert (('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c',) ==
+ spec['libelf'].variants['patches'].value)
+
+ # conditional patch on libdwarf
+ assert 'patches' in list(spec['libdwarf'].variants.keys())
+ # bar
+ assert (('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730',) ==
+ spec['libdwarf'].variants['patches'].value)
+ # baz is conditional on libdwarf version
+ assert ('bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c'
+ not in spec['libdwarf'].variants['patches'].value)
+
+ # URL patches
+ assert 'patches' in list(spec['fake'].variants.keys())
+ # urlpatch.patch, urlpatch.patch.gz
+ assert (('abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234',
+ '1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd') ==
+ spec['fake'].variants['patches'].value)
+
+
+def test_conditional_patched_deps_with_conditions(builtin_mock, config):
+ """Test whether conditional patched dependencies with conditions work."""
+ spec = Spec('patch-several-dependencies @1.0 ^libdwarf@20111030')
+ spec.concretize()
+
+ # basic patch on libelf
+ assert 'patches' in list(spec['libelf'].variants.keys())
+ # foo
+ assert ('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'
+ in spec['libelf'].variants['patches'].value)
+
+ # conditional patch on libdwarf
+ assert 'patches' in list(spec['libdwarf'].variants.keys())
+ # bar
+ assert ('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730'
+ in spec['libdwarf'].variants['patches'].value)
+ # baz is conditional on libdwarf version (no guarantee on order w/conds)
+ assert ('bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c'
+ in spec['libdwarf'].variants['patches'].value)
+
+ # URL patches
+ assert 'patches' in list(spec['fake'].variants.keys())
+ # urlpatch.patch, urlpatch.patch.gz
+ assert (('abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234',
+ '1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd') ==
+ spec['fake'].variants['patches'].value)
diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py
index 719b3a2569..a45cb7a961 100644
--- a/lib/spack/spack/test/spec_dag.py
+++ b/lib/spack/spack/test/spec_dag.py
@@ -53,7 +53,7 @@ def saved_deps():
@pytest.fixture()
def set_dependency(saved_deps):
"""Returns a function that alters the dependency information
- for a package.
+ for a package in the ``saved_deps`` fixture.
"""
def _mock(pkg_name, spec, deptypes=all_deptypes):
"""Alters dependence information for a package.
@@ -67,7 +67,7 @@ def set_dependency(saved_deps):
saved_deps[pkg_name] = (pkg, pkg.dependencies.copy())
cond = Spec(pkg.name)
- dependency = Dependency(spec, deptypes)
+ dependency = Dependency(pkg, spec, type=deptypes)
pkg.dependencies[spec.name] = {cond: dependency}
return _mock
diff --git a/lib/spack/spack/variant.py b/lib/spack/spack/variant.py
index 0d75b83ebc..3f9ede1047 100644
--- a/lib/spack/spack/variant.py
+++ b/lib/spack/spack/variant.py
@@ -47,8 +47,7 @@ class Variant(object):
description,
values=(True, False),
multi=False,
- validator=None
- ):
+ validator=None):
"""Initialize a package variant.
Args:
@@ -220,10 +219,15 @@ class AbstractVariant(object):
def from_node_dict(name, value):
"""Reconstruct a variant from a node dict."""
if isinstance(value, list):
- value = ','.join(value)
- return MultiValuedVariant(name, value)
+ # read multi-value variants in and be faithful to the YAML
+ mvar = MultiValuedVariant(name, ())
+ mvar._value = tuple(value)
+ mvar._original_value = mvar._value
+ return mvar
+
elif str(value).upper() == 'TRUE' or str(value).upper() == 'FALSE':
return BoolValuedVariant(name, value)
+
return SingleValuedVariant(name, value)
def yaml_entry(self):
@@ -252,15 +256,16 @@ class AbstractVariant(object):
# Store the original value
self._original_value = value
- # Store a tuple of CSV string representations
- # Tuple is necessary here instead of list because the
- # values need to be hashed
- t = re.split(r'\s*,\s*', str(value))
+ if not isinstance(value, (tuple, list)):
+ # Store a tuple of CSV string representations
+ # Tuple is necessary here instead of list because the
+ # values need to be hashed
+ value = re.split(r'\s*,\s*', str(value))
# With multi-value variants it is necessary
# to remove duplicates and give an order
# to a set
- self._value = tuple(sorted(set(t)))
+ self._value = tuple(sorted(set(value)))
def _cmp_key(self):
return self.name, self.value
diff --git a/var/spack/repos/builtin.mock/packages/patch-a-dependency/foo.patch b/var/spack/repos/builtin.mock/packages/patch-a-dependency/foo.patch
new file mode 100644
index 0000000000..257cc5642c
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch-a-dependency/foo.patch
@@ -0,0 +1 @@
+foo
diff --git a/var/spack/repos/builtin.mock/packages/patch-a-dependency/package.py b/var/spack/repos/builtin.mock/packages/patch-a-dependency/package.py
new file mode 100644
index 0000000000..4d4f8113f9
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch-a-dependency/package.py
@@ -0,0 +1,39 @@
+##############################################################################
+# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+from spack import *
+
+
+class PatchADependency(Package):
+ """Package that requries a patched version of a dependency."""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/patch-a-dependency-1.0.tar.gz"
+
+ version('1.0', '0123456789abcdef0123456789abcdef')
+
+ depends_on('libelf', patches=patch('foo.patch'))
+
+ def install(self, spec, prefix):
+ pass
diff --git a/var/spack/repos/builtin.mock/packages/patch-several-dependencies/bar.patch b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/bar.patch
new file mode 100644
index 0000000000..5716ca5987
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/bar.patch
@@ -0,0 +1 @@
+bar
diff --git a/var/spack/repos/builtin.mock/packages/patch-several-dependencies/baz.patch b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/baz.patch
new file mode 100644
index 0000000000..76018072e0
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/baz.patch
@@ -0,0 +1 @@
+baz
diff --git a/var/spack/repos/builtin.mock/packages/patch-several-dependencies/foo.patch b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/foo.patch
new file mode 100644
index 0000000000..257cc5642c
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/foo.patch
@@ -0,0 +1 @@
+foo
diff --git a/var/spack/repos/builtin.mock/packages/patch-several-dependencies/package.py b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/package.py
new file mode 100644
index 0000000000..777c818c21
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch-several-dependencies/package.py
@@ -0,0 +1,60 @@
+##############################################################################
+# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+from spack import *
+
+
+class PatchSeveralDependencies(Package):
+ """Package that requries multiple patches on a dependency."""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/patch-a-dependency-1.0.tar.gz"
+
+ version('2.0', '0123456789abcdef0123456789abcdef')
+ version('1.0', '0123456789abcdef0123456789abcdef')
+
+ # demonstrate all the different ways to patch things
+
+ # single patch file in repo
+ depends_on('libelf', patches='foo.patch')
+
+ # using a list of patches in one depends_on
+ depends_on('libdwarf', patches=[
+ patch('bar.patch'), # nested patch directive
+ patch('baz.patch', when='@20111030') # and with a conditional
+ ], when='@1.0') # with a depends_on conditional
+
+ # URL patches
+ depends_on('fake', patches=[
+ # uncompressed URL patch
+ patch('http://example.com/urlpatch.patch',
+ sha256='abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234'),
+ # compressed URL patch requires separate archive sha
+ patch('http://example.com/urlpatch2.patch.gz',
+ archive_sha256='abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd',
+ sha256='1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd')
+ ])
+
+ def install(self, spec, prefix):
+ pass
diff --git a/var/spack/repos/builtin.mock/packages/patch/bar.patch b/var/spack/repos/builtin.mock/packages/patch/bar.patch
new file mode 100644
index 0000000000..5716ca5987
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch/bar.patch
@@ -0,0 +1 @@
+bar
diff --git a/var/spack/repos/builtin.mock/packages/patch/baz.patch b/var/spack/repos/builtin.mock/packages/patch/baz.patch
new file mode 100644
index 0000000000..76018072e0
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch/baz.patch
@@ -0,0 +1 @@
+baz
diff --git a/var/spack/repos/builtin.mock/packages/patch/foo.patch b/var/spack/repos/builtin.mock/packages/patch/foo.patch
new file mode 100644
index 0000000000..257cc5642c
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch/foo.patch
@@ -0,0 +1 @@
+foo
diff --git a/var/spack/repos/builtin.mock/packages/patch/package.py b/var/spack/repos/builtin.mock/packages/patch/package.py
new file mode 100644
index 0000000000..fd9c9ba2bd
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/patch/package.py
@@ -0,0 +1,41 @@
+##############################################################################
+# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+from spack import *
+
+
+class Patch(Package):
+ """Package that requries a patched version of a dependency."""
+
+ homepage = "http://www.example.com"
+ url = "http://www.example.com/patch-1.0.tar.gz"
+
+ version('1.0', '0123456789abcdef0123456789abcdef')
+
+ patch('foo.patch')
+ patch('bar.patch')
+ patch('baz.patch')
+
+ def install(self, spec, prefix):
+ pass
diff --git a/var/spack/repos/builtin/packages/nauty/package.py b/var/spack/repos/builtin/packages/nauty/package.py
index dcb02c417a..a46dbdc4ab 100644
--- a/var/spack/repos/builtin/packages/nauty/package.py
+++ b/var/spack/repos/builtin/packages/nauty/package.py
@@ -39,28 +39,36 @@ class Nauty(AutotoolsPackage):
urls_for_patches = {
'@2.6r7': [
# Debian patch to fix the gt_numorbits declaration
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-fix-gt_numorbits.patch', 'a6e1ef4897aabd67c104fd1d78bcc334'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-fix-gt_numorbits.patch',
+ 'c8e4546a7b262c92cee226beb1dc71d87d644b115375e9c8550598efcc00254f'),
# Debian patch to add explicit extern declarations where needed
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-fix-include-extern.patch', '741034dec2d2f8b418b6e186aa3eb50f'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-fix-include-extern.patch',
+ 'c52c62e4dc46532ad89632a3f59a9faf13dd7988e9ef29fc5e5b2a3e17449bb6'),
# Debian patch to use zlib instead of invoking zcat through a pipe
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-zlib-blisstog.patch', '667e1ce341f2506482ad30afd04f17e3'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-zlib-blisstog.patch',
+ 'b1210bfb41ddbeb4c956d660266f62e806026a559a4700ce78024a9db2b82168'),
# Debian patch to improve usage and help information
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-help2man.patch', '4202e6d83362daa2c4c4ab0788e11ac5'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-help2man.patch',
+ 'c11544938446a3eca70d55b0f1084ce56fb1fb415db1ec1b5a69fd310a02b16c'),
# Debian patch to add libtool support for building a shared library
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-autotoolization.patch', 'ea75f19c8a980c4d6d4e07223785c751'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-autotoolization.patch',
+ '7f60ae3d8aeee830306db991c908efae461f103527a7899ce79d936bb15212b5'),
# Debian patch to canonicalize header file usage
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-includes.patch', 'c6ce4209d1381fb5489ed552ef35d7dc'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-includes.patch',
+ '9a305f0cd3f1136a9885518bd7912c669d1ca4b2b43bd039d6fc5535b9679778'),
# Debian patch to prefix "nauty-" to the names of the generic tools
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-tool-prefix.patch', 'e89d87b4450adc5d0009ce11438dc975'), # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-tool-prefix.patch',
+ '736266813a62b3151e0b81ded6578bd0f53f03fc8ffbc54c7c2a2c64ac07b25f'),
# Fedora patch to detect availability of the popcnt
# instruction at runtime
- ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-popcnt.patch', '8a32d31a7150c8f5f21ccb1f6dc857b1') # noqa: E50
+ ('https://src.fedoraproject.org/rpms/nauty/raw/0f07d01caf84e9d30cb06b11af4860dd3837636a/f/nauty-popcnt.patch',
+ '0dc2e0374491dddf5757f0717d0ea3f949f85b540202385662f10c358b4a08e8')
]
}
# Iterate over patches
for condition, urls in urls_for_patches.items():
- for url, md5 in urls:
- patch(url, when=condition, level=1, md5=md5)
+ for url, sha256 in urls:
+ patch(url, when=condition, level=1, sha256=sha256)
depends_on('m4', type='build', when='@2.6r7')
depends_on('autoconf', type='build', when='@2.6r7')
diff --git a/var/spack/repos/builtin/packages/nwchem/package.py b/var/spack/repos/builtin/packages/nwchem/package.py
index 8148d385a3..24e9ee9277 100644
--- a/var/spack/repos/builtin/packages/nwchem/package.py
+++ b/var/spack/repos/builtin/packages/nwchem/package.py
@@ -43,34 +43,36 @@ class Nwchem(Package):
depends_on('python@2.7:2.8', type=('build', 'run'))
+ # first hash is sha256 of the patch (required for URL patches),
+ # second is sha256 for the archive.
# patches for 6.6-27746:
urls_for_patches = {
'@6.6': [
- ('http://www.nwchem-sw.org/images/Tddft_mxvec20.patch.gz', 'f91c6a04df56e228fe946291d2f38c9a'),
- ('http://www.nwchem-sw.org/images/Tools_lib64.patch.gz', 'b71e8dbad27f1c97b60a53ec34d3f6e0'),
- ('http://www.nwchem-sw.org/images/Config_libs66.patch.gz', 'cc4be792e7b5128c3f9b7b1167ade2cf'),
- ('http://www.nwchem-sw.org/images/Cosmo_meminit.patch.gz', '1d94685bf3b72d8ecd40c46334348ca7'),
- ('http://www.nwchem-sw.org/images/Sym_abelian.patch.gz', 'b19cade61c787916a73a4aaf6e2445d6'),
- ('http://www.nwchem-sw.org/images/Xccvs98.patch.gz', 'b9aecc516a3551dcf871cb2f066598cb'),
- ('http://www.nwchem-sw.org/images/Dplot_tolrho.patch.gz', '0a5bdad63d2d0ffe46b28db7ad6d9cec'),
- ('http://www.nwchem-sw.org/images/Driver_smalleig.patch.gz', 'c3f609947220c0adb524b02c316b5564'),
- ('http://www.nwchem-sw.org/images/Ga_argv.patch.gz', '7a665c981cfc17187455e1826f095f6f'),
- ('http://www.nwchem-sw.org/images/Raman_displ.patch.gz', 'ed334ca0b2fe81ce103ef8cada990c4c'),
- ('http://www.nwchem-sw.org/images/Ga_defs.patch.gz', '0c3cab4d5cbef5acac16ffc5e6f869ef'),
- ('http://www.nwchem-sw.org/images/Zgesvd.patch.gz', '8fd5a11622968ef4351bd3d5cddce8f2'),
- ('http://www.nwchem-sw.org/images/Cosmo_dftprint.patch.gz', '64dcf27f3c6ced2cadfb504fa66e9d08'),
- ('http://www.nwchem-sw.org/images/Txs_gcc6.patch.gz', '56595a7252da051da13f94edc54fe059'),
- ('http://www.nwchem-sw.org/images/Gcc6_optfix.patch.gz', 'c6642c21363c09223784b47b8636047d'),
- ('http://www.nwchem-sw.org/images/Util_gnumakefile.patch.gz', 'af74ea2e32088030137001ce5cb047c5'),
- ('http://www.nwchem-sw.org/images/Util_getppn.patch.gz', '8dec8ee198bf5ec4c3a22a6dbf31683c'),
- ('http://www.nwchem-sw.org/images/Gcc6_macs_optfix.patch.gz', 'a891a2713aac8b0423c8096461c243eb'),
- ('http://www.nwchem-sw.org/images/Notdir_fc.patch.gz', '2dc997d4ab3719ac7964201adbc6fd79')
+ ('http://www.nwchem-sw.org/images/Tddft_mxvec20.patch.gz', 'ae04d4754c25fc324329dab085d4cc64148c94118ee702a7e14fce6152b4a0c5', 'cdfa8a5ae7d6ee09999407573b171beb91e37e1558a3bfb2d651982a85f0bc8f'),
+ ('http://www.nwchem-sw.org/images/Tools_lib64.patch.gz', 'ef2eadef89c055c4651ea807079577bd90e1bc99ef6c89f112f1f0e7560ec9b4', '76b8d3e1b77829b683234c8307fde55bc9249b87410914b605a76586c8f32dae'),
+ ('http://www.nwchem-sw.org/images/Config_libs66.patch.gz', '56f9c4bab362d82fb30d97564469e77819985a38e15ccaf04f647402c1ee248e', 'aa17f03cbb22ad7d883e799e0fddad1b5957f5f30b09f14a1a2caeeb9663cc07'),
+ ('http://www.nwchem-sw.org/images/Cosmo_meminit.patch.gz', 'f05f09ca235ad222fe47d880bfd05a1b88d0148b990ca8c7437fa231924be04b', '569c5ee528f3922ee60ca831eb20ec6591633a36f80efa76cbbe41cabeb9b624'),
+ ('http://www.nwchem-sw.org/images/Sym_abelian.patch.gz', 'e3470fb5786ab30bf2eda3bb4acc1e4c48fb5e640a09554abecf7d22b315c8fd', 'aa693e645a98dbafbb990e26145d65b100d6075254933f36326cf00bac3c29e0'),
+ ('http://www.nwchem-sw.org/images/Xccvs98.patch.gz', '75540e0436c12e193ed0b644cff41f5036d78c101f14141846083f03ad157afa', '1c0b0f1293e3b9b05e9e51e7d5b99977ccf1edb4b072872c8316452f6cea6f13'),
+ ('http://www.nwchem-sw.org/images/Dplot_tolrho.patch.gz', '8c30f92730d15f923ec8a623e3b311291eb2ba8b9d5a9884716db69a18d14f24', '2ebb1a5575c44eef4139da91f0e1e60057b2eccdba7f57a8fb577e840c326cbb'),
+ ('http://www.nwchem-sw.org/images/Driver_smalleig.patch.gz', 'a040df6f1d807402ce552ba6d35c9610d5efea7a9d6342bbfbf03c8d380a4058', 'dd65bfbae6b472b94c8ee81d74f6c3ece37c8fc8766ff7a3551d8005d44815b8'),
+ ('http://www.nwchem-sw.org/images/Ga_argv.patch.gz', '6fcd3920978ab95083483d5ed538cd9a6f2a80c2cafa0c5c7450fa5621f0a314', '8a78cb2af14314b92be9d241b801e9b9fed5527b9cb47a083134c7becdfa7cf1'),
+ ('http://www.nwchem-sw.org/images/Raman_displ.patch.gz', 'ca4312cd3ed1ceacdc3a7d258bb05b7824c393bf44f44c28a789ebeb29a8dba4', '6a16f0f589a5cbb8d316f68bd2e6a0d46cd47f1c699a4b256a3973130061f6c3'),
+ ('http://www.nwchem-sw.org/images/Ga_defs.patch.gz', 'f8ac827fbc11f7d2a9d8ec840c6f79d4759ef782bd4d291f2e88ec81b1b230aa', 'c6f1a48338d196e1db22bcfc6087e2b2e6eea50a34d3a2b2d3e90cccf43742a9'),
+ ('http://www.nwchem-sw.org/images/Zgesvd.patch.gz', 'c333a94ceb2c35a490f24b007485ac6e334e153b03cfc1d093b6037221a03517', '4af592c047dc3e0bc4962376ae2c6ca868eb7a0b40a347ed9b88e887016ad9ed'),
+ ('http://www.nwchem-sw.org/images/Cosmo_dftprint.patch.gz', '449d59983dc68c23b34e6581370b2fb3d5ea425b05c3182f0973e5b0e1a62651', 'd3b73431a68d6733eb7b669d471e18a83e03fa8e40c48e536fe8edecd99250ff'),
+ ('http://www.nwchem-sw.org/images/Txs_gcc6.patch.gz', '1dab87f23b210e941c765f7dd7cc2bed06d292a2621419dede73f10ba1ca1bcd', '139692215718cd7414896470c0cc8b7817a73ece1e4ca93bf752cf1081a195af'),
+ ('http://www.nwchem-sw.org/images/Gcc6_optfix.patch.gz', '8f8a5f8246bc1e42ef0137049acab4448a2e560339f44308703589adf753c148', '15cff43ab0509e0b0e83c49890032a848d6b7116bd6c8e5678e6c933f2d051ab'),
+ ('http://www.nwchem-sw.org/images/Util_gnumakefile.patch.gz', '173e17206a9099c3512b87e3f42441f5b089db82be1d2b306fe2a0070e5c8fad', '5dd82b9bd55583152295c999a0e4d72dd9d5c6ab7aa91117c2aae57a95a14ba1'),
+ ('http://www.nwchem-sw.org/images/Util_getppn.patch.gz', 'c4a23592fdcfb1fb6b65bc6c1906ac36f9966eec4899c4329bc8ce12015d2495', '8be418e1f8750778a31056f1fdf2a693fa4a12ea86a531f1ddf6f3620421027e'),
+ ('http://www.nwchem-sw.org/images/Gcc6_macs_optfix.patch.gz', 'ff33d5f1ccd33385ffbe6ce7a18ec1506d55652be6e7434dc8065af64c879aaa', 'fade16098a1f54983040cdeb807e4e310425d7f66358807554e08392685a7164'),
+ ('http://www.nwchem-sw.org/images/Notdir_fc.patch.gz', '54c722fa807671d6bf1a056586f0923593319d09c654338e7dd461dcd29ff118', 'a6a233951eb254d8aff5b243ca648def21fa491807a66c442f59c437f040ee69')
]
}
# Iterate over patches
for condition, urls in urls_for_patches.items():
- for url, md5 in urls:
- patch(url, when=condition, level=0, md5=md5)
+ for url, sha256, archive_sha256 in urls:
+ patch(url, when=condition, level=0, sha256=sha256, archive_sha256=archive_sha256)
def install(self, spec, prefix):
scalapack = spec['scalapack'].libs
diff --git a/var/spack/repos/builtin/packages/tcsh/package.py b/var/spack/repos/builtin/packages/tcsh/package.py
index ccf844a6c5..a007001701 100644
--- a/var/spack/repos/builtin/packages/tcsh/package.py
+++ b/var/spack/repos/builtin/packages/tcsh/package.py
@@ -43,19 +43,19 @@ class Tcsh(AutotoolsPackage):
patch('{0}{1}'.format(prefix, file), **kwargs)
# Upstream patches
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-000-add-all-flags-for-gethost-build.patch', when='@6.20.00', md5='05f85110bf2dd17324fc9825590df63e') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-001-delay-arginp-interpreting.patch', when='@6.20.00', md5='7df17b51be5c24bc02f854f3b4237324') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-002-type-of-read-in-prompt-confirm.patch', when='@6.20.00', md5='27941364ec07e797b533902a6445e0de') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-003-fix-out-of-bounds-read.patch', when='@6.20.00', md5='da300b7bf28667ee69bbdc5219f8e0b3') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-004-do-not-use-old-pointer-tricks.patch', when='@6.20.00', md5='702a0011e96495acb93653733f36b073') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-005-reset-fixes-numbering.patch', when='@6.20.00', md5='8a0fc5b74107b4d7ea7b10b1d6aebe9d') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-006-cleanup-in-readme-files.patch', when='@6.20.00', md5='2c8fec7652af53229eb22535363e9eac') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-007-look-for-tgetent-in-libtinfo.patch', when='@6.20.00', md5='69eacbbe9d9768164f1272c303df44aa') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-008-guard-ascii-only-reversion.patch', when='@6.20.00', md5='0415789a4804cf6320cc83f5c8414a63') # noqa: E501
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-009-fix-regexp-for-backlash-quoting-tests.patch', when='@6.20.00', md5='90b3f10eb744c2b26155618d8232a4e9') # noqa: E501
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-000-add-all-flags-for-gethost-build.patch', when='@6.20.00', sha256='f8266916189ebbdfbad5c2c28ac00ed25f07be70f054d9830eb84ba84b3d03ef')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-001-delay-arginp-interpreting.patch', when='@6.20.00', sha256='57c7a9b0d94dd41e4276b57b0a4a89d91303d36180c1068b9e3ab8f6149b18dd')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-002-type-of-read-in-prompt-confirm.patch', when='@6.20.00', sha256='837a6a82f815c0905cf7ea4c4ef0112f36396fc8b2138028204000178a1befa5')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-003-fix-out-of-bounds-read.patch', when='@6.20.00', sha256='f973bd33a7fd8af0002a9b8992216ffc04fdf2927917113e42e58f28b702dc14')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-004-do-not-use-old-pointer-tricks.patch', when='@6.20.00', sha256='333e111ed39f7452f904590b47b996812590b8818f1c51ad68407dc05a1b18b0')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-005-reset-fixes-numbering.patch', when='@6.20.00', sha256='d1b54b5c5432faed9791ffde813560e226896a68fc5933d066172bcf3b2eb8bd')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-006-cleanup-in-readme-files.patch', when='@6.20.00', sha256='b4e7428ac6c2918beacc1b73f33e784ac520ef981d87e98285610b1bfa299d7b')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-007-look-for-tgetent-in-libtinfo.patch', when='@6.20.00', sha256='e6c88ffc291c9d4bda4d6bedf3c9be89cb96ce7dc245163e251345221fa77216')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-008-guard-ascii-only-reversion.patch', when='@6.20.00', sha256='7ee195e4ce4c9eac81920843b4d4d27254bec7b43e0b744f457858a9f156e621')
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-009-fix-regexp-for-backlash-quoting-tests.patch', when='@6.20.00', sha256='d2358c930d5ab89e5965204dded499591b42a22d0a865e2149b8c0f1446fac34')
# Downstream patches
- fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-manpage-memoryuse.patch', md5='1fd35c430992aaa52dc90261e331acd5') # noqa: E501
+ fedora_patch('8a6066c901fb4fc75013dd488ba958387f00c74d', 'tcsh-6.20.00-manpage-memoryuse.patch', sha256='3a4e60fe56a450632140c48acbf14d22850c1d72835bf441e3f8514d6c617a9f') # noqa: E501
depends_on('ncurses')