Documentation for dependency patching.
This commit is contained in:
		| @@ -1212,27 +1212,47 @@ structure like this: | |||||||
|            package.py |            package.py | ||||||
|            ad_lustre_rwcontig_open_source.patch |            ad_lustre_rwcontig_open_source.patch | ||||||
|  |  | ||||||
| If you supply a URL instead of a filename, you need to supply a checksum, | If you supply a URL instead of a filename, you need to supply a | ||||||
| like this: | ``sha256`` checksum, like this: | ||||||
|  |  | ||||||
|  | .. code-block:: python | ||||||
|  |  | ||||||
|  |    patch('http://www.nwchem-sw.org/images/Tddft_mxvec20.patch', | ||||||
|  |          sha256='252c0af58be3d90e5dc5e0d16658434c9efa5d20a5df6c10bf72c2d77f780866') | ||||||
|  |  | ||||||
|  | Spack includes the hashes of patches in its versioning information, so | ||||||
|  | that the same package with different patches applied will have different | ||||||
|  | hash identifiers.  To ensure that the hashing scheme is consistent, you | ||||||
|  | must use a ``sha256`` checksum for the patch.  Patches will be fetched | ||||||
|  | from their URLs, checked, and applied to your source code.  You can use | ||||||
|  | the ``spack sha256`` command to generate a checksum for a patch file or | ||||||
|  | URL. | ||||||
|  |  | ||||||
|  | Spack can also handle compressed patches.  If you use these, Spack needs | ||||||
|  | a little more help.  Specifically, it needs *two* checksums: the | ||||||
|  | ``sha256`` of the patch and ``archive_sha256`` for the compressed | ||||||
|  | archive.  ``archive_sha256`` helps Spack ensure that the downloaded | ||||||
|  | file is not corrupted or malicious, before running it through a tool like | ||||||
|  | ``tar`` or ``zip``.  The ``sha256`` of the patch is still required so | ||||||
|  | that it can be included in specs.  Providing it in the package file | ||||||
|  | ensures that Spack won't have to download and decompress patches it won't | ||||||
|  | end up using at install time.  Both the archive and patch checksum are | ||||||
|  | checked when patch archives are downloaded. | ||||||
|  |  | ||||||
| .. code-block:: python | .. code-block:: python | ||||||
|  |  | ||||||
|    patch('http://www.nwchem-sw.org/images/Tddft_mxvec20.patch.gz', |    patch('http://www.nwchem-sw.org/images/Tddft_mxvec20.patch.gz', | ||||||
|          md5='f91c6a04df56e228fe946291d2f38c9a') |          sha256='252c0af58be3d90e5dc5e0d16658434c9efa5d20a5df6c10bf72c2d77f780866', | ||||||
|  |          archive_sha256='4e8092a161ec6c3a1b5253176fcf33ce7ba23ee2ff27c75dbced589dabacd06e') | ||||||
|  |  | ||||||
| This directive provides an ``md5`` checksum.  You can use other hashing | ``patch`` keyword arguments are described below. | ||||||
| algorihtms like ``sha256`` as well.  The patch will be fetched from the |  | ||||||
| URL, checked, and applied to your source code.  You can use the ``spack |  | ||||||
| md5`` command to generate a checksum for a patch file. |  | ||||||
|  |  | ||||||
| ``patch`` can take two options keyword arguments.  They are: | """""""""""""""""""""""""""""" | ||||||
|  | ``sha256``, ``archive_sha256`` | ||||||
|  | """""""""""""""""""""""""""""" | ||||||
|  |  | ||||||
| """""""""""""""""""""""""""""""""""""" | Hashes of downloaded patch and compressed archive, respectively.  Only | ||||||
| ``md5``, ``sha256``, ``sha512``, etc. | needed for patches fetched from URLs. | ||||||
| """""""""""""""""""""""""""""""""""""" |  | ||||||
|  |  | ||||||
| Use one of these when you supply a patch to be downloaded from a remote |  | ||||||
| site. The downloaded file will be validated using the given checksum. |  | ||||||
|  |  | ||||||
| """""""" | """""""" | ||||||
| ``when`` | ``when`` | ||||||
| @@ -1309,6 +1329,21 @@ if you run install, hit ctrl-C, and run install again, the code in the | |||||||
| patch function is only run once.  Also, you can tell Spack to run only | patch function is only run once.  Also, you can tell Spack to run only | ||||||
| the patching part of the build using the :ref:`cmd-spack-patch` command. | the patching part of the build using the :ref:`cmd-spack-patch` command. | ||||||
|  |  | ||||||
|  | .. _patch_dependency_patching: | ||||||
|  |  | ||||||
|  | ^^^^^^^^^^^^^^^^^^^ | ||||||
|  | Dependency patching | ||||||
|  | ^^^^^^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
|  | So far we've covered how the ``patch`` directive can be used by a package | ||||||
|  | to patch *its own* source code. Packages can *also* specify patches to be | ||||||
|  | applied to their dependencies, if they require special modifications.  As | ||||||
|  | with all packages in Spack, a patched dependency library can coexist with | ||||||
|  | other versions of that library.  See the `section on depends_on | ||||||
|  | <dependency_dependency_patching_>`_ for more details. | ||||||
|  |  | ||||||
|  | .. _handling_rpaths: | ||||||
|  |  | ||||||
| --------------- | --------------- | ||||||
| Handling RPATHs | Handling RPATHs | ||||||
| --------------- | --------------- | ||||||
| @@ -1482,7 +1517,7 @@ particular constraints, and package authors can use specs to describe | |||||||
| relationships between packages. | relationships between packages. | ||||||
|  |  | ||||||
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^ | ||||||
| Version Ranges | Version ranges | ||||||
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
| Although some packages require a specific version for their dependencies, | Although some packages require a specific version for their dependencies, | ||||||
| @@ -1530,7 +1565,7 @@ correct way to specify this would be: | |||||||
|  |  | ||||||
|  |  | ||||||
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^ | ||||||
| Dependency Types | Dependency types | ||||||
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
| Not all dependencies are created equal, and Spack allows you to specify | Not all dependencies are created equal, and Spack allows you to specify | ||||||
| @@ -1566,6 +1601,91 @@ inject the dependency's ``prefix/lib`` directory, but the package needs to | |||||||
| be in ``PATH`` and ``PYTHONPATH`` during the build process and later when | be in ``PATH`` and ``PYTHONPATH`` during the build process and later when | ||||||
| a user wants to run the package. | a user wants to run the package. | ||||||
|  |  | ||||||
|  | .. _dependency_dependency_patching: | ||||||
|  |  | ||||||
|  | ^^^^^^^^^^^^^^^^^^^ | ||||||
|  | Dependency patching | ||||||
|  | ^^^^^^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
|  | Some packages maintain special patches on their dependencies, either to | ||||||
|  | add new features or to fix bugs.  This typically makes a package harder | ||||||
|  | to maintain, and we encourage developers to upstream (contribute back) | ||||||
|  | their changes rather than maintaining patches.  However, in some cases | ||||||
|  | it's not possible to upstream. Maybe the dependency's developers don't | ||||||
|  | accept changes, or maybe they just haven't had time to integrate them. | ||||||
|  |  | ||||||
|  | For times like these, Spack's ``depends_on`` directive can optionally | ||||||
|  | take a patch or list of patches: | ||||||
|  |  | ||||||
|  | .. code-block:: python | ||||||
|  |  | ||||||
|  |     class SpecialTool(Package): | ||||||
|  |         ... | ||||||
|  |         depends_on('binutils', patches='special-binutils-feature.patch') | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  | Here, the ``special-tool`` package requires a special feature in | ||||||
|  | ``binutils``, so it provides an extra ``patches=<filename>`` keyword | ||||||
|  | argument.  This is similar to the `patch directive <patching_>`_, with | ||||||
|  | one small difference.  Here, ``special-tool`` is responsible for the | ||||||
|  | patch, so it should live in ``special-tool``'s directory in the package | ||||||
|  | repository, not the ``binutils`` directory. | ||||||
|  |  | ||||||
|  | If you need something more sophisticated than this, you can simply nest a | ||||||
|  | ``patch()`` directive inside of ``depends_on``: | ||||||
|  |  | ||||||
|  | .. code-block:: python | ||||||
|  |  | ||||||
|  |     class SpecialTool(Package): | ||||||
|  |         ... | ||||||
|  |         depends_on( | ||||||
|  |             'binutils', | ||||||
|  |             patches=patch('special-binutils-feature.patch', | ||||||
|  |                           level=3, | ||||||
|  |                           when='@:1.3'),   # condition on binutils | ||||||
|  |             when='@2.0:')                  # condition on special-tool | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  | Note that there are two optional ``when`` conditions here -- one on the | ||||||
|  | ``patch`` directive and the other on ``depends_on``.  The condition in | ||||||
|  | the ``patch`` directive applies to ``binutils`` (the package being | ||||||
|  | patched), while the condition in ``depends_on`` applies to | ||||||
|  | ``special-tool``.  See `patch directive <patching_>`_ for details on all | ||||||
|  | the arguments the ``patch`` directive can take. | ||||||
|  |  | ||||||
|  | Finally, if you need *multiple* patches on a dependency, you can provide | ||||||
|  | a list for ``patches``, e.g.: | ||||||
|  |  | ||||||
|  | .. code-block:: python | ||||||
|  |  | ||||||
|  |     class SpecialTool(Package): | ||||||
|  |         ... | ||||||
|  |         depends_on( | ||||||
|  |             'binutils', | ||||||
|  |             patches=[ | ||||||
|  |                 'binutils-bugfix1.patch', | ||||||
|  |                 'binutils-bugfix2.patch', | ||||||
|  |                 patch('https://example.com/special-binutils-feature.patch', | ||||||
|  |                       sha256='252c0af58be3d90e5dc5e0d16658434c9efa5d20a5df6c10bf72c2d77f780866', | ||||||
|  |                       when='@:1.3')], | ||||||
|  |             when='@2.0:') | ||||||
|  |         ... | ||||||
|  |  | ||||||
|  | As with ``patch`` directives, patches are applied in the order they | ||||||
|  | appear in the package file (or in this case, in the list). | ||||||
|  |  | ||||||
|  | .. note:: | ||||||
|  |  | ||||||
|  |    You may wonder whether dependency patching will interfere with other | ||||||
|  |    packages that depend on ``binutils``.  It won't. | ||||||
|  |  | ||||||
|  |    As described in patching_, Patching a package adds the ``sha256`` of | ||||||
|  |    the patch to the package's spec, which means it will have a | ||||||
|  |    *different* unique hash than other versions without the patch.  The | ||||||
|  |    patched version coexists with unpatched versions, and Spack's support | ||||||
|  |    for handling_rpaths_ guarantees that each installation finds the | ||||||
|  |    right version. If two packages depend on ``binutils`` patched *the | ||||||
|  |    same* way, they can both use a single installation of ``binutils``. | ||||||
|  |  | ||||||
| .. _setup-dependent-environment: | .. _setup-dependent-environment: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -42,13 +42,15 @@ def setup_parser(subparser): | |||||||
|                            help="files/urls to checksum") |                            help="files/urls to checksum") | ||||||
|  |  | ||||||
|  |  | ||||||
| def compute_md5_checksum(url): | def compute_checksum(url, algo): | ||||||
|  |     algo = getattr(hashlib, algo) | ||||||
|  |  | ||||||
|     if not os.path.isfile(url): |     if not os.path.isfile(url): | ||||||
|         with Stage(url) as stage: |         with Stage(url) as stage: | ||||||
|             stage.fetch() |             stage.fetch() | ||||||
|             value = spack.util.crypto.checksum(hashlib.md5, stage.archive_file) |             value = spack.util.crypto.checksum(algo, stage.archive_file) | ||||||
|     else: |     else: | ||||||
|         value = spack.util.crypto.checksum(hashlib.md5, url) |         value = spack.util.crypto.checksum(algo, url) | ||||||
|     return value |     return value | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -61,7 +63,7 @@ def normalized(files): | |||||||
|         yield value |         yield value | ||||||
|  |  | ||||||
|  |  | ||||||
| def md5(parser, args): | def do_checksum(parser, args, algo): | ||||||
|     if not args.files: |     if not args.files: | ||||||
|         setup_parser.parser.print_help() |         setup_parser.parser.print_help() | ||||||
|         return 1 |         return 1 | ||||||
| @@ -70,7 +72,7 @@ def md5(parser, args): | |||||||
|     results = [] |     results = [] | ||||||
|     for url in urls: |     for url in urls: | ||||||
|         try: |         try: | ||||||
|             checksum = compute_md5_checksum(url) |             checksum = compute_checksum(url, algo) | ||||||
|             results.append((checksum, url)) |             results.append((checksum, url)) | ||||||
|         except FailedDownloadError as e: |         except FailedDownloadError as e: | ||||||
|             tty.warn("Failed to fetch %s" % url) |             tty.warn("Failed to fetch %s" % url) | ||||||
| @@ -79,8 +81,12 @@ def md5(parser, args): | |||||||
|             tty.warn("Error when reading %s" % url) |             tty.warn("Error when reading %s" % url) | ||||||
|             tty.warn("%s" % e) |             tty.warn("%s" % e) | ||||||
|  |  | ||||||
|     # Dump the MD5s at last without interleaving them with downloads |     # Dump the hashes last, without interleaving them with downloads | ||||||
|     checksum = 'checksum' if len(results) == 1 else 'checksums' |     checksum = 'checksum' if len(results) == 1 else 'checksums' | ||||||
|     tty.msg("%d MD5 %s:" % (len(results), checksum)) |     tty.msg("%d %s %s:" % (len(results), algo, checksum)) | ||||||
|     for checksum, url in results: |     for checksum, url in results: | ||||||
|         print("{0}  {1}".format(checksum, url)) |         print("{0}  {1}".format(checksum, url)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def md5(parser, args): | ||||||
|  |     do_checksum(parser, args, 'md5') | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								lib/spack/spack/cmd/sha256.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/spack/spack/cmd/sha256.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | ############################################################################## | ||||||
|  | # 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 | ||||||
|  | ############################################################################## | ||||||
|  | import argparse | ||||||
|  | from spack.cmd.md5 import do_checksum | ||||||
|  |  | ||||||
|  | description = "calculate sha256 checksums for files/urls" | ||||||
|  | section = "packaging" | ||||||
|  | level = "long" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def setup_parser(subparser): | ||||||
|  |     setup_parser.parser = subparser | ||||||
|  |     subparser.add_argument('files', nargs=argparse.REMAINDER, | ||||||
|  |                            help="files/urls to checksum") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def sha256(parser, args): | ||||||
|  |     do_checksum(parser, args, 'sha256') | ||||||
		Reference in New Issue
	
	Block a user
	 Todd Gamblin
					Todd Gamblin