Install: Add use-buildcache option to install (#32537)

Install: Add use-buildcache option to install

* Allow differentiating between top level packages and dependencies when
determining whether to install from the cache or not.

* Add unit test for --use-buildcache

* Use metavar to display use-buildcache options.

* Update spack-completion
This commit is contained in:
kwryankrattiger 2022-09-29 13:48:06 -05:00 committed by GitHub
parent 7a25f416b8
commit a01c36da45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 9 deletions

View file

@ -5,6 +5,7 @@
import argparse import argparse
import os import os
import re
import shutil import shutil
import sys import sys
import textwrap import textwrap
@ -31,10 +32,50 @@
level = "short" level = "short"
# Pass in the value string passed to use-buildcache and get back
# the package and dependencies values.
def parse_use_buildcache(opt):
bc_keys = ["package:", "dependencies:", ""]
bc_values = ["only", "never", "auto"]
kv_list = re.findall("([a-z]+:)?([a-z]+)", opt)
# Verify keys and values
bc_map = {k: v for k, v in kv_list if k in bc_keys and v in bc_values}
if not len(kv_list) == len(bc_map):
tty.error("Unrecognized arguments passed to use-buildcache")
tty.error(
"Expected: --use-buildcache "
"[[auto|only|never],[package:[auto|only|never]],[dependencies:[auto|only|never]]]"
)
exit(1)
for _group in ["package:", "dependencies:"]:
if _group not in bc_map:
if "" in bc_map:
bc_map[_group] = bc_map[""]
else:
bc_map[_group] = "auto"
return bc_map["package:"], bc_map["dependencies:"]
# Determine value of cache flag
def cache_opt(default_opt, use_buildcache):
if use_buildcache == "auto":
return default_opt
elif use_buildcache == "only":
return True
elif use_buildcache == "never":
return False
def install_kwargs_from_args(args): def install_kwargs_from_args(args):
"""Translate command line arguments into a dictionary that will be passed """Translate command line arguments into a dictionary that will be passed
to the package installer. to the package installer.
""" """
pkg_use_bc, dep_use_bc = parse_use_buildcache(args.use_buildcache)
return { return {
"fail_fast": args.fail_fast, "fail_fast": args.fail_fast,
"keep_prefix": args.keep_prefix, "keep_prefix": args.keep_prefix,
@ -44,8 +85,10 @@ def install_kwargs_from_args(args):
"verbose": args.verbose or args.install_verbose, "verbose": args.verbose or args.install_verbose,
"fake": args.fake, "fake": args.fake,
"dirty": args.dirty, "dirty": args.dirty,
"use_cache": args.use_cache, "package_use_cache": cache_opt(args.use_cache, pkg_use_bc),
"cache_only": args.cache_only, "package_cache_only": cache_opt(args.cache_only, pkg_use_bc),
"dependencies_use_cache": cache_opt(args.use_cache, dep_use_bc),
"dependencies_cache_only": cache_opt(args.cache_only, dep_use_bc),
"include_build_deps": args.include_build_deps, "include_build_deps": args.include_build_deps,
"explicit": True, # Use true as a default for install command "explicit": True, # Use true as a default for install command
"stop_at": args.until, "stop_at": args.until,
@ -123,6 +166,18 @@ def setup_parser(subparser):
default=False, default=False,
help="only install package from binary mirrors", help="only install package from binary mirrors",
) )
cache_group.add_argument(
"--use-buildcache",
dest="use_buildcache",
default="package:auto,dependencies:auto",
metavar="[{auto,only,never},][package:{auto,only,never},][dependencies:{auto,only,never}]",
help="""select the mode of buildcache for the 'package' and 'dependencies'.
Default: package:auto,dependencies:auto
- `auto` behaves like --use-cache
- `only` behaves like --cache-only
- `never` behaves like --no-cache
""",
)
subparser.add_argument( subparser.add_argument(
"--include-build-deps", "--include-build-deps",

View file

@ -1182,12 +1182,12 @@ def _install_task(self, task):
Args: Args:
task (BuildTask): the installation build task for a package""" task (BuildTask): the installation build task for a package"""
install_args = task.request.install_args
cache_only = install_args.get("cache_only")
explicit = task.explicit explicit = task.explicit
install_args = task.request.install_args
cache_only = task.cache_only
use_cache = task.use_cache
tests = install_args.get("tests") tests = install_args.get("tests")
unsigned = install_args.get("unsigned") unsigned = install_args.get("unsigned")
use_cache = install_args.get("use_cache")
pkg, pkg_id = task.pkg, task.pkg_id pkg, pkg_id = task.pkg, task.pkg_id
@ -2220,7 +2220,29 @@ def flag_installed(self, installed):
@property @property
def explicit(self): def explicit(self):
"""The package was explicitly requested by the user.""" """The package was explicitly requested by the user."""
return self.pkg == self.request.pkg and self.request.install_args.get("explicit", True) return self.is_root and self.request.install_args.get("explicit", True)
@property
def is_root(self):
"""The package was requested directly, but may or may not be explicit
in an environment."""
return self.pkg == self.request.pkg
@property
def use_cache(self):
_use_cache = True
if self.is_root:
return self.request.install_args.get("package_use_cache", _use_cache)
else:
return self.request.install_args.get("dependencies_use_cache", _use_cache)
@property
def cache_only(self):
_cache_only = False
if self.is_root:
return self.request.install_args.get("package_cache_only", _cache_only)
else:
return self.request.install_args.get("dependencies_cache_only", _cache_only)
@property @property
def key(self): def key(self):
@ -2302,21 +2324,23 @@ def __str__(self):
def _add_default_args(self): def _add_default_args(self):
"""Ensure standard install options are set to at least the default.""" """Ensure standard install options are set to at least the default."""
for arg, default in [ for arg, default in [
("cache_only", False),
("context", "build"), # installs *always* build ("context", "build"), # installs *always* build
("dependencies_cache_only", False),
("dependencies_use_cache", True),
("dirty", False), ("dirty", False),
("fail_fast", False), ("fail_fast", False),
("fake", False), ("fake", False),
("install_deps", True), ("install_deps", True),
("install_package", True), ("install_package", True),
("install_source", False), ("install_source", False),
("package_cache_only", False),
("package_use_cache", True),
("keep_prefix", False), ("keep_prefix", False),
("keep_stage", False), ("keep_stage", False),
("restage", False), ("restage", False),
("skip_patch", False), ("skip_patch", False),
("tests", False), ("tests", False),
("unsigned", False), ("unsigned", False),
("use_cache", True),
("verbose", False), ("verbose", False),
]: ]:
_ = self.install_args.setdefault(arg, default) _ = self.install_args.setdefault(arg, default)

View file

@ -5,6 +5,7 @@
import argparse import argparse
import filecmp import filecmp
import itertools
import os import os
import re import re
import sys import sys
@ -14,6 +15,7 @@
from six.moves import builtins from six.moves import builtins
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
import llnl.util.tty as tty
import spack.cmd.install import spack.cmd.install
import spack.compilers as compilers import spack.compilers as compilers
@ -1090,3 +1092,80 @@ def test_install_callbacks_fail(install_mockery, mock_fetch, name, method):
assert output.count(method) == 2 assert output.count(method) == 2
assert output.count("method not implemented") == 1 assert output.count("method not implemented") == 1
assert output.count("TestFailure: 1 tests failed") == 1 assert output.count("TestFailure: 1 tests failed") == 1
def test_install_use_buildcache(
capsys,
mock_packages,
mock_fetch,
mock_archive,
mock_binary_index,
tmpdir,
install_mockery_mutable_config,
):
"""
Make sure installing with use-buildcache behaves correctly.
"""
package_name = "dependent-install"
dependency_name = "dependency-install"
def validate(mode, out, pkg):
def assert_auto(pkg, out):
assert "==> Extracting {0}".format(pkg) in out
def assert_only(pkg, out):
assert "==> Extracting {0}".format(pkg) in out
def assert_never(pkg, out):
assert "==> {0}: Executing phase: 'install'".format(pkg) in out
if mode == "auto":
assert_auto(pkg, out)
elif mode == "only":
assert_only(pkg, out)
else:
assert_never(pkg, out)
def install_use_buildcache(opt):
out = install(
"--no-check-signature", "--use-buildcache", opt, package_name, fail_on_error=True
)
pkg_opt, dep_opt = spack.cmd.install.parse_use_buildcache(opt)
validate(dep_opt, out, dependency_name)
validate(pkg_opt, out, package_name)
# Clean up installed packages
uninstall("-y", "-a")
# Setup the mirror
# Create a temp mirror directory for buildcache usage
mirror_dir = tmpdir.join("mirror_dir")
mirror_url = "file://{0}".format(mirror_dir.strpath)
# Populate the buildcache
install(package_name)
buildcache("create", "-u", "-a", "-f", "-d", mirror_dir.strpath, package_name, dependency_name)
# Uninstall the all of the packages for clean slate
uninstall("-y", "-a")
# Configure the mirror where we put that buildcache w/ the compiler
mirror("add", "test-mirror", mirror_url)
with capsys.disabled():
# Install using the matrix of possible combinations with --use-buildcache
for pkg, deps in itertools.product(["auto", "only", "never"], repeat=2):
tty.debug(
"Testing `spack install --use-buildcache package:{0},dependencies:{1}`".format(
pkg, deps
)
)
install_use_buildcache("package:{0},dependencies:{1}".format(pkg, deps))
install_use_buildcache("dependencies:{0},package:{1}".format(deps, pkg))
# Install using a default override option
# Alternative to --cache-only (always) or --no-cache (never)
for opt in ["auto", "only", "never"]:
install_use_buildcache(opt)

View file

@ -1188,7 +1188,7 @@ _spack_info() {
_spack_install() { _spack_install() {
if $list_options if $list_options
then then
SPACK_COMPREPLY="-h --help --only -u --until -j --jobs --overwrite --fail-fast --keep-prefix --keep-stage --dont-restage --use-cache --no-cache --cache-only --include-build-deps --no-check-signature --show-log-on-error --source -n --no-checksum --deprecated -v --verbose --fake --only-concrete --no-add -f --file --clean --dirty --test --log-format --log-file --help-cdash --cdash-upload-url --cdash-build --cdash-site --cdash-track --cdash-buildstamp -y --yes-to-all -U --fresh --reuse" SPACK_COMPREPLY="-h --help --only -u --until -j --jobs --overwrite --fail-fast --keep-prefix --keep-stage --dont-restage --use-cache --no-cache --cache-only --use-buildcache --include-build-deps --no-check-signature --show-log-on-error --source -n --no-checksum --deprecated -v --verbose --fake --only-concrete --no-add -f --file --clean --dirty --test --log-format --log-file --help-cdash --cdash-upload-url --cdash-build --cdash-site --cdash-track --cdash-buildstamp -y --yes-to-all -U --fresh --reuse"
else else
_all_packages _all_packages
fi fi