Compare commits
9 Commits
containers
...
packages/u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bc67e8e84 | ||
|
|
aeaa922eef | ||
|
|
a6d5a34be3 | ||
|
|
ba79542f3c | ||
|
|
dc10c8a1ed | ||
|
|
5ab814505e | ||
|
|
1d8bdcfc04 | ||
|
|
95cf341b50 | ||
|
|
a134485b1b |
4
.github/workflows/build-containers.yml
vendored
4
.github/workflows/build-containers.yml
vendored
@@ -87,7 +87,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload Dockerfile
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b
|
||||
with:
|
||||
name: dockerfiles_${{ matrix.dockerfile[0] }}
|
||||
path: dockerfiles
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
needs: deploy-images
|
||||
steps:
|
||||
- name: Merge Artifacts
|
||||
uses: actions/upload-artifact/merge@65462800fd760344b1a7b4382951275a0abb4808
|
||||
uses: actions/upload-artifact/merge@0b2256b8c012f0828dc542b3febcab082c67f72b
|
||||
with:
|
||||
name: dockerfiles
|
||||
pattern: dockerfiles_*
|
||||
|
||||
@@ -60,7 +60,7 @@ packages:
|
||||
szip: [libaec, libszip]
|
||||
tbb: [intel-tbb]
|
||||
unwind: [libunwind]
|
||||
uuid: [util-linux-uuid, libuuid]
|
||||
uuid: [util-linux-uuid, util-linux+uuid, libuuid]
|
||||
xxd: [xxd-standalone, vim]
|
||||
yacc: [bison, byacc]
|
||||
ziglang: [zig]
|
||||
|
||||
@@ -129,10 +129,10 @@ def _bootstrap_config_scopes() -> Sequence["spack.config.ConfigScope"]:
|
||||
configuration_paths = (spack.config.CONFIGURATION_DEFAULTS_PATH, ("bootstrap", _config_path()))
|
||||
for name, path in configuration_paths:
|
||||
platform = spack.platforms.host().name
|
||||
platform_scope = spack.config.ConfigScope(
|
||||
"/".join([name, platform]), os.path.join(path, platform)
|
||||
platform_scope = spack.config.DirectoryConfigScope(
|
||||
f"{name}/{platform}", os.path.join(path, platform)
|
||||
)
|
||||
generic_scope = spack.config.ConfigScope(name, path)
|
||||
generic_scope = spack.config.DirectoryConfigScope(name, path)
|
||||
config_scopes.extend([generic_scope, platform_scope])
|
||||
msg = "[BOOTSTRAP CONFIG SCOPE] name={0}, path={1}"
|
||||
tty.debug(msg.format(generic_scope.name, generic_scope.path))
|
||||
|
||||
@@ -34,6 +34,8 @@ def _misc_cache():
|
||||
return spack.util.file_cache.FileCache(path)
|
||||
|
||||
|
||||
FileCacheType = Union[spack.util.file_cache.FileCache, llnl.util.lang.Singleton]
|
||||
|
||||
#: Spack's cache for small data
|
||||
MISC_CACHE: Union[spack.util.file_cache.FileCache, llnl.util.lang.Singleton] = (
|
||||
llnl.util.lang.Singleton(_misc_cache)
|
||||
|
||||
@@ -809,7 +809,8 @@ def ensure_expected_target_path(path):
|
||||
cli_scopes = [
|
||||
os.path.relpath(s.path, concrete_env_dir)
|
||||
for s in cfg.scopes().values()
|
||||
if isinstance(s, cfg.ImmutableConfigScope)
|
||||
if not s.writable
|
||||
and isinstance(s, (cfg.DirectoryConfigScope))
|
||||
and s.path not in env_includes
|
||||
and os.path.exists(s.path)
|
||||
]
|
||||
|
||||
@@ -165,7 +165,7 @@ def _reset(args):
|
||||
if not ok_to_continue:
|
||||
raise RuntimeError("Aborting")
|
||||
|
||||
for scope in spack.config.CONFIG.file_scopes:
|
||||
for scope in spack.config.CONFIG.writable_scopes:
|
||||
# The default scope should stay untouched
|
||||
if scope.name == "defaults":
|
||||
continue
|
||||
|
||||
@@ -264,7 +264,9 @@ def config_remove(args):
|
||||
def _can_update_config_file(scope: spack.config.ConfigScope, cfg_file):
|
||||
if isinstance(scope, spack.config.SingleFileScope):
|
||||
return fs.can_access(cfg_file)
|
||||
return fs.can_write_to_dir(scope.path) and fs.can_access(cfg_file)
|
||||
elif isinstance(scope, spack.config.DirectoryConfigScope):
|
||||
return fs.can_write_to_dir(scope.path) and fs.can_access(cfg_file)
|
||||
return False
|
||||
|
||||
|
||||
def _config_change_requires_scope(path, spec, scope, match_spec=None):
|
||||
@@ -362,14 +364,11 @@ def config_change(args):
|
||||
def config_update(args):
|
||||
# Read the configuration files
|
||||
spack.config.CONFIG.get_config(args.section, scope=args.scope)
|
||||
updates: List[spack.config.ConfigScope] = list(
|
||||
filter(
|
||||
lambda s: not isinstance(
|
||||
s, (spack.config.InternalConfigScope, spack.config.ImmutableConfigScope)
|
||||
),
|
||||
spack.config.CONFIG.format_updates[args.section],
|
||||
)
|
||||
)
|
||||
updates: List[spack.config.ConfigScope] = [
|
||||
x
|
||||
for x in spack.config.CONFIG.format_updates[args.section]
|
||||
if not isinstance(x, spack.config.InternalConfigScope) and x.writable
|
||||
]
|
||||
|
||||
cannot_overwrite, skip_system_scope = [], False
|
||||
for scope in updates:
|
||||
@@ -447,7 +446,7 @@ def _can_revert_update(scope_dir, cfg_file, bkp_file):
|
||||
|
||||
|
||||
def config_revert(args):
|
||||
scopes = [args.scope] if args.scope else [x.name for x in spack.config.CONFIG.file_scopes]
|
||||
scopes = [args.scope] if args.scope else [x.name for x in spack.config.CONFIG.writable_scopes]
|
||||
|
||||
# Search for backup files in the configuration scopes
|
||||
Entry = collections.namedtuple("Entry", ["scope", "cfg", "bkp"])
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
@@ -934,7 +933,7 @@ def get_repository(args, name):
|
||||
# Figure out where the new package should live
|
||||
repo_path = args.repo
|
||||
if repo_path is not None:
|
||||
repo = spack.repo.Repo(repo_path)
|
||||
repo = spack.repo.from_path(repo_path)
|
||||
if spec.namespace and spec.namespace != repo.namespace:
|
||||
tty.die(
|
||||
"Can't create package with namespace {0} in repo with "
|
||||
|
||||
@@ -123,7 +123,7 @@ def edit(parser, args):
|
||||
spack.util.editor.editor(*paths)
|
||||
elif names:
|
||||
if args.repo:
|
||||
repo = spack.repo.Repo(args.repo)
|
||||
repo = spack.repo.from_path(args.repo)
|
||||
elif args.namespace:
|
||||
repo = spack.repo.PATH.get_repo(args.namespace)
|
||||
else:
|
||||
|
||||
@@ -56,7 +56,6 @@ def roots_from_environments(args, active_env):
|
||||
|
||||
# -e says "also preserve things needed by this particular env"
|
||||
for env_name_or_dir in args.except_environment:
|
||||
print("HMM", env_name_or_dir)
|
||||
if ev.exists(env_name_or_dir):
|
||||
env = ev.read(env_name_or_dir)
|
||||
elif ev.is_env_dir(env_name_or_dir):
|
||||
|
||||
@@ -91,7 +91,7 @@ def repo_add(args):
|
||||
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)
|
||||
repo = spack.repo.from_path(canon_path)
|
||||
|
||||
# If that succeeds, finally add it to the configuration.
|
||||
repos = spack.config.get("repos", scope=args.scope)
|
||||
@@ -124,7 +124,7 @@ def repo_remove(args):
|
||||
# If it is a namespace, remove corresponding repo
|
||||
for path in repos:
|
||||
try:
|
||||
repo = spack.repo.Repo(path)
|
||||
repo = spack.repo.from_path(path)
|
||||
if repo.namespace == namespace_or_path:
|
||||
repos.remove(path)
|
||||
spack.config.set("repos", repos, args.scope)
|
||||
@@ -142,7 +142,7 @@ def repo_list(args):
|
||||
repos = []
|
||||
for r in roots:
|
||||
try:
|
||||
repos.append(spack.repo.Repo(r))
|
||||
repos.append(spack.repo.from_path(r))
|
||||
except spack.repo.RepoError:
|
||||
continue
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ def _init_compiler_config(
|
||||
def compiler_config_files():
|
||||
config_files = list()
|
||||
config = spack.config.CONFIG
|
||||
for scope in config.file_scopes:
|
||||
for scope in config.writable_scopes:
|
||||
name = scope.name
|
||||
compiler_config = config.get("compilers", scope=name)
|
||||
if compiler_config:
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Type, Union
|
||||
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Union
|
||||
|
||||
from llnl.util import filesystem, lang, tty
|
||||
|
||||
@@ -117,21 +117,39 @@
|
||||
|
||||
|
||||
class ConfigScope:
|
||||
"""This class represents a configuration scope.
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.writable = False
|
||||
self.sections = syaml.syaml_dict()
|
||||
|
||||
A scope is one directory containing named configuration files.
|
||||
Each file is a config "section" (e.g., mirrors, compilers, etc.).
|
||||
"""
|
||||
def get_section_filename(self, section: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def __init__(self, name, path) -> None:
|
||||
self.name = name # scope name.
|
||||
self.path = path # path to directory containing configs.
|
||||
self.sections = syaml.syaml_dict() # sections read from config files.
|
||||
def get_section(self, section: str) -> Optional[YamlConfigDict]:
|
||||
raise NotImplementedError
|
||||
|
||||
def _write_section(self, section: str) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def is_platform_dependent(self) -> bool:
|
||||
"""Returns true if the scope name is platform specific"""
|
||||
return os.sep in self.name
|
||||
return False
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Empty cached config information."""
|
||||
self.sections = syaml.syaml_dict()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<ConfigScope: {self.name}>"
|
||||
|
||||
|
||||
class DirectoryConfigScope(ConfigScope):
|
||||
"""Config scope backed by a directory containing one file per section."""
|
||||
|
||||
def __init__(self, name: str, path: str, *, writable: bool = True) -> None:
|
||||
super().__init__(name)
|
||||
self.path = path
|
||||
self.writable = writable
|
||||
|
||||
def get_section_filename(self, section: str) -> str:
|
||||
"""Returns the filename associated with a given section"""
|
||||
@@ -148,6 +166,9 @@ def get_section(self, section: str) -> Optional[YamlConfigDict]:
|
||||
return self.sections[section]
|
||||
|
||||
def _write_section(self, section: str) -> None:
|
||||
if not self.writable:
|
||||
raise ConfigError(f"Cannot write to immutable scope {self}")
|
||||
|
||||
filename = self.get_section_filename(section)
|
||||
data = self.get_section(section)
|
||||
if data is None:
|
||||
@@ -164,19 +185,23 @@ def _write_section(self, section: str) -> None:
|
||||
except (syaml.SpackYAMLError, OSError) as e:
|
||||
raise ConfigFileError(f"cannot write to '{filename}'") from e
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Empty cached config information."""
|
||||
self.sections = syaml.syaml_dict()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<ConfigScope: {self.name}: {self.path}>"
|
||||
@property
|
||||
def is_platform_dependent(self) -> bool:
|
||||
"""Returns true if the scope name is platform specific"""
|
||||
return "/" in self.name
|
||||
|
||||
|
||||
class SingleFileScope(ConfigScope):
|
||||
"""This class represents a configuration scope in a single YAML file."""
|
||||
|
||||
def __init__(
|
||||
self, name: str, path: str, schema: YamlConfigDict, yaml_path: Optional[List[str]] = None
|
||||
self,
|
||||
name: str,
|
||||
path: str,
|
||||
schema: YamlConfigDict,
|
||||
*,
|
||||
yaml_path: Optional[List[str]] = None,
|
||||
writable: bool = True,
|
||||
) -> None:
|
||||
"""Similar to ``ConfigScope`` but can be embedded in another schema.
|
||||
|
||||
@@ -195,15 +220,13 @@ def __init__(
|
||||
config:
|
||||
install_tree: $spack/opt/spack
|
||||
"""
|
||||
super().__init__(name, path)
|
||||
super().__init__(name)
|
||||
self._raw_data: Optional[YamlConfigDict] = None
|
||||
self.schema = schema
|
||||
self.path = path
|
||||
self.writable = writable
|
||||
self.yaml_path = yaml_path or []
|
||||
|
||||
@property
|
||||
def is_platform_dependent(self) -> bool:
|
||||
return False
|
||||
|
||||
def get_section_filename(self, section) -> str:
|
||||
return self.path
|
||||
|
||||
@@ -257,6 +280,8 @@ def get_section(self, section: str) -> Optional[YamlConfigDict]:
|
||||
return self.sections.get(section, None)
|
||||
|
||||
def _write_section(self, section: str) -> None:
|
||||
if not self.writable:
|
||||
raise ConfigError(f"Cannot write to immutable scope {self}")
|
||||
data_to_write: Optional[YamlConfigDict] = self._raw_data
|
||||
|
||||
# If there is no existing data, this section SingleFileScope has never
|
||||
@@ -301,19 +326,6 @@ def __repr__(self) -> str:
|
||||
return f"<SingleFileScope: {self.name}: {self.path}>"
|
||||
|
||||
|
||||
class ImmutableConfigScope(ConfigScope):
|
||||
"""A configuration scope that cannot be written to.
|
||||
|
||||
This is used for ConfigScopes passed on the command line.
|
||||
"""
|
||||
|
||||
def _write_section(self, section) -> None:
|
||||
raise ConfigError(f"Cannot write to immutable scope {self}")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<ImmutableConfigScope: {self.name}: {self.path}>"
|
||||
|
||||
|
||||
class InternalConfigScope(ConfigScope):
|
||||
"""An internal configuration scope that is not persisted to a file.
|
||||
|
||||
@@ -323,7 +335,7 @@ class InternalConfigScope(ConfigScope):
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, data: Optional[YamlConfigDict] = None) -> None:
|
||||
super().__init__(name, None)
|
||||
super().__init__(name)
|
||||
self.sections = syaml.syaml_dict()
|
||||
|
||||
if data is not None:
|
||||
@@ -333,9 +345,6 @@ def __init__(self, name: str, data: Optional[YamlConfigDict] = None) -> None:
|
||||
validate({section: dsec}, SECTION_SCHEMAS[section])
|
||||
self.sections[section] = _mark_internal(syaml.syaml_dict({section: dsec}), name)
|
||||
|
||||
def get_section_filename(self, section: str) -> str:
|
||||
raise NotImplementedError("Cannot get filename for InternalConfigScope.")
|
||||
|
||||
def get_section(self, section: str) -> Optional[YamlConfigDict]:
|
||||
"""Just reads from an internal dictionary."""
|
||||
if section not in self.sections:
|
||||
@@ -440,27 +449,21 @@ def remove_scope(self, scope_name: str) -> Optional[ConfigScope]:
|
||||
return scope
|
||||
|
||||
@property
|
||||
def file_scopes(self) -> List[ConfigScope]:
|
||||
"""List of writable scopes with an associated file."""
|
||||
return [
|
||||
s
|
||||
for s in self.scopes.values()
|
||||
if (type(s) is ConfigScope or type(s) is SingleFileScope)
|
||||
]
|
||||
def writable_scopes(self) -> Generator[ConfigScope, None, None]:
|
||||
"""Generator of writable scopes with an associated file."""
|
||||
return (s for s in self.scopes.values() if s.writable)
|
||||
|
||||
def highest_precedence_scope(self) -> ConfigScope:
|
||||
"""Non-internal scope with highest precedence."""
|
||||
return next(reversed(self.file_scopes))
|
||||
"""Writable scope with highest precedence."""
|
||||
return next(s for s in reversed(self.scopes.values()) if s.writable) # type: ignore
|
||||
|
||||
def highest_precedence_non_platform_scope(self) -> ConfigScope:
|
||||
"""Non-internal non-platform scope with highest precedence
|
||||
|
||||
Platform-specific scopes are of the form scope/platform"""
|
||||
generator = reversed(self.file_scopes)
|
||||
highest = next(generator)
|
||||
while highest and highest.is_platform_dependent:
|
||||
highest = next(generator)
|
||||
return highest
|
||||
"""Writable non-platform scope with highest precedence"""
|
||||
return next(
|
||||
s
|
||||
for s in reversed(self.scopes.values()) # type: ignore
|
||||
if s.writable and not s.is_platform_dependent
|
||||
)
|
||||
|
||||
def matching_scopes(self, reg_expr) -> List[ConfigScope]:
|
||||
"""
|
||||
@@ -755,13 +758,14 @@ def override(
|
||||
|
||||
|
||||
def _add_platform_scope(
|
||||
cfg: Union[Configuration, lang.Singleton], scope_type: Type[ConfigScope], name: str, path: str
|
||||
cfg: Union[Configuration, lang.Singleton], name: str, path: str, writable: bool = True
|
||||
) -> None:
|
||||
"""Add a platform-specific subdirectory for the current platform."""
|
||||
platform = spack.platforms.host().name
|
||||
plat_name = os.path.join(name, platform)
|
||||
plat_path = os.path.join(path, platform)
|
||||
cfg.push_scope(scope_type(plat_name, plat_path))
|
||||
scope = DirectoryConfigScope(
|
||||
f"{name}/{platform}", os.path.join(path, platform), writable=writable
|
||||
)
|
||||
cfg.push_scope(scope)
|
||||
|
||||
|
||||
def config_paths_from_entry_points() -> List[Tuple[str, str]]:
|
||||
@@ -806,8 +810,8 @@ def _add_command_line_scopes(
|
||||
|
||||
# name based on order on the command line
|
||||
name = f"cmd_scope_{i:d}"
|
||||
cfg.push_scope(ImmutableConfigScope(name, path))
|
||||
_add_platform_scope(cfg, ImmutableConfigScope, name, path)
|
||||
cfg.push_scope(DirectoryConfigScope(name, path, writable=False))
|
||||
_add_platform_scope(cfg, name, path, writable=False)
|
||||
|
||||
|
||||
def create() -> Configuration:
|
||||
@@ -851,10 +855,10 @@ def create() -> Configuration:
|
||||
|
||||
# add each scope and its platform-specific directory
|
||||
for name, path in configuration_paths:
|
||||
cfg.push_scope(ConfigScope(name, path))
|
||||
cfg.push_scope(DirectoryConfigScope(name, path))
|
||||
|
||||
# Each scope can have per-platfom overrides in subdirectories
|
||||
_add_platform_scope(cfg, ConfigScope, name, path)
|
||||
_add_platform_scope(cfg, name, path)
|
||||
|
||||
# add command-line scopes
|
||||
_add_command_line_scopes(cfg, COMMAND_LINE_SCOPES)
|
||||
@@ -969,7 +973,7 @@ def set(path: str, value: Any, scope: Optional[str] = None) -> None:
|
||||
def add_default_platform_scope(platform: str) -> None:
|
||||
plat_name = os.path.join("defaults", platform)
|
||||
plat_path = os.path.join(CONFIGURATION_DEFAULTS_PATH[1], platform)
|
||||
CONFIG.push_scope(ConfigScope(plat_name, plat_path))
|
||||
CONFIG.push_scope(DirectoryConfigScope(plat_name, plat_path))
|
||||
|
||||
|
||||
def scopes() -> Dict[str, ConfigScope]:
|
||||
@@ -978,19 +982,10 @@ def scopes() -> Dict[str, ConfigScope]:
|
||||
|
||||
|
||||
def writable_scopes() -> List[ConfigScope]:
|
||||
"""
|
||||
Return list of writable scopes. Higher-priority scopes come first in the
|
||||
list.
|
||||
"""
|
||||
return list(
|
||||
reversed(
|
||||
list(
|
||||
x
|
||||
for x in CONFIG.scopes.values()
|
||||
if not isinstance(x, (InternalConfigScope, ImmutableConfigScope))
|
||||
)
|
||||
)
|
||||
)
|
||||
"""Return list of writable scopes. Higher-priority scopes come first in the list."""
|
||||
scopes = [x for x in CONFIG.scopes.values() if x.writable]
|
||||
scopes.reverse()
|
||||
return scopes
|
||||
|
||||
|
||||
def writable_scope_names() -> List[str]:
|
||||
@@ -1599,7 +1594,7 @@ def _config_from(scopes_or_paths: List[Union[ConfigScope, str]]) -> Configuratio
|
||||
path = os.path.normpath(scope_or_path)
|
||||
assert os.path.isdir(path), f'"{path}" must be a directory'
|
||||
name = os.path.basename(path)
|
||||
scopes.append(ConfigScope(name, path))
|
||||
scopes.append(DirectoryConfigScope(name, path))
|
||||
|
||||
configuration = Configuration(*scopes)
|
||||
return configuration
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
from llnl.util.link_tree import ConflictingSpecsError
|
||||
from llnl.util.symlink import readlink, symlink
|
||||
|
||||
import spack.caches
|
||||
import spack.cmd
|
||||
import spack.compilers
|
||||
import spack.concretize
|
||||
@@ -2542,7 +2543,7 @@ def _concretize_task(packed_arguments) -> Tuple[int, Spec, float]:
|
||||
|
||||
def make_repo_path(root):
|
||||
"""Make a RepoPath from the repo subdirectories in an environment."""
|
||||
path = spack.repo.RepoPath()
|
||||
path = spack.repo.RepoPath(cache=spack.caches.MISC_CACHE)
|
||||
|
||||
if os.path.isdir(root):
|
||||
for repo_root in os.listdir(root):
|
||||
@@ -2551,7 +2552,7 @@ def make_repo_path(root):
|
||||
if not os.path.isdir(repo_root):
|
||||
continue
|
||||
|
||||
repo = spack.repo.Repo(repo_root)
|
||||
repo = spack.repo.from_path(repo_root)
|
||||
path.put_last(repo)
|
||||
|
||||
return path
|
||||
@@ -3027,7 +3028,7 @@ def included_config_scopes(self) -> List[spack.config.ConfigScope]:
|
||||
SpackEnvironmentError: if the manifest includes a remote file but
|
||||
no configuration stage directory has been identified
|
||||
"""
|
||||
scopes = []
|
||||
scopes: List[spack.config.ConfigScope] = []
|
||||
|
||||
# load config scopes added via 'include:', in reverse so that
|
||||
# highest-precedence scopes are last.
|
||||
@@ -3096,23 +3097,21 @@ def included_config_scopes(self) -> List[spack.config.ConfigScope]:
|
||||
if os.path.isdir(config_path):
|
||||
# directories are treated as regular ConfigScopes
|
||||
config_name = "env:%s:%s" % (env_name, os.path.basename(config_path))
|
||||
tty.debug("Creating ConfigScope {0} for '{1}'".format(config_name, config_path))
|
||||
scope = spack.config.ConfigScope(config_name, config_path)
|
||||
tty.debug(f"Creating DirectoryConfigScope {config_name} for '{config_path}'")
|
||||
scopes.append(spack.config.DirectoryConfigScope(config_name, config_path))
|
||||
elif os.path.exists(config_path):
|
||||
# files are assumed to be SingleFileScopes
|
||||
config_name = "env:%s:%s" % (env_name, config_path)
|
||||
tty.debug(
|
||||
"Creating SingleFileScope {0} for '{1}'".format(config_name, config_path)
|
||||
)
|
||||
scope = spack.config.SingleFileScope(
|
||||
config_name, config_path, spack.schema.merged.schema
|
||||
tty.debug(f"Creating SingleFileScope {config_name} for '{config_path}'")
|
||||
scopes.append(
|
||||
spack.config.SingleFileScope(
|
||||
config_name, config_path, spack.schema.merged.schema
|
||||
)
|
||||
)
|
||||
else:
|
||||
missing.append(config_path)
|
||||
continue
|
||||
|
||||
scopes.append(scope)
|
||||
|
||||
if missing:
|
||||
msg = "Detected {0} missing include path(s):".format(len(missing))
|
||||
msg += "\n {0}".format("\n ".join(missing))
|
||||
@@ -3129,7 +3128,10 @@ def env_config_scopes(self) -> List[spack.config.ConfigScope]:
|
||||
scopes: List[spack.config.ConfigScope] = [
|
||||
*self.included_config_scopes,
|
||||
spack.config.SingleFileScope(
|
||||
self.scope_name, str(self.manifest_file), spack.schema.env.schema, [TOP_LEVEL_KEY]
|
||||
self.scope_name,
|
||||
str(self.manifest_file),
|
||||
spack.schema.env.schema,
|
||||
yaml_path=[TOP_LEVEL_KEY],
|
||||
),
|
||||
]
|
||||
ensure_no_disallowed_env_config_mods(scopes)
|
||||
|
||||
@@ -582,7 +582,7 @@ def dump_packages(spec: "spack.spec.Spec", path: str) -> None:
|
||||
|
||||
# Create a source repo and get the pkg directory out of it.
|
||||
try:
|
||||
source_repo = spack.repo.Repo(source_repo_root)
|
||||
source_repo = spack.repo.from_path(source_repo_root)
|
||||
source_pkg_dir = source_repo.dirname_for_package_name(node.name)
|
||||
except spack.repo.RepoError as err:
|
||||
tty.debug(f"Failed to create source repo for {node.name}: {str(err)}")
|
||||
@@ -593,7 +593,7 @@ def dump_packages(spec: "spack.spec.Spec", path: str) -> None:
|
||||
dest_repo_root = os.path.join(path, node.namespace)
|
||||
if not os.path.exists(dest_repo_root):
|
||||
spack.repo.create_repo(dest_repo_root)
|
||||
repo = spack.repo.Repo(dest_repo_root)
|
||||
repo = spack.repo.from_path(dest_repo_root)
|
||||
|
||||
# Get the location of the package in the dest repo.
|
||||
dest_pkg_dir = repo.dirname_for_package_name(node.name)
|
||||
|
||||
@@ -748,11 +748,6 @@ def __init__(self, spec):
|
||||
self._fetch_time = 0.0
|
||||
|
||||
self.win_rpath = fsys.WindowsSimulatedRPath(self)
|
||||
|
||||
if self.is_extension:
|
||||
pkg_cls = spack.repo.PATH.get_pkg_class(self.extendee_spec.name)
|
||||
pkg_cls(self.extendee_spec)._check_extendable()
|
||||
|
||||
super().__init__()
|
||||
|
||||
@classmethod
|
||||
@@ -2388,10 +2383,6 @@ def do_deprecate(self, deprecator, link_fn):
|
||||
PackageBase.uninstall_by_spec(spec, force=True, deprecator=deprecator)
|
||||
link_fn(deprecator.prefix, spec.prefix)
|
||||
|
||||
def _check_extendable(self):
|
||||
if not self.extendable:
|
||||
raise ValueError("Package %s is not extendable!" % self.name)
|
||||
|
||||
def view(self):
|
||||
"""Create a view with the prefix of this package as the root.
|
||||
Extensions added to this view will modify the installation prefix of
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
import traceback
|
||||
import types
|
||||
import uuid
|
||||
from typing import Any, Dict, List, Set, Tuple, Union
|
||||
import warnings
|
||||
from typing import Any, Dict, Generator, List, Optional, Set, Tuple, Type, Union
|
||||
|
||||
import llnl.path
|
||||
import llnl.util.filesystem as fs
|
||||
@@ -126,11 +127,35 @@ def exec_module(self, module):
|
||||
|
||||
|
||||
class ReposFinder:
|
||||
"""MetaPathFinder class that loads a Python module corresponding to a Spack package
|
||||
"""MetaPathFinder class that loads a Python module corresponding to a Spack package.
|
||||
|
||||
Return a loader based on the inspection of the current global repository list.
|
||||
Returns a loader based on the inspection of the current repository list.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._repo_init = _path
|
||||
self._repo = None
|
||||
|
||||
@property
|
||||
def current_repository(self):
|
||||
if self._repo is None:
|
||||
self._repo = self._repo_init()
|
||||
return self._repo
|
||||
|
||||
@current_repository.setter
|
||||
def current_repository(self, value):
|
||||
self._repo = value
|
||||
|
||||
@contextlib.contextmanager
|
||||
def switch_repo(self, substitute: "RepoType"):
|
||||
"""Switch the current repository list for the duration of the context manager."""
|
||||
old = self.current_repository
|
||||
try:
|
||||
self.current_repository = substitute
|
||||
yield
|
||||
finally:
|
||||
self.current_repository = old
|
||||
|
||||
def find_spec(self, fullname, python_path, target=None):
|
||||
# "target" is not None only when calling importlib.reload()
|
||||
if target is not None:
|
||||
@@ -149,9 +174,14 @@ def compute_loader(self, fullname):
|
||||
# namespaces are added to repo, and package modules are leaves.
|
||||
namespace, dot, module_name = fullname.rpartition(".")
|
||||
|
||||
# If it's a module in some repo, or if it is the repo's
|
||||
# namespace, let the repo handle it.
|
||||
for repo in PATH.repos:
|
||||
# If it's a module in some repo, or if it is the repo's namespace, let the repo handle it.
|
||||
is_repo_path = isinstance(self.current_repository, RepoPath)
|
||||
if is_repo_path:
|
||||
repos = self.current_repository.repos
|
||||
else:
|
||||
repos = [self.current_repository]
|
||||
|
||||
for repo in repos:
|
||||
# We are using the namespace of the repo and the repo contains the package
|
||||
if namespace == repo.full_namespace:
|
||||
# With 2 nested conditionals we can call "repo.real_name" only once
|
||||
@@ -165,7 +195,7 @@ def compute_loader(self, fullname):
|
||||
|
||||
# No repo provides the namespace, but it is a valid prefix of
|
||||
# something in the RepoPath.
|
||||
if PATH.by_namespace.is_prefix(fullname):
|
||||
if is_repo_path and self.current_repository.by_namespace.is_prefix(fullname):
|
||||
return SpackNamespaceLoader()
|
||||
|
||||
return None
|
||||
@@ -560,7 +590,7 @@ def __init__(
|
||||
self,
|
||||
package_checker: FastPackageChecker,
|
||||
namespace: str,
|
||||
cache: spack.util.file_cache.FileCache,
|
||||
cache: spack.caches.FileCacheType,
|
||||
):
|
||||
self.checker = package_checker
|
||||
self.packages_path = self.checker.packages_path
|
||||
@@ -648,11 +678,9 @@ class RepoPath:
|
||||
repos (list): list Repo objects or paths to put in this RepoPath
|
||||
"""
|
||||
|
||||
def __init__(self, *repos, **kwargs):
|
||||
cache = kwargs.get("cache", spack.caches.MISC_CACHE)
|
||||
def __init__(self, *repos, cache, overrides=None):
|
||||
self.repos = []
|
||||
self.by_namespace = nm.NamespaceTrie()
|
||||
|
||||
self._provider_index = None
|
||||
self._patch_index = None
|
||||
self._tag_index = None
|
||||
@@ -661,7 +689,8 @@ def __init__(self, *repos, **kwargs):
|
||||
for repo in repos:
|
||||
try:
|
||||
if isinstance(repo, str):
|
||||
repo = Repo(repo, cache=cache)
|
||||
repo = Repo(repo, cache=cache, overrides=overrides)
|
||||
repo.finder(self)
|
||||
self.put_last(repo)
|
||||
except RepoError as e:
|
||||
tty.warn(
|
||||
@@ -915,18 +944,28 @@ class Repo:
|
||||
Each package repository must have a top-level configuration file
|
||||
called `repo.yaml`.
|
||||
|
||||
Currently, `repo.yaml` this must define:
|
||||
Currently, `repo.yaml` must define:
|
||||
|
||||
`namespace`:
|
||||
A Python namespace where the repository's packages should live.
|
||||
|
||||
`subdirectory`:
|
||||
An optional subdirectory name where packages are placed
|
||||
"""
|
||||
|
||||
def __init__(self, root, cache=None):
|
||||
def __init__(
|
||||
self,
|
||||
root: str,
|
||||
*,
|
||||
cache: spack.caches.FileCacheType,
|
||||
overrides: Optional[Dict[str, Any]] = None,
|
||||
) -> None:
|
||||
"""Instantiate a package repository from a filesystem path.
|
||||
|
||||
Args:
|
||||
root: the root directory of the repository
|
||||
cache: file cache associated with this repository
|
||||
overrides: dict mapping package name to class attribute overrides for that package
|
||||
"""
|
||||
# Root directory, containing _repo.yaml and package dirs
|
||||
# Allow roots to by spack-relative by starting with '$spack'
|
||||
@@ -939,20 +978,20 @@ def check(condition, msg):
|
||||
|
||||
# Validate repository layout.
|
||||
self.config_file = os.path.join(self.root, repo_config_name)
|
||||
check(os.path.isfile(self.config_file), "No %s found in '%s'" % (repo_config_name, root))
|
||||
check(os.path.isfile(self.config_file), f"No {repo_config_name} found in '{root}'")
|
||||
|
||||
# Read configuration and validate namespace
|
||||
config = self._read_config()
|
||||
check(
|
||||
"namespace" in config,
|
||||
"%s must define a namespace." % os.path.join(root, repo_config_name),
|
||||
f"{os.path.join(root, repo_config_name)} must define a namespace.",
|
||||
)
|
||||
|
||||
self.namespace = config["namespace"]
|
||||
check(
|
||||
re.match(r"[a-zA-Z][a-zA-Z0-9_.]+", self.namespace),
|
||||
("Invalid namespace '%s' in repo '%s'. " % (self.namespace, self.root))
|
||||
+ "Namespaces must be valid python identifiers separated by '.'",
|
||||
f"Invalid namespace '{self.namespace}' in repo '{self.root}'. "
|
||||
"Namespaces must be valid python identifiers separated by '.'",
|
||||
)
|
||||
|
||||
# Set up 'full_namespace' to include the super-namespace
|
||||
@@ -964,23 +1003,26 @@ def check(condition, msg):
|
||||
packages_dir = config.get("subdirectory", packages_dir_name)
|
||||
self.packages_path = os.path.join(self.root, packages_dir)
|
||||
check(
|
||||
os.path.isdir(self.packages_path),
|
||||
"No directory '%s' found in '%s'" % (packages_dir, root),
|
||||
os.path.isdir(self.packages_path), f"No directory '{packages_dir}' found in '{root}'"
|
||||
)
|
||||
|
||||
# These are internal cache variables.
|
||||
self._modules = {}
|
||||
self._classes = {}
|
||||
self._instances = {}
|
||||
# Class attribute overrides by package name
|
||||
self.overrides = overrides or {}
|
||||
|
||||
# Optional reference to a RepoPath to influence module import from spack.pkg
|
||||
self._finder: Optional[RepoPath] = None
|
||||
|
||||
# Maps that goes from package name to corresponding file stat
|
||||
self._fast_package_checker = None
|
||||
self._fast_package_checker: Optional[FastPackageChecker] = None
|
||||
|
||||
# Indexes for this repository, computed lazily
|
||||
self._repo_index = None
|
||||
self._cache = cache or spack.caches.MISC_CACHE
|
||||
self._repo_index: Optional[RepoIndex] = None
|
||||
self._cache = cache
|
||||
|
||||
def real_name(self, import_name):
|
||||
def finder(self, value: RepoPath) -> None:
|
||||
self._finder = value
|
||||
|
||||
def real_name(self, import_name: str) -> Optional[str]:
|
||||
"""Allow users to import Spack packages using Python identifiers.
|
||||
|
||||
A python identifier might map to many different Spack package
|
||||
@@ -999,18 +1041,21 @@ def real_name(self, import_name):
|
||||
return import_name
|
||||
|
||||
options = nm.possible_spack_module_names(import_name)
|
||||
options.remove(import_name)
|
||||
try:
|
||||
options.remove(import_name)
|
||||
except ValueError:
|
||||
pass
|
||||
for name in options:
|
||||
if name in self:
|
||||
return name
|
||||
return None
|
||||
|
||||
def is_prefix(self, fullname):
|
||||
def is_prefix(self, fullname: str) -> bool:
|
||||
"""True if fullname is a prefix of this Repo's namespace."""
|
||||
parts = fullname.split(".")
|
||||
return self._names[: len(parts)] == parts
|
||||
|
||||
def _read_config(self):
|
||||
def _read_config(self) -> Dict[str, str]:
|
||||
"""Check for a YAML config file in this db's root directory."""
|
||||
try:
|
||||
with open(self.config_file) as reponame_file:
|
||||
@@ -1021,14 +1066,14 @@ def _read_config(self):
|
||||
or "repo" not in yaml_data
|
||||
or not isinstance(yaml_data["repo"], dict)
|
||||
):
|
||||
tty.die("Invalid %s in repository %s" % (repo_config_name, self.root))
|
||||
tty.die(f"Invalid {repo_config_name} in repository {self.root}")
|
||||
|
||||
return yaml_data["repo"]
|
||||
|
||||
except IOError:
|
||||
tty.die("Error reading %s when opening %s" % (self.config_file, self.root))
|
||||
tty.die(f"Error reading {self.config_file} when opening {self.root}")
|
||||
|
||||
def get(self, spec):
|
||||
def get(self, spec: "spack.spec.Spec") -> "spack.package_base.PackageBase":
|
||||
"""Returns the package associated with the supplied spec."""
|
||||
msg = "Repo.get can only be called on concrete specs"
|
||||
assert isinstance(spec, spack.spec.Spec) and spec.concrete, msg
|
||||
@@ -1049,16 +1094,13 @@ def get(self, spec):
|
||||
# pass these through as their error messages will be fine.
|
||||
raise
|
||||
except Exception as e:
|
||||
tty.debug(e)
|
||||
|
||||
# Make sure other errors in constructors hit the error
|
||||
# handler by wrapping them
|
||||
if spack.config.get("config:debug"):
|
||||
sys.excepthook(*sys.exc_info())
|
||||
raise FailedConstructorError(spec.fullname, *sys.exc_info())
|
||||
tty.debug(e)
|
||||
raise FailedConstructorError(spec.fullname, *sys.exc_info()) from e
|
||||
|
||||
@autospec
|
||||
def dump_provenance(self, spec, path):
|
||||
def dump_provenance(self, spec: "spack.spec.Spec", path: str) -> None:
|
||||
"""Dump provenance information for a spec to a particular path.
|
||||
|
||||
This dumps the package file and any associated patch files.
|
||||
@@ -1066,7 +1108,7 @@ def dump_provenance(self, spec, path):
|
||||
"""
|
||||
if spec.namespace and spec.namespace != self.namespace:
|
||||
raise UnknownPackageError(
|
||||
"Repository %s does not contain package %s." % (self.namespace, spec.fullname)
|
||||
f"Repository {self.namespace} does not contain package {spec.fullname}."
|
||||
)
|
||||
|
||||
package_path = self.filename_for_package_name(spec.name)
|
||||
@@ -1083,17 +1125,13 @@ def dump_provenance(self, spec, path):
|
||||
if os.path.exists(patch.path):
|
||||
fs.install(patch.path, path)
|
||||
else:
|
||||
tty.warn("Patch file did not exist: %s" % patch.path)
|
||||
warnings.warn(f"Patch file did not exist: {patch.path}")
|
||||
|
||||
# Install the package.py file itself.
|
||||
fs.install(self.filename_for_package_name(spec.name), path)
|
||||
|
||||
def purge(self):
|
||||
"""Clear entire package instance cache."""
|
||||
self._instances.clear()
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
def index(self) -> RepoIndex:
|
||||
"""Construct the index for this repo lazily."""
|
||||
if self._repo_index is None:
|
||||
self._repo_index = RepoIndex(self._pkg_checker, self.namespace, cache=self._cache)
|
||||
@@ -1103,42 +1141,40 @@ def index(self):
|
||||
return self._repo_index
|
||||
|
||||
@property
|
||||
def provider_index(self):
|
||||
def provider_index(self) -> spack.provider_index.ProviderIndex:
|
||||
"""A provider index with names *specific* to this repo."""
|
||||
return self.index["providers"]
|
||||
|
||||
@property
|
||||
def tag_index(self):
|
||||
def tag_index(self) -> spack.tag.TagIndex:
|
||||
"""Index of tags and which packages they're defined on."""
|
||||
return self.index["tags"]
|
||||
|
||||
@property
|
||||
def patch_index(self):
|
||||
def patch_index(self) -> spack.patch.PatchCache:
|
||||
"""Index of patches and packages they're defined on."""
|
||||
return self.index["patches"]
|
||||
|
||||
@autospec
|
||||
def providers_for(self, vpkg_spec):
|
||||
def providers_for(self, vpkg_spec: "spack.spec.Spec") -> List["spack.spec.Spec"]:
|
||||
providers = self.provider_index.providers_for(vpkg_spec)
|
||||
if not providers:
|
||||
raise UnknownPackageError(vpkg_spec.fullname)
|
||||
return providers
|
||||
|
||||
@autospec
|
||||
def extensions_for(self, extendee_spec):
|
||||
return [
|
||||
pkg_cls(spack.spec.Spec(pkg_cls.name))
|
||||
for pkg_cls in self.all_package_classes()
|
||||
if pkg_cls(spack.spec.Spec(pkg_cls.name)).extends(extendee_spec)
|
||||
]
|
||||
def extensions_for(
|
||||
self, extendee_spec: "spack.spec.Spec"
|
||||
) -> List["spack.package_base.PackageBase"]:
|
||||
result = [pkg_cls(spack.spec.Spec(pkg_cls.name)) for pkg_cls in self.all_package_classes()]
|
||||
return [x for x in result if x.extends(extendee_spec)]
|
||||
|
||||
def dirname_for_package_name(self, pkg_name):
|
||||
"""Get the directory name for a particular package. This is the
|
||||
directory that contains its package.py file."""
|
||||
def dirname_for_package_name(self, pkg_name: str) -> str:
|
||||
"""Given a package name, get the directory containing its package.py file."""
|
||||
_, unqualified_name = self.partition_package_name(pkg_name)
|
||||
return os.path.join(self.packages_path, unqualified_name)
|
||||
|
||||
def filename_for_package_name(self, pkg_name):
|
||||
def filename_for_package_name(self, pkg_name: str) -> str:
|
||||
"""Get the filename for the module we should load for a particular
|
||||
package. Packages for a Repo live in
|
||||
``$root/<package_name>/package.py``
|
||||
@@ -1151,23 +1187,23 @@ def filename_for_package_name(self, pkg_name):
|
||||
return os.path.join(pkg_dir, package_file_name)
|
||||
|
||||
@property
|
||||
def _pkg_checker(self):
|
||||
def _pkg_checker(self) -> FastPackageChecker:
|
||||
if self._fast_package_checker is None:
|
||||
self._fast_package_checker = FastPackageChecker(self.packages_path)
|
||||
return self._fast_package_checker
|
||||
|
||||
def all_package_names(self, include_virtuals=False):
|
||||
def all_package_names(self, include_virtuals: bool = False) -> List[str]:
|
||||
"""Returns a sorted list of all package names in the Repo."""
|
||||
names = sorted(self._pkg_checker.keys())
|
||||
if include_virtuals:
|
||||
return names
|
||||
return [x for x in names if not self.is_virtual(x)]
|
||||
|
||||
def package_path(self, name):
|
||||
def package_path(self, name: str) -> str:
|
||||
"""Get path to package.py file for this repo."""
|
||||
return os.path.join(self.packages_path, name, package_file_name)
|
||||
|
||||
def all_package_paths(self):
|
||||
def all_package_paths(self) -> Generator[str, None, None]:
|
||||
for name in self.all_package_names():
|
||||
yield self.package_path(name)
|
||||
|
||||
@@ -1176,7 +1212,7 @@ def packages_with_tags(self, *tags: str) -> Set[str]:
|
||||
v.intersection_update(*(self.tag_index[tag.lower()] for tag in tags))
|
||||
return v
|
||||
|
||||
def all_package_classes(self):
|
||||
def all_package_classes(self) -> Generator[Type["spack.package_base.PackageBase"], None, None]:
|
||||
"""Iterator over all package *classes* in the repository.
|
||||
|
||||
Use this with care, because loading packages is slow.
|
||||
@@ -1184,7 +1220,7 @@ def all_package_classes(self):
|
||||
for name in self.all_package_names():
|
||||
yield self.get_pkg_class(name)
|
||||
|
||||
def exists(self, pkg_name):
|
||||
def exists(self, pkg_name: str) -> bool:
|
||||
"""Whether a package with the supplied name exists."""
|
||||
if pkg_name is None:
|
||||
return False
|
||||
@@ -1201,28 +1237,22 @@ def last_mtime(self):
|
||||
"""Time a package file in this repo was last updated."""
|
||||
return self._pkg_checker.last_mtime()
|
||||
|
||||
def is_virtual(self, pkg_name):
|
||||
def is_virtual(self, pkg_name: str) -> bool:
|
||||
"""Return True if the package with this name is virtual, False otherwise.
|
||||
|
||||
This function use the provider index. If calling from a code block that
|
||||
is used to construct the provider index use the ``is_virtual_safe`` function.
|
||||
|
||||
Args:
|
||||
pkg_name (str): name of the package we want to check
|
||||
"""
|
||||
return pkg_name in self.provider_index
|
||||
|
||||
def is_virtual_safe(self, pkg_name):
|
||||
def is_virtual_safe(self, pkg_name: str) -> bool:
|
||||
"""Return True if the package with this name is virtual, False otherwise.
|
||||
|
||||
This function doesn't use the provider index.
|
||||
|
||||
Args:
|
||||
pkg_name (str): name of the package we want to check
|
||||
"""
|
||||
return not self.exists(pkg_name) or self.get_pkg_class(pkg_name).virtual
|
||||
|
||||
def get_pkg_class(self, pkg_name):
|
||||
def get_pkg_class(self, pkg_name: str) -> Type["spack.package_base.PackageBase"]:
|
||||
"""Get the class for the package out of its module.
|
||||
|
||||
First loads (or fetches from cache) a module for the
|
||||
@@ -1234,7 +1264,8 @@ def get_pkg_class(self, pkg_name):
|
||||
fullname = f"{self.full_namespace}.{pkg_name}"
|
||||
|
||||
try:
|
||||
module = importlib.import_module(fullname)
|
||||
with REPOS_FINDER.switch_repo(self._finder or self):
|
||||
module = importlib.import_module(fullname)
|
||||
except ImportError:
|
||||
raise UnknownPackageError(fullname)
|
||||
except Exception as e:
|
||||
@@ -1245,26 +1276,21 @@ def get_pkg_class(self, pkg_name):
|
||||
if not inspect.isclass(cls):
|
||||
tty.die(f"{pkg_name}.{class_name} is not a class")
|
||||
|
||||
new_cfg_settings = (
|
||||
spack.config.get("packages").get(pkg_name, {}).get("package_attributes", {})
|
||||
)
|
||||
|
||||
# Clear any prior changes to class attributes in case the class was loaded from the
|
||||
# same repo, but with different overrides
|
||||
overridden_attrs = getattr(cls, "overridden_attrs", {})
|
||||
attrs_exclusively_from_config = getattr(cls, "attrs_exclusively_from_config", [])
|
||||
# Clear any prior changes to class attributes in case the config has
|
||||
# since changed
|
||||
for key, val in overridden_attrs.items():
|
||||
setattr(cls, key, val)
|
||||
for key in attrs_exclusively_from_config:
|
||||
delattr(cls, key)
|
||||
|
||||
# Keep track of every class attribute that is overridden by the config:
|
||||
# if the config changes between calls to this method, we make sure to
|
||||
# restore the original config values (in case the new config no longer
|
||||
# sets attributes that it used to)
|
||||
# Keep track of every class attribute that is overridden: if different overrides
|
||||
# dictionaries are used on the same physical repo, we make sure to restore the original
|
||||
# config values
|
||||
new_overridden_attrs = {}
|
||||
new_attrs_exclusively_from_config = set()
|
||||
for key, val in new_cfg_settings.items():
|
||||
for key, val in self.overrides.get(pkg_name, {}).items():
|
||||
if hasattr(cls, key):
|
||||
new_overridden_attrs[key] = getattr(cls, key)
|
||||
else:
|
||||
@@ -1291,13 +1317,13 @@ def partition_package_name(self, pkg_name: str) -> Tuple[str, str]:
|
||||
|
||||
return namespace, pkg_name
|
||||
|
||||
def __str__(self):
|
||||
return "[Repo '%s' at '%s']" % (self.namespace, self.root)
|
||||
def __str__(self) -> str:
|
||||
return f"Repo '{self.namespace}' at {self.root}"
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
def __contains__(self, pkg_name):
|
||||
def __contains__(self, pkg_name: str) -> bool:
|
||||
return self.exists(pkg_name)
|
||||
|
||||
|
||||
@@ -1373,12 +1399,17 @@ def create_repo(root, namespace=None, subdir=packages_dir_name):
|
||||
return full_path, namespace
|
||||
|
||||
|
||||
def from_path(path: str) -> "Repo":
|
||||
"""Returns a repository from the path passed as input. Injects the global misc cache."""
|
||||
return Repo(path, cache=spack.caches.MISC_CACHE)
|
||||
|
||||
|
||||
def create_or_construct(path, namespace=None):
|
||||
"""Create a repository, or just return a Repo if it already exists."""
|
||||
if not os.path.exists(path):
|
||||
fs.mkdirp(path)
|
||||
create_repo(path, namespace)
|
||||
return Repo(path)
|
||||
return from_path(path)
|
||||
|
||||
|
||||
def _path(configuration=None):
|
||||
@@ -1396,7 +1427,17 @@ def create(configuration):
|
||||
repo_dirs = configuration.get("repos")
|
||||
if not repo_dirs:
|
||||
raise NoRepoConfiguredError("Spack configuration contains no package repositories.")
|
||||
return RepoPath(*repo_dirs)
|
||||
|
||||
overrides = {}
|
||||
for pkg_name, data in configuration.get("packages").items():
|
||||
if pkg_name == "all":
|
||||
continue
|
||||
value = data.get("package_attributes", {})
|
||||
if not value:
|
||||
continue
|
||||
overrides[pkg_name] = value
|
||||
|
||||
return RepoPath(*repo_dirs, cache=spack.caches.MISC_CACHE, overrides=overrides)
|
||||
|
||||
|
||||
#: Singleton repo path instance
|
||||
|
||||
@@ -115,8 +115,8 @@ def default_config(tmpdir, config_directory, monkeypatch, install_mockery_mutabl
|
||||
|
||||
cfg = spack.config.Configuration(
|
||||
*[
|
||||
spack.config.ConfigScope(name, str(mutable_dir))
|
||||
for name in ["site/%s" % platform.system().lower(), "site", "user"]
|
||||
spack.config.DirectoryConfigScope(name, str(mutable_dir))
|
||||
for name in [f"site/{platform.system().lower()}", "site", "user"]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import spack.cmd.pkg
|
||||
import spack.main
|
||||
import spack.repo
|
||||
import spack.util.file_cache
|
||||
|
||||
#: new fake package template
|
||||
pkg_template = """\
|
||||
@@ -34,13 +35,14 @@ def install(self, spec, prefix):
|
||||
|
||||
# Force all tests to use a git repository *in* the mock packages repo.
|
||||
@pytest.fixture(scope="module")
|
||||
def mock_pkg_git_repo(git, tmpdir_factory):
|
||||
def mock_pkg_git_repo(git, tmp_path_factory):
|
||||
"""Copy the builtin.mock repo and make a mutable git repo inside it."""
|
||||
tmproot = tmpdir_factory.mktemp("mock_pkg_git_repo")
|
||||
repo_path = tmproot.join("builtin.mock")
|
||||
root_dir = tmp_path_factory.mktemp("mock_pkg_git_repo")
|
||||
repo_dir = root_dir / "builtin.mock"
|
||||
shutil.copytree(spack.paths.mock_packages_path, str(repo_dir))
|
||||
|
||||
shutil.copytree(spack.paths.mock_packages_path, str(repo_path))
|
||||
mock_repo = spack.repo.RepoPath(str(repo_path))
|
||||
repo_cache = spack.util.file_cache.FileCache(str(root_dir / "cache"))
|
||||
mock_repo = spack.repo.RepoPath(str(repo_dir), cache=repo_cache)
|
||||
mock_repo_packages = mock_repo.repos[0].packages_path
|
||||
|
||||
with working_dir(mock_repo_packages):
|
||||
@@ -75,7 +77,7 @@ def mock_pkg_git_repo(git, tmpdir_factory):
|
||||
git("rm", "-rf", "pkg-c")
|
||||
git("-c", "commit.gpgsign=false", "commit", "-m", "change pkg-b, remove pkg-c, add pkg-d")
|
||||
|
||||
with spack.repo.use_repositories(str(repo_path)):
|
||||
with spack.repo.use_repositories(str(repo_dir)):
|
||||
yield mock_repo_packages
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ def flake8_package(tmpdir):
|
||||
change to the ``flake8`` mock package, yields the filename, then undoes the
|
||||
change on cleanup.
|
||||
"""
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path)
|
||||
repo = spack.repo.from_path(spack.paths.mock_packages_path)
|
||||
filename = repo.filename_for_package_name("flake8")
|
||||
rel_path = os.path.dirname(os.path.relpath(filename, spack.paths.prefix))
|
||||
tmp = tmpdir / rel_path / "flake8-ci-package.py"
|
||||
@@ -54,7 +54,7 @@ def flake8_package(tmpdir):
|
||||
@pytest.fixture
|
||||
def flake8_package_with_errors(scope="function"):
|
||||
"""A flake8 package with errors."""
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path)
|
||||
repo = spack.repo.from_path(spack.paths.mock_packages_path)
|
||||
filename = repo.filename_for_package_name("flake8")
|
||||
tmp = filename + ".tmp"
|
||||
|
||||
@@ -130,7 +130,7 @@ def test_changed_files_all_files():
|
||||
assert os.path.join(spack.paths.module_path, "spec.py") in files
|
||||
|
||||
# a mock package
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path)
|
||||
repo = spack.repo.from_path(spack.paths.mock_packages_path)
|
||||
filename = repo.filename_for_package_name("flake8")
|
||||
assert filename in files
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import spack.platforms
|
||||
import spack.repo
|
||||
import spack.solver.asp
|
||||
import spack.util.file_cache
|
||||
import spack.util.libc
|
||||
import spack.variant as vt
|
||||
from spack.concretize import find_spec
|
||||
@@ -168,19 +169,18 @@ def reverser(pkg_name):
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def repo_with_changing_recipe(tmpdir_factory, mutable_mock_repo):
|
||||
def repo_with_changing_recipe(tmp_path_factory, mutable_mock_repo):
|
||||
repo_namespace = "changing"
|
||||
repo_dir = tmpdir_factory.mktemp(repo_namespace)
|
||||
repo_dir = tmp_path_factory.mktemp(repo_namespace)
|
||||
|
||||
repo_dir.join("repo.yaml").write(
|
||||
(repo_dir / "repo.yaml").write_text(
|
||||
"""
|
||||
repo:
|
||||
namespace: changing
|
||||
""",
|
||||
ensure=True,
|
||||
"""
|
||||
)
|
||||
|
||||
packages_dir = repo_dir.ensure("packages", dir=True)
|
||||
packages_dir = repo_dir / "packages"
|
||||
root_pkg_str = """
|
||||
class Root(Package):
|
||||
homepage = "http://www.example.com"
|
||||
@@ -191,7 +191,9 @@ class Root(Package):
|
||||
|
||||
conflicts("^changing~foo")
|
||||
"""
|
||||
packages_dir.join("root", "package.py").write(root_pkg_str, ensure=True)
|
||||
package_py = packages_dir / "root" / "package.py"
|
||||
package_py.parent.mkdir(parents=True)
|
||||
package_py.write_text(root_pkg_str)
|
||||
|
||||
changing_template = """
|
||||
class Changing(Package):
|
||||
@@ -225,7 +227,9 @@ class _ChangingPackage:
|
||||
|
||||
def __init__(self, repo_directory):
|
||||
self.repo_dir = repo_directory
|
||||
self.repo = spack.repo.Repo(str(repo_directory))
|
||||
cache_dir = tmp_path_factory.mktemp("cache")
|
||||
self.repo_cache = spack.util.file_cache.FileCache(str(cache_dir))
|
||||
self.repo = spack.repo.Repo(str(repo_directory), cache=self.repo_cache)
|
||||
|
||||
def change(self, changes=None):
|
||||
changes = changes or {}
|
||||
@@ -246,10 +250,12 @@ def change(self, changes=None):
|
||||
# Change the recipe
|
||||
t = jinja2.Template(changing_template)
|
||||
changing_pkg_str = t.render(**context)
|
||||
packages_dir.join("changing", "package.py").write(changing_pkg_str, ensure=True)
|
||||
package_py = packages_dir / "changing" / "package.py"
|
||||
package_py.parent.mkdir(parents=True, exist_ok=True)
|
||||
package_py.write_text(changing_pkg_str)
|
||||
|
||||
# Re-add the repository
|
||||
self.repo = spack.repo.Repo(str(self.repo_dir))
|
||||
self.repo = spack.repo.Repo(str(self.repo_dir), cache=self.repo_cache)
|
||||
repository.put_first(self.repo)
|
||||
|
||||
_changing_pkg = _ChangingPackage(repo_dir)
|
||||
|
||||
@@ -161,21 +161,24 @@ def test_preferred_providers(self):
|
||||
spec = concretize("mpileaks")
|
||||
assert "zmpi" in spec
|
||||
|
||||
def test_config_set_pkg_property_url(self, mutable_mock_repo):
|
||||
@pytest.mark.parametrize(
|
||||
"update,expected",
|
||||
[
|
||||
(
|
||||
{"url": "http://www.somewhereelse.com/mpileaks-1.0.tar.gz"},
|
||||
"http://www.somewhereelse.com/mpileaks-2.3.tar.gz",
|
||||
),
|
||||
({}, "http://www.llnl.gov/mpileaks-2.3.tar.gz"),
|
||||
],
|
||||
)
|
||||
def test_config_set_pkg_property_url(self, update, expected, mock_repo_path):
|
||||
"""Test setting an existing attribute in the package class"""
|
||||
update_packages(
|
||||
"mpileaks",
|
||||
"package_attributes",
|
||||
{"url": "http://www.somewhereelse.com/mpileaks-1.0.tar.gz"},
|
||||
)
|
||||
spec = concretize("mpileaks")
|
||||
assert spec.package.fetcher.url == "http://www.somewhereelse.com/mpileaks-2.3.tar.gz"
|
||||
update_packages("mpileaks", "package_attributes", update)
|
||||
with spack.repo.use_repositories(mock_repo_path):
|
||||
spec = concretize("mpileaks")
|
||||
assert spec.package.fetcher.url == expected
|
||||
|
||||
update_packages("mpileaks", "package_attributes", {})
|
||||
spec = concretize("mpileaks")
|
||||
assert spec.package.fetcher.url == "http://www.llnl.gov/mpileaks-2.3.tar.gz"
|
||||
|
||||
def test_config_set_pkg_property_new(self, mutable_mock_repo):
|
||||
def test_config_set_pkg_property_new(self, mock_repo_path):
|
||||
"""Test that you can set arbitrary attributes on the Package class"""
|
||||
conf = syaml.load_config(
|
||||
"""\
|
||||
@@ -194,19 +197,20 @@ def test_config_set_pkg_property_new(self, mutable_mock_repo):
|
||||
"""
|
||||
)
|
||||
spack.config.set("packages", conf, scope="concretize")
|
||||
|
||||
spec = concretize("mpileaks")
|
||||
assert spec.package.v1 == 1
|
||||
assert spec.package.v2 is True
|
||||
assert spec.package.v3 == "yesterday"
|
||||
assert spec.package.v4 == "true"
|
||||
assert dict(spec.package.v5) == {"x": 1, "y": 2}
|
||||
assert list(spec.package.v6) == [1, 2]
|
||||
with spack.repo.use_repositories(mock_repo_path):
|
||||
spec = concretize("mpileaks")
|
||||
assert spec.package.v1 == 1
|
||||
assert spec.package.v2 is True
|
||||
assert spec.package.v3 == "yesterday"
|
||||
assert spec.package.v4 == "true"
|
||||
assert dict(spec.package.v5) == {"x": 1, "y": 2}
|
||||
assert list(spec.package.v6) == [1, 2]
|
||||
|
||||
update_packages("mpileaks", "package_attributes", {})
|
||||
spec = concretize("mpileaks")
|
||||
with pytest.raises(AttributeError):
|
||||
spec.package.v1
|
||||
with spack.repo.use_repositories(mock_repo_path):
|
||||
spec = concretize("mpileaks")
|
||||
with pytest.raises(AttributeError):
|
||||
spec.package.v1
|
||||
|
||||
def test_preferred(self):
|
||||
""" "Test packages with some version marked as preferred=True"""
|
||||
|
||||
@@ -774,7 +774,7 @@ def test_keys_are_ordered(configuration_dir):
|
||||
"./",
|
||||
)
|
||||
|
||||
config_scope = spack.config.ConfigScope("modules", configuration_dir.join("site"))
|
||||
config_scope = spack.config.DirectoryConfigScope("modules", configuration_dir.join("site"))
|
||||
|
||||
data = config_scope.get_section("modules")
|
||||
|
||||
@@ -956,7 +956,7 @@ def test_immutable_scope(tmpdir):
|
||||
root: dummy_tree_value
|
||||
"""
|
||||
)
|
||||
scope = spack.config.ImmutableConfigScope("test", str(tmpdir))
|
||||
scope = spack.config.DirectoryConfigScope("test", str(tmpdir), writable=False)
|
||||
|
||||
data = scope.get_section("config")
|
||||
assert data["config"]["install_tree"] == {"root": "dummy_tree_value"}
|
||||
@@ -966,7 +966,9 @@ def test_immutable_scope(tmpdir):
|
||||
|
||||
|
||||
def test_single_file_scope(config, env_yaml):
|
||||
scope = spack.config.SingleFileScope("env", env_yaml, spack.schema.env.schema, ["spack"])
|
||||
scope = spack.config.SingleFileScope(
|
||||
"env", env_yaml, spack.schema.env.schema, yaml_path=["spack"]
|
||||
)
|
||||
|
||||
with spack.config.override(scope):
|
||||
# from the single-file config
|
||||
@@ -1002,7 +1004,9 @@ def test_single_file_scope_section_override(tmpdir, config):
|
||||
"""
|
||||
)
|
||||
|
||||
scope = spack.config.SingleFileScope("env", env_yaml, spack.schema.env.schema, ["spack"])
|
||||
scope = spack.config.SingleFileScope(
|
||||
"env", env_yaml, spack.schema.env.schema, yaml_path=["spack"]
|
||||
)
|
||||
|
||||
with spack.config.override(scope):
|
||||
# from the single-file config
|
||||
@@ -1018,7 +1022,7 @@ def test_single_file_scope_section_override(tmpdir, config):
|
||||
def test_write_empty_single_file_scope(tmpdir):
|
||||
env_schema = spack.schema.env.schema
|
||||
scope = spack.config.SingleFileScope(
|
||||
"test", str(tmpdir.ensure("config.yaml")), env_schema, ["spack"]
|
||||
"test", str(tmpdir.ensure("config.yaml")), env_schema, yaml_path=["spack"]
|
||||
)
|
||||
scope._write_section("config")
|
||||
# confirm we can write empty config
|
||||
@@ -1217,7 +1221,9 @@ def test_license_dir_config(mutable_config, mock_packages):
|
||||
|
||||
@pytest.mark.regression("22547")
|
||||
def test_single_file_scope_cache_clearing(env_yaml):
|
||||
scope = spack.config.SingleFileScope("env", env_yaml, spack.schema.env.schema, ["spack"])
|
||||
scope = spack.config.SingleFileScope(
|
||||
"env", env_yaml, spack.schema.env.schema, yaml_path=["spack"]
|
||||
)
|
||||
# Check that we can retrieve data from the single file scope
|
||||
before = scope.get_section("config")
|
||||
assert before
|
||||
|
||||
@@ -561,7 +561,7 @@ def _use_test_platform(test_platform):
|
||||
#
|
||||
@pytest.fixture(scope="session")
|
||||
def mock_repo_path():
|
||||
yield spack.repo.Repo(spack.paths.mock_packages_path)
|
||||
yield spack.repo.from_path(spack.paths.mock_packages_path)
|
||||
|
||||
|
||||
def _pkg_install_fn(pkg, spec, prefix):
|
||||
@@ -588,7 +588,7 @@ def mock_packages(mock_repo_path, mock_pkg_install, request):
|
||||
def mutable_mock_repo(mock_repo_path, request):
|
||||
"""Function-scoped mock packages, for tests that need to modify them."""
|
||||
ensure_configuration_fixture_run_before(request)
|
||||
mock_repo = spack.repo.Repo(spack.paths.mock_packages_path)
|
||||
mock_repo = spack.repo.from_path(spack.paths.mock_packages_path)
|
||||
with spack.repo.use_repositories(mock_repo) as mock_repo_path:
|
||||
yield mock_repo_path
|
||||
|
||||
@@ -719,9 +719,9 @@ def _create_mock_configuration_scopes(configuration_dir):
|
||||
"""Create the configuration scopes used in `config` and `mutable_config`."""
|
||||
return [
|
||||
spack.config.InternalConfigScope("_builtin", spack.config.CONFIG_DEFAULTS),
|
||||
spack.config.ConfigScope("site", str(configuration_dir.join("site"))),
|
||||
spack.config.ConfigScope("system", str(configuration_dir.join("system"))),
|
||||
spack.config.ConfigScope("user", str(configuration_dir.join("user"))),
|
||||
spack.config.DirectoryConfigScope("site", str(configuration_dir.join("site"))),
|
||||
spack.config.DirectoryConfigScope("system", str(configuration_dir.join("system"))),
|
||||
spack.config.DirectoryConfigScope("user", str(configuration_dir.join("user"))),
|
||||
spack.config.InternalConfigScope("command_line"),
|
||||
]
|
||||
|
||||
@@ -755,7 +755,7 @@ def mutable_empty_config(tmpdir_factory, configuration_dir):
|
||||
"""Empty configuration that can be modified by the tests."""
|
||||
mutable_dir = tmpdir_factory.mktemp("mutable_config").join("tmp")
|
||||
scopes = [
|
||||
spack.config.ConfigScope(name, str(mutable_dir.join(name)))
|
||||
spack.config.DirectoryConfigScope(name, str(mutable_dir.join(name)))
|
||||
for name in ["site", "system", "user"]
|
||||
]
|
||||
|
||||
@@ -790,7 +790,7 @@ def concretize_scope(mutable_config, tmpdir):
|
||||
"""Adds a scope for concretization preferences"""
|
||||
tmpdir.ensure_dir("concretize")
|
||||
mutable_config.push_scope(
|
||||
spack.config.ConfigScope("concretize", str(tmpdir.join("concretize")))
|
||||
spack.config.DirectoryConfigScope("concretize", str(tmpdir.join("concretize")))
|
||||
)
|
||||
|
||||
yield str(tmpdir.join("concretize"))
|
||||
@@ -802,10 +802,10 @@ def concretize_scope(mutable_config, tmpdir):
|
||||
@pytest.fixture
|
||||
def no_compilers_yaml(mutable_config):
|
||||
"""Creates a temporary configuration without compilers.yaml"""
|
||||
for scope, local_config in mutable_config.scopes.items():
|
||||
if not local_config.path: # skip internal scopes
|
||||
for local_config in mutable_config.scopes.values():
|
||||
if not isinstance(local_config, spack.config.DirectoryConfigScope):
|
||||
continue
|
||||
compilers_yaml = os.path.join(local_config.path, "compilers.yaml")
|
||||
compilers_yaml = local_config.get_section_filename("compilers")
|
||||
if os.path.exists(compilers_yaml):
|
||||
os.remove(compilers_yaml)
|
||||
return mutable_config
|
||||
@@ -814,7 +814,9 @@ def no_compilers_yaml(mutable_config):
|
||||
@pytest.fixture()
|
||||
def mock_low_high_config(tmpdir):
|
||||
"""Mocks two configuration scopes: 'low' and 'high'."""
|
||||
scopes = [spack.config.ConfigScope(name, str(tmpdir.join(name))) for name in ["low", "high"]]
|
||||
scopes = [
|
||||
spack.config.DirectoryConfigScope(name, str(tmpdir.join(name))) for name in ["low", "high"]
|
||||
]
|
||||
|
||||
with spack.config.use_configuration(*scopes) as config:
|
||||
yield config
|
||||
@@ -2019,7 +2021,8 @@ def create_test_repo(tmpdir, pkg_name_content_tuples):
|
||||
with open(str(pkg_file), "w") as f:
|
||||
f.write(pkg_str)
|
||||
|
||||
return spack.repo.Repo(repo_path)
|
||||
repo_cache = spack.util.file_cache.FileCache(str(tmpdir.join("cache")))
|
||||
return spack.repo.Repo(repo_path, cache=repo_cache)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@@ -2061,3 +2064,9 @@ def _c_compiler_always_exists():
|
||||
spack.solver.asp.c_compiler_runs = _true
|
||||
yield
|
||||
spack.solver.asp.c_compiler_runs = fn
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def mock_test_cache(tmp_path_factory):
|
||||
cache_dir = tmp_path_factory.mktemp("cache")
|
||||
return spack.util.file_cache.FileCache(str(cache_dir))
|
||||
|
||||
@@ -146,7 +146,7 @@ def test_read_and_write_spec(temporary_store, config, mock_packages):
|
||||
assert not os.path.exists(install_dir)
|
||||
|
||||
|
||||
def test_handle_unknown_package(temporary_store, config, mock_packages):
|
||||
def test_handle_unknown_package(temporary_store, config, mock_packages, tmp_path):
|
||||
"""This test ensures that spack can at least do *some*
|
||||
operations with packages that are installed but that it
|
||||
does not know about. This is actually not such an uncommon
|
||||
@@ -158,7 +158,9 @@ def test_handle_unknown_package(temporary_store, config, mock_packages):
|
||||
or query them again if the package goes away.
|
||||
"""
|
||||
layout = temporary_store.layout
|
||||
mock_db = spack.repo.RepoPath(spack.paths.mock_packages_path)
|
||||
|
||||
repo_cache = spack.util.file_cache.FileCache(str(tmp_path / "cache"))
|
||||
mock_db = spack.repo.RepoPath(spack.paths.mock_packages_path, cache=repo_cache)
|
||||
|
||||
not_in_mock = set.difference(
|
||||
set(spack.repo.all_package_names()), set(mock_db.all_package_names())
|
||||
|
||||
@@ -32,12 +32,12 @@ def test_package_name(self):
|
||||
assert pkg_cls.name == "mpich"
|
||||
|
||||
def test_package_filename(self):
|
||||
repo = spack.repo.Repo(mock_packages_path)
|
||||
repo = spack.repo.from_path(mock_packages_path)
|
||||
filename = repo.filename_for_package_name("mpich")
|
||||
assert filename == os.path.join(mock_packages_path, "packages", "mpich", "package.py")
|
||||
|
||||
def test_nonexisting_package_filename(self):
|
||||
repo = spack.repo.Repo(mock_packages_path)
|
||||
repo = spack.repo.from_path(mock_packages_path)
|
||||
filename = repo.filename_for_package_name("some-nonexisting-package")
|
||||
assert filename == os.path.join(
|
||||
mock_packages_path, "packages", "some-nonexisting-package", "package.py"
|
||||
|
||||
@@ -12,21 +12,28 @@
|
||||
|
||||
|
||||
@pytest.fixture(params=["packages", "", "foo"])
|
||||
def extra_repo(tmpdir_factory, request):
|
||||
def extra_repo(tmp_path_factory, request):
|
||||
repo_namespace = "extra_test_repo"
|
||||
repo_dir = tmpdir_factory.mktemp(repo_namespace)
|
||||
repo_dir.ensure(request.param, dir=True)
|
||||
|
||||
with open(str(repo_dir.join("repo.yaml")), "w") as f:
|
||||
f.write(
|
||||
repo_dir = tmp_path_factory.mktemp(repo_namespace)
|
||||
cache_dir = tmp_path_factory.mktemp("cache")
|
||||
(repo_dir / request.param).mkdir(parents=True, exist_ok=True)
|
||||
if request.param == "packages":
|
||||
(repo_dir / "repo.yaml").write_text(
|
||||
"""
|
||||
repo:
|
||||
namespace: extra_test_repo
|
||||
"""
|
||||
)
|
||||
if request.param != "packages":
|
||||
f.write(f" subdirectory: '{request.param}'")
|
||||
return (spack.repo.Repo(str(repo_dir)), request.param)
|
||||
else:
|
||||
(repo_dir / "repo.yaml").write_text(
|
||||
f"""
|
||||
repo:
|
||||
namespace: extra_test_repo
|
||||
subdirectory: '{request.param}'
|
||||
"""
|
||||
)
|
||||
repo_cache = spack.util.file_cache.FileCache(str(cache_dir))
|
||||
return spack.repo.Repo(str(repo_dir), cache=repo_cache), request.param
|
||||
|
||||
|
||||
def test_repo_getpkg(mutable_mock_repo):
|
||||
@@ -177,8 +184,11 @@ def test_repo_dump_virtuals(tmpdir, mutable_mock_repo, mock_packages, ensure_deb
|
||||
([spack.paths.mock_packages_path, spack.paths.packages_path], ["builtin.mock", "builtin"]),
|
||||
],
|
||||
)
|
||||
def test_repository_construction_doesnt_use_globals(nullify_globals, repo_paths, namespaces):
|
||||
repo_path = spack.repo.RepoPath(*repo_paths)
|
||||
def test_repository_construction_doesnt_use_globals(
|
||||
nullify_globals, tmp_path, repo_paths, namespaces
|
||||
):
|
||||
repo_cache = spack.util.file_cache.FileCache(str(tmp_path / "cache"))
|
||||
repo_path = spack.repo.RepoPath(*repo_paths, cache=repo_cache)
|
||||
assert len(repo_path.repos) == len(namespaces)
|
||||
assert [x.namespace for x in repo_path.repos] == namespaces
|
||||
|
||||
@@ -188,8 +198,84 @@ def test_path_computation_with_names(method_name, mock_repo_path):
|
||||
"""Tests that repositories can compute the correct paths when using both fully qualified
|
||||
names and unqualified names.
|
||||
"""
|
||||
repo_path = spack.repo.RepoPath(mock_repo_path)
|
||||
repo_path = spack.repo.RepoPath(mock_repo_path, cache=None)
|
||||
method = getattr(repo_path, method_name)
|
||||
unqualified = method("mpileaks")
|
||||
qualified = method("builtin.mock.mpileaks")
|
||||
assert qualified == unqualified
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("nullify_globals")
|
||||
class TestRepo:
|
||||
"""Test that the Repo class work correctly, and does not depend on globals,
|
||||
except the REPOS_FINDER.
|
||||
"""
|
||||
|
||||
def test_creation(self, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
assert repo.config_file.endswith("repo.yaml")
|
||||
assert repo.namespace == "builtin.mock"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,expected", [("mpi", True), ("mpich", False), ("mpileaks", False)]
|
||||
)
|
||||
def test_is_virtual(self, name, expected, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
assert repo.is_virtual(name) is expected
|
||||
assert repo.is_virtual_safe(name) is expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"module_name,expected",
|
||||
[
|
||||
("dla_future", "dla-future"),
|
||||
("num7zip", "7zip"),
|
||||
# If no package is there, None is returned
|
||||
("unknown", None),
|
||||
],
|
||||
)
|
||||
def test_real_name(self, module_name, expected, mock_test_cache):
|
||||
"""Test that we can correctly compute the 'real' name of a package, from the one
|
||||
used to import the Python module.
|
||||
"""
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
assert repo.real_name(module_name) == expected
|
||||
|
||||
@pytest.mark.parametrize("name", ["mpileaks", "7zip", "dla-future"])
|
||||
def test_get(self, name, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
mock_spec = spack.spec.Spec(name)
|
||||
mock_spec._mark_concrete()
|
||||
pkg = repo.get(mock_spec)
|
||||
assert pkg.__class__ == repo.get_pkg_class(name)
|
||||
|
||||
@pytest.mark.parametrize("virtual_name,expected", [("mpi", ["mpich", "zmpi"])])
|
||||
def test_providers(self, virtual_name, expected, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
provider_names = {x.name for x in repo.providers_for(virtual_name)}
|
||||
assert provider_names.issuperset(expected)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"extended,expected",
|
||||
[("python", ["py-extension1", "python-venv"]), ("perl", ["perl-extension"])],
|
||||
)
|
||||
def test_extensions(self, extended, expected, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
provider_names = {x.name for x in repo.extensions_for(extended)}
|
||||
assert provider_names.issuperset(expected)
|
||||
|
||||
def test_all_package_names(self, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
all_names = repo.all_package_names(include_virtuals=True)
|
||||
real_names = repo.all_package_names(include_virtuals=False)
|
||||
assert set(all_names).issuperset(real_names)
|
||||
for name in set(all_names) - set(real_names):
|
||||
assert repo.is_virtual(name)
|
||||
assert repo.is_virtual_safe(name)
|
||||
|
||||
def test_packages_with_tags(self, mock_test_cache):
|
||||
repo = spack.repo.Repo(spack.paths.mock_packages_path, cache=mock_test_cache)
|
||||
r1 = repo.packages_with_tags("tag1")
|
||||
r2 = repo.packages_with_tags("tag1", "tag2")
|
||||
assert "mpich" in r1 and "mpich" in r2
|
||||
assert "mpich2" in r1 and "mpich2" not in r2
|
||||
assert set(r2).issubset(r1)
|
||||
|
||||
14
var/spack/repos/builtin.mock/packages/7zip/package.py
Normal file
14
var/spack/repos/builtin.mock/packages/7zip/package.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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)
|
||||
from spack.package import *
|
||||
|
||||
|
||||
class _7zip(AutotoolsPackage):
|
||||
"""Simple package with a name starting with a digit"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/a-1.0.tar.gz"
|
||||
|
||||
version("1.0", md5="0123456789abcdef0123456789abcdef")
|
||||
@@ -12,7 +12,7 @@ class ActsDd4hep(CMakePackage):
|
||||
homepage = "https://github.com/acts-project/acts-dd4hep"
|
||||
url = "https://github.com/acts-project/acts-dd4hep/archive/refs/tags/v1.0.0.tar.gz"
|
||||
|
||||
maintainers("HadrienG2", "wdconinc")
|
||||
maintainers("wdconinc")
|
||||
|
||||
version("1.0.1", sha256="e40f34ebc30b3c33a6802c9d94136e65072d8dcee0b7db57a645f08a64ea5334")
|
||||
version("1.0.0", sha256="991f996944c88efa837880f919239e50d12c5c9361e220bc9422438dd608308c")
|
||||
|
||||
@@ -33,7 +33,7 @@ class Acts(CMakePackage, CudaPackage):
|
||||
homepage = "https://acts.web.cern.ch/ACTS/"
|
||||
git = "https://github.com/acts-project/acts.git"
|
||||
list_url = "https://github.com/acts-project/acts/releases/"
|
||||
maintainers("HadrienG2")
|
||||
maintainers("wdconinc")
|
||||
|
||||
tags = ["hep"]
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class Actsvg(CMakePackage):
|
||||
list_url = "https://github.com/acts-project/actsvg/tags"
|
||||
git = "https://github.com/acts-project/actsvg.git"
|
||||
|
||||
maintainers("HadrienG2", "wdconinc", "stephenswat")
|
||||
maintainers("wdconinc", "stephenswat")
|
||||
|
||||
license("MPL-2.0")
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class Autodiff(CMakePackage):
|
||||
list_url = "https://github.com/autodiff/autodiff/tags"
|
||||
git = "https://github.com/autodiff/autodiff.git"
|
||||
|
||||
maintainers("wdconinc", "HadrienG2")
|
||||
maintainers("wdconinc")
|
||||
|
||||
license("MIT")
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class Gmsh(CMakePackage):
|
||||
|
||||
version("master", branch="master")
|
||||
version("4.13.1", sha256="77972145f431726026d50596a6a44fb3c1c95c21255218d66955806b86edbe8d")
|
||||
version("4.13.0", sha256="c85f056ee549a433e814a61c385c97952bbfe514b442b999f6149fffb1e54f64.")
|
||||
version("4.13.0", sha256="c85f056ee549a433e814a61c385c97952bbfe514b442b999f6149fffb1e54f64")
|
||||
version("4.12.2", sha256="13e09d9ca8102e5c40171d6ee150c668742b98c3a6ca57f837f7b64e1e2af48f")
|
||||
version("4.12.0", sha256="2a6007872ba85abd9901914826f6986a2437ab7104f564ccefa1b7a3de742c17")
|
||||
version("4.11.1", sha256="c5fe1b7cbd403888a814929f2fd0f5d69e27600222a18c786db5b76e8005b365")
|
||||
|
||||
@@ -11,17 +11,30 @@ class PyFlashAttn(PythonPackage):
|
||||
This package provides the official implementation of FlashAttention.
|
||||
"""
|
||||
|
||||
pypi = "flash-attn/flash_attn-2.5.4.tar.gz"
|
||||
homepage = "https://github.com/Dao-AILab/flash-attention.git"
|
||||
pypi = "flash-attn/flash_attn-0.0.0.tar.gz"
|
||||
git = "https://github.com/Dao-AILab/flash-attention.git"
|
||||
|
||||
maintainers("aurianer")
|
||||
|
||||
license("BSD")
|
||||
|
||||
version("main", branch="main")
|
||||
version(
|
||||
"2.5.9.post1", sha256="a92db1683a5b141a0f4371d251ae9f73e9aef629b3a58a50d0ef430266c68782"
|
||||
)
|
||||
version("2.5.8", sha256="2e5b2bcff6d5cff40d494af91ecd1eb3c5b4520a6ce7a0a8b1f9c1ed129fb402")
|
||||
version("2.5.7", sha256="7c079aef4e77c4e9a71a3cd88662362e0fe82f658db0b2dbff6f279de2a387a8")
|
||||
version("2.5.6", sha256="d25801aa060877cad997939bd7130faf620fdbeda947c3ffde5865906d430c36")
|
||||
version("2.5.5", sha256="751cee17711d006fe7341cdd78584af86a6239afcfe43b9ed11c84db93126267")
|
||||
version("2.5.4", sha256="d83bb427b517b07e9db655f6e5166eb2607dccf4d6ca3229e3a3528c206b0175")
|
||||
version("2.4.2", sha256="eb822a8c4219b610e9d734cbc8cd9ee4547f27433815a2b90dc1462766feefc1")
|
||||
|
||||
depends_on("py-setuptools", type="build")
|
||||
with default_args(type="build"):
|
||||
depends_on("py-ninja")
|
||||
depends_on("py-packaging")
|
||||
depends_on("py-psutil")
|
||||
depends_on("py-setuptools")
|
||||
|
||||
with default_args(type=("build", "run")):
|
||||
depends_on("py-torch+cuda")
|
||||
@@ -32,6 +45,4 @@ class PyFlashAttn(PythonPackage):
|
||||
with default_args(type=("build", "link", "run")):
|
||||
depends_on("py-pybind11")
|
||||
|
||||
depends_on("py-psutil", type="build")
|
||||
|
||||
depends_on("python@3.7:", type=("build", "run"))
|
||||
|
||||
@@ -12,7 +12,9 @@ class PySafetensors(PythonPackage):
|
||||
homepage = "https://github.com/huggingface/safetensors"
|
||||
pypi = "safetensors/safetensors-0.3.1.tar.gz"
|
||||
|
||||
version("0.4.3", sha256="2f85fc50c4e07a21e95c24e07460fe6f7e2859d0ce88092838352b798ce711c2")
|
||||
version("0.3.1", sha256="571da56ff8d0bec8ae54923b621cda98d36dcef10feb36fd492c4d0c2cd0e869")
|
||||
|
||||
depends_on("py-setuptools", type="build")
|
||||
depends_on("py-setuptools-rust", type="build")
|
||||
depends_on("py-maturin", type="build", when="@0.4.3")
|
||||
|
||||
@@ -13,6 +13,7 @@ class PyTokenizers(PythonPackage):
|
||||
homepage = "https://github.com/huggingface/tokenizers"
|
||||
pypi = "tokenizers/tokenizers-0.6.0.tar.gz"
|
||||
|
||||
version("0.19.1", sha256="ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3")
|
||||
version("0.15.0", sha256="10c7e6e7b4cabd757da59e93f5f8d1126291d16f8b54f28510825ef56a3e5d0e")
|
||||
version("0.13.3", sha256="2e546dbb68b623008a5442353137fbb0123d311a6d7ba52f2667c8862a75af2e")
|
||||
version("0.13.1", sha256="3333d1cee5c8f47c96362ea0abc1f81c77c9b92c6c3d11cbf1d01985f0d5cf1d")
|
||||
|
||||
@@ -18,33 +18,42 @@ class PyTransformers(PythonPackage):
|
||||
|
||||
license("Apache-2.0")
|
||||
|
||||
version("4.42.3", sha256="7539873ff45809145265cbc94ea4619d2713c41ceaa277b692d8b0be3430f7eb")
|
||||
version("4.38.1", sha256="86dc84ccbe36123647e84cbd50fc31618c109a41e6be92514b064ab55bf1304c")
|
||||
version("4.35.2", sha256="2d125e197d77b0cdb6c9201df9fa7e2101493272e448b9fba9341c695bee2f52")
|
||||
version("4.31.0", sha256="4302fba920a1c24d3a429a29efff6a63eac03f3f3cf55b55927fc795d01cb273")
|
||||
version("4.24.0", sha256="486f353a8e594002e48be0e2aba723d96eda839e63bfe274702a4b5eda85559b")
|
||||
version("4.6.1", sha256="83dbff763b7e7dc57cbef1a6b849655d4fcab6bffdd955c5e8bea12a4f76dc10")
|
||||
version("2.8.0", sha256="b9f29cdfd39c28f29e0806c321270dea337d6174a7aa60daf9625bf83dbb12ee")
|
||||
|
||||
depends_on("py-setuptools", type="build")
|
||||
depends_on("py-filelock", type=("build", "run"))
|
||||
depends_on("py-huggingface-hub@0.16.4:0", when="@4.34:", type=("build", "run"))
|
||||
depends_on("py-huggingface-hub@0.14.1:0", when="@4.26:", type=("build", "run"))
|
||||
depends_on("py-huggingface-hub@0.10:0", when="@4.24:", type=("build", "run"))
|
||||
depends_on("py-huggingface-hub@0.0.8", when="@4.6.1", type=("build", "run"))
|
||||
depends_on("py-numpy@1.17:", when="@4.6:", type=("build", "run"))
|
||||
depends_on("py-numpy", type=("build", "run"))
|
||||
depends_on("py-packaging@20:", when="@4.24:", type=("build", "run"))
|
||||
depends_on("py-packaging", when="@4.6.1", type=("build", "run"))
|
||||
depends_on("py-pyyaml@5.1:", when="@4.24:", type=("build", "run"))
|
||||
depends_on("py-regex@:2019.12.16,2019.12.18:", type=("build", "run"))
|
||||
depends_on("py-requests", type=("build", "run"))
|
||||
depends_on("py-safetensors@0.3.1:", when="@4.31:", type=("build", "run"))
|
||||
depends_on("py-tokenizers@0.14:0.18", when="@4.35:", type=("build", "run"))
|
||||
depends_on("py-tokenizers@0.11.1:0.11.2,0.11.4:0.13", when="@4.24:4.33", type=("build", "run"))
|
||||
depends_on("py-tokenizers@0.10.1:0.10", when="@4.6.1", type=("build", "run"))
|
||||
depends_on("py-tokenizers@0.5.2", when="@2.8.0", type=("build", "run"))
|
||||
depends_on("py-tqdm@4.27:", type=("build", "run"))
|
||||
with default_args(type="build"):
|
||||
depends_on("py-setuptools")
|
||||
|
||||
# Historical requirements
|
||||
depends_on("py-sacremoses", when="@:4.6", type=("build", "run"))
|
||||
depends_on("py-boto3", when="@2.8.0", type=("build", "run"))
|
||||
depends_on("py-sentencepiece", when="@2.8.0", type=("build", "run"))
|
||||
with default_args(type=("build", "run")):
|
||||
depends_on("py-filelock")
|
||||
depends_on("py-huggingface-hub@0.23.2:", when="@4.42.3:")
|
||||
depends_on("py-huggingface-hub@0.19.3:", when="@4.38.1:")
|
||||
depends_on("py-huggingface-hub@0.16.4:0", when="@4.34:")
|
||||
depends_on("py-huggingface-hub@0.14.1:0", when="@4.26:")
|
||||
depends_on("py-huggingface-hub@0.10:0", when="@4.24:")
|
||||
depends_on("py-huggingface-hub@0.0.8", when="@4.6.1")
|
||||
depends_on("py-numpy@1.17:", when="@4.6:")
|
||||
depends_on("py-numpy")
|
||||
depends_on("py-packaging@20:", when="@4.24:")
|
||||
depends_on("py-packaging", when="@4.6.1")
|
||||
depends_on("py-pyyaml@5.1:", when="@4.24:")
|
||||
depends_on("py-regex@:2019.12.16,2019.12.18:")
|
||||
depends_on("py-requests")
|
||||
depends_on("py-safetensors@0.4.1:", when="@4.38.1:")
|
||||
depends_on("py-safetensors@0.3.1:", when="@4.31:")
|
||||
depends_on("py-tokenizers@0.19", when="@4.40.0:")
|
||||
depends_on("py-tokenizers@0.14:0.18", when="@4.35:4.39.3")
|
||||
depends_on("py-tokenizers@0.11.1:0.11.2,0.11.4:0.13", when="@4.24:4.33")
|
||||
depends_on("py-tokenizers@0.10.1:0.10", when="@4.6.1")
|
||||
depends_on("py-tokenizers@0.5.2", when="@2.8.0")
|
||||
depends_on("py-tqdm@4.27:")
|
||||
|
||||
# Historical requirements
|
||||
depends_on("py-sacremoses", when="@:4.6")
|
||||
depends_on("py-boto3", when="@2.8.0")
|
||||
depends_on("py-sentencepiece", when="@2.8.0")
|
||||
|
||||
@@ -22,9 +22,7 @@ class Root(CMakePackage):
|
||||
|
||||
tags = ["hep"]
|
||||
|
||||
maintainers(
|
||||
"drbenmorgan", "gartung", "greenc-FNAL", "HadrienG2", "marcmengel", "vitodb", "vvolkl"
|
||||
)
|
||||
maintainers("drbenmorgan", "gartung", "greenc-FNAL", "marcmengel", "vitodb", "vvolkl")
|
||||
|
||||
# ###################### Versions ##########################
|
||||
|
||||
|
||||
@@ -45,19 +45,39 @@ class UtilLinux(AutotoolsPackage):
|
||||
depends_on("libxcrypt", type="link") # sbin/sulogin
|
||||
|
||||
variant("bash", default=False, description="Install bash completion scripts")
|
||||
variant("uuid", default=False, description="Build libuuid and uuid utilities")
|
||||
|
||||
depends_on("bash", when="+bash", type="run")
|
||||
depends_on("pkgconfig", when="+uuid", type="build")
|
||||
|
||||
# TODO likely applies regardless of uuid
|
||||
conflicts("%gcc@:4", when="@2.37: +uuid")
|
||||
|
||||
provides("uuid", when="+uuid")
|
||||
|
||||
def url_for_version(self, version):
|
||||
url = "https://www.kernel.org/pub/linux/utils/util-linux/v{0}/util-linux-{1}.tar.gz"
|
||||
return url.format(version.up_to(2), version)
|
||||
|
||||
# TODO does not appear used by builtin packages
|
||||
# TODO does when=[virtual=uuid] work?
|
||||
@property
|
||||
@when("[virtual=uuid]")
|
||||
def libs(self):
|
||||
return find_libraries("libuuid", self.prefix, recursive=True)
|
||||
|
||||
# TODO does not appear used by builtin packages
|
||||
# TODO does when=[virtual=uuid] work?
|
||||
@property
|
||||
@when("[virtual=uuid]")
|
||||
def headers(self):
|
||||
return find_headers("uuid", self.prefix, recursive=True)
|
||||
|
||||
def configure_args(self):
|
||||
config_args = [
|
||||
"--disable-use-tty-group",
|
||||
"--disable-makeinstall-chown",
|
||||
"--without-systemd",
|
||||
"--disable-libuuid",
|
||||
]
|
||||
if "+bash" in self.spec:
|
||||
config_args.extend(
|
||||
@@ -72,6 +92,11 @@ def configure_args(self):
|
||||
else:
|
||||
config_args.append("--disable-bash-completion")
|
||||
|
||||
if self.spec.satisfied("+uuid"):
|
||||
config_args.append("--enable-libuuid")
|
||||
else:
|
||||
config.args.append("--disable-libuuid")
|
||||
|
||||
if self.spec.satisfies("platform=darwin"):
|
||||
# Does not build on macOS
|
||||
config_args.extend(
|
||||
@@ -82,6 +107,10 @@ def configure_args(self):
|
||||
# Disable liblastlog2, which depends on sqlite
|
||||
config_args.append("--disable-liblastlog2")
|
||||
|
||||
# Fixes #31123
|
||||
if self.spec.satisfies("+uuid %intel"):
|
||||
config_args.append("CFLAGS=-restrict")
|
||||
|
||||
return config_args
|
||||
|
||||
def install(self, spec, prefix):
|
||||
|
||||
@@ -13,7 +13,7 @@ class Vecmem(CMakePackage, CudaPackage):
|
||||
url = "https://github.com/acts-project/vecmem/archive/refs/tags/v0.5.0.tar.gz"
|
||||
list_url = "https://github.com/acts-project/vecmem/tags"
|
||||
|
||||
maintainers("wdconinc", "HadrienG2", "stephenswat")
|
||||
maintainers("wdconinc", "stephenswat")
|
||||
|
||||
license("MPL-2.0-no-copyleft-exception")
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ class Verrou(AutotoolsPackage):
|
||||
url = "https://github.com/edf-hpc/verrou/archive/v2.0.0.tar.gz"
|
||||
git = "https://github.com/edf-hpc/verrou.git"
|
||||
|
||||
maintainers("HadrienG2")
|
||||
|
||||
license("GPL-2.0-only")
|
||||
|
||||
version("develop", branch="master")
|
||||
|
||||
Reference in New Issue
Block a user