buildcache command updates (#5860)

* Add better spec matching to spack buildcache

* skip download of spec.yaml and keys if they exist

* autopep8
This commit is contained in:
Patrick Gartung 2017-10-20 22:33:01 -05:00 committed by Todd Gamblin
parent 969c8b177f
commit a8ee2a912b
3 changed files with 159 additions and 37 deletions

View File

@ -412,7 +412,7 @@ def extract_tarball(spec, filename, yes_to_all=False, force=False):
relocate_package(installpath) relocate_package(installpath)
def get_specs(): def get_specs(force=False):
""" """
Get spec.yaml's for build caches available on mirror Get spec.yaml's for build caches available on mirror
""" """
@ -443,10 +443,13 @@ def get_specs():
urls.add(link) urls.add(link)
for link in urls: for link in urls:
with Stage(link, name="build_cache", keep=True) as stage: with Stage(link, name="build_cache", keep=True) as stage:
try: if force and os.path.exists(stage.save_filename):
stage.fetch() os.remove(stage.save_filename)
except fs.FetchError: if not os.path.exists(stage.save_filename):
continue try:
stage.fetch()
except fs.FetchError:
continue
with open(stage.save_filename, 'r') as f: with open(stage.save_filename, 'r') as f:
# read the spec from the build cache file. All specs # read the spec from the build cache file. All specs
# in build caches are concrete (as they aer built) so # in build caches are concrete (as they aer built) so
@ -459,7 +462,7 @@ def get_specs():
return specs, durls return specs, durls
def get_keys(install=False, yes_to_all=False): def get_keys(install=False, yes_to_all=False, force=False):
""" """
Get pgp public keys available on mirror Get pgp public keys available on mirror
""" """
@ -487,10 +490,13 @@ def get_keys(install=False, yes_to_all=False):
keys.add(link) keys.add(link)
for link in keys: for link in keys:
with Stage(link, name="build_cache", keep=True) as stage: with Stage(link, name="build_cache", keep=True) as stage:
try: if os.path.exists(stage.save_filename) and force:
stage.fetch() os.remove(stage.save_filename)
except fs.FetchError: if not os.path.exists(stage.save_filename):
continue try:
stage.fetch()
except fs.FetchError:
continue
tty.msg('Found key %s' % link) tty.msg('Found key %s' % link)
if install: if install:
if yes_to_all: if yes_to_all:

View File

@ -25,7 +25,6 @@
import argparse import argparse
import os import os
import re
import llnl.util.tty as tty import llnl.util.tty as tty
import spack import spack
@ -39,6 +38,18 @@
section = "caching" section = "caching"
level = "long" level = "long"
# Arguments for display_specs when we find ambiguity
display_args = {
'long': True,
'show_flags': True,
'variants': True
}
error_message = """You can either:
a) use a more specific spec, or
b) use the package hash with a leading /
"""
def setup_parser(subparser): def setup_parser(subparser):
setup_parser.parser = subparser setup_parser.parser = subparser
@ -76,6 +87,8 @@ def setup_parser(subparser):
install.set_defaults(func=installtarball) install.set_defaults(func=installtarball)
listcache = subparsers.add_parser('list') listcache = subparsers.add_parser('list')
listcache.add_argument('-f', '--force', action='store_true',
help="force new download of specs")
listcache.add_argument( listcache.add_argument(
'packages', nargs=argparse.REMAINDER, 'packages', nargs=argparse.REMAINDER,
help="specs of packages to search for") help="specs of packages to search for")
@ -88,9 +101,91 @@ def setup_parser(subparser):
dlkeys.add_argument( dlkeys.add_argument(
'-y', '--yes-to-all', action='store_true', '-y', '--yes-to-all', action='store_true',
help="answer yes to all trust questions") help="answer yes to all trust questions")
dlkeys.add_argument('-f', '--force', action='store_true',
help="force new download of keys")
dlkeys.set_defaults(func=getkeys) dlkeys.set_defaults(func=getkeys)
def find_matching_specs(specs, allow_multiple_matches=False, force=False):
"""Returns a list of specs matching the not necessarily
concretized specs given from cli
Args:
specs: list of specs to be matched against installed packages
allow_multiple_matches : if True multiple matches are admitted
Return:
list of specs
"""
# List of specs that match expressions given via command line
specs_from_cli = []
has_errors = False
for spec in specs:
matching = spack.store.db.query(spec)
# For each spec provided, make sure it refers to only one package.
# Fail and ask user to be unambiguous if it doesn't
if not allow_multiple_matches and len(matching) > 1:
tty.error('{0} matches multiple packages:'.format(spec))
print()
spack.cmd.display_specs(matching, **display_args)
print()
has_errors = True
# No installed package matches the query
if len(matching) == 0 and spec is not any:
tty.error('{0} does not match any installed packages.'.format(
spec))
has_errors = True
specs_from_cli.extend(matching)
if has_errors:
tty.die(error_message)
return specs_from_cli
def match_downloaded_specs(pkgs, allow_multiple_matches=False, force=False):
"""Returns a list of specs matching the not necessarily
concretized specs given from cli
Args:
specs: list of specs to be matched against buildcaches on mirror
allow_multiple_matches : if True multiple matches are admitted
Return:
list of specs
"""
# List of specs that match expressions given via command line
specs_from_cli = []
has_errors = False
specs, links = bindist.get_specs(force)
for pkg in pkgs:
matches = []
tty.msg("buildcache spec(s) matching %s \n" % pkg)
for spec in sorted(specs):
if spec.satisfies(pkg):
matches.append(spec)
# For each pkg provided, make sure it refers to only one package.
# Fail and ask user to be unambiguous if it doesn't
if not allow_multiple_matches and len(matches) > 1:
tty.error('%s matches multiple downloaded packages:' % pkg)
print()
spack.cmd.display_specs(matches, **display_args)
print()
has_errors = True
# No downloaded package matches the query
if len(matches) == 0:
tty.error('%s does not match any downloaded packages.' % pkg)
has_errors = True
specs_from_cli.extend(matches)
if has_errors:
tty.die(error_message)
return specs_from_cli
def createtarball(args): def createtarball(args):
if not args.packages: if not args.packages:
tty.die("build cache file creation requires at least one" + tty.die("build cache file creation requires at least one" +
@ -112,16 +207,22 @@ def createtarball(args):
force = True force = True
if args.rel: if args.rel:
relative = True relative = True
for pkg in pkgs:
for spec in spack.cmd.parse_specs(pkg, concretize=True): matches = find_matching_specs(pkgs, False, False)
specs.add(spec) for match in matches:
tty.msg('recursing dependencies') tty.msg('adding matching spec %s' % match.format())
for d, node in spec.traverse(order='post', specs.add(match)
depth=True, tty.msg('recursing dependencies')
deptype=('link', 'run')): for d, node in match.traverse(order='post',
if not node.external: depth=True,
tty.msg('adding dependency %s' % node.format()) deptype=('link', 'run')):
specs.add(node) if node.external or node.virtual:
tty.msg('Skipping external or virtual dependency %s' %
node.format())
else:
tty.msg('adding dependency %s' % node.format())
specs.add(node)
for spec in specs: for spec in specs:
tty.msg('creating binary cache file for package %s ' % spec.format()) tty.msg('creating binary cache file for package %s ' % spec.format())
try: try:
@ -147,14 +248,13 @@ def installtarball(args):
tty.die("build cache file installation requires" + tty.die("build cache file installation requires" +
" at least one package spec argument") " at least one package spec argument")
pkgs = set(args.packages) pkgs = set(args.packages)
specs, links = bindist.get_specs() yes_to_all = False
matches = set() if args.yes_to_all:
for spec in specs: yes_to_all = True
for pkg in pkgs: force = False
if re.match(re.escape(pkg), str(spec)): if args.force:
matches.add(spec) force = True
if re.match(re.escape(pkg), '/%s' % spec.dag_hash()): matches = match_downloaded_specs(pkgs, yes_to_all, force)
matches.add(spec)
for match in matches: for match in matches:
install_tarball(match, args) install_tarball(match, args)
@ -162,10 +262,13 @@ def installtarball(args):
def install_tarball(spec, args): def install_tarball(spec, args):
s = spack.spec.Spec(spec) s = spack.spec.Spec(spec)
if s.external or s.virtual:
tty.warn("Skipping external or virtual package %s" % spec.format())
return
yes_to_all = False yes_to_all = False
force = False
if args.yes_to_all: if args.yes_to_all:
yes_to_all = True yes_to_all = True
force = False
if args.force: if args.force:
force = True force = True
for d in s.dependencies(): for d in s.dependencies():
@ -197,20 +300,24 @@ def install_tarball(spec, args):
def listspecs(args): def listspecs(args):
specs, links = bindist.get_specs() force = False
if args.force:
force = True
specs, links = bindist.get_specs(force)
if args.packages: if args.packages:
pkgs = set(args.packages) pkgs = set(args.packages)
for pkg in pkgs: for pkg in pkgs:
tty.msg("buildcache spec(s) matching %s \n" % pkg) tty.msg("buildcache spec(s) matching %s \n" % pkgs)
for spec in sorted(specs): for spec in sorted(specs):
if re.search("^" + re.escape(pkg), str(spec)): if spec.satisfies(pkg):
tty.msg('run "spack buildcache install /%s"' % tty.msg('spack buildcache install /%s\n' %
spec.dag_hash(7) + ' to install %s\n' % spec.dag_hash(7) +
' to install %s' %
spec.format()) spec.format())
else: else:
tty.msg("buildcache specs ") tty.msg("buildcache specs ")
for spec in sorted(specs): for spec in sorted(specs):
tty.msg('run "spack buildcache install /%s" to install %s\n' % tty.msg('spack buildcache install /%s\n to install %s' %
(spec.dag_hash(7), spec.format())) (spec.dag_hash(7), spec.format()))
@ -221,7 +328,10 @@ def getkeys(args):
yes_to_all = False yes_to_all = False
if args.yes_to_all: if args.yes_to_all:
yes_to_all = True yes_to_all = True
bindist.get_keys(install, yes_to_all) force = False
if args.force:
force = True
bindist.get_keys(install, yes_to_all, force)
def buildcache(parser, args): def buildcache(parser, args):

View File

@ -189,6 +189,9 @@ def test_packaging(mock_archive, tmpdir):
args = parser.parse_args(['list']) args = parser.parse_args(['list'])
buildcache.buildcache(parser, args) buildcache.buildcache(parser, args)
args = parser.parse_args(['list', '-f'])
buildcache.buildcache(parser, args)
args = parser.parse_args(['list', 'trivial']) args = parser.parse_args(['list', 'trivial'])
buildcache.buildcache(parser, args) buildcache.buildcache(parser, args)
@ -199,6 +202,9 @@ def test_packaging(mock_archive, tmpdir):
args = parser.parse_args(['keys']) args = parser.parse_args(['keys'])
buildcache.buildcache(parser, args) buildcache.buildcache(parser, args)
args = parser.parse_args(['keys', '-f'])
buildcache.buildcache(parser, args)
# unregister mirror with spack config # unregister mirror with spack config
mirrors = {} mirrors = {}
spack.config.update_config('mirrors', mirrors) spack.config.update_config('mirrors', mirrors)