Bootstrap GnuPG (#24003)

* GnuPG: allow bootstrapping from buildcache and sources

* Add a test to bootstrap GnuPG from binaries

* Disable bootstrapping in tests

* Add e2e test to bootstrap GnuPG from sources on Ubuntu

* Add e2e test to bootstrap GnuPG on macOS
This commit is contained in:
Massimiliano Culpo 2021-11-03 07:15:24 +01:00 committed by GitHub
parent 1a3747b2b3
commit 78c08fccd5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 549 additions and 79 deletions

View file

@ -19,7 +19,7 @@ on:
jobs:
fedora-sources:
fedora-clingo-sources:
runs-on: ubuntu-latest
container: "fedora:latest"
steps:
@ -46,7 +46,7 @@ jobs:
spack -d solve zlib
tree ~/.spack/bootstrap/store/
ubuntu-sources:
ubuntu-clingo-sources:
runs-on: ubuntu-latest
container: "ubuntu:latest"
steps:
@ -76,7 +76,7 @@ jobs:
spack -d solve zlib
tree ~/.spack/bootstrap/store/
opensuse-sources:
opensuse-clingo-sources:
runs-on: ubuntu-latest
container: "opensuse/leap:latest"
steps:
@ -101,7 +101,7 @@ jobs:
spack -d solve zlib
tree ~/.spack/bootstrap/store/
macos-sources:
macos-clingo-sources:
runs-on: macos-latest
steps:
- name: Install dependencies
@ -137,7 +137,6 @@ jobs:
spack -d solve zlib
tree ~/.spack/bootstrap/store/
ubuntu-clingo-binaries:
runs-on: ubuntu-latest
strategy:
@ -159,3 +158,94 @@ jobs:
spack bootstrap untrust spack-install
spack -d solve zlib
tree ~/.spack/bootstrap/store/
ubuntu-gnupg-binaries:
runs-on: ubuntu-latest
container: "ubuntu:latest"
steps:
- name: Install dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt-get update -y && apt-get upgrade -y
apt-get install -y \
bzip2 curl file g++ gcc patchelf gfortran git gzip \
make patch unzip xz-utils python3 python3-dev tree
- uses: actions/checkout@v2
- name: Setup repo and non-root user
run: |
git --version
git fetch --unshallow
. .github/workflows/setup_git.sh
useradd -m spack-test
chown -R spack-test .
- name: Bootstrap GnuPG
shell: runuser -u spack-test -- bash {0}
run: |
source share/spack/setup-env.sh
spack bootstrap untrust spack-install
spack -d gpg list
tree ~/.spack/bootstrap/store/
ubuntu-gnupg-sources:
runs-on: ubuntu-latest
container: "ubuntu:latest"
steps:
- name: Install dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt-get update -y && apt-get upgrade -y
apt-get install -y \
bzip2 curl file g++ gcc patchelf gfortran git gzip \
make patch unzip xz-utils python3 python3-dev tree \
gawk
- uses: actions/checkout@v2
- name: Setup repo and non-root user
run: |
git --version
git fetch --unshallow
. .github/workflows/setup_git.sh
useradd -m spack-test
chown -R spack-test .
- name: Bootstrap GnuPG
shell: runuser -u spack-test -- bash {0}
run: |
source share/spack/setup-env.sh
spack solve zlib
spack bootstrap untrust github-actions
spack -d gpg list
tree ~/.spack/bootstrap/store/
macos-gnupg-binaries:
runs-on: macos-latest
steps:
- name: Install dependencies
run: |
brew install tree
# Remove GnuPG since we want to bootstrap it
sudo rm -rf /usr/local/bin/gpg
- uses: actions/checkout@v2
- name: Bootstrap GnuPG
run: |
source share/spack/setup-env.sh
spack bootstrap untrust spack-install
spack -d gpg list
tree ~/.spack/bootstrap/store/
macos-gnupg-sources:
runs-on: macos-latest
steps:
- name: Install dependencies
run: |
brew install gawk tree
# Remove GnuPG since we want to bootstrap it
sudo rm -rf /usr/local/bin/gpg
- uses: actions/checkout@v2
- name: Bootstrap GnuPG
run: |
source share/spack/setup-env.sh
spack solve zlib
spack bootstrap untrust github-actions
spack -d gpg list
tree ~/.spack/bootstrap/store/

View file

@ -6,6 +6,7 @@
import contextlib
import fnmatch
import functools
import json
import os
import os.path
@ -34,11 +35,14 @@
import spack.repo
import spack.spec
import spack.store
import spack.user_environment as uenv
import spack.user_environment
import spack.util.executable
import spack.util.path
from spack.util.environment import EnvironmentModifications
#: "spack buildcache" command, initialized lazily
_buildcache_cmd = None
#: Map a bootstrapper type to the corresponding class
_bootstrap_methods = {}
@ -171,6 +175,34 @@ def _fix_ext_suffix(candidate_spec):
os.symlink(abs_path, link_name)
def _executables_in_store(executables, abstract_spec_str):
"""Return True if at least one of the executables can be retrieved from
a spec in store, False otherwise.
The different executables must provide the same functionality and are
"alternate" to each other, i.e. the function will exit True on the first
executable found.
Args:
executables: list of executables to be searched
abstract_spec_str: abstract spec that may provide the executable
"""
executables_str = ', '.join(executables)
msg = "[BOOTSTRAP EXECUTABLES {0}] Try installed specs with query '{1}'"
tty.debug(msg.format(executables_str, abstract_spec_str))
installed_specs = spack.store.db.query(abstract_spec_str, installed=True)
if installed_specs:
for concrete_spec in installed_specs:
bin_dir = concrete_spec.prefix.bin
# IF we have a "bin" directory and it contains
# the executables we are looking for
if (os.path.exists(bin_dir) and os.path.isdir(bin_dir) and
spack.util.executable.which_string(*executables, path=bin_dir)):
spack.util.environment.path_put_first('PATH', [bin_dir])
return True
return False
@_bootstrapper(type='buildcache')
class _BuildcacheBootstrapper(object):
"""Install the software needed during bootstrapping from a buildcache."""
@ -178,93 +210,140 @@ def __init__(self, conf):
self.name = conf['name']
self.url = conf['info']['url']
def try_import(self, module, abstract_spec_str):
if _try_import_from_store(module, abstract_spec_str):
return True
@staticmethod
def _spec_and_platform(abstract_spec_str):
"""Return the spec object and platform we need to use when
querying the buildcache.
tty.info("Bootstrapping {0} from pre-built binaries".format(module))
Args:
abstract_spec_str: abstract spec string we are looking for
"""
# This import is local since it is needed only on Cray
import spack.platforms.linux
# Try to install from an unsigned binary cache
abstract_spec = spack.spec.Spec(
abstract_spec_str + ' ^' + spec_for_current_python()
)
abstract_spec = spack.spec.Spec(abstract_spec_str)
# On Cray we want to use Linux binaries if available from mirrors
bincache_platform = spack.platforms.real_host()
if str(bincache_platform) == 'cray':
bincache_platform = spack.platforms.Linux()
with spack.platforms.use_platform(bincache_platform):
abstract_spec = spack.spec.Spec(
abstract_spec_str + ' ^' + spec_for_current_python()
)
abstract_spec = spack.spec.Spec(abstract_spec_str)
return abstract_spec, bincache_platform
# Read information on verified clingo binaries
json_filename = '{0}.json'.format(module)
def _read_metadata(self, package_name):
"""Return metadata about the given package."""
json_filename = '{0}.json'.format(package_name)
json_path = os.path.join(
spack.paths.share_path, 'bootstrap', self.name, json_filename
)
with open(json_path) as f:
data = json.load(f)
return data
buildcache = spack.main.SpackCommand('buildcache')
def _install_by_hash(self, pkg_hash, pkg_sha256, index, bincache_platform):
global _buildcache_cmd
if _buildcache_cmd is None:
_buildcache_cmd = spack.main.SpackCommand('buildcache')
index_spec = next(x for x in index if x.dag_hash() == pkg_hash)
# Reconstruct the compiler that we need to use for bootstrapping
compiler_entry = {
"modules": [],
"operating_system": str(index_spec.os),
"paths": {
"cc": "/dev/null",
"cxx": "/dev/null",
"f77": "/dev/null",
"fc": "/dev/null"
},
"spec": str(index_spec.compiler),
"target": str(index_spec.target.family)
}
with spack.platforms.use_platform(bincache_platform):
with spack.config.override(
'compilers', [{'compiler': compiler_entry}]
):
spec_str = '/' + pkg_hash
install_args = [
'install',
'--sha256', pkg_sha256,
'--only-root',
'-a', '-u', '-o', '-f', spec_str
]
_buildcache_cmd(*install_args, fail_on_error=False)
def _install_and_test(
self, abstract_spec, bincache_platform, bincache_data, test_fn
):
# Ensure we see only the buildcache being used to bootstrap
mirror_scope = spack.config.InternalConfigScope(
'bootstrap_buildcache', {'mirrors:': {self.name: self.url}}
)
with spack.config.override(mirror_scope):
with spack.config.override(self.mirror_scope):
# This index is currently needed to get the compiler used to build some
# specs that wwe know by dag hash.
# specs that we know by dag hash.
spack.binary_distribution.binary_index.regenerate_spec_cache()
index = spack.binary_distribution.update_cache_and_get_specs()
if not index:
raise RuntimeError("The binary index is empty")
for item in data['verified']:
for item in bincache_data['verified']:
candidate_spec = item['spec']
python_spec = item['python']
# This will be None for things that don't depend on python
python_spec = item.get('python', None)
# Skip specs which are not compatible
if not abstract_spec.satisfies(candidate_spec):
continue
if python_spec not in abstract_spec:
if python_spec is not None and python_spec not in abstract_spec:
continue
for pkg_name, pkg_hash, pkg_sha256 in item['binaries']:
msg = ('[BOOTSTRAP MODULE {0}] Try installing "{1}" from binary '
'cache at "{2}"')
tty.debug(msg.format(module, pkg_name, self.url))
index_spec = next(x for x in index if x.dag_hash() == pkg_hash)
# Reconstruct the compiler that we need to use for bootstrapping
compiler_entry = {
"modules": [],
"operating_system": str(index_spec.os),
"paths": {
"cc": "/dev/null",
"cxx": "/dev/null",
"f77": "/dev/null",
"fc": "/dev/null"
},
"spec": str(index_spec.compiler),
"target": str(index_spec.target.family)
}
with spack.platforms.use_platform(bincache_platform):
with spack.config.override(
'compilers', [{'compiler': compiler_entry}]
):
spec_str = '/' + pkg_hash
install_args = [
'install',
'--sha256', pkg_sha256,
'-a', '-u', '-o', '-f', spec_str
]
buildcache(*install_args, fail_on_error=False)
# TODO: undo installations that didn't complete?
# TODO: undo installations that didn't complete?
self._install_by_hash(
pkg_hash, pkg_sha256, index, bincache_platform
)
if _try_import_from_store(module, abstract_spec_str):
if test_fn():
return True
return False
@property
def mirror_scope(self):
return spack.config.InternalConfigScope(
'bootstrap', {'mirrors:': {self.name: self.url}}
)
def try_import(self, module, abstract_spec_str):
test_fn = functools.partial(_try_import_from_store, module, abstract_spec_str)
if test_fn():
return True
tty.info("Bootstrapping {0} from pre-built binaries".format(module))
abstract_spec, bincache_platform = self._spec_and_platform(
abstract_spec_str + ' ^' + spec_for_current_python()
)
data = self._read_metadata(module)
return self._install_and_test(
abstract_spec, bincache_platform, data, test_fn
)
def try_search_path(self, executables, abstract_spec_str):
test_fn = functools.partial(
_executables_in_store, executables, abstract_spec_str
)
if test_fn():
return True
abstract_spec, bincache_platform = self._spec_and_platform(
abstract_spec_str
)
tty.info("Bootstrapping {0} from pre-built binaries".format(abstract_spec.name))
data = self._read_metadata(abstract_spec.name)
return self._install_and_test(
abstract_spec, bincache_platform, data, test_fn
)
@_bootstrapper(type='install')
class _SourceBootstrapper(object):
@ -307,6 +386,26 @@ def try_import(module, abstract_spec_str):
return _try_import_from_store(module, abstract_spec_str=abstract_spec_str)
def try_search_path(self, executables, abstract_spec_str):
if _executables_in_store(executables, abstract_spec_str):
return True
# If we compile code from sources detecting a few build tools
# might reduce compilation time by a fair amount
_add_externals_if_missing()
# Add hint to use frontend operating system on Cray
if str(spack.platforms.host()) == 'cray':
abstract_spec_str += ' os=fe'
concrete_spec = spack.spec.Spec(abstract_spec_str)
concrete_spec.concretize()
msg = "[BOOTSTRAP GnuPG] Try installing '{0}' from sources"
tty.debug(msg.format(abstract_spec_str))
concrete_spec.package.do_install()
return _executables_in_store(executables, abstract_spec_str)
def _make_bootstrapper(conf):
"""Return a bootstrap object built according to the
@ -418,6 +517,44 @@ def ensure_module_importable_or_raise(module, abstract_spec=None):
raise ImportError(msg)
def ensure_executables_in_path_or_raise(executables, abstract_spec):
"""Ensure that some executables are in path or raise.
Args:
executables (list): list of executables to be searched in the PATH,
in order. The function exits on the first one found.
abstract_spec (str): abstract spec that provides the executables
Raises:
RuntimeError: if the executables cannot be ensured to be in PATH
"""
if spack.util.executable.which_string(*executables):
return
executables_str = ', '.join(executables)
source_configs = spack.config.get('bootstrap:sources', [])
for current_config in source_configs:
if not _source_is_trusted(current_config):
msg = ('[BOOTSTRAP EXECUTABLES {0}] Skipping source "{1}" since it is '
'not trusted').format(executables_str, current_config['name'])
tty.debug(msg)
continue
b = _make_bootstrapper(current_config)
try:
if b.try_search_path(executables, abstract_spec):
return
except Exception as e:
msg = '[BOOTSTRAP EXECUTABLES {0}] Unexpected error "{1}"'
tty.debug(msg.format(executables_str, str(e)))
# We couldn't import in any way, so raise an import error
msg = 'cannot bootstrap any of the {0} executables'.format(executables_str)
if abstract_spec:
msg += ' from spec "{0}"'.format(abstract_spec)
raise RuntimeError(msg)
def _python_import(module):
try:
__import__(module)
@ -455,7 +592,9 @@ def get_executable(exe, spec=None, install=False):
ret = spack.util.executable.Executable(exe_path[0])
envmod = EnvironmentModifications()
for dep in ispec.traverse(root=True, order='post'):
envmod.extend(uenv.environment_modifications_for_spec(dep))
envmod.extend(
spack.user_environment.environment_modifications_for_spec(dep)
)
ret.add_default_envmod(envmod)
return ret
else:
@ -484,7 +623,9 @@ def _raise_error(executable, exe_spec):
ret = spack.util.executable.Executable(exe_path[0])
envmod = EnvironmentModifications()
for dep in spec.traverse(root=True, order='post'):
envmod.extend(uenv.environment_modifications_for_spec(dep))
envmod.extend(
spack.user_environment.environment_modifications_for_spec(dep)
)
ret.add_default_envmod(envmod)
return ret
@ -523,8 +664,11 @@ def _add_compilers_if_missing():
def _add_externals_if_missing():
search_list = [
# clingo
spack.repo.path.get('cmake'),
spack.repo.path.get('bison')
spack.repo.path.get('bison'),
# GnuPG
spack.repo.path.get('gawk')
]
detected_packages = spack.detection.by_executable(search_list)
spack.detection.update_configuration(detected_packages, scope='bootstrap')
@ -600,10 +744,12 @@ def _config_path():
)
def clingo_root_spec():
# Construct the root spec that will be used to bootstrap clingo
spec_str = 'clingo-bootstrap@spack+python'
def _root_spec(spec_str):
"""Add a proper compiler and target to a spec used during bootstrapping.
Args:
spec_str (str): spec to be bootstrapped. Must be without compiler and target.
"""
# Add a proper compiler hint to the root spec. We use GCC for
# everything but MacOS.
if str(spack.platforms.host()) == 'darwin':
@ -611,17 +757,32 @@ def clingo_root_spec():
else:
spec_str += ' %gcc'
# Add the generic target
generic_target = archspec.cpu.host().family
spec_str += ' target={0}'.format(str(generic_target))
tty.debug('[BOOTSTRAP ROOT SPEC] clingo: {0}'.format(spec_str))
target = archspec.cpu.host().family
spec_str += ' target={0}'.format(target)
tty.debug('[BOOTSTRAP ROOT SPEC] {0}'.format(spec_str))
return spec_str
def clingo_root_spec():
"""Return the root spec used to bootstrap clingo"""
return _root_spec('clingo-bootstrap@spack+python')
def ensure_clingo_importable_or_raise():
"""Ensure that the clingo module is available for import."""
ensure_module_importable_or_raise(
module='clingo', abstract_spec=clingo_root_spec()
)
def gnupg_root_spec():
"""Return the root spec used to bootstrap GnuPG"""
return _root_spec('gnupg@2.3:')
def ensure_gpg_in_path_or_raise():
"""Ensure gpg or gpg2 are in the PATH or raise."""
ensure_executables_in_path_or_raise(
executables=['gpg2', 'gpg'], abstract_spec=gnupg_root_spec(),
)

View file

@ -104,6 +104,9 @@ def setup_parser(subparser):
" instead of default platform and OS")
# This argument is needed by the bootstrapping logic to verify checksums
install.add_argument('--sha256', help=argparse.SUPPRESS)
install.add_argument(
'--only-root', action='store_true', help=argparse.SUPPRESS
)
arguments.add_common_arguments(install, ['specs'])
install.set_defaults(func=installtarball)
@ -534,9 +537,14 @@ def install_tarball(spec, args):
if s.external or s.virtual:
tty.warn("Skipping external or virtual package %s" % spec.format())
return
for d in s.dependencies(deptype=('link', 'run')):
tty.msg("Installing buildcache for dependency spec %s" % d)
install_tarball(d, args)
# This argument is used only for bootstrapping specs without signatures,
# since we need to check the sha256 of each tarball
if not args.only_root:
for d in s.dependencies(deptype=('link', 'run')):
tty.msg("Installing buildcache for dependency spec %s" % d)
install_tarball(d, args)
package = spack.repo.get(spec)
if s.concrete and package.installed and not args.force:
tty.warn("Package for spec %s already installed." % spec.format())

View file

@ -40,7 +40,7 @@
import llnl.util.tty as tty
from llnl.util.lang import dedupe
import spack.build_environment as build_environment
import spack.build_environment
import spack.config
import spack.environment
import spack.error
@ -732,12 +732,12 @@ def environment_modifications(self):
# Let the extendee/dependency modify their extensions/dependencies
# before asking for package-specific modifications
env.extend(
build_environment.modifications_from_dependencies(
spack.build_environment.modifications_from_dependencies(
spec, context='run'
)
)
# Package specific modifications
build_environment.set_module_variables_for_package(spec.package)
spack.build_environment.set_module_variables_for_package(spec.package)
spec.package.setup_run_environment(env)
# Modifications required from modules.yaml

View file

@ -9,6 +9,7 @@
import llnl.util.filesystem as fs
import spack.bootstrap
import spack.util.executable
import spack.util.gpg
from spack.main import SpackCommand
@ -17,6 +18,7 @@
#: spack command used by tests below
gpg = SpackCommand('gpg')
bootstrap = SpackCommand('bootstrap')
# test gpg command detection
@ -46,9 +48,10 @@ def test_find_gpg(cmd_name, version, tmpdir, mock_gnupghome, monkeypatch):
assert spack.util.gpg.GPGCONF is not None
def test_no_gpg_in_path(tmpdir, mock_gnupghome, monkeypatch):
def test_no_gpg_in_path(tmpdir, mock_gnupghome, monkeypatch, mutable_config):
monkeypatch.setitem(os.environ, "PATH", str(tmpdir))
with pytest.raises(spack.util.gpg.SpackGPGError):
bootstrap('disable')
with pytest.raises(RuntimeError):
spack.util.gpg.init(force=True)

View file

@ -8,6 +8,7 @@
import os
import re
import spack.bootstrap
import spack.error
import spack.paths
import spack.util.executable
@ -59,7 +60,10 @@ def init(gnupghome=None, force=False):
spack.paths.gpg_path)
# Set the executable objects for "gpg" and "gpgconf"
GPG, GPGCONF = _gpg(), _gpgconf()
with spack.bootstrap.ensure_bootstrap_configuration():
spack.bootstrap.ensure_gpg_in_path_or_raise()
GPG, GPGCONF = _gpg(), _gpgconf()
GPG.add_default_env('GNUPGHOME', GNUPGHOME)
if GPGCONF:
GPGCONF.add_default_env('GNUPGHOME', GNUPGHOME)

View file

@ -0,0 +1,204 @@
{
"verified": [
{
"binaries": [
[
"libgpg-error",
"hph66gvb7vlinpzwoytxq27ojb7gtl2j",
"f040f513e212c428ee863d75924331f58b5f8f3946f98c757a9e0af0d0a34f63"
],
[
"libiconv",
"ckpif6rcf7mxdmceyv6sqvtnwfqi7fmc",
"1d745d04f7c8a1c4d17d9735eba0ee88c8acfbb213c22a15e45e58637867ed4c"
],
[
"npth",
"fjuoy73whriauk3bt6ma5fwet6iric7y",
"78d5d9e339ef901b0def0924a72ce87a93e0a8acb7394ec2c35be6c328154444"
],
[
"zlib",
"qo6otxqnn6mxpw4zhqc4wdwqmgcfjdfe",
"f00c38ecaf316cd665399ed14c233f839ae5868387ff04998e1ec949c1f4dcd6"
],
[
"libassuan",
"2upi74qccouj4k6d7wultp2u5fntayi3",
"f2118b102f9a94bb1e2804492689b44b260b7f6e46ac1208d5110ebffe24bf99"
],
[
"libgcrypt",
"xzhvvm44cfxwvgqgkpbeucpnl4dbj4p2",
"ae717e068f2f7f4eaeee4bdec4a6b20ff299c59c3d724c1661b6045fda628a9b"
],
[
"libksba",
"aodyg5mzfule3vimuetmzulv5mzdx37g",
"c665eb20f27b2d28fcb634fe958829165e44a27b1ad69995d5040f13d5693d52"
],
[
"pinentry",
"ihqcvdm5okxuvnln463l7h4flbkhrp44",
"b0c7781354eb4a7c9e979802590c0e4fb7eb53f440191367f0142eac4868f8d6"
],
[
"gnupg",
"47vilwybwuxved7jov7esiad3qlkv5rp",
"83f3de13b2712a05f520d16b5141797493f8b117041dd32aa5414a25d9d53439"
]
],
"spec": "gnupg@2.3: %apple-clang platform=darwin target=x86_64"
},
{
"binaries": [
[
"libgpg-error",
"3dkguooajaaejhsebigs2e3lhk37mtem",
"09c5edd93fb479961d62d9938c1ea02b8f443babf0e79776f1539085c3422cd5"
],
[
"libiconv",
"i2eqtudh3zcxt5fvxuhe6n2ztuqbadtp",
"838786e029474d668b5f83a9669d227c812253c3c3f0328aa4f888542a7de830"
],
[
"npth",
"c3z6gg3ilutvvryxkjrboampqv5u5s2s",
"967522ae988ccce8c922f28aa2124170246f501f0a45179b919d827bf28c35d2"
],
[
"zlib",
"p2jozvok56voja7652dms4gvthpcjzta",
"41cbc69850640ed4466dbedc1bb4ccb0ade0c1a1e8fcd70d1e247b1387b937b5"
],
[
"libassuan",
"s2wx2xvt3iz3sigcdt5tvppj2m7e2bsf",
"5f766af4ff355769e3e456a9c95636a20a64f5ba32aecec633216a3d83a854f8"
],
[
"libgcrypt",
"gznmtryix6ck4x3chnuvbctz4xa3fmxl",
"0261b03f790c5320980d27bf0a471a1a4663929689ddfaeb5e568d33be8dc889"
],
[
"libksba",
"uxaryyfybbcw563jcwumufhfmbsawlbz",
"f45fff7a6a5c626a1474c7354fd00e18e629fcd086787336f7d62d1ead50c94f"
],
[
"pinentry",
"ias6sb4qi24u6i7axr5hkj4liq5dtr6l",
"a2a8e7652dceb7d080ff78726d28099f9050cb9f6e290d97f1f59f4b42521b9c"
],
[
"gnupg",
"qpm457bujhmfqy66euzhswokumuvufbz",
"d2371e26412e10fc702b9b2482aff776108194b84e1927644a3d64f5710fd163"
]
],
"spec": "gnupg@2.3: %gcc platform=linux target=aarch64"
},
{
"binaries": [
[
"libgpg-error",
"4bp6dcfdbzbd4uuzvbgjyqunhjedg3lf",
"9a9947240c6af7e915aa8336bfaed8706c9129967eb9ab1895598217df91f301"
],
[
"libiconv",
"dscneqtpyy32r6ds24izlkki3euthnbr",
"a9dc099f6c7ee9fd6c63757cb81a59fe4b81672543d5253a50bb73bc819440ef"
],
[
"npth",
"jukmafxhkxo2s4udlzi5r5b6bbb3udw5",
"d2a2b11c0f1794ab0de813753221bde073508fbec19f0b15dbfd23455bc6de87"
],
[
"zlib",
"amqfrcbn67rochzkeu2ashklo35ayqqq",
"686fc10058d208530889bc5c3779aa2cc324b77301878a5405cf65ca53d613ba"
],
[
"libassuan",
"lyeih2j3miy7yugmwh37h667fogqn3fl",
"f87c474d81c890232cd8e1e4d93b5b232aa0ad428dcaa7231d7a8d182cea9ecc"
],
[
"libgcrypt",
"zb33zulvwcansfzu5km4d34avujnazfa",
"e67ae6a5345f9e864bd2009c1a9d7eb65a63ca2841368bebc477a770fb8dcaf5"
],
[
"libksba",
"yjuh2aplj23qyvaqixukd7a6eokfdgyp",
"6944fc047e8f0eb41daec734e2505016369319c64929f5ec8d3a8f99e01928d4"
],
[
"pinentry",
"xd7vajghgcueohv5qgahdvbjpcnrurns",
"a6b37efd6ef9f9374aa0c7d1869da990ae3668938b47ad6af50138d2ea05da02"
],
[
"gnupg",
"ti2ddl27nilobj2ctwsgzl52qque5o7z",
"43781437e3dfae158e7a6911313a4162d8ffa5313182438d1e6547a854f6f38a"
]
],
"spec": "gnupg@2.3: %gcc platform=linux target=ppc64le"
},
{
"binaries": [
[
"libgpg-error",
"p7chd5hhecdkc27npia4uaoeabjit4gh",
"4b5e1f418b7afdd91755d54d38a51d5d048aa3b1e5239bcaf3453c8ca1cca4b6"
],
[
"libiconv",
"scpkgy6bmk3wcgfwzoiv7hw74drmnaoi",
"2bcb9a2868c20284ce65ab53d4f7bb4c7edccd4c14390460858e25a8bc48faa3"
],
[
"npth",
"6vh3jypaf7u2zez3vohn66fvo6znt35l",
"23a333c4e83910eb1f87c91caffb07f40b592561a4c44924fed9459751c017f7"
],
[
"zlib",
"uc25tb5r57nykfrxszsdy54trzqnk2jn",
"9e18c1146bc3dcb8454d18502013b8621ecf00d2f2d4d66d76cbe1e07f351ac8"
],
[
"libassuan",
"vdoskg5mldu6ixhvftwplp4zdftwxwws",
"1413b84af0c58127032e7bde86dbacf35dc65205aee1c2071718678bc57ce793"
],
[
"libgcrypt",
"ng7gfusjpnypmqgckq7rp4vq3bvylp3b",
"1a09e97eb2333812f8381d4737aca4d7cfd9f27ebae30eddbcf99f399ad67fec"
],
[
"libksba",
"p4feho36xa7dhabk766fzglwyo2dfbj6",
"000ef0f2ad3aa05c07272849be68e059ec60946970ab8875a824305afe832c9a"
],
[
"pinentry",
"m423kpm7k52r66q3sdctqcjxtekiyrrp",
"5739bee66271d7f0d5b9bcf5c248f1a434e9cdcb327a4a5a22fc47f565ac0de7"
],
[
"gnupg",
"dlapzqxrwduafgfq2evptizb7p4kgpkh",
"262177fa8f66468e589f8b3e10d17531f17a74ea0f5ac6905ac948198dca3c3c"
]
],
"spec": "gnupg@2.3: %gcc platform=linux target=x86_64"
}
]
}

View file

@ -511,7 +511,7 @@ _spack_buildcache_create() {
_spack_buildcache_install() {
if $list_options
then
SPACK_COMPREPLY="-h --help -f --force -m --multiple -a --allow-root -u --unsigned -o --otherarch --sha256"
SPACK_COMPREPLY="-h --help -f --force -m --multiple -a --allow-root -u --unsigned -o --otherarch --sha256 --only-root"
else
_all_packages
fi