Fix create, diy, edit, and repo commands to use multiple repos.
This commit is contained in:
parent
5984bc2ad3
commit
97b492756a
@ -36,7 +36,9 @@
|
|||||||
import spack.cmd.checksum
|
import spack.cmd.checksum
|
||||||
import spack.url
|
import spack.url
|
||||||
import spack.util.web
|
import spack.util.web
|
||||||
|
from spack.spec import Spec
|
||||||
from spack.util.naming import *
|
from spack.util.naming import *
|
||||||
|
from spack.repository import Repo, RepoError
|
||||||
import spack.util.crypto as crypto
|
import spack.util.crypto as crypto
|
||||||
|
|
||||||
from spack.util.executable import which
|
from spack.util.executable import which
|
||||||
@ -85,21 +87,34 @@ def install(self, spec, prefix):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def make_version_calls(ver_hash_tuples):
|
||||||
|
"""Adds a version() call to the package for each version found."""
|
||||||
|
max_len = max(len(str(v)) for v, h in ver_hash_tuples)
|
||||||
|
format = " version(%%-%ds, '%%s')" % (max_len + 2)
|
||||||
|
return '\n'.join(format % ("'%s'" % v, h) for v, h in ver_hash_tuples)
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
subparser.add_argument('url', nargs='?', help="url of package archive")
|
subparser.add_argument('url', nargs='?', help="url of package archive")
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'--keep-stage', action='store_true', dest='keep_stage',
|
'--keep-stage', action='store_true',
|
||||||
help="Don't clean up staging area when command completes.")
|
help="Don't clean up staging area when command completes.")
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-n', '--name', dest='alternate_name', default=None,
|
'-n', '--name', dest='alternate_name', default=None, metavar='NAME',
|
||||||
help="Override the autodetected name for the created package.")
|
help="Override the autodetected name for the created package.")
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-p', '--package-repo', dest='package_repo', default=None,
|
'-r', '--repo', default=None,
|
||||||
help="Create the package in the specified packagerepo.")
|
help="Path to a repository where the package should be created.")
|
||||||
|
subparser.add_argument(
|
||||||
|
'-N', '--namespace',
|
||||||
|
help="Specify a namespace for the package. Must be the namespace of "
|
||||||
|
"a repository registered with Spack.")
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-f', '--force', action='store_true', dest='force',
|
'-f', '--force', action='store_true', dest='force',
|
||||||
help="Overwrite any existing package file with the same name.")
|
help="Overwrite any existing package file with the same name.")
|
||||||
|
|
||||||
|
setup_parser.subparser = subparser
|
||||||
|
|
||||||
|
|
||||||
class ConfigureGuesser(object):
|
class ConfigureGuesser(object):
|
||||||
def __call__(self, stage):
|
def __call__(self, stage):
|
||||||
@ -137,16 +152,7 @@ def __call__(self, stage):
|
|||||||
self.build_system = build_system
|
self.build_system = build_system
|
||||||
|
|
||||||
|
|
||||||
def make_version_calls(ver_hash_tuples):
|
def guess_name_and_version(url, args):
|
||||||
"""Adds a version() call to the package for each version found."""
|
|
||||||
max_len = max(len(str(v)) for v, h in ver_hash_tuples)
|
|
||||||
format = " version(%%-%ds, '%%s')" % (max_len + 2)
|
|
||||||
return '\n'.join(format % ("'%s'" % v, h) for v, h in ver_hash_tuples)
|
|
||||||
|
|
||||||
|
|
||||||
def create(parser, args):
|
|
||||||
url = args.url
|
|
||||||
|
|
||||||
# Try to deduce name and version of the new package from the URL
|
# Try to deduce name and version of the new package from the URL
|
||||||
version = spack.url.parse_version(url)
|
version = spack.url.parse_version(url)
|
||||||
if not version:
|
if not version:
|
||||||
@ -163,21 +169,52 @@ def create(parser, args):
|
|||||||
tty.die("Couldn't guess a name for this package. Try running:", "",
|
tty.die("Couldn't guess a name for this package. Try running:", "",
|
||||||
"spack create --name <name> <url>")
|
"spack create --name <name> <url>")
|
||||||
|
|
||||||
package_repo = args.package_repo
|
if not valid_fully_qualified_module_name(name):
|
||||||
|
|
||||||
if not valid_module_name(name):
|
|
||||||
tty.die("Package name can only contain A-Z, a-z, 0-9, '_' and '-'")
|
tty.die("Package name can only contain A-Z, a-z, 0-9, '_' and '-'")
|
||||||
|
|
||||||
tty.msg("This looks like a URL for %s version %s." % (name, version))
|
return name, version
|
||||||
tty.msg("Creating template for package %s" % name)
|
|
||||||
|
|
||||||
# Create a directory for the new package.
|
|
||||||
pkg_path = spack.repo.filename_for_package_name(name, package_repo)
|
def find_repository(spec, args):
|
||||||
if os.path.exists(pkg_path) and not args.force:
|
# figure out namespace for spec
|
||||||
tty.die("%s already exists." % pkg_path)
|
if spec.namespace and args.namespace and spec.namespace != args.namespace:
|
||||||
|
tty.die("Namespaces '%s' and '%s' do not match." % (spec.namespace, args.namespace))
|
||||||
|
|
||||||
|
if not spec.namespace and args.namespace:
|
||||||
|
spec.namespace = args.namespace
|
||||||
|
|
||||||
|
# Figure out where the new package should live.
|
||||||
|
repo_path = args.repo
|
||||||
|
if repo_path is not None:
|
||||||
|
try:
|
||||||
|
repo = Repo(repo_path)
|
||||||
|
if spec.namespace and spec.namespace != repo.namespace:
|
||||||
|
tty.die("Can't create package with namespace %s in repo with namespace %s."
|
||||||
|
% (spec.namespace, repo.namespace))
|
||||||
|
except RepoError as e:
|
||||||
|
tty.die(str(e))
|
||||||
else:
|
else:
|
||||||
mkdirp(os.path.dirname(pkg_path))
|
if spec.namespace:
|
||||||
|
repo = spack.repo.get_repo(spec.namespace, None)
|
||||||
|
if not repo:
|
||||||
|
tty.die("Unknown namespace: %s" % spec.namespace)
|
||||||
|
else:
|
||||||
|
repo = spack.repo.first_repo()
|
||||||
|
|
||||||
|
# Set the namespace on the spec if it's not there already
|
||||||
|
if not spec.namespace:
|
||||||
|
spec.namespace = repo.namespace
|
||||||
|
|
||||||
|
return repo
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_tarballs(url, name, args):
|
||||||
|
"""Try to find versions of the supplied archive by scraping the web.
|
||||||
|
|
||||||
|
Prompts the user to select how many to download if many are found.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
versions = spack.util.web.find_versions_of_archive(url)
|
versions = spack.util.web.find_versions_of_archive(url)
|
||||||
rkeys = sorted(versions.keys(), reverse=True)
|
rkeys = sorted(versions.keys(), reverse=True)
|
||||||
versions = OrderedDict(zip(rkeys, (versions[v] for v in rkeys)))
|
versions = OrderedDict(zip(rkeys, (versions[v] for v in rkeys)))
|
||||||
@ -196,13 +233,35 @@ def create(parser, args):
|
|||||||
default=5, abort='q')
|
default=5, abort='q')
|
||||||
|
|
||||||
if not archives_to_fetch:
|
if not archives_to_fetch:
|
||||||
tty.msg("Aborted.")
|
tty.die("Aborted.")
|
||||||
return
|
|
||||||
|
|
||||||
|
sorted_versions = sorted(versions.keys(), reverse=True)
|
||||||
|
sorted_urls = [versions[v] for v in sorted_versions]
|
||||||
|
return sorted_versions[:archives_to_fetch], sorted_urls[:archives_to_fetch]
|
||||||
|
|
||||||
|
|
||||||
|
def create(parser, args):
|
||||||
|
url = args.url
|
||||||
|
if not url:
|
||||||
|
setup_parser.subparser.print_help()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Figure out a name and repo for the package.
|
||||||
|
name, version = guess_name_and_version(url, args)
|
||||||
|
spec = Spec(name)
|
||||||
|
name = spec.name # factors out namespace, if any
|
||||||
|
repo = find_repository(spec, args)
|
||||||
|
|
||||||
|
tty.msg("This looks like a URL for %s version %s." % (name, version))
|
||||||
|
tty.msg("Creating template for package %s" % name)
|
||||||
|
|
||||||
|
# Fetch tarballs (prompting user if necessary)
|
||||||
|
versions, urls = fetch_tarballs(url, name, args)
|
||||||
|
|
||||||
|
# Try to guess what configure system is used.
|
||||||
guesser = ConfigureGuesser()
|
guesser = ConfigureGuesser()
|
||||||
ver_hash_tuples = spack.cmd.checksum.get_checksums(
|
ver_hash_tuples = spack.cmd.checksum.get_checksums(
|
||||||
versions.keys()[:archives_to_fetch],
|
versions, urls,
|
||||||
[versions[v] for v in versions.keys()[:archives_to_fetch]],
|
|
||||||
first_stage_function=guesser,
|
first_stage_function=guesser,
|
||||||
keep_stage=args.keep_stage)
|
keep_stage=args.keep_stage)
|
||||||
|
|
||||||
@ -214,7 +273,7 @@ def create(parser, args):
|
|||||||
name = 'py-%s' % name
|
name = 'py-%s' % name
|
||||||
|
|
||||||
# Create a directory for the new package.
|
# Create a directory for the new package.
|
||||||
pkg_path = spack.repo.filename_for_package_name(name)
|
pkg_path = repo.filename_for_package_name(name)
|
||||||
if os.path.exists(pkg_path) and not args.force:
|
if os.path.exists(pkg_path) and not args.force:
|
||||||
tty.die("%s already exists." % pkg_path)
|
tty.die("%s already exists." % pkg_path)
|
||||||
else:
|
else:
|
||||||
|
@ -69,7 +69,7 @@ def diy(self, args):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
tty.msg("Running 'spack edit -f %s'" % spec.name)
|
tty.msg("Running 'spack edit -f %s'" % spec.name)
|
||||||
edit_package(spec.name, True)
|
edit_package(spec.name, spack.repo.first_repo(), None, True)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not spec.version.concrete:
|
if not spec.version.concrete:
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
|
from spack.spec import Spec
|
||||||
|
from spack.repository import Repo
|
||||||
from spack.util.naming import mod_to_class
|
from spack.util.naming import mod_to_class
|
||||||
|
|
||||||
description = "Open package files in $EDITOR"
|
description = "Open package files in $EDITOR"
|
||||||
@ -53,9 +55,16 @@ def install(self, spec, prefix):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
def edit_package(name, force=False):
|
def edit_package(name, repo_path, namespace, force=False):
|
||||||
path = spack.repo.filename_for_package_name(name)
|
if repo_path:
|
||||||
|
repo = Repo(repo_path)
|
||||||
|
elif namespace:
|
||||||
|
repo = spack.repo.get_repo(namespace)
|
||||||
|
else:
|
||||||
|
repo = spack.repo
|
||||||
|
path = repo.filename_for_package_name(name)
|
||||||
|
|
||||||
|
spec = Spec(name)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
tty.die("Something's wrong. '%s' is not a file!" % path)
|
tty.die("Something's wrong. '%s' is not a file!" % path)
|
||||||
@ -63,13 +72,13 @@ def edit_package(name, force=False):
|
|||||||
tty.die("Insufficient permissions on '%s'!" % path)
|
tty.die("Insufficient permissions on '%s'!" % path)
|
||||||
elif not force:
|
elif not force:
|
||||||
tty.die("No package '%s'. Use spack create, or supply -f/--force "
|
tty.die("No package '%s'. Use spack create, or supply -f/--force "
|
||||||
"to edit a new file." % name)
|
"to edit a new file." % spec.name)
|
||||||
else:
|
else:
|
||||||
mkdirp(os.path.dirname(path))
|
mkdirp(os.path.dirname(path))
|
||||||
with open(path, "w") as pkg_file:
|
with open(path, "w") as pkg_file:
|
||||||
pkg_file.write(
|
pkg_file.write(
|
||||||
package_template.substitute(
|
package_template.substitute(
|
||||||
name=name, class_name=mod_to_class(name)))
|
name=spec.name, class_name=mod_to_class(spec.name)))
|
||||||
|
|
||||||
spack.editor(path)
|
spack.editor(path)
|
||||||
|
|
||||||
@ -79,17 +88,25 @@ def setup_parser(subparser):
|
|||||||
'-f', '--force', dest='force', action='store_true',
|
'-f', '--force', dest='force', action='store_true',
|
||||||
help="Open a new file in $EDITOR even if package doesn't exist.")
|
help="Open a new file in $EDITOR even if package doesn't exist.")
|
||||||
|
|
||||||
filetypes = subparser.add_mutually_exclusive_group()
|
excl_args = subparser.add_mutually_exclusive_group()
|
||||||
filetypes.add_argument(
|
|
||||||
|
# Various filetypes you can edit directly from the cmd line.
|
||||||
|
excl_args.add_argument(
|
||||||
'-c', '--command', dest='path', action='store_const',
|
'-c', '--command', dest='path', action='store_const',
|
||||||
const=spack.cmd.command_path, help="Edit the command with the supplied name.")
|
const=spack.cmd.command_path, help="Edit the command with the supplied name.")
|
||||||
filetypes.add_argument(
|
excl_args.add_argument(
|
||||||
'-t', '--test', dest='path', action='store_const',
|
'-t', '--test', dest='path', action='store_const',
|
||||||
const=spack.test_path, help="Edit the test with the supplied name.")
|
const=spack.test_path, help="Edit the test with the supplied name.")
|
||||||
filetypes.add_argument(
|
excl_args.add_argument(
|
||||||
'-m', '--module', dest='path', action='store_const',
|
'-m', '--module', dest='path', action='store_const',
|
||||||
const=spack.module_path, help="Edit the main spack module with the supplied name.")
|
const=spack.module_path, help="Edit the main spack module with the supplied name.")
|
||||||
|
|
||||||
|
# Options for editing packages
|
||||||
|
excl_args.add_argument(
|
||||||
|
'-r', '--repo', default=None, help="Path to repo to edit package in.")
|
||||||
|
excl_args.add_argument(
|
||||||
|
'-N', '--namespace', default=None, help="Namespace of package to edit.")
|
||||||
|
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'name', nargs='?', default=None, help="name of package to edit")
|
'name', nargs='?', default=None, help="name of package to edit")
|
||||||
|
|
||||||
@ -107,7 +124,7 @@ def edit(parser, args):
|
|||||||
spack.editor(path)
|
spack.editor(path)
|
||||||
|
|
||||||
elif name:
|
elif name:
|
||||||
edit_package(name, args.force)
|
edit_package(name, args.repo, args.namespace, args.force)
|
||||||
else:
|
else:
|
||||||
# By default open the directory where packages or commands live.
|
# By default open the directory where packages or commands live.
|
||||||
spack.editor(path)
|
spack.editor(path)
|
||||||
|
@ -44,9 +44,10 @@ def setup_parser(subparser):
|
|||||||
# Create
|
# Create
|
||||||
create_parser = sp.add_parser('create', help=repo_create.__doc__)
|
create_parser = sp.add_parser('create', help=repo_create.__doc__)
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
'namespace', help="Namespace to identify packages in the repository.")
|
'directory', help="Directory to create the repo in.")
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
'directory', help="Directory to create the repo in. Defaults to same as namespace.", nargs='?')
|
'namespace', help="Namespace to identify packages in the repository. "
|
||||||
|
"Defaults to the directory name.", nargs='?')
|
||||||
|
|
||||||
# List
|
# List
|
||||||
list_parser = sp.add_parser('list', help=repo_list.__doc__)
|
list_parser = sp.add_parser('list', help=repo_list.__doc__)
|
||||||
@ -72,14 +73,15 @@ def setup_parser(subparser):
|
|||||||
|
|
||||||
|
|
||||||
def repo_create(args):
|
def repo_create(args):
|
||||||
"""Create a new package repo for a particular namespace."""
|
"""Create a new package repository."""
|
||||||
|
root = canonicalize_path(args.directory)
|
||||||
namespace = args.namespace
|
namespace = args.namespace
|
||||||
if not re.match(r'\w[\.\w-]*', namespace):
|
|
||||||
tty.die("Invalid namespace: '%s'" % namespace)
|
|
||||||
|
|
||||||
root = args.directory
|
if not args.namespace:
|
||||||
if not root:
|
namespace = os.path.basename(root)
|
||||||
root = namespace
|
|
||||||
|
if not re.match(r'\w[\.\w-]*', namespace):
|
||||||
|
tty.die("'%s' is not a valid namespace." % namespace)
|
||||||
|
|
||||||
existed = False
|
existed = False
|
||||||
if os.path.exists(root):
|
if os.path.exists(root):
|
||||||
@ -123,27 +125,22 @@ def repo_create(args):
|
|||||||
|
|
||||||
|
|
||||||
def repo_add(args):
|
def repo_add(args):
|
||||||
"""Add a package source to the Spack configuration"""
|
"""Add a package source to Spack's configuration."""
|
||||||
path = args.path
|
path = args.path
|
||||||
|
|
||||||
# check if the path is relative to the spack directory.
|
# real_path is absolute and handles substitution.
|
||||||
real_path = path
|
canon_path = canonicalize_path(path)
|
||||||
if path.startswith('$spack'):
|
|
||||||
real_path = spack.repository.substitute_spack_prefix(path)
|
|
||||||
elif not os.path.isabs(real_path):
|
|
||||||
real_path = os.path.abspath(real_path)
|
|
||||||
path = real_path
|
|
||||||
|
|
||||||
# check if the path exists
|
# check if the path exists
|
||||||
if not os.path.exists(real_path):
|
if not os.path.exists(canon_path):
|
||||||
tty.die("No such file or directory: '%s'." % path)
|
tty.die("No such file or directory: '%s'." % path)
|
||||||
|
|
||||||
# Make sure the path is a directory.
|
# Make sure the path is a directory.
|
||||||
if not os.path.isdir(real_path):
|
if not os.path.isdir(canon_path):
|
||||||
tty.die("Not a Spack repository: '%s'." % path)
|
tty.die("Not a Spack repository: '%s'." % path)
|
||||||
|
|
||||||
# Make sure it's actually a spack repository by constructing it.
|
# Make sure it's actually a spack repository by constructing it.
|
||||||
repo = Repo(real_path)
|
repo = Repo(canon_path)
|
||||||
|
|
||||||
# If that succeeds, finally add it to the configuration.
|
# If that succeeds, finally add it to the configuration.
|
||||||
repos = spack.config.get_config('repos', args.scope)
|
repos = spack.config.get_config('repos', args.scope)
|
||||||
@ -152,30 +149,32 @@ def repo_add(args):
|
|||||||
if repo.root in repos or path in repos:
|
if repo.root in repos or path in repos:
|
||||||
tty.die("Repository is already registered with Spack: '%s'" % path)
|
tty.die("Repository is already registered with Spack: '%s'" % path)
|
||||||
|
|
||||||
repos.insert(0, path)
|
repos.insert(0, canon_path)
|
||||||
spack.config.update_config('repos', repos, args.scope)
|
spack.config.update_config('repos', repos, args.scope)
|
||||||
tty.msg("Created repo with namespace '%s'." % repo.namespace)
|
tty.msg("Created repo with namespace '%s'." % repo.namespace)
|
||||||
|
|
||||||
|
|
||||||
def repo_remove(args):
|
def repo_remove(args):
|
||||||
"""Remove a repository from the Spack configuration."""
|
"""Remove a repository from Spack's configuration."""
|
||||||
repos = spack.config.get_config('repos', args.scope)
|
repos = spack.config.get_config('repos', args.scope)
|
||||||
path_or_namespace = args.path_or_namespace
|
path_or_namespace = args.path_or_namespace
|
||||||
|
|
||||||
# If the argument is a path, remove that repository from config.
|
# If the argument is a path, remove that repository from config.
|
||||||
path = os.path.abspath(path_or_namespace)
|
canon_path = canonicalize_path(path_or_namespace)
|
||||||
if path in repos:
|
for repo_path in repos:
|
||||||
repos.remove(path)
|
repo_canon_path = canonicalize_path(repo_path)
|
||||||
spack.config.update_config('repos', repos, args.scope)
|
if canon_path == repo_canon_path:
|
||||||
tty.msg("Removed repository '%s'." % path)
|
repos.remove(repo_path)
|
||||||
return
|
spack.config.update_config('repos', repos, args.scope)
|
||||||
|
tty.msg("Removed repository '%s'." % repo_path)
|
||||||
|
return
|
||||||
|
|
||||||
# If it is a namespace, remove corresponding repo
|
# If it is a namespace, remove corresponding repo
|
||||||
for path in repos:
|
for path in repos:
|
||||||
try:
|
try:
|
||||||
repo = Repo(path)
|
repo = Repo(path)
|
||||||
if repo.namespace == path_or_namespace:
|
if repo.namespace == path_or_namespace:
|
||||||
repos.remove(repo.root)
|
repos.remove(path)
|
||||||
spack.config.update_config('repos', repos, args.scope)
|
spack.config.update_config('repos', repos, args.scope)
|
||||||
tty.msg("Removed repository '%s' with namespace %s."
|
tty.msg("Removed repository '%s' with namespace %s."
|
||||||
% (repo.root, repo.namespace))
|
% (repo.root, repo.namespace))
|
||||||
@ -188,7 +187,7 @@ def repo_remove(args):
|
|||||||
|
|
||||||
|
|
||||||
def repo_list(args):
|
def repo_list(args):
|
||||||
"""List package sources and their mnemoics"""
|
"""Show registered repositories and their namespaces."""
|
||||||
roots = spack.config.get_config('repos', args.scope)
|
roots = spack.config.get_config('repos', args.scope)
|
||||||
repos = []
|
repos = []
|
||||||
for r in roots:
|
for r in roots:
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
packages_dir_name = 'packages' # Top-level repo directory containing pkgs.
|
packages_dir_name = 'packages' # Top-level repo directory containing pkgs.
|
||||||
package_file_name = 'package.py' # Filename for packages in a repository.
|
package_file_name = 'package.py' # Filename for packages in a repository.
|
||||||
|
|
||||||
|
# Guaranteed unused default value for some functions.
|
||||||
|
NOT_PROVIDED = object()
|
||||||
|
|
||||||
|
|
||||||
def _autospec(function):
|
def _autospec(function):
|
||||||
"""Decorator that automatically converts the argument of a single-arg
|
"""Decorator that automatically converts the argument of a single-arg
|
||||||
@ -75,7 +78,15 @@ def _make_namespace_module(ns):
|
|||||||
|
|
||||||
def substitute_spack_prefix(path):
|
def substitute_spack_prefix(path):
|
||||||
"""Replaces instances of $spack with Spack's prefix."""
|
"""Replaces instances of $spack with Spack's prefix."""
|
||||||
return path.replace('$spack', spack.prefix)
|
return re.sub(r'^\$spack', spack.prefix, path)
|
||||||
|
|
||||||
|
|
||||||
|
def canonicalize_path(path):
|
||||||
|
"""Substitute $spack, expand user home, take abspath."""
|
||||||
|
path = substitute_spack_prefix(path)
|
||||||
|
path = os.path.expanduser(path)
|
||||||
|
path = os.path.abspath(path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
class RepoPath(object):
|
class RepoPath(object):
|
||||||
@ -109,7 +120,10 @@ def __init__(self, *repo_dirs, **kwargs):
|
|||||||
repo = Repo(root, self.super_namespace)
|
repo = Repo(root, self.super_namespace)
|
||||||
self.put_last(repo)
|
self.put_last(repo)
|
||||||
except RepoError as e:
|
except RepoError as e:
|
||||||
tty.warn("Failed to initialize repository at '%s'." % root, e.message)
|
tty.warn("Failed to initialize repository at '%s'." % root,
|
||||||
|
e.message,
|
||||||
|
"To remove the bad repository, run this command:",
|
||||||
|
" spack repo rm %s" % root)
|
||||||
|
|
||||||
|
|
||||||
def swap(self, other):
|
def swap(self, other):
|
||||||
@ -173,6 +187,31 @@ def remove(self, repo):
|
|||||||
self.repos.remove(repo)
|
self.repos.remove(repo)
|
||||||
|
|
||||||
|
|
||||||
|
def get_repo(self, namespace, default=NOT_PROVIDED):
|
||||||
|
"""Get a repository by namespace.
|
||||||
|
Arguments
|
||||||
|
namespace
|
||||||
|
Look up this namespace in the RepoPath, and return
|
||||||
|
it if found.
|
||||||
|
|
||||||
|
Optional Arguments
|
||||||
|
default
|
||||||
|
If default is provided, return it when the namespace
|
||||||
|
isn't found. If not, raise an UnknownNamespaceError.
|
||||||
|
"""
|
||||||
|
fullspace = '%s.%s' % (self.super_namespace, namespace)
|
||||||
|
if fullspace not in self.by_namespace:
|
||||||
|
if default == NOT_PROVIDED:
|
||||||
|
raise UnknownNamespaceError(namespace)
|
||||||
|
return default
|
||||||
|
return self.by_namespace[fullspace]
|
||||||
|
|
||||||
|
|
||||||
|
def first_repo(self):
|
||||||
|
"""Get the first repo in precedence order."""
|
||||||
|
return self.repos[0] if self.repos else None
|
||||||
|
|
||||||
|
|
||||||
def all_package_names(self):
|
def all_package_names(self):
|
||||||
"""Return all unique package names in all repositories."""
|
"""Return all unique package names in all repositories."""
|
||||||
return self._all_package_names
|
return self._all_package_names
|
||||||
@ -229,7 +268,6 @@ def load_module(self, fullname):
|
|||||||
if fullname in sys.modules:
|
if fullname in sys.modules:
|
||||||
return sys.modules[fullname]
|
return sys.modules[fullname]
|
||||||
|
|
||||||
|
|
||||||
# partition fullname into prefix and module name.
|
# partition fullname into prefix and module name.
|
||||||
namespace, dot, module_name = fullname.rpartition('.')
|
namespace, dot, module_name = fullname.rpartition('.')
|
||||||
|
|
||||||
@ -242,11 +280,23 @@ def load_module(self, fullname):
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
def repo_for_pkg(self, pkg_name):
|
@_autospec
|
||||||
|
def repo_for_pkg(self, spec):
|
||||||
|
"""Given a spec, get the repository for its package."""
|
||||||
|
# If the spec already has a namespace, then return the
|
||||||
|
# corresponding repo if we know about it.
|
||||||
|
if spec.namespace:
|
||||||
|
fullspace = '%s.%s' % (self.super_namespace, spec.namespace)
|
||||||
|
if fullspace not in self.by_namespace:
|
||||||
|
raise UnknownNamespaceError(spec.namespace)
|
||||||
|
return self.by_namespace[fullspace]
|
||||||
|
|
||||||
|
# If there's no namespace, search in the RepoPath.
|
||||||
for repo in self.repos:
|
for repo in self.repos:
|
||||||
if pkg_name in repo:
|
if spec.name in repo:
|
||||||
return repo
|
return repo
|
||||||
raise UnknownPackageError(pkg_name)
|
else:
|
||||||
|
raise UnknownPackageError(spec.name)
|
||||||
|
|
||||||
|
|
||||||
@_autospec
|
@_autospec
|
||||||
@ -255,16 +305,7 @@ def get(self, spec, new=False):
|
|||||||
|
|
||||||
Raises UnknownPackageError if not found.
|
Raises UnknownPackageError if not found.
|
||||||
"""
|
"""
|
||||||
# if the spec has a fully qualified namespace, we grab it
|
return self.repo_for_pkg(spec).get(spec)
|
||||||
# directly and ignore overlay precedence.
|
|
||||||
if spec.namespace:
|
|
||||||
fullspace = '%s.%s' % (self.super_namespace, spec.namespace)
|
|
||||||
if not fullspace in self.by_namespace:
|
|
||||||
raise UnknownPackageError(
|
|
||||||
"No configured repository contains package %s." % spec.fullname)
|
|
||||||
return self.by_namespace[fullspace].get(spec)
|
|
||||||
else:
|
|
||||||
return self.repo_for_pkg(spec.name).get(spec)
|
|
||||||
|
|
||||||
|
|
||||||
def dirname_for_package_name(self, pkg_name):
|
def dirname_for_package_name(self, pkg_name):
|
||||||
@ -310,7 +351,7 @@ def __init__(self, root, namespace=repo_namespace):
|
|||||||
"""
|
"""
|
||||||
# Root directory, containing _repo.yaml and package dirs
|
# Root directory, containing _repo.yaml and package dirs
|
||||||
# Allow roots to by spack-relative by starting with '$spack'
|
# Allow roots to by spack-relative by starting with '$spack'
|
||||||
self.root = substitute_spack_prefix(root)
|
self.root = canonicalize_path(root)
|
||||||
|
|
||||||
# super-namespace for all packages in the Repo
|
# super-namespace for all packages in the Repo
|
||||||
self.super_namespace = namespace
|
self.super_namespace = namespace
|
||||||
@ -330,7 +371,7 @@ def check(condition, msg):
|
|||||||
# Read configuration and validate namespace
|
# Read configuration and validate namespace
|
||||||
config = self._read_config()
|
config = self._read_config()
|
||||||
check('namespace' in config, '%s must define a namespace.'
|
check('namespace' in config, '%s must define a namespace.'
|
||||||
% join_path(self.root, repo_config_name))
|
% join_path(root, repo_config_name))
|
||||||
|
|
||||||
self.namespace = config['namespace']
|
self.namespace = config['namespace']
|
||||||
check(re.match(r'[a-zA-Z][a-zA-Z0-9_.]+', self.namespace),
|
check(re.match(r'[a-zA-Z][a-zA-Z0-9_.]+', self.namespace),
|
||||||
@ -524,13 +565,22 @@ def extensions_for(self, extendee_spec):
|
|||||||
return [p for p in self.all_packages() if p.extends(extendee_spec)]
|
return [p for p in self.all_packages() if p.extends(extendee_spec)]
|
||||||
|
|
||||||
|
|
||||||
def dirname_for_package_name(self, pkg_name):
|
def _check_namespace(self, spec):
|
||||||
|
"""Check that the spec's namespace is the same as this repository's."""
|
||||||
|
if spec.namespace and spec.namespace != self.namespace:
|
||||||
|
raise UnknownNamespaceError(spec.namespace)
|
||||||
|
|
||||||
|
|
||||||
|
@_autospec
|
||||||
|
def dirname_for_package_name(self, spec):
|
||||||
"""Get the directory name for a particular package. This is the
|
"""Get the directory name for a particular package. This is the
|
||||||
directory that contains its package.py file."""
|
directory that contains its package.py file."""
|
||||||
return join_path(self.packages_path, pkg_name)
|
self._check_namespace(spec)
|
||||||
|
return join_path(self.packages_path, spec.name)
|
||||||
|
|
||||||
|
|
||||||
def filename_for_package_name(self, pkg_name):
|
@_autospec
|
||||||
|
def filename_for_package_name(self, spec):
|
||||||
"""Get the filename for the module we should load for a particular
|
"""Get the filename for the module we should load for a particular
|
||||||
package. Packages for a Repo live in
|
package. Packages for a Repo live in
|
||||||
``$root/<package_name>/package.py``
|
``$root/<package_name>/package.py``
|
||||||
@ -539,8 +589,8 @@ def filename_for_package_name(self, pkg_name):
|
|||||||
package doesn't exist yet, so callers will need to ensure
|
package doesn't exist yet, so callers will need to ensure
|
||||||
the package exists before importing.
|
the package exists before importing.
|
||||||
"""
|
"""
|
||||||
validate_module_name(pkg_name)
|
self._check_namespace(spec)
|
||||||
pkg_dir = self.dirname_for_package_name(pkg_name)
|
pkg_dir = self.dirname_for_package_name(spec.name)
|
||||||
return join_path(pkg_dir, package_file_name)
|
return join_path(pkg_dir, package_file_name)
|
||||||
|
|
||||||
|
|
||||||
@ -679,6 +729,13 @@ def __init__(self, name, repo=None):
|
|||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownNamespaceError(PackageLoadError):
|
||||||
|
"""Raised when we encounter an unknown namespace"""
|
||||||
|
def __init__(self, namespace):
|
||||||
|
super(UnknownNamespaceError, self).__init__(
|
||||||
|
"Unknown namespace: %s" % namespace)
|
||||||
|
|
||||||
|
|
||||||
class FailedConstructorError(PackageLoadError):
|
class FailedConstructorError(PackageLoadError):
|
||||||
"""Raised when a package's class constructor fails."""
|
"""Raised when a package's class constructor fails."""
|
||||||
def __init__(self, name, exc_type, exc_obj, exc_tb):
|
def __init__(self, name, exc_type, exc_obj, exc_tb):
|
||||||
|
@ -8,11 +8,15 @@
|
|||||||
import spack
|
import spack
|
||||||
|
|
||||||
__all__ = ['mod_to_class', 'spack_module_to_python_module', 'valid_module_name',
|
__all__ = ['mod_to_class', 'spack_module_to_python_module', 'valid_module_name',
|
||||||
|
'valid_fully_qualified_module_name', 'validate_fully_qualified_module_name',
|
||||||
'validate_module_name', 'possible_spack_module_names', 'NamespaceTrie']
|
'validate_module_name', 'possible_spack_module_names', 'NamespaceTrie']
|
||||||
|
|
||||||
# Valid module names can contain '-' but can't start with it.
|
# Valid module names can contain '-' but can't start with it.
|
||||||
_valid_module_re = r'^\w[\w-]*$'
|
_valid_module_re = r'^\w[\w-]*$'
|
||||||
|
|
||||||
|
# Valid module names can contain '-' but can't start with it.
|
||||||
|
_valid_fully_qualified_module_re = r'^(\w[\w-]*)(\.\w[\w-]*)*$'
|
||||||
|
|
||||||
|
|
||||||
def mod_to_class(mod_name):
|
def mod_to_class(mod_name):
|
||||||
"""Convert a name from module style to class name style. Spack mostly
|
"""Convert a name from module style to class name style. Spack mostly
|
||||||
@ -75,16 +79,27 @@ def possible_spack_module_names(python_mod_name):
|
|||||||
|
|
||||||
|
|
||||||
def valid_module_name(mod_name):
|
def valid_module_name(mod_name):
|
||||||
"""Return whether the mod_name is valid for use in Spack."""
|
"""Return whether mod_name is valid for use in Spack."""
|
||||||
return bool(re.match(_valid_module_re, mod_name))
|
return bool(re.match(_valid_module_re, mod_name))
|
||||||
|
|
||||||
|
|
||||||
|
def valid_fully_qualified_module_name(mod_name):
|
||||||
|
"""Return whether mod_name is a valid namespaced module name."""
|
||||||
|
return bool(re.match(_valid_fully_qualified_module_re, mod_name))
|
||||||
|
|
||||||
|
|
||||||
def validate_module_name(mod_name):
|
def validate_module_name(mod_name):
|
||||||
"""Raise an exception if mod_name is not valid."""
|
"""Raise an exception if mod_name is not valid."""
|
||||||
if not valid_module_name(mod_name):
|
if not valid_module_name(mod_name):
|
||||||
raise InvalidModuleNameError(mod_name)
|
raise InvalidModuleNameError(mod_name)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_fully_qualified_module_name(mod_name):
|
||||||
|
"""Raise an exception if mod_name is not a valid namespaced module name."""
|
||||||
|
if not valid_fully_qualified_module_name(mod_name):
|
||||||
|
raise InvalidFullyQualifiedModuleNameError(mod_name)
|
||||||
|
|
||||||
|
|
||||||
class InvalidModuleNameError(spack.error.SpackError):
|
class InvalidModuleNameError(spack.error.SpackError):
|
||||||
"""Raised when we encounter a bad module name."""
|
"""Raised when we encounter a bad module name."""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
@ -93,6 +108,14 @@ def __init__(self, name):
|
|||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidFullyQualifiedModuleNameError(spack.error.SpackError):
|
||||||
|
"""Raised when we encounter a bad full package name."""
|
||||||
|
def __init__(self, name):
|
||||||
|
super(InvalidFullyQualifiedModuleNameError, self).__init__(
|
||||||
|
"Invalid fully qualified package name: " + name)
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
class NamespaceTrie(object):
|
class NamespaceTrie(object):
|
||||||
class Element(object):
|
class Element(object):
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
|
Loading…
Reference in New Issue
Block a user