rework spack help (#3033)
- Full help is now only generated lazily, when needed. - Executing specific commands doesn't require loading all of them. - All commands are only loaded if we need them for help. - There is now short and long help: - short help (spack help) shows only basic spack options - long help (spack help -a) shows all spack options - Both divide help on commands into high-level sections - Commands now specify attributes from which help is auto-generated: - description: used in help to describe the command. - section: help section - level: short or long - Clean up command descriptions - Add a `spack docs` command to open full documentation in the browser. - move `spack doc` command to `spack pydoc` for clarity - Add a `spack --spec` command to show documentation on the spec syntax.
This commit is contained in:
parent
7923579a42
commit
ff3b5d88e4
57 changed files with 736 additions and 218 deletions
222
bin/spack
222
bin/spack
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# flake8: noqa
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
|
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
|
||||||
# Produced at the Lawrence Livermore National Laboratory.
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
@ -26,34 +25,32 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if sys.version_info[:2] < (2, 6):
|
if sys.version_info[:2] < (2, 6):
|
||||||
v_info = sys.version_info[:3]
|
v_info = sys.version_info[:3]
|
||||||
sys.exit("Spack requires Python 2.6 or higher."
|
sys.exit("Spack requires Python 2.6 or higher."
|
||||||
"This is Python %d.%d.%d." % v_info)
|
"This is Python %d.%d.%d." % v_info)
|
||||||
|
|
||||||
import os
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
# Find spack's location and its prefix.
|
# Find spack's location and its prefix.
|
||||||
SPACK_FILE = os.path.realpath(os.path.expanduser(__file__))
|
spack_file = os.path.realpath(os.path.expanduser(__file__))
|
||||||
os.environ["SPACK_FILE"] = SPACK_FILE
|
spack_prefix = os.path.dirname(os.path.dirname(spack_file))
|
||||||
SPACK_PREFIX = os.path.dirname(os.path.dirname(SPACK_FILE))
|
|
||||||
|
|
||||||
# Allow spack libs to be imported in our scripts
|
# Allow spack libs to be imported in our scripts
|
||||||
SPACK_LIB_PATH = os.path.join(SPACK_PREFIX, "lib", "spack")
|
spack_lib_path = os.path.join(spack_prefix, "lib", "spack")
|
||||||
sys.path.insert(0, SPACK_LIB_PATH)
|
sys.path.insert(0, spack_lib_path)
|
||||||
|
|
||||||
# Add external libs
|
# Add external libs
|
||||||
SPACK_EXTERNAL_LIBS = os.path.join(SPACK_LIB_PATH, "external")
|
spack_external_libs = os.path.join(spack_lib_path, "external")
|
||||||
sys.path.insert(0, SPACK_EXTERNAL_LIBS)
|
sys.path.insert(0, spack_external_libs)
|
||||||
|
|
||||||
# Handle vendoring of YAML specially, as it has two versions.
|
# Handle vendoring of YAML specially, as it has two versions.
|
||||||
if sys.version_info[0] == 2:
|
if sys.version_info[0] == 2:
|
||||||
SPACK_YAML_LIBS = os.path.join(SPACK_EXTERNAL_LIBS, "yaml/lib")
|
spack_yaml_libs = os.path.join(spack_external_libs, "yaml/lib")
|
||||||
else:
|
else:
|
||||||
SPACK_YAML_LIBS = os.path.join(SPACK_EXTERNAL_LIBS, "yaml/lib3")
|
spack_yaml_libs = os.path.join(spack_external_libs, "yaml/lib3")
|
||||||
sys.path.insert(0, SPACK_YAML_LIBS)
|
sys.path.insert(0, spack_yaml_libs)
|
||||||
|
|
||||||
# Quick and dirty check to clean orphaned .pyc files left over from
|
# Quick and dirty check to clean orphaned .pyc files left over from
|
||||||
# previous revisions. These files were present in earlier versions of
|
# previous revisions. These files were present in earlier versions of
|
||||||
|
@ -61,13 +58,13 @@ sys.path.insert(0, SPACK_YAML_LIBS)
|
||||||
# imports. If we leave them, Spack will fail in mysterious ways.
|
# imports. If we leave them, Spack will fail in mysterious ways.
|
||||||
# TODO: more elegant solution for orphaned pyc files.
|
# TODO: more elegant solution for orphaned pyc files.
|
||||||
orphaned_pyc_files = [
|
orphaned_pyc_files = [
|
||||||
os.path.join(SPACK_EXTERNAL_LIBS, 'functools.pyc'),
|
os.path.join(spack_external_libs, 'functools.pyc'),
|
||||||
os.path.join(SPACK_EXTERNAL_LIBS, 'ordereddict.pyc'),
|
os.path.join(spack_external_libs, 'ordereddict.pyc'),
|
||||||
os.path.join(SPACK_LIB_PATH, 'spack', 'platforms', 'cray_xc.pyc'),
|
os.path.join(spack_lib_path, 'spack', 'platforms', 'cray_xc.pyc'),
|
||||||
os.path.join(SPACK_LIB_PATH, 'spack', 'cmd', 'package-list.pyc'),
|
os.path.join(spack_lib_path, 'spack', 'cmd', 'package-list.pyc'),
|
||||||
os.path.join(SPACK_LIB_PATH, 'spack', 'cmd', 'test-install.pyc'),
|
os.path.join(spack_lib_path, 'spack', 'cmd', 'test-install.pyc'),
|
||||||
os.path.join(SPACK_LIB_PATH, 'spack', 'cmd', 'url-parse.pyc'),
|
os.path.join(spack_lib_path, 'spack', 'cmd', 'url-parse.pyc'),
|
||||||
os.path.join(SPACK_LIB_PATH, 'spack', 'test', 'yaml.pyc')
|
os.path.join(spack_lib_path, 'spack', 'test', 'yaml.pyc')
|
||||||
]
|
]
|
||||||
|
|
||||||
for pyc_file in orphaned_pyc_files:
|
for pyc_file in orphaned_pyc_files:
|
||||||
|
@ -79,183 +76,6 @@ for pyc_file in orphaned_pyc_files:
|
||||||
print("WARNING: Spack may fail mysteriously. "
|
print("WARNING: Spack may fail mysteriously. "
|
||||||
"Couldn't remove orphaned .pyc file: %s" % pyc_file)
|
"Couldn't remove orphaned .pyc file: %s" % pyc_file)
|
||||||
|
|
||||||
# If there is no working directory, use the spack prefix.
|
# Once we've set up the system path, run the spack main method
|
||||||
try:
|
import spack.main # noqa
|
||||||
working_dir = os.getcwd()
|
sys.exit(spack.main.main())
|
||||||
except OSError:
|
|
||||||
os.chdir(SPACK_PREFIX)
|
|
||||||
working_dir = SPACK_PREFIX
|
|
||||||
|
|
||||||
# clean up the scope and start using spack package instead.
|
|
||||||
del SPACK_FILE, SPACK_PREFIX, SPACK_LIB_PATH
|
|
||||||
import llnl.util.tty as tty
|
|
||||||
from llnl.util.tty.color import *
|
|
||||||
import spack
|
|
||||||
from spack.error import SpackError
|
|
||||||
import argparse
|
|
||||||
import pstats
|
|
||||||
|
|
||||||
# Get the allowed names of statistics for cProfile, and make a list of
|
|
||||||
# groups of 7 names to wrap them nicely.
|
|
||||||
stat_names = pstats.Stats.sort_arg_dict_default
|
|
||||||
stat_lines = list(zip(*(iter(stat_names),)*7))
|
|
||||||
|
|
||||||
# Command parsing
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
formatter_class=argparse.RawTextHelpFormatter,
|
|
||||||
description="Spack: the Supercomputing PACKage Manager." + colorize("""
|
|
||||||
|
|
||||||
spec expressions:
|
|
||||||
PACKAGE [CONSTRAINTS]
|
|
||||||
|
|
||||||
CONSTRAINTS:
|
|
||||||
@c{@version}
|
|
||||||
@g{%compiler @compiler_version}
|
|
||||||
@B{+variant}
|
|
||||||
@r{-variant} or @r{~variant}
|
|
||||||
@m{=architecture}
|
|
||||||
[^DEPENDENCY [CONSTRAINTS] ...]"""))
|
|
||||||
|
|
||||||
parser.add_argument('-d', '--debug', action='store_true',
|
|
||||||
help="write out debug logs during compile")
|
|
||||||
parser.add_argument('-D', '--pdb', action='store_true',
|
|
||||||
help="run spack under the pdb debugger")
|
|
||||||
parser.add_argument('-k', '--insecure', action='store_true',
|
|
||||||
help="do not check ssl certificates when downloading")
|
|
||||||
parser.add_argument('-m', '--mock', action='store_true',
|
|
||||||
help="use mock packages instead of real ones")
|
|
||||||
parser.add_argument('-p', '--profile', action='store_true',
|
|
||||||
help="profile execution using cProfile")
|
|
||||||
parser.add_argument('-P', '--sorted-profile', default=None, metavar="STAT",
|
|
||||||
help="profile and sort by one or more of:\n[%s]" %
|
|
||||||
',\n '.join([', '.join(line) for line in stat_lines]))
|
|
||||||
parser.add_argument('--lines', default=20, action='store',
|
|
||||||
help="lines of profile output: default 20; 'all' for all")
|
|
||||||
parser.add_argument('-v', '--verbose', action='store_true',
|
|
||||||
help="print additional output during builds")
|
|
||||||
parser.add_argument('-s', '--stacktrace', action='store_true',
|
|
||||||
help="add stacktrace info to all printed statements")
|
|
||||||
parser.add_argument('-V', '--version', action='version',
|
|
||||||
version="%s" % spack.spack_version)
|
|
||||||
|
|
||||||
# each command module implements a parser() function, to which we pass its
|
|
||||||
# subparser for setup.
|
|
||||||
subparsers = parser.add_subparsers(metavar='SUBCOMMAND', dest="command")
|
|
||||||
|
|
||||||
|
|
||||||
import spack.cmd
|
|
||||||
for cmd in spack.cmd.commands:
|
|
||||||
module = spack.cmd.get_module(cmd)
|
|
||||||
cmd_name = cmd.replace('_', '-')
|
|
||||||
subparser = subparsers.add_parser(cmd_name, help=module.description)
|
|
||||||
module.setup_parser(subparser)
|
|
||||||
|
|
||||||
|
|
||||||
def _main(args, unknown_args):
|
|
||||||
# Set up environment based on args.
|
|
||||||
tty.set_verbose(args.verbose)
|
|
||||||
tty.set_debug(args.debug)
|
|
||||||
tty.set_stacktrace(args.stacktrace)
|
|
||||||
spack.debug = args.debug
|
|
||||||
|
|
||||||
if spack.debug:
|
|
||||||
import spack.util.debug as debug
|
|
||||||
debug.register_interrupt_handler()
|
|
||||||
|
|
||||||
# Run any available pre-run hooks
|
|
||||||
spack.hooks.pre_run()
|
|
||||||
|
|
||||||
spack.spack_working_dir = working_dir
|
|
||||||
if args.mock:
|
|
||||||
from spack.repository import RepoPath
|
|
||||||
spack.repo.swap(RepoPath(spack.mock_packages_path))
|
|
||||||
|
|
||||||
# If the user asked for it, don't check ssl certs.
|
|
||||||
if args.insecure:
|
|
||||||
tty.warn("You asked for --insecure. Will NOT check SSL certificates.")
|
|
||||||
spack.insecure = True
|
|
||||||
|
|
||||||
# Try to load the particular command asked for and run it
|
|
||||||
command = spack.cmd.get_command(args.command.replace('-', '_'))
|
|
||||||
|
|
||||||
# Allow commands to inject an optional argument and get unknown args
|
|
||||||
# if they want to handle them.
|
|
||||||
info = dict(inspect.getmembers(command))
|
|
||||||
varnames = info['__code__'].co_varnames
|
|
||||||
argcount = info['__code__'].co_argcount
|
|
||||||
|
|
||||||
# Actually execute the command
|
|
||||||
try:
|
|
||||||
if argcount == 3 and varnames[2] == 'unknown_args':
|
|
||||||
return_val = command(parser, args, unknown_args)
|
|
||||||
else:
|
|
||||||
if unknown_args:
|
|
||||||
tty.die('unrecognized arguments: %s' % ' '.join(unknown_args))
|
|
||||||
return_val = command(parser, args)
|
|
||||||
except SpackError as e:
|
|
||||||
e.die()
|
|
||||||
except Exception as e:
|
|
||||||
tty.die(str(e))
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
sys.stderr.write('\n')
|
|
||||||
tty.die("Keyboard interrupt.")
|
|
||||||
|
|
||||||
# Allow commands to return values if they want to exit with some other code.
|
|
||||||
if return_val is None:
|
|
||||||
sys.exit(0)
|
|
||||||
elif isinstance(return_val, int):
|
|
||||||
sys.exit(return_val)
|
|
||||||
else:
|
|
||||||
tty.die("Bad return value from command %s: %s"
|
|
||||||
% (args.command, return_val))
|
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
|
||||||
# Just print help and exit if run with no arguments at all
|
|
||||||
if len(args) == 1:
|
|
||||||
parser.print_help()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# actually parse the args.
|
|
||||||
args, unknown = parser.parse_known_args()
|
|
||||||
|
|
||||||
if args.profile or args.sorted_profile:
|
|
||||||
import cProfile
|
|
||||||
|
|
||||||
try:
|
|
||||||
nlines = int(args.lines)
|
|
||||||
except ValueError:
|
|
||||||
if args.lines != 'all':
|
|
||||||
tty.die('Invalid number for --lines: %s' % args.lines)
|
|
||||||
nlines = -1
|
|
||||||
|
|
||||||
# allow comma-separated list of fields
|
|
||||||
sortby = ['time']
|
|
||||||
if args.sorted_profile:
|
|
||||||
sortby = args.sorted_profile.split(',')
|
|
||||||
for stat in sortby:
|
|
||||||
if stat not in stat_names:
|
|
||||||
tty.die("Invalid sort field: %s" % stat)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# make a profiler and run the code.
|
|
||||||
pr = cProfile.Profile()
|
|
||||||
pr.enable()
|
|
||||||
_main(args, unknown)
|
|
||||||
finally:
|
|
||||||
pr.disable()
|
|
||||||
|
|
||||||
# print out profile stats.
|
|
||||||
stats = pstats.Stats(pr)
|
|
||||||
stats.sort_stats(*sortby)
|
|
||||||
stats.print_stats(nlines)
|
|
||||||
|
|
||||||
elif args.pdb:
|
|
||||||
import pdb
|
|
||||||
pdb.runctx('_main(args, unknown)', globals(), locals())
|
|
||||||
else:
|
|
||||||
_main(args, unknown)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main(sys.argv)
|
|
||||||
|
|
|
@ -217,5 +217,5 @@
|
||||||
|
|
||||||
# Add default values for attributes that would otherwise be modified from
|
# Add default values for attributes that would otherwise be modified from
|
||||||
# Spack main script
|
# Spack main script
|
||||||
debug = True
|
debug = False
|
||||||
spack_working_dir = None
|
spack_working_dir = None
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "activate a package extension"
|
description = "activate a package extension"
|
||||||
|
section = "extensions"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
import spack.architecture as architecture
|
import spack.architecture as architecture
|
||||||
|
|
||||||
description = "print architecture information about this machine"
|
description = "print architecture information about this machine"
|
||||||
|
section = "system"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
_SPACK_UPSTREAM = 'https://github.com/llnl/spack'
|
_SPACK_UPSTREAM = 'https://github.com/llnl/spack'
|
||||||
|
|
||||||
description = "create a new installation of spack in another prefix"
|
description = "create a new installation of spack in another prefix"
|
||||||
|
section = "admin"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
from spack import *
|
from spack import *
|
||||||
|
|
||||||
description = 'stops at build stage when installing a package, if possible'
|
description = 'stops at build stage when installing a package, if possible'
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
build_system_to_phase = {
|
build_system_to_phase = {
|
||||||
AutotoolsPackage: 'build',
|
AutotoolsPackage: 'build',
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
import spack.modules
|
import spack.modules
|
||||||
|
|
||||||
description = "cd to spack directories in the shell"
|
description = "cd to spack directories in the shell"
|
||||||
|
section = "environment"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
from spack.version import *
|
from spack.version import *
|
||||||
|
|
||||||
description = "checksum available versions of a package"
|
description = "checksum available versions of a package"
|
||||||
|
section = "packaging"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "remove build stage and source tarball for packages"
|
description = "remove build stage and source tarball for packages"
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
from spack.util.environment import get_path
|
from spack.util.environment import get_path
|
||||||
|
|
||||||
description = "manage compilers"
|
description = "manage compilers"
|
||||||
|
section = "system"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -25,7 +25,9 @@
|
||||||
import spack
|
import spack
|
||||||
from spack.cmd.compiler import compiler_list
|
from spack.cmd.compiler import compiler_list
|
||||||
|
|
||||||
description = "list available compilers, same as 'spack compiler list'"
|
description = "list available compilers"
|
||||||
|
section = "system"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
import spack.config
|
import spack.config
|
||||||
|
|
||||||
description = "get and set configuration options"
|
description = "get and set configuration options"
|
||||||
|
section = "config"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
|
|
||||||
from spack import *
|
from spack import *
|
||||||
|
|
||||||
description = 'stops at configuration stage when installing a package, if possible' # NOQA: ignore=E501
|
description = 'stage and configure a package but do not install'
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
build_system_to_phase = {
|
build_system_to_phase = {
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
from spack.url import *
|
from spack.url import *
|
||||||
|
|
||||||
description = "create a new package file"
|
description = "create a new package file"
|
||||||
|
section = "packaging"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
package_template = '''\
|
package_template = '''\
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
from spack.graph import topological_sort
|
from spack.graph import topological_sort
|
||||||
|
|
||||||
description = "deactivate a package extension"
|
description = "deactivate a package extension"
|
||||||
|
section = "extensions"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
from spack.util.executable import which
|
from spack.util.executable import which
|
||||||
|
|
||||||
description = "debugging commands for troubleshooting Spack"
|
description = "debugging commands for troubleshooting Spack"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "show installed packages that depend on another"
|
description = "show installed packages that depend on another"
|
||||||
|
section = "basic"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
from spack.stage import DIYStage
|
from spack.stage import DIYStage
|
||||||
|
|
||||||
description = "do-it-yourself: build from an existing source directory"
|
description = "do-it-yourself: build from an existing source directory"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
33
lib/spack/spack/cmd/docs.py
Normal file
33
lib/spack/spack/cmd/docs.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
##############################################################################
|
||||||
|
# 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 LICENSE file 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 webbrowser
|
||||||
|
|
||||||
|
description = 'open spack documentation in a web browser'
|
||||||
|
section = 'help'
|
||||||
|
level = 'short'
|
||||||
|
|
||||||
|
|
||||||
|
def docs(parser, args):
|
||||||
|
webbrowser.open('https://spack.readthedocs.io')
|
|
@ -33,6 +33,8 @@
|
||||||
from spack.repository import Repo
|
from spack.repository import Repo
|
||||||
|
|
||||||
description = "open package files in $EDITOR"
|
description = "open package files in $EDITOR"
|
||||||
|
section = "packaging"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def edit_package(name, repo_path, namespace):
|
def edit_package(name, repo_path, namespace):
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import spack.build_environment as build_env
|
import spack.build_environment as build_env
|
||||||
|
|
||||||
description = "run a command with the install environment for a spec"
|
description = "show install environment for a spec, and run commands"
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
import spack.store
|
import spack.store
|
||||||
|
|
||||||
description = "list extensions for package"
|
description = "list extensions for package"
|
||||||
|
section = "extensions"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "fetch archives for packages"
|
description = "fetch archives for packages"
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -29,7 +29,9 @@
|
||||||
|
|
||||||
from spack.cmd import display_specs
|
from spack.cmd import display_specs
|
||||||
|
|
||||||
description = "find installed spack packages"
|
description = "list and search installed packages"
|
||||||
|
section = "basic"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -36,7 +36,11 @@
|
||||||
import spack
|
import spack
|
||||||
from spack.util.executable import *
|
from spack.util.executable import *
|
||||||
|
|
||||||
|
|
||||||
description = "runs source code style checks on Spack. requires flake8"
|
description = "runs source code style checks on Spack. requires flake8"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
"""List of directories to exclude from checks."""
|
"""List of directories to exclude from checks."""
|
||||||
exclude_directories = [spack.external_path]
|
exclude_directories = [spack.external_path]
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
from spack.graph import *
|
from spack.graph import *
|
||||||
|
|
||||||
description = "generate graphs of package dependency relationships"
|
description = "generate graphs of package dependency relationships"
|
||||||
|
section = "basic"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -22,16 +22,100 @@
|
||||||
# License along with this program; if not, write to the Free Software
|
# License along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
import sys
|
||||||
|
from llnl.util.tty import colorize
|
||||||
|
|
||||||
description = "get help on spack and its commands"
|
description = "get help on spack and its commands"
|
||||||
|
section = "help"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
#
|
||||||
|
# These are longer guides on particular aspects of Spack. Currently there
|
||||||
|
# is only one on spec syntax.
|
||||||
|
#
|
||||||
|
spec_guide = """\
|
||||||
|
spec expression syntax:
|
||||||
|
|
||||||
|
package [constraints] [^dependency [constraints] ...]
|
||||||
|
|
||||||
|
package any package from 'spack list'
|
||||||
|
|
||||||
|
constraints:
|
||||||
|
versions:
|
||||||
|
@c{@version} single version
|
||||||
|
@c{@min:max} version range (inclusive)
|
||||||
|
@c{@min:} version <min> or higher
|
||||||
|
@c{@:max} up to version <max> (inclusive)
|
||||||
|
|
||||||
|
compilers:
|
||||||
|
@g{%compiler} build with <compiler>
|
||||||
|
@g{%compiler@version} build with specific compiler version
|
||||||
|
@g{%compiler@min:max} specific version range (see above)
|
||||||
|
|
||||||
|
variants:
|
||||||
|
@B{+variant} enable <variant>
|
||||||
|
@r{-variant} or @r{~variant} disable <variant>
|
||||||
|
@B{variant=value} set non-boolean <variant> to <value>
|
||||||
|
@B{variant=value1,value2,value3} set multi-value <variant> values
|
||||||
|
|
||||||
|
architecture variants:
|
||||||
|
@m{target=target} specific <target> processor
|
||||||
|
@m{os=operating_system} specific <operating_system>
|
||||||
|
@m{platform=platform} linux, darwin, cray, bgq, etc.
|
||||||
|
@m{arch=platform-os-target} shortcut for all three above
|
||||||
|
|
||||||
|
cross-compiling:
|
||||||
|
@m{os=backend} or @m{os=be} build for compute node (backend)
|
||||||
|
@m{os=frontend} or @m{os=fe} build for login node (frontend)
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
^dependency [constraints] specify constraints on dependencies
|
||||||
|
|
||||||
|
examples:
|
||||||
|
hdf5 any hdf5 configuration
|
||||||
|
hdf5 @c{@1.10.1} hdf5 version 1.10.1
|
||||||
|
hdf5 @c{@1.8:} hdf5 1.8 or higher
|
||||||
|
hdf5 @c{@1.8:} @g{%gcc} hdf5 1.8 or higher built with gcc
|
||||||
|
hdf5 @B{+mpi} hdf5 with mpi enabled
|
||||||
|
hdf5 @r{~mpi} hdf5 with mpi disabled
|
||||||
|
hdf5 @B{+mpi} ^mpich hdf5 with mpi, using mpich
|
||||||
|
hdf5 @B{+mpi} ^openmpi@c{@1.7} hdf5 wtih mpi, using openmpi 1.7
|
||||||
|
boxlib @B{dim=2} boxlib built for 2 dimensions
|
||||||
|
libdwarf @g{%intel} ^libelf@g{%gcc}
|
||||||
|
libdwarf, built with intel compiler, linked to libelf built with gcc
|
||||||
|
mvapich2 @g{%pgi} @B{fabrics=psm,mrail,sock}
|
||||||
|
mvapich2, built with pgi compiler, with support for multiple fabrics
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
guides = {
|
||||||
|
'spec': spec_guide,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
subparser.add_argument('help_command', nargs='?', default=None,
|
help_cmd_group = subparser.add_mutually_exclusive_group()
|
||||||
help='command to get help on')
|
help_cmd_group.add_argument('help_command', nargs='?', default=None,
|
||||||
|
help='command to get help on')
|
||||||
|
|
||||||
|
help_all_group = subparser.add_mutually_exclusive_group()
|
||||||
|
help_all_group.add_argument(
|
||||||
|
'-a', '--all', action='store_const', const='long', default='short',
|
||||||
|
help='print all available commands')
|
||||||
|
|
||||||
|
help_spec_group = subparser.add_mutually_exclusive_group()
|
||||||
|
help_spec_group.add_argument(
|
||||||
|
'--spec', action='store_const', dest='guide', const='spec',
|
||||||
|
default=None, help='print all available commands')
|
||||||
|
|
||||||
|
|
||||||
def help(parser, args):
|
def help(parser, args):
|
||||||
|
if args.guide:
|
||||||
|
print(colorize(guides[args.guide]))
|
||||||
|
return 0
|
||||||
|
|
||||||
if args.help_command:
|
if args.help_command:
|
||||||
|
parser.add_command(args.help_command)
|
||||||
parser.parse_args([args.help_command, '-h'])
|
parser.parse_args([args.help_command, '-h'])
|
||||||
else:
|
else:
|
||||||
parser.print_help()
|
sys.stdout.write(parser.format_help(level=args.all))
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
import spack.fetch_strategy as fs
|
import spack.fetch_strategy as fs
|
||||||
|
|
||||||
description = "get detailed information on a particular package"
|
description = "get detailed information on a particular package"
|
||||||
|
section = "basic"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def padder(str_list, extra=0):
|
def padder(str_list, extra=0):
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
from spack.package import PackageBase
|
from spack.package import PackageBase
|
||||||
|
|
||||||
description = "build and install packages"
|
description = "build and install packages"
|
||||||
|
section = "build"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -35,7 +35,10 @@
|
||||||
import spack
|
import spack
|
||||||
from llnl.util.tty.colify import colify
|
from llnl.util.tty.colify import colify
|
||||||
|
|
||||||
description = "print available spack packages to stdout in different formats"
|
description = "list and search available packages"
|
||||||
|
section = "basic"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
formatters = {}
|
formatters = {}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,9 @@
|
||||||
import argparse
|
import argparse
|
||||||
import spack.modules
|
import spack.modules
|
||||||
|
|
||||||
description = "add package to environment using modules"
|
description = "add package to environment using `module load`"
|
||||||
|
section = "environment"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "print out locations of various directories used by Spack"
|
description = "print out locations of various directories used by Spack"
|
||||||
|
section = "environment"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
from spack.stage import Stage, FailedDownloadError
|
from spack.stage import Stage, FailedDownloadError
|
||||||
|
|
||||||
description = "calculate md5 checksums for files/urls"
|
description = "calculate md5 checksums for files/urls"
|
||||||
|
section = "packaging"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
from spack.util.spack_yaml import syaml_dict
|
from spack.util.spack_yaml import syaml_dict
|
||||||
|
|
||||||
description = "manage mirrors"
|
description = "manage mirrors"
|
||||||
|
section = "config"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -36,6 +36,9 @@
|
||||||
from spack.modules import module_types
|
from spack.modules import module_types
|
||||||
|
|
||||||
description = "manipulate module files"
|
description = "manipulate module files"
|
||||||
|
section = "environment"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
# Dictionary that will be populated with the list of sub-commands
|
# Dictionary that will be populated with the list of sub-commands
|
||||||
# Each sub-command must be callable and accept 3 arguments :
|
# Each sub-command must be callable and accept 3 arguments :
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
|
|
||||||
description = "patch expanded archive sources in preparation for install"
|
description = "patch expanded archive sources in preparation for install"
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
from spack.util.executable import *
|
from spack.util.executable import *
|
||||||
|
|
||||||
description = "query packages associated with particular git revisions"
|
description = "query packages associated with particular git revisions"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "list packages that provide a particular virtual package"
|
description = "list packages that provide a particular virtual package"
|
||||||
|
section = "basic"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
import spack.stage as stage
|
import spack.stage as stage
|
||||||
|
|
||||||
description = "remove temporary build files and/or downloaded archives"
|
description = "remove temporary build files and/or downloaded archives"
|
||||||
|
section = "admin"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
|
# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
|
||||||
# Produced at the Lawrence Livermore National Laboratory.
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
#
|
#
|
||||||
# This file is part of Spack.
|
# This file is part of Spack.
|
||||||
|
@ -24,11 +24,13 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
description = "run pydoc from within spack"
|
description = "run pydoc from within spack"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
subparser.add_argument('entity', help="run pydoc help on entity")
|
subparser.add_argument('entity', help="run pydoc help on entity")
|
||||||
|
|
||||||
|
|
||||||
def doc(parser, args):
|
def pydoc(parser, args):
|
||||||
help(args.entity)
|
help(args.entity)
|
|
@ -32,6 +32,8 @@
|
||||||
|
|
||||||
|
|
||||||
description = "launch an interpreter as spack would launch a command"
|
description = "launch an interpreter as spack would launch a command"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
import spack.store
|
import spack.store
|
||||||
description = "rebuild Spack's package database"
|
description = "rebuild Spack's package database"
|
||||||
|
|
||||||
|
section = "admin"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def reindex(parser, args):
|
def reindex(parser, args):
|
||||||
spack.store.db.reindex(spack.store.layout)
|
spack.store.db.reindex(spack.store.layout)
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
from spack.repository import *
|
from spack.repository import *
|
||||||
|
|
||||||
description = "manage package source repositories"
|
description = "manage package source repositories"
|
||||||
|
section = "config"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "revert checked out package source code"
|
description = "revert checked out package source code"
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
from spack.stage import DIYStage
|
from spack.stage import DIYStage
|
||||||
|
|
||||||
description = "create a configuration script and module, but don't build"
|
description = "create a configuration script and module, but don't build"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -29,7 +29,9 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import spack.cmd.common.arguments as arguments
|
import spack.cmd.common.arguments as arguments
|
||||||
|
|
||||||
description = "print out abstract and concrete versions of a spec"
|
description = "show what would be installed, given a spec"
|
||||||
|
section = "build"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
|
||||||
description = "expand downloaded archive in preparation for install"
|
description = "expand downloaded archive in preparation for install"
|
||||||
|
section = "build"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -36,7 +36,9 @@
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
|
||||||
description = "a thin wrapper around the pytest command"
|
description = "run spack's unit tests"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -33,7 +33,9 @@
|
||||||
|
|
||||||
from llnl.util import tty
|
from llnl.util import tty
|
||||||
|
|
||||||
description = "remove an installed package"
|
description = "remove installed packages"
|
||||||
|
section = "build"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
error_message = """You can either:
|
error_message = """You can either:
|
||||||
a) use a more specific spec, or
|
a) use a more specific spec, or
|
||||||
|
|
|
@ -25,7 +25,9 @@
|
||||||
import argparse
|
import argparse
|
||||||
import spack.modules
|
import spack.modules
|
||||||
|
|
||||||
description = "remove package from environment using module"
|
description = "remove package from environment using `module unload`"
|
||||||
|
section = "environment"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
import spack.modules
|
import spack.modules
|
||||||
|
|
||||||
description = "remove package from environment using dotkit"
|
description = "remove package from environment using dotkit"
|
||||||
|
section = "environment"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
from spack.util.naming import simplify_name
|
from spack.util.naming import simplify_name
|
||||||
|
|
||||||
description = "debugging tool for url parsing"
|
description = "debugging tool for url parsing"
|
||||||
|
section = "developer"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
import spack.modules
|
import spack.modules
|
||||||
|
|
||||||
description = "add package to environment using dotkit"
|
description = "add package to environment using dotkit"
|
||||||
|
section = "environment"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
import spack
|
import spack
|
||||||
|
|
||||||
description = "list available versions of a package"
|
description = "list available versions of a package"
|
||||||
|
section = "packaging"
|
||||||
|
level = "long"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
|
|
@ -69,7 +69,9 @@
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
description = "produce a single-rooted directory view of a spec"
|
description = "produce a single-rooted directory view of packages"
|
||||||
|
section = "environment"
|
||||||
|
level = "short"
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(sp):
|
def setup_parser(sp):
|
||||||
|
|
468
lib/spack/spack/main.py
Normal file
468
lib/spack/spack/main.py
Normal file
|
@ -0,0 +1,468 @@
|
||||||
|
##############################################################################
|
||||||
|
# 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 LICENSE file 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
|
||||||
|
##############################################################################
|
||||||
|
"""This is the implementation of the Spack command line executable.
|
||||||
|
|
||||||
|
In a normal Spack installation, this is invoked from the bin/spack script
|
||||||
|
after the system path is set up.
|
||||||
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import inspect
|
||||||
|
from argparse import _ArgumentGroup, ArgumentParser, RawTextHelpFormatter
|
||||||
|
import pstats
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
from llnl.util.tty.color import *
|
||||||
|
|
||||||
|
import spack
|
||||||
|
import spack.cmd
|
||||||
|
from spack.error import SpackError
|
||||||
|
|
||||||
|
|
||||||
|
# names of profile statistics
|
||||||
|
stat_names = pstats.Stats.sort_arg_dict_default
|
||||||
|
|
||||||
|
# help levels in order of detail (i.e., number of commands shown)
|
||||||
|
levels = ['short', 'long']
|
||||||
|
|
||||||
|
# intro text for help at different levels
|
||||||
|
intro_by_level = {
|
||||||
|
'short': 'These are common spack commands:',
|
||||||
|
'long': 'Complete list of spack commands:',
|
||||||
|
}
|
||||||
|
|
||||||
|
# control top-level spack options shown in basic vs. advanced help
|
||||||
|
options_by_level = {
|
||||||
|
'short': 'hkV',
|
||||||
|
'long': 'all'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Longer text for each section, to show in help
|
||||||
|
section_descriptions = {
|
||||||
|
'admin': 'administration',
|
||||||
|
'basic': 'query packages',
|
||||||
|
'build': 'build packages',
|
||||||
|
'config': 'configuration',
|
||||||
|
'developer': 'developer',
|
||||||
|
'environment': 'environment',
|
||||||
|
'extensions': 'extensions',
|
||||||
|
'help': 'more help',
|
||||||
|
'packaging': 'create packages',
|
||||||
|
'system': 'system',
|
||||||
|
}
|
||||||
|
|
||||||
|
# preferential command order for some sections (e.g., build pipeline is
|
||||||
|
# in execution order, not alphabetical)
|
||||||
|
section_order = {
|
||||||
|
'basic': ['list', 'info', 'find'],
|
||||||
|
'build': ['fetch', 'stage', 'patch', 'configure', 'build', 'restage',
|
||||||
|
'install', 'uninstall', 'clean']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Properties that commands are required to set.
|
||||||
|
required_command_properties = ['level', 'section', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
def set_working_dir():
|
||||||
|
"""Change the working directory to getcwd, or spack prefix if no cwd."""
|
||||||
|
try:
|
||||||
|
spack.spack_working_dir = os.getcwd()
|
||||||
|
except OSError:
|
||||||
|
os.chdir(spack_prefix)
|
||||||
|
spack.spack_working_dir = spack_prefix
|
||||||
|
|
||||||
|
|
||||||
|
def add_all_commands(parser):
|
||||||
|
"""Add all spack subcommands to the parser."""
|
||||||
|
for cmd in spack.cmd.commands:
|
||||||
|
parser.add_command(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def index_commands():
|
||||||
|
"""create an index of commands by section for this help level"""
|
||||||
|
index = {}
|
||||||
|
for command in spack.cmd.commands:
|
||||||
|
cmd_module = spack.cmd.get_module(command)
|
||||||
|
|
||||||
|
# make sure command modules have required properties
|
||||||
|
for p in required_command_properties:
|
||||||
|
prop = getattr(cmd_module, p, None)
|
||||||
|
if not prop:
|
||||||
|
tty.die("Command doesn't define a property '%s': %s"
|
||||||
|
% (p, command))
|
||||||
|
|
||||||
|
# add commands to lists for their level and higher levels
|
||||||
|
for level in reversed(levels):
|
||||||
|
level_sections = index.setdefault(level, {})
|
||||||
|
commands = level_sections.setdefault(cmd_module.section, [])
|
||||||
|
commands.append(command)
|
||||||
|
if level == cmd_module.level:
|
||||||
|
break
|
||||||
|
|
||||||
|
return index
|
||||||
|
|
||||||
|
|
||||||
|
class SpackArgumentParser(ArgumentParser):
|
||||||
|
def format_help_sections(self, level):
|
||||||
|
"""Format help on sections for a particular verbosity level.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level (str): 'short' or 'long' (more commands shown for long)
|
||||||
|
"""
|
||||||
|
if level not in levels:
|
||||||
|
raise ValueError("level must be one of: %s" % levels)
|
||||||
|
|
||||||
|
# lazily add all commands to the parser when needed.
|
||||||
|
add_all_commands(self)
|
||||||
|
|
||||||
|
"""Print help on subcommands in neatly formatted sections."""
|
||||||
|
formatter = self._get_formatter()
|
||||||
|
|
||||||
|
# Create a list of subcommand actions. Argparse internals are nasty!
|
||||||
|
# Note: you can only call _get_subactions() once. Even nastier!
|
||||||
|
if not hasattr(self, 'actions'):
|
||||||
|
self.actions = self._subparsers._actions[-1]._get_subactions()
|
||||||
|
|
||||||
|
# make a set of commands not yet added.
|
||||||
|
remaining = set(spack.cmd.commands)
|
||||||
|
|
||||||
|
def add_group(group):
|
||||||
|
formatter.start_section(group.title)
|
||||||
|
formatter.add_text(group.description)
|
||||||
|
formatter.add_arguments(group._group_actions)
|
||||||
|
formatter.end_section()
|
||||||
|
|
||||||
|
def add_subcommand_group(title, commands):
|
||||||
|
"""Add informational help group for a specific subcommand set."""
|
||||||
|
cmd_set = set(commands)
|
||||||
|
|
||||||
|
# make a dict of commands of interest
|
||||||
|
cmds = dict((action.metavar, action) for action in self.actions
|
||||||
|
if action.metavar in cmd_set)
|
||||||
|
|
||||||
|
# add commands to a group in order, and add the group
|
||||||
|
group = _ArgumentGroup(self, title=title)
|
||||||
|
for name in commands:
|
||||||
|
group._add_action(cmds[name])
|
||||||
|
if name in remaining:
|
||||||
|
remaining.remove(name)
|
||||||
|
add_group(group)
|
||||||
|
|
||||||
|
# select only the options for the particular level we're showing.
|
||||||
|
show_options = options_by_level[level]
|
||||||
|
if show_options != 'all':
|
||||||
|
opts = dict((opt.option_strings[0].strip('-'), opt)
|
||||||
|
for opt in self._optionals._group_actions)
|
||||||
|
|
||||||
|
new_actions = [opts[letter] for letter in show_options]
|
||||||
|
self._optionals._group_actions = new_actions
|
||||||
|
|
||||||
|
options = ''.join(opt.option_strings[0].strip('-')
|
||||||
|
for opt in self._optionals._group_actions)
|
||||||
|
|
||||||
|
index = index_commands()
|
||||||
|
|
||||||
|
# usage
|
||||||
|
formatter.add_text(
|
||||||
|
"usage: %s [-%s] <command> [...]" % (self.prog, options))
|
||||||
|
|
||||||
|
# description
|
||||||
|
formatter.add_text(self.description)
|
||||||
|
|
||||||
|
# start subcommands
|
||||||
|
formatter.add_text(intro_by_level[level])
|
||||||
|
|
||||||
|
# add argument groups based on metadata in commands
|
||||||
|
sections = index[level]
|
||||||
|
for section in sorted(sections):
|
||||||
|
if section == 'help':
|
||||||
|
continue # Cover help in the epilog.
|
||||||
|
|
||||||
|
group_description = section_descriptions.get(section, section)
|
||||||
|
|
||||||
|
to_display = sections[section]
|
||||||
|
commands = []
|
||||||
|
|
||||||
|
# add commands whose order we care about first.
|
||||||
|
if section in section_order:
|
||||||
|
commands.extend(cmd for cmd in section_order[section]
|
||||||
|
if cmd in to_display)
|
||||||
|
|
||||||
|
# add rest in alphabetical order.
|
||||||
|
commands.extend(cmd for cmd in sorted(sections[section])
|
||||||
|
if cmd not in commands)
|
||||||
|
|
||||||
|
# add the group to the parser
|
||||||
|
add_subcommand_group(group_description, commands)
|
||||||
|
|
||||||
|
# optionals
|
||||||
|
add_group(self._optionals)
|
||||||
|
|
||||||
|
# epilog
|
||||||
|
formatter.add_text("""\
|
||||||
|
{help}:
|
||||||
|
spack help -a list all available commands
|
||||||
|
spack help <command> help on a specific command
|
||||||
|
spack help --spec help on the spec syntax
|
||||||
|
spack docs open http://spack.rtfd.io/ in a browser"""
|
||||||
|
.format(help=section_descriptions['help']))
|
||||||
|
|
||||||
|
# determine help from format above
|
||||||
|
return formatter.format_help()
|
||||||
|
|
||||||
|
def add_command(self, name):
|
||||||
|
"""Add one subcommand to this parser."""
|
||||||
|
# lazily initialize any subparsers
|
||||||
|
if not hasattr(self, 'subparsers'):
|
||||||
|
# remove the dummy "command" argument.
|
||||||
|
self._remove_action(self._actions[-1])
|
||||||
|
self.subparsers = self.add_subparsers(metavar='COMMAND',
|
||||||
|
dest="command")
|
||||||
|
|
||||||
|
# each command module implements a parser() function, to which we
|
||||||
|
# pass its subparser for setup.
|
||||||
|
module = spack.cmd.get_module(name)
|
||||||
|
cmd_name = name.replace('_', '-')
|
||||||
|
subparser = self.subparsers.add_parser(
|
||||||
|
cmd_name, help=module.description, description=module.description)
|
||||||
|
module.setup_parser(subparser)
|
||||||
|
return module
|
||||||
|
|
||||||
|
def format_help(self, level='short'):
|
||||||
|
if self.prog == 'spack':
|
||||||
|
# use format_help_sections for the main spack parser, but not
|
||||||
|
# for subparsers
|
||||||
|
return self.format_help_sections(level)
|
||||||
|
else:
|
||||||
|
# in subparsers, self.prog is, e.g., 'spack install'
|
||||||
|
return super(SpackArgumentParser, self).format_help()
|
||||||
|
|
||||||
|
|
||||||
|
def make_argument_parser():
|
||||||
|
"""Create an basic argument parser without any subcommands added."""
|
||||||
|
parser = SpackArgumentParser(
|
||||||
|
formatter_class=RawTextHelpFormatter, add_help=False,
|
||||||
|
description=(
|
||||||
|
"A flexible package manager that supports multiple versions,\n"
|
||||||
|
"configurations, platforms, and compilers."))
|
||||||
|
|
||||||
|
# stat names in groups of 7, for nice wrapping.
|
||||||
|
stat_lines = list(zip(*(iter(stat_names),) * 7))
|
||||||
|
|
||||||
|
parser.add_argument('-h', '--help', action='store_true',
|
||||||
|
help="show this help message and exit")
|
||||||
|
parser.add_argument('-d', '--debug', action='store_true',
|
||||||
|
help="write out debug logs during compile")
|
||||||
|
parser.add_argument('-D', '--pdb', action='store_true',
|
||||||
|
help="run spack under the pdb debugger")
|
||||||
|
parser.add_argument('-k', '--insecure', action='store_true',
|
||||||
|
help="do not check ssl certificates when downloading")
|
||||||
|
parser.add_argument('-m', '--mock', action='store_true',
|
||||||
|
help="use mock packages instead of real ones")
|
||||||
|
parser.add_argument('-p', '--profile', action='store_true',
|
||||||
|
help="profile execution using cProfile")
|
||||||
|
parser.add_argument('-P', '--sorted-profile', default=None, metavar="STAT",
|
||||||
|
help="profile and sort by one or more of:\n[%s]" %
|
||||||
|
',\n '.join([', '.join(line) for line in stat_lines]))
|
||||||
|
parser.add_argument('--lines', default=20, action='store',
|
||||||
|
help="lines of profile output; default 20; or 'all'")
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true',
|
||||||
|
help="print additional output during builds")
|
||||||
|
parser.add_argument('-s', '--stacktrace', action='store_true',
|
||||||
|
help="add stacktraces to all printed statements")
|
||||||
|
parser.add_argument('-V', '--version', action='store_true',
|
||||||
|
help='show version number and exit')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def setup_main_options(args):
|
||||||
|
"""Configure spack globals based on the basic options."""
|
||||||
|
# Set up environment based on args.
|
||||||
|
tty.set_verbose(args.verbose)
|
||||||
|
tty.set_debug(args.debug)
|
||||||
|
tty.set_stacktrace(args.stacktrace)
|
||||||
|
spack.debug = args.debug
|
||||||
|
|
||||||
|
if spack.debug:
|
||||||
|
import spack.util.debug as debug
|
||||||
|
debug.register_interrupt_handler()
|
||||||
|
|
||||||
|
if args.mock:
|
||||||
|
from spack.repository import RepoPath
|
||||||
|
spack.repo.swap(RepoPath(spack.mock_packages_path))
|
||||||
|
|
||||||
|
# If the user asked for it, don't check ssl certs.
|
||||||
|
if args.insecure:
|
||||||
|
tty.warn("You asked for --insecure. Will NOT check SSL certificates.")
|
||||||
|
spack.insecure = True
|
||||||
|
|
||||||
|
|
||||||
|
def allows_unknown_args(command):
|
||||||
|
"""This is a basic argument injection test.
|
||||||
|
|
||||||
|
Commands may add an optional argument called "unknown args" to
|
||||||
|
indicate they can handle unknonwn args, and we'll pass the unknown
|
||||||
|
args in.
|
||||||
|
"""
|
||||||
|
info = dict(inspect.getmembers(command))
|
||||||
|
varnames = info['__code__'].co_varnames
|
||||||
|
argcount = info['__code__'].co_argcount
|
||||||
|
return (argcount == 3 and varnames[2] == 'unknown_args')
|
||||||
|
|
||||||
|
|
||||||
|
def _main(command, parser, args, unknown_args):
|
||||||
|
# many operations will fail without a working directory.
|
||||||
|
set_working_dir()
|
||||||
|
|
||||||
|
# only setup main options in here, after the real parse (we'll get it
|
||||||
|
# wrong if we do it after the initial, partial parse)
|
||||||
|
setup_main_options(args)
|
||||||
|
spack.hooks.pre_run()
|
||||||
|
|
||||||
|
# Now actually execute the command
|
||||||
|
try:
|
||||||
|
if allows_unknown_args(command):
|
||||||
|
return_val = command(parser, args, unknown_args)
|
||||||
|
else:
|
||||||
|
if unknown_args:
|
||||||
|
tty.die('unrecognized arguments: %s' % ' '.join(unknown_args))
|
||||||
|
return_val = command(parser, args)
|
||||||
|
except SpackError as e:
|
||||||
|
e.die() # gracefully die on any SpackErrors
|
||||||
|
except Exception as e:
|
||||||
|
if spack.debug:
|
||||||
|
raise
|
||||||
|
tty.die(str(e))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.stderr.write('\n')
|
||||||
|
tty.die("Keyboard interrupt.")
|
||||||
|
|
||||||
|
# Allow commands to return and error code if they want
|
||||||
|
return 0 if return_val is None else return_val
|
||||||
|
|
||||||
|
|
||||||
|
def _profile_wrapper(command, parser, args, unknown_args):
|
||||||
|
import cProfile
|
||||||
|
|
||||||
|
try:
|
||||||
|
nlines = int(args.lines)
|
||||||
|
except ValueError:
|
||||||
|
if args.lines != 'all':
|
||||||
|
tty.die('Invalid number for --lines: %s' % args.lines)
|
||||||
|
nlines = -1
|
||||||
|
|
||||||
|
# allow comma-separated list of fields
|
||||||
|
sortby = ['time']
|
||||||
|
if args.sorted_profile:
|
||||||
|
sortby = args.sorted_profile.split(',')
|
||||||
|
for stat in sortby:
|
||||||
|
if stat not in stat_names:
|
||||||
|
tty.die("Invalid sort field: %s" % stat)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# make a profiler and run the code.
|
||||||
|
pr = cProfile.Profile()
|
||||||
|
pr.enable()
|
||||||
|
return _main(command, parser, args, unknown_args)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
pr.disable()
|
||||||
|
|
||||||
|
# print out profile stats.
|
||||||
|
stats = pstats.Stats(pr)
|
||||||
|
stats.sort_stats(*sortby)
|
||||||
|
stats.print_stats(nlines)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
"""This is the entry point for the Spack command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
argv (list of str or None): command line arguments, NOT including
|
||||||
|
the executable name. If None, parses from sys.argv.
|
||||||
|
"""
|
||||||
|
# Create a parser with a simple positional argument first. We'll
|
||||||
|
# lazily load the subcommand(s) we need later. This allows us to
|
||||||
|
# avoid loading all the modules from spack.cmd when we don't need
|
||||||
|
# them, which reduces startup latency.
|
||||||
|
parser = make_argument_parser()
|
||||||
|
parser.add_argument(
|
||||||
|
'command', metavar='COMMAND', nargs='?', action='store')
|
||||||
|
args, unknown = parser.parse_known_args(argv)
|
||||||
|
|
||||||
|
# Just print help and exit if run with no arguments at all
|
||||||
|
no_args = (len(sys.argv) == 1) if argv is None else (len(argv) == 0)
|
||||||
|
if no_args:
|
||||||
|
parser.print_help()
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# -h and -V are special as they do not require a command, but all the
|
||||||
|
# other options do nothing without a command.
|
||||||
|
if not args.command:
|
||||||
|
if args.version:
|
||||||
|
print(spack.spack_version)
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
parser.print_help()
|
||||||
|
return 0 if args.help else 1
|
||||||
|
|
||||||
|
# Try to load the particular command the caller asked for. If there
|
||||||
|
# is no module for it, just die.
|
||||||
|
command_name = args.command.replace('-', '_')
|
||||||
|
try:
|
||||||
|
parser.add_command(command_name)
|
||||||
|
except ImportError:
|
||||||
|
if spack.debug:
|
||||||
|
raise
|
||||||
|
tty.die("Unknown command: %s" % args.command)
|
||||||
|
|
||||||
|
# Re-parse with the proper sub-parser added.
|
||||||
|
args, unknown = parser.parse_known_args()
|
||||||
|
|
||||||
|
# we now know whether options go with spack or the command
|
||||||
|
if args.version:
|
||||||
|
print(spack.spack_version)
|
||||||
|
return 0
|
||||||
|
elif args.help:
|
||||||
|
parser.print_help()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# now we can actually execute the command.
|
||||||
|
command = spack.cmd.get_command(command_name)
|
||||||
|
try:
|
||||||
|
if args.profile or args.sorted_profile:
|
||||||
|
_profile_wrapper(command, parser, args, unknown)
|
||||||
|
elif args.pdb:
|
||||||
|
import pdb
|
||||||
|
pdb.runctx('_main(command, parser, args, unknown)',
|
||||||
|
globals(), locals())
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return _main(command, parser, args, unknown)
|
||||||
|
|
||||||
|
except SystemExit as e:
|
||||||
|
return e.code
|
|
@ -20,6 +20,10 @@ cd "$SPACK_ROOT"
|
||||||
# Print compiler information
|
# Print compiler information
|
||||||
spack config get compilers
|
spack config get compilers
|
||||||
|
|
||||||
|
# Run spack help to cover command import
|
||||||
|
${coverage_run} bin/spack -h
|
||||||
|
${coverage_run} bin/spack help -a
|
||||||
|
|
||||||
# Profile and print top 20 lines for a simple call to spack spec
|
# Profile and print top 20 lines for a simple call to spack spec
|
||||||
${coverage_run} bin/spack -p --lines 20 spec mpileaks
|
${coverage_run} bin/spack -p --lines 20 spec mpileaks
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue