Bootstrap clingo from sources (#21446)

* Allow the bootstrapping of clingo from sources

Allow python builds with system python as external
for MacOS

* Ensure consistent configuration when bootstrapping clingo

This commit uses context managers to ensure we can
bootstrap clingo using a consistent configuration
regardless of the use case being managed.

* Github actions: test clingo with bootstrapping from sources

* Add command to inspect and clean the bootstrap store

 Prevent users to set the install tree root to the bootstrap store

* clingo: documented how to bootstrap from sources

Co-authored-by: Gregory Becker <becker33@llnl.gov>
(cherry picked from commit 10e9e142b7)
This commit is contained in:
Massimiliano Culpo 2021-03-03 18:37:46 +01:00 committed by Todd Gamblin
parent 6c1b348d91
commit 16f7a02654
12 changed files with 363 additions and 46 deletions

View file

@ -15,6 +15,7 @@ jobs:
strategy: strategy:
matrix: matrix:
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9]
concretizer: ['original', 'clingo']
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -50,16 +51,23 @@ jobs:
mkdir -p ${KCOV_ROOT}/build mkdir -p ${KCOV_ROOT}/build
cd ${KCOV_ROOT}/build && cmake -Wno-dev ${KCOV_ROOT}/kcov-${KCOV_VERSION} && cd - cd ${KCOV_ROOT}/build && cmake -Wno-dev ${KCOV_ROOT}/kcov-${KCOV_VERSION} && cd -
make -C ${KCOV_ROOT}/build && sudo make -C ${KCOV_ROOT}/build install make -C ${KCOV_ROOT}/build && sudo make -C ${KCOV_ROOT}/build install
- name: Bootstrap clingo from sources
if: ${{ matrix.concretizer == 'clingo' }}
run: |
. share/spack/setup-env.sh
spack external find --not-buildable cmake bison
spack -v solve zlib
- name: Run unit tests - name: Run unit tests
env: env:
COVERAGE: true COVERAGE: true
SPACK_TEST_SOLVER: ${{ matrix.concretizer }}
run: | run: |
share/spack/qa/run-unit-tests share/spack/qa/run-unit-tests
coverage combine coverage combine
coverage xml coverage xml
- uses: codecov/codecov-action@v1 - uses: codecov/codecov-action@v1
with: with:
flags: unittests,linux flags: unittests,linux,${{ matrix.concretizer }}
shell: shell:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -103,6 +111,7 @@ jobs:
- uses: codecov/codecov-action@v1 - uses: codecov/codecov-action@v1
with: with:
flags: shelltests,linux flags: shelltests,linux
centos6: centos6:
# Test for Python2.6 run on Centos 6 # Test for Python2.6 run on Centos 6
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -117,27 +126,7 @@ jobs:
git fetch origin ${{ github.ref }}:test-branch git fetch origin ${{ github.ref }}:test-branch
git checkout test-branch git checkout test-branch
share/spack/qa/run-unit-tests share/spack/qa/run-unit-tests
clingo:
# Test for the clingo based solver
runs-on: ubuntu-latest
container: spack/github-actions:clingo
steps:
- name: Run unit tests
run: |
whoami && echo PWD=$PWD && echo HOME=$HOME && echo SPACK_TEST_SOLVER=$SPACK_TEST_SOLVER
which clingo && clingo --version
git clone https://github.com/spack/spack.git && cd spack
git fetch origin ${{ github.ref }}:test-branch
git checkout test-branch
. share/spack/setup-env.sh
spack compiler find
spack solve mpileaks%gcc
coverage run $(which spack) unit-test -v
coverage combine
coverage xml
- uses: codecov/codecov-action@v1
with:
flags: unittests,linux,clingo
clingo-cffi: clingo-cffi:
# Test for the clingo based solver (using clingo-cffi) # Test for the clingo based solver (using clingo-cffi)
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -103,6 +103,53 @@ environment*, especially for ``PATH``. Only software that comes with
the system, or that you know you wish to use with Spack, should be the system, or that you know you wish to use with Spack, should be
included. This procedure will avoid many strange build errors. included. This procedure will avoid many strange build errors.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Optional: Bootstrapping clingo
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Spack supports using clingo as an external solver to compute which software
needs to be installed. If you have a default compiler supporting C++14 Spack
can automatically bootstrap this tool from sources the first time it is
needed:
.. code-block:: console
$ spack solve zlib
[+] /usr (external bison-3.0.4-wu5pgjchxzemk5ya2l3ddqug2d7jv6eb)
[+] /usr (external cmake-3.19.4-a4kmcfzxxy45mzku4ipmj5kdiiz5a57b)
[+] /usr (external python-3.6.9-x4fou4iqqlh5ydwddx3pvfcwznfrqztv)
==> Installing re2c-1.2.1-e3x6nxtk3ahgd63ykgy44mpuva6jhtdt
[ ... ]
==> Optimization: [0, 0, 0, 0, 0, 1, 0, 0, 0]
zlib@1.2.11%gcc@10.1.0+optimize+pic+shared arch=linux-ubuntu18.04-broadwell
If you want to speed-up bootstrapping, you may try to search for ``cmake`` and ``bison``
on your system:
.. code-block:: console
$ spack external find cmake bison
==> The following specs have been detected on this system and added to /home/spack/.spack/packages.yaml
bison@3.0.4 cmake@3.19.4
All the tools Spack needs for its own functioning are installed in a separate store, which lives
under the ``${HOME}/.spack`` directory. The software installed there can be queried with:
.. code-block:: console
$ spack find --bootstrap
==> Showing internal bootstrap store at "/home/spack/.spack/bootstrap/store"
==> 3 installed packages
-- linux-ubuntu18.04-x86_64 / gcc@10.1.0 ------------------------
clingo-bootstrap@spack python@3.6.9 re2c@1.2.1
In case it's needed the bootstrap store can also be cleaned with:
.. code-block:: console
$ spack clean -b
==> Removing software in "/home/spack/.spack/bootstrap/store"
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
Optional: Alternate Prefix Optional: Alternate Prefix
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -0,0 +1,188 @@
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import contextlib
import os
import sys
import llnl.util.filesystem as fs
import llnl.util.tty as tty
import spack.architecture
import spack.config
import spack.paths
import spack.repo
import spack.spec
import spack.store
import spack.user_environment as uenv
import spack.util.executable
from spack.util.environment import EnvironmentModifications
@contextlib.contextmanager
def spack_python_interpreter():
"""Override the current configuration to set the interpreter under
which Spack is currently running as the only Python external spec
available.
"""
python_cls = type(spack.spec.Spec('python').package)
python_prefix = os.path.dirname(os.path.dirname(sys.executable))
externals = python_cls.determine_spec_details(
python_prefix, [os.path.basename(sys.executable)])
external_python = externals[0]
entry = {
'buildable': False,
'externals': [
{'prefix': python_prefix, 'spec': str(external_python)}
]
}
with spack.config.override('packages:python::', entry):
yield
def make_module_available(module, spec=None, install=False):
"""Ensure module is importable"""
# If we already can import it, that's great
try:
__import__(module)
return
except ImportError:
pass
# If it's already installed, use it
# Search by spec
spec = spack.spec.Spec(spec or module)
# We have to run as part of this python
# We can constrain by a shortened version in place of a version range
# because this spec is only used for querying or as a placeholder to be
# replaced by an external that already has a concrete version. This syntax
# is not suffucient when concretizing without an external, as it will
# concretize to python@X.Y instead of python@X.Y.Z
spec.constrain('^python@%d.%d' % sys.version_info[:2])
installed_specs = spack.store.db.query(spec, installed=True)
for ispec in installed_specs:
# TODO: make sure run-environment is appropriate
module_path = os.path.join(ispec.prefix,
ispec['python'].package.site_packages_dir)
module_path_64 = module_path.replace('/lib/', '/lib64/')
try:
sys.path.append(module_path)
sys.path.append(module_path_64)
__import__(module)
return
except ImportError:
tty.warn("Spec %s did not provide module %s" % (ispec, module))
sys.path = sys.path[:-2]
def _raise_error(module_name, module_spec):
error_msg = 'cannot import module "{0}"'.format(module_name)
if module_spec:
error_msg += ' from spec "{0}'.format(module_spec)
raise ImportError(error_msg)
if not install:
_raise_error(module, spec)
with spack_python_interpreter():
# We will install for ourselves, using this python if needed
# Concretize the spec
spec.concretize()
spec.package.do_install()
module_path = os.path.join(spec.prefix,
spec['python'].package.site_packages_dir)
module_path_64 = module_path.replace('/lib/', '/lib64/')
try:
sys.path.append(module_path)
sys.path.append(module_path_64)
__import__(module)
return
except ImportError:
sys.path = sys.path[:-2]
_raise_error(module, spec)
def get_executable(exe, spec=None, install=False):
"""Find an executable named exe, either in PATH or in Spack
Args:
exe (str): needed executable name
spec (Spec or str): spec to search for exe in (default exe)
install (bool): install spec if not available
When ``install`` is True, Spack will use the python used to run Spack as an
external. The ``install`` option should only be used with packages that
install quickly (when using external python) or are guaranteed by Spack
organization to be in a binary mirror (clingo)."""
# Search the system first
runner = spack.util.executable.which(exe)
if runner:
return runner
# Check whether it's already installed
spec = spack.spec.Spec(spec or exe)
installed_specs = spack.store.db.query(spec, installed=True)
for ispec in installed_specs:
# filter out directories of the same name as the executable
exe_path = [exe_p for exe_p in fs.find(ispec.prefix, exe)
if fs.is_exe(exe_p)]
if exe_path:
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))
ret.add_default_envmod(envmod)
return ret
else:
tty.warn('Exe %s not found in prefix %s' % (exe, ispec.prefix))
def _raise_error(executable, exe_spec):
error_msg = 'cannot find the executable "{0}"'.format(executable)
if exe_spec:
error_msg += ' from spec "{0}'.format(exe_spec)
raise RuntimeError(error_msg)
# If we're not allowed to install this for ourselves, we can't find it
if not install:
_raise_error(exe, spec)
with spack_python_interpreter():
# We will install for ourselves, using this python if needed
# Concretize the spec
spec.concretize()
spec.package.do_install()
# filter out directories of the same name as the executable
exe_path = [exe_p for exe_p in fs.find(spec.prefix, exe)
if fs.is_exe(exe_p)]
if exe_path:
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))
ret.add_default_envmod(envmod)
return ret
_raise_error(exe, spec)
@contextlib.contextmanager
def ensure_bootstrap_configuration():
# Default configuration scopes excluding command
# line and builtin
config_scopes = [
spack.config.ConfigScope(name, path)
for name, path in spack.config.configuration_paths
]
with spack.architecture.use_platform(spack.architecture.real_platform()):
with spack.config.use_configuration(*config_scopes):
with spack.repo.use_repositories(spack.paths.packages_path):
with spack.store.use_store(spack.paths.user_bootstrap_store):
with spack_python_interpreter():
yield

View file

@ -233,7 +233,28 @@ def install_args(self, spec, prefix):
if ('py-setuptools' == spec.name or # this is setuptools, or if ('py-setuptools' == spec.name or # this is setuptools, or
'py-setuptools' in spec._dependencies and # it's an immediate dep 'py-setuptools' in spec._dependencies and # it's an immediate dep
'build' in spec._dependencies['py-setuptools'].deptypes): 'build' in spec._dependencies['py-setuptools'].deptypes):
args += ['--single-version-externally-managed', '--root=/'] args += ['--single-version-externally-managed']
# Get all relative paths since we set the root to `prefix`
# We query the python with which these will be used for the lib and inc
# directories. This ensures we use `lib`/`lib64` as expected by python.
python = spec['python'].package.command
command_start = 'print(distutils.sysconfig.'
commands = ';'.join([
'import distutils.sysconfig',
command_start + 'get_python_lib(plat_specific=False, prefix=""))',
command_start + 'get_python_lib(plat_specific=True, prefix=""))',
command_start + 'get_python_inc(plat_specific=True, prefix=""))'])
pure_site_packages_dir, plat_site_packages_dir, inc_dir = python(
'-c', commands, output=str, error=str).strip().split('\n')
args += ['--root=%s' % prefix,
'--install-purelib=%s' % pure_site_packages_dir,
'--install-platlib=%s' % plat_site_packages_dir,
'--install-scripts=bin',
'--install-data=""',
'--install-headers=%s' % inc_dir
]
return args return args

View file

@ -10,11 +10,12 @@
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.caches import spack.caches
import spack.config
import spack.cmd.test import spack.cmd.test
import spack.cmd.common.arguments as arguments import spack.cmd.common.arguments as arguments
import spack.main
import spack.repo import spack.repo
import spack.stage import spack.stage
import spack.config
from spack.paths import lib_path, var_path from spack.paths import lib_path, var_path
@ -26,7 +27,7 @@
class AllClean(argparse.Action): class AllClean(argparse.Action):
"""Activates flags -s -d -f -m and -p simultaneously""" """Activates flags -s -d -f -m and -p simultaneously"""
def __call__(self, parser, namespace, values, option_string=None): def __call__(self, parser, namespace, values, option_string=None):
parser.parse_args(['-sdfmp'], namespace=namespace) parser.parse_args(['-sdfmpb'], namespace=namespace)
def setup_parser(subparser): def setup_parser(subparser):
@ -46,7 +47,10 @@ def setup_parser(subparser):
'-p', '--python-cache', action='store_true', '-p', '--python-cache', action='store_true',
help="remove .pyc, .pyo files and __pycache__ folders") help="remove .pyc, .pyo files and __pycache__ folders")
subparser.add_argument( subparser.add_argument(
'-a', '--all', action=AllClean, help="equivalent to -sdfmp", nargs=0 '-b', '--bootstrap', action='store_true',
help="remove software needed to bootstrap Spack")
subparser.add_argument(
'-a', '--all', action=AllClean, help="equivalent to -sdfmpb", nargs=0
) )
arguments.add_common_arguments(subparser, ['specs']) arguments.add_common_arguments(subparser, ['specs'])
@ -54,7 +58,7 @@ def setup_parser(subparser):
def clean(parser, args): def clean(parser, args):
# If nothing was set, activate the default # If nothing was set, activate the default
if not any([args.specs, args.stage, args.downloads, args.failures, if not any([args.specs, args.stage, args.downloads, args.failures,
args.misc_cache, args.python_cache]): args.misc_cache, args.python_cache, args.bootstrap]):
args.stage = True args.stage = True
# Then do the cleaning falling through the cases # Then do the cleaning falling through the cases
@ -96,3 +100,10 @@ def clean(parser, args):
dname = os.path.join(root, d) dname = os.path.join(root, d)
tty.debug('Removing {0}'.format(dname)) tty.debug('Removing {0}'.format(dname))
shutil.rmtree(dname) shutil.rmtree(dname)
if args.bootstrap:
msg = 'Removing software in "{0}"'
tty.msg(msg.format(spack.paths.user_bootstrap_store))
with spack.store.use_store(spack.paths.user_bootstrap_store):
uninstall = spack.main.SpackCommand('uninstall')
uninstall('-a', '-y')

View file

@ -109,6 +109,10 @@ def setup_parser(subparser):
subparser.add_argument( subparser.add_argument(
'--end-date', help='latest date of installation [YYYY-MM-DD]' '--end-date', help='latest date of installation [YYYY-MM-DD]'
) )
subparser.add_argument(
'-b', '--bootstrap', action='store_true',
help='show software in the internal bootstrap store'
)
arguments.add_common_arguments(subparser, ['constraint']) arguments.add_common_arguments(subparser, ['constraint'])
@ -201,7 +205,14 @@ def display_env(env, args, decorator):
def find(parser, args): def find(parser, args):
q_args = query_arguments(args) q_args = query_arguments(args)
results = args.specs(**q_args) # Query the current store or the internal bootstrap store if required
if args.bootstrap:
msg = 'Showing internal bootstrap store at "{0}"'
tty.msg(msg.format(spack.paths.user_bootstrap_store))
with spack.store.use_store(spack.paths.user_bootstrap_store):
results = args.specs(**q_args)
else:
results = args.specs(**q_args)
decorator = lambda s, f: f decorator = lambda s, f: f
added = set() added = set()

View file

@ -50,7 +50,8 @@
#: User configuration location #: User configuration location
user_config_path = os.path.expanduser('~/.spack') user_config_path = os.path.expanduser('~/.spack')
user_bootstrap_path = os.path.join(user_config_path, 'bootstrap')
user_bootstrap_store = os.path.join(user_bootstrap_path, 'store')
opt_path = os.path.join(prefix, "opt") opt_path = os.path.join(prefix, "opt")
etc_path = os.path.join(prefix, "etc") etc_path = os.path.join(prefix, "etc")

View file

@ -1,4 +1,4 @@
# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other # Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
@ -22,6 +22,7 @@
clingo_cffi = hasattr(clingo.Symbol, '_rep') clingo_cffi = hasattr(clingo.Symbol, '_rep')
except ImportError: except ImportError:
clingo = None clingo = None
clingo_cffi = False
import llnl.util.lang import llnl.util.lang
import llnl.util.tty as tty import llnl.util.tty as tty
@ -38,6 +39,7 @@
import spack.package import spack.package
import spack.package_prefs import spack.package_prefs
import spack.repo import spack.repo
import spack.bootstrap
import spack.variant import spack.variant
import spack.version import spack.version
@ -246,7 +248,20 @@ def __init__(self, cores=True, asp=None):
asp (file-like): optional stream to write a text-based ASP program asp (file-like): optional stream to write a text-based ASP program
for debugging or verification. for debugging or verification.
""" """
assert clingo, "PyclingoDriver requires clingo with Python support" global clingo
if not clingo:
# TODO: Find a way to vendor the concrete spec
# in a cross-platform way
with spack.bootstrap.ensure_bootstrap_configuration():
generic_target = archspec.cpu.host().family
spec_str = 'clingo-bootstrap@spack+python target={0}'.format(
str(generic_target)
)
clingo_spec = spack.spec.Spec(spec_str)
clingo_spec._old_concretize()
spack.bootstrap.make_module_available(
'clingo', spec=clingo_spec, install=True)
import clingo
self.out = asp or llnl.util.lang.Devnull() self.out = asp or llnl.util.lang.Devnull()
self.cores = cores self.cores = cores
@ -1112,7 +1127,6 @@ def target_defaults(self, specs):
if not spec.architecture or not spec.architecture.target: if not spec.architecture or not spec.architecture.target:
continue continue
print("TTYPE:", type(platform.target(spec.target.name)))
target = archspec.cpu.TARGETS.get(spec.target.name) target = archspec.cpu.TARGETS.get(spec.target.name)
if not target: if not target:
self.target_ranges(spec, None) self.target_ranges(spec, None)

View file

@ -173,6 +173,16 @@ def _store():
config_dict = spack.config.get('config') config_dict = spack.config.get('config')
root, unpadded_root, projections = parse_install_tree(config_dict) root, unpadded_root, projections = parse_install_tree(config_dict)
hash_length = spack.config.get('config:install_hash_length') hash_length = spack.config.get('config:install_hash_length')
# Check that the user is not trying to install software into the store
# reserved by Spack to bootstrap its own dependencies, since this would
# lead to bizarre behaviors (e.g. cleaning the bootstrap area would wipe
# user installed software)
if spack.paths.user_bootstrap_store == root:
msg = ('please change the install tree root "{0}" in your '
'configuration [path reserved for Spack internal use]')
raise ValueError(msg.format(root))
return Store(root=root, return Store(root=root,
unpadded_root=unpadded_root, unpadded_root=unpadded_root,
projections=projections, projections=projections,

View file

@ -528,13 +528,18 @@ def reversed(self):
return rev return rev
def apply_modifications(self): def apply_modifications(self, env=None):
"""Applies the modifications and clears the list.""" """Applies the modifications and clears the list."""
# Use os.environ if not specified
# Do not copy, we want to modify it in place
if env is None:
env = os.environ
modifications = self.group_by_name() modifications = self.group_by_name()
# Apply modifications one variable at a time # Apply modifications one variable at a time
for name, actions in sorted(modifications.items()): for name, actions in sorted(modifications.items()):
for x in actions: for x in actions:
x.execute(os.environ) x.execute(env)
def shell_modifications(self, shell='sh'): def shell_modifications(self, shell='sh'):
"""Return shell code to apply the modifications and clears the list.""" """Return shell code to apply the modifications and clears the list."""

View file

@ -22,6 +22,8 @@ class Executable(object):
def __init__(self, name): def __init__(self, name):
self.exe = shlex.split(str(name)) self.exe = shlex.split(str(name))
self.default_env = {} self.default_env = {}
from spack.util.environment import EnvironmentModifications # no cycle
self.default_envmod = EnvironmentModifications()
self.returncode = None self.returncode = None
if not self.exe: if not self.exe:
@ -40,6 +42,10 @@ def add_default_env(self, key, value):
""" """
self.default_env[key] = value self.default_env[key] = value
def add_default_envmod(self, envmod):
"""Set an EnvironmentModifications to use when the command is run."""
self.default_envmod.extend(envmod)
@property @property
def command(self): def command(self):
"""The command-line string. """The command-line string.
@ -76,9 +82,10 @@ def __call__(self, *args, **kwargs):
Keyword Arguments: Keyword Arguments:
_dump_env (dict): Dict to be set to the environment actually _dump_env (dict): Dict to be set to the environment actually
used (envisaged for testing purposes only) used (envisaged for testing purposes only)
env (dict): The environment to run the executable with env (dict or EnvironmentModifications): The environment with which
extra_env (dict): Extra items to add to the environment to run the executable
(neither requires nor precludes env) extra_env (dict or EnvironmentModifications): Extra items to add to
the environment (neither requires nor precludes env)
fail_on_error (bool): Raise an exception if the subprocess returns fail_on_error (bool): Raise an exception if the subprocess returns
an error. Default is True. The return code is available as an error. Default is True. The return code is available as
``exe.returncode`` ``exe.returncode``
@ -107,13 +114,26 @@ def __call__(self, *args, **kwargs):
""" """
# Environment # Environment
env_arg = kwargs.get('env', None) env_arg = kwargs.get('env', None)
if env_arg is None:
env = os.environ.copy() # Setup default environment
env.update(self.default_env) env = os.environ.copy() if env_arg is None else {}
else: self.default_envmod.apply_modifications(env)
env = self.default_env.copy() env.update(self.default_env)
from spack.util.environment import EnvironmentModifications # no cycle
# Apply env argument
if isinstance(env_arg, EnvironmentModifications):
env_arg.apply_modifications(env)
elif env_arg:
env.update(env_arg) env.update(env_arg)
env.update(kwargs.get('extra_env', {}))
# Apply extra env
extra_env = kwargs.get('extra_env', {})
if isinstance(extra_env, EnvironmentModifications):
extra_env.apply_modifications(env)
else:
env.update(extra_env)
if '_dump_env' in kwargs: if '_dump_env' in kwargs:
kwargs['_dump_env'].clear() kwargs['_dump_env'].clear()
kwargs['_dump_env'].update(env) kwargs['_dump_env'].update(env)

View file

@ -1,4 +1,4 @@
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other # Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
@ -475,7 +475,7 @@ _spack_ci_rebuild() {
_spack_clean() { _spack_clean() {
if $list_options if $list_options
then then
SPACK_COMPREPLY="-h --help -s --stage -d --downloads -f --failures -m --misc-cache -p --python-cache -a --all" SPACK_COMPREPLY="-h --help -s --stage -d --downloads -f --failures -m --misc-cache -p --python-cache -b --bootstrap -a --all"
else else
_all_packages _all_packages
fi fi
@ -891,7 +891,7 @@ _spack_fetch() {
_spack_find() { _spack_find() {
if $list_options if $list_options
then then
SPACK_COMPREPLY="-h --help --format --json -d --deps -p --paths --groups --no-groups -l --long -L --very-long -t --tags -c --show-concretized -f --show-flags --show-full-compiler -x --explicit -X --implicit -u --unknown -m --missing -v --variants --loaded -M --only-missing --deprecated --only-deprecated -N --namespace --start-date --end-date" SPACK_COMPREPLY="-h --help --format --json -d --deps -p --paths --groups --no-groups -l --long -L --very-long -t --tags -c --show-concretized -f --show-flags --show-full-compiler -x --explicit -X --implicit -u --unknown -m --missing -v --variants --loaded -M --only-missing --deprecated --only-deprecated -N --namespace --start-date --end-date -b --bootstrap"
else else
_installed_packages _installed_packages
fi fi