# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import sys
import llnl.util.tty as tty
import spack.config
import spack.repo
import spack.util.path
from spack.cmd.common import arguments
description = "manage package source repositories"
section = "config"
level = "long"
[docs]
def setup_parser(subparser):
sp = subparser.add_subparsers(metavar="SUBCOMMAND", dest="repo_command")
# Create
create_parser = sp.add_parser("create", help=repo_create.__doc__)
create_parser.add_argument("directory", help="directory to create the repo in")
create_parser.add_argument(
"namespace",
help="namespace to identify packages in the repository (defaults to the directory name)",
nargs="?",
)
create_parser.add_argument(
"-d",
"--subdirectory",
action="store",
dest="subdir",
default=spack.repo.packages_dir_name,
help="subdirectory to store packages in the repository\n\n"
"default 'packages'. use an empty string for no subdirectory",
)
# List
list_parser = sp.add_parser("list", help=repo_list.__doc__)
list_parser.add_argument(
"--scope", action=arguments.ConfigScope, help="configuration scope to read from"
)
# Add
add_parser = sp.add_parser("add", help=repo_add.__doc__)
add_parser.add_argument("path", help="path to a Spack package repository directory")
add_parser.add_argument(
"--scope",
action=arguments.ConfigScope,
default=lambda: spack.config.default_modify_scope(),
help="configuration scope to modify",
)
# Remove
remove_parser = sp.add_parser("remove", help=repo_remove.__doc__, aliases=["rm"])
remove_parser.add_argument(
"namespace_or_path", help="namespace or path of a Spack package repository"
)
remove_parser.add_argument(
"--scope",
action=arguments.ConfigScope,
default=lambda: spack.config.default_modify_scope(),
help="configuration scope to modify",
)
[docs]
def repo_create(args):
"""create a new package repository"""
full_path, namespace = spack.repo.create_repo(args.directory, args.namespace, args.subdir)
tty.msg("Created repo with namespace '%s'." % namespace)
tty.msg("To register it with spack, run this command:", "spack repo add %s" % full_path)
[docs]
def repo_add(args):
"""add a package source to Spack's configuration"""
path = args.path
# real_path is absolute and handles substitution.
canon_path = spack.util.path.canonicalize_path(path)
# check if the path exists
if not os.path.exists(canon_path):
tty.die("No such file or directory: %s" % path)
# Make sure the path is a directory.
if not os.path.isdir(canon_path):
tty.die("Not a Spack repository: %s" % path)
# Make sure it's actually a spack repository by constructing it.
repo = spack.repo.Repo(canon_path)
# If that succeeds, finally add it to the configuration.
repos = spack.config.get("repos", scope=args.scope)
if not repos:
repos = []
if repo.root in repos or path in repos:
tty.die("Repository is already registered with Spack: %s" % path)
repos.insert(0, canon_path)
spack.config.set("repos", repos, args.scope)
tty.msg("Added repo with namespace '%s'." % repo.namespace)
[docs]
def repo_remove(args):
"""remove a repository from Spack's configuration"""
repos = spack.config.get("repos", scope=args.scope)
namespace_or_path = args.namespace_or_path
# If the argument is a path, remove that repository from config.
canon_path = spack.util.path.canonicalize_path(namespace_or_path)
for repo_path in repos:
repo_canon_path = spack.util.path.canonicalize_path(repo_path)
if canon_path == repo_canon_path:
repos.remove(repo_path)
spack.config.set("repos", repos, args.scope)
tty.msg("Removed repository %s" % repo_path)
return
# If it is a namespace, remove corresponding repo
for path in repos:
try:
repo = spack.repo.Repo(path)
if repo.namespace == namespace_or_path:
repos.remove(path)
spack.config.set("repos", repos, args.scope)
tty.msg("Removed repository %s with namespace '%s'." % (repo.root, repo.namespace))
return
except spack.repo.RepoError:
continue
tty.die("No repository with path or namespace: %s" % namespace_or_path)
[docs]
def repo_list(args):
"""show registered repositories and their namespaces"""
roots = spack.config.get("repos", scope=args.scope)
repos = []
for r in roots:
try:
repos.append(spack.repo.Repo(r))
except spack.repo.RepoError:
continue
if sys.stdout.isatty():
msg = "%d package repositor" % len(repos)
msg += "y." if len(repos) == 1 else "ies."
tty.msg(msg)
if not repos:
return
max_ns_len = max(len(r.namespace) for r in repos)
for repo in repos:
fmt = "%%-%ds%%s" % (max_ns_len + 4)
print(fmt % (repo.namespace, repo.root))
[docs]
def repo(parser, args):
action = {
"create": repo_create,
"list": repo_list,
"add": repo_add,
"remove": repo_remove,
"rm": repo_remove,
}
action[args.repo_command](args)