Add virtual information on DAG edges (#34821)
* DependencySpec: add virtuals attribute on edges This works for both the new and the old concretizer. Also, added type hints to involved functions. * Improve virtual reconstruction from old format * Reconstruct virtuals when reading from Cray manifest * Reconstruct virtual information on test dependencies
This commit is contained in:
parent
c638311796
commit
f27d012e0c
@ -223,7 +223,7 @@ def update_external_dependencies(self, extendee_spec=None):
|
|||||||
|
|
||||||
python.external_path = self.spec.external_path
|
python.external_path = self.spec.external_path
|
||||||
python._mark_concrete()
|
python._mark_concrete()
|
||||||
self.spec.add_dependency_edge(python, deptypes=("build", "link", "run"))
|
self.spec.add_dependency_edge(python, deptypes=("build", "link", "run"), virtuals=())
|
||||||
|
|
||||||
|
|
||||||
class PythonPackage(PythonExtension):
|
class PythonPackage(PythonExtension):
|
||||||
|
@ -164,7 +164,10 @@ def entries_to_specs(entries):
|
|||||||
continue
|
continue
|
||||||
parent_spec = spec_dict[entry["hash"]]
|
parent_spec = spec_dict[entry["hash"]]
|
||||||
dep_spec = spec_dict[dep_hash]
|
dep_spec = spec_dict[dep_hash]
|
||||||
parent_spec._add_dependency(dep_spec, deptypes=deptypes)
|
parent_spec._add_dependency(dep_spec, deptypes=deptypes, virtuals=())
|
||||||
|
|
||||||
|
for spec in spec_dict.values():
|
||||||
|
spack.spec.reconstruct_virtuals_on_edges(spec)
|
||||||
|
|
||||||
return spec_dict
|
return spec_dict
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
# DB version. This is stuck in the DB file to track changes in format.
|
# DB version. This is stuck in the DB file to track changes in format.
|
||||||
# Increment by one when the database format changes.
|
# Increment by one when the database format changes.
|
||||||
# Versions before 5 were not integers.
|
# Versions before 5 were not integers.
|
||||||
_db_version = vn.Version("6")
|
_db_version = vn.Version("7")
|
||||||
|
|
||||||
# For any version combinations here, skip reindex when upgrading.
|
# For any version combinations here, skip reindex when upgrading.
|
||||||
# Reindexing can take considerable time and is not always necessary.
|
# Reindexing can take considerable time and is not always necessary.
|
||||||
@ -72,6 +72,7 @@
|
|||||||
# version is saved to disk the first time the DB is written.
|
# version is saved to disk the first time the DB is written.
|
||||||
(vn.Version("0.9.3"), vn.Version("5")),
|
(vn.Version("0.9.3"), vn.Version("5")),
|
||||||
(vn.Version("5"), vn.Version("6")),
|
(vn.Version("5"), vn.Version("6")),
|
||||||
|
(vn.Version("6"), vn.Version("7")),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Default timeout for spack database locks in seconds or None (no timeout).
|
# Default timeout for spack database locks in seconds or None (no timeout).
|
||||||
@ -105,7 +106,11 @@
|
|||||||
|
|
||||||
|
|
||||||
def reader(version):
|
def reader(version):
|
||||||
reader_cls = {vn.Version("5"): spack.spec.SpecfileV1, vn.Version("6"): spack.spec.SpecfileV3}
|
reader_cls = {
|
||||||
|
vn.Version("5"): spack.spec.SpecfileV1,
|
||||||
|
vn.Version("6"): spack.spec.SpecfileV3,
|
||||||
|
vn.Version("7"): spack.spec.SpecfileV4,
|
||||||
|
}
|
||||||
return reader_cls[version]
|
return reader_cls[version]
|
||||||
|
|
||||||
|
|
||||||
@ -743,7 +748,9 @@ def _assign_dependencies(self, spec_reader, hash_key, installs, data):
|
|||||||
spec_node_dict = spec_node_dict[spec.name]
|
spec_node_dict = spec_node_dict[spec.name]
|
||||||
if "dependencies" in spec_node_dict:
|
if "dependencies" in spec_node_dict:
|
||||||
yaml_deps = spec_node_dict["dependencies"]
|
yaml_deps = spec_node_dict["dependencies"]
|
||||||
for dname, dhash, dtypes, _ in spec_reader.read_specfile_dep_specs(yaml_deps):
|
for dname, dhash, dtypes, _, virtuals in spec_reader.read_specfile_dep_specs(
|
||||||
|
yaml_deps
|
||||||
|
):
|
||||||
# It is important that we always check upstream installations
|
# It is important that we always check upstream installations
|
||||||
# in the same order, and that we always check the local
|
# in the same order, and that we always check the local
|
||||||
# installation first: if a downstream Spack installs a package
|
# installation first: if a downstream Spack installs a package
|
||||||
@ -766,7 +773,7 @@ def _assign_dependencies(self, spec_reader, hash_key, installs, data):
|
|||||||
tty.warn(msg)
|
tty.warn(msg)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
spec._add_dependency(child, deptypes=dtypes)
|
spec._add_dependency(child, deptypes=dtypes, virtuals=virtuals)
|
||||||
|
|
||||||
def _read_from_file(self, filename):
|
def _read_from_file(self, filename):
|
||||||
"""Fill database from file, do not maintain old data.
|
"""Fill database from file, do not maintain old data.
|
||||||
@ -1172,7 +1179,7 @@ def _add(
|
|||||||
for dep in spec.edges_to_dependencies(deptype=_tracked_deps):
|
for dep in spec.edges_to_dependencies(deptype=_tracked_deps):
|
||||||
dkey = dep.spec.dag_hash()
|
dkey = dep.spec.dag_hash()
|
||||||
upstream, record = self.query_by_spec_hash(dkey)
|
upstream, record = self.query_by_spec_hash(dkey)
|
||||||
new_spec._add_dependency(record.spec, deptypes=dep.deptypes)
|
new_spec._add_dependency(record.spec, deptypes=dep.deptypes, virtuals=dep.virtuals)
|
||||||
if not upstream:
|
if not upstream:
|
||||||
record.ref_count += 1
|
record.ref_count += 1
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ def default_manifest_yaml():
|
|||||||
valid_environment_name_re = r"^\w[\w-]*$"
|
valid_environment_name_re = r"^\w[\w-]*$"
|
||||||
|
|
||||||
#: version of the lockfile format. Must increase monotonically.
|
#: version of the lockfile format. Must increase monotonically.
|
||||||
lockfile_format_version = 4
|
lockfile_format_version = 5
|
||||||
|
|
||||||
|
|
||||||
READER_CLS = {
|
READER_CLS = {
|
||||||
@ -133,6 +133,7 @@ def default_manifest_yaml():
|
|||||||
2: spack.spec.SpecfileV1,
|
2: spack.spec.SpecfileV1,
|
||||||
3: spack.spec.SpecfileV2,
|
3: spack.spec.SpecfileV2,
|
||||||
4: spack.spec.SpecfileV3,
|
4: spack.spec.SpecfileV3,
|
||||||
|
5: spack.spec.SpecfileV4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1548,12 +1549,13 @@ def _concretize_separately(self, tests=False):
|
|||||||
for h in self.specs_by_hash:
|
for h in self.specs_by_hash:
|
||||||
current_spec, computed_spec = self.specs_by_hash[h], by_hash[h]
|
current_spec, computed_spec = self.specs_by_hash[h], by_hash[h]
|
||||||
for node in computed_spec.traverse():
|
for node in computed_spec.traverse():
|
||||||
test_deps = node.dependencies(deptype="test")
|
test_edges = node.edges_to_dependencies(deptype="test")
|
||||||
for test_dependency in test_deps:
|
for current_edge in test_edges:
|
||||||
|
test_dependency = current_edge.spec
|
||||||
if test_dependency in current_spec[node.name]:
|
if test_dependency in current_spec[node.name]:
|
||||||
continue
|
continue
|
||||||
current_spec[node.name].add_dependency_edge(
|
current_spec[node.name].add_dependency_edge(
|
||||||
test_dependency.copy(), deptypes="test"
|
test_dependency.copy(), deptypes="test", virtuals=current_edge.virtuals
|
||||||
)
|
)
|
||||||
|
|
||||||
results = [
|
results = [
|
||||||
@ -2184,9 +2186,9 @@ def _read_lockfile_dict(self, d):
|
|||||||
# and add them to the spec
|
# and add them to the spec
|
||||||
for lockfile_key, node_dict in json_specs_by_hash.items():
|
for lockfile_key, node_dict in json_specs_by_hash.items():
|
||||||
name, data = reader.name_and_data(node_dict)
|
name, data = reader.name_and_data(node_dict)
|
||||||
for _, dep_hash, deptypes, _ in reader.dependencies_from_node_dict(data):
|
for _, dep_hash, deptypes, _, virtuals in reader.dependencies_from_node_dict(data):
|
||||||
specs_by_hash[lockfile_key]._add_dependency(
|
specs_by_hash[lockfile_key]._add_dependency(
|
||||||
specs_by_hash[dep_hash], deptypes=deptypes
|
specs_by_hash[dep_hash], deptypes=deptypes, virtuals=virtuals
|
||||||
)
|
)
|
||||||
|
|
||||||
# Traverse the root specs one at a time in the order they appear.
|
# Traverse the root specs one at a time in the order they appear.
|
||||||
|
@ -544,6 +544,7 @@ def _static_edges(specs, deptype):
|
|||||||
spack.spec.Spec(parent_name),
|
spack.spec.Spec(parent_name),
|
||||||
spack.spec.Spec(dependency_name),
|
spack.spec.Spec(dependency_name),
|
||||||
deptypes=deptype,
|
deptypes=deptype,
|
||||||
|
virtuals=(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,7 +231,9 @@ def _packages_needed_to_bootstrap_compiler(compiler, architecture, pkgs):
|
|||||||
dep.concretize()
|
dep.concretize()
|
||||||
# mark compiler as depended-on by the packages that use it
|
# mark compiler as depended-on by the packages that use it
|
||||||
for pkg in pkgs:
|
for pkg in pkgs:
|
||||||
dep._dependents.add(spack.spec.DependencySpec(pkg.spec, dep, deptypes=("build",)))
|
dep._dependents.add(
|
||||||
|
spack.spec.DependencySpec(pkg.spec, dep, deptypes=("build",), virtuals=())
|
||||||
|
)
|
||||||
packages = [(s.package, False) for s in dep.traverse(order="post", root=False)]
|
packages = [(s.package, False) for s in dep.traverse(order="post", root=False)]
|
||||||
|
|
||||||
packages.append((dep.package, True))
|
packages.append((dep.package, True))
|
||||||
|
@ -291,7 +291,7 @@ def next_spec(
|
|||||||
if root_spec.concrete:
|
if root_spec.concrete:
|
||||||
raise spack.spec.RedundantSpecError(root_spec, "^" + str(dependency))
|
raise spack.spec.RedundantSpecError(root_spec, "^" + str(dependency))
|
||||||
|
|
||||||
root_spec._add_dependency(dependency, deptypes=())
|
root_spec._add_dependency(dependency, deptypes=(), virtuals=())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
@ -292,8 +292,8 @@ def from_json(stream, repository):
|
|||||||
index.providers = _transform(
|
index.providers = _transform(
|
||||||
providers,
|
providers,
|
||||||
lambda vpkg, plist: (
|
lambda vpkg, plist: (
|
||||||
spack.spec.SpecfileV3.from_node_dict(vpkg),
|
spack.spec.SpecfileV4.from_node_dict(vpkg),
|
||||||
set(spack.spec.SpecfileV3.from_node_dict(p) for p in plist),
|
set(spack.spec.SpecfileV4.from_node_dict(p) for p in plist),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return index
|
return index
|
||||||
|
@ -2500,10 +2500,15 @@ def depends_on(self, pkg, dep, type):
|
|||||||
assert len(dependencies) < 2, msg
|
assert len(dependencies) < 2, msg
|
||||||
|
|
||||||
if not dependencies:
|
if not dependencies:
|
||||||
self._specs[pkg].add_dependency_edge(self._specs[dep], deptypes=(type,))
|
self._specs[pkg].add_dependency_edge(self._specs[dep], deptypes=(type,), virtuals=())
|
||||||
else:
|
else:
|
||||||
# TODO: This assumes that each solve unifies dependencies
|
# TODO: This assumes that each solve unifies dependencies
|
||||||
dependencies[0].add_type(type)
|
dependencies[0].update_deptypes(deptypes=(type,))
|
||||||
|
|
||||||
|
def virtual_on_edge(self, pkg, provider, virtual):
|
||||||
|
dependencies = self._specs[pkg].edges_to_dependencies(name=provider)
|
||||||
|
assert len(dependencies) == 1
|
||||||
|
dependencies[0].update_virtuals((virtual,))
|
||||||
|
|
||||||
def reorder_flags(self):
|
def reorder_flags(self):
|
||||||
"""Order compiler flags on specs in predefined order.
|
"""Order compiler flags on specs in predefined order.
|
||||||
@ -2581,6 +2586,8 @@ def sort_fn(function_tuple):
|
|||||||
return (-2, 0)
|
return (-2, 0)
|
||||||
elif name == "external_spec_selected":
|
elif name == "external_spec_selected":
|
||||||
return (0, 0) # note out of order so this goes last
|
return (0, 0) # note out of order so this goes last
|
||||||
|
elif name == "virtual_on_edge":
|
||||||
|
return (1, 0)
|
||||||
else:
|
else:
|
||||||
return (-1, 0)
|
return (-1, 0)
|
||||||
|
|
||||||
|
@ -300,6 +300,11 @@ attr("depends_on", Package, Provider, Type)
|
|||||||
provider(Provider, Virtual),
|
provider(Provider, Virtual),
|
||||||
not external(Package).
|
not external(Package).
|
||||||
|
|
||||||
|
attr("virtual_on_edge", Package, Provider, Virtual)
|
||||||
|
:- dependency_holds(Package, Virtual, Type),
|
||||||
|
provider(Provider, Virtual),
|
||||||
|
not external(Package).
|
||||||
|
|
||||||
% dependencies on virtuals also imply that the virtual is a virtual node
|
% dependencies on virtuals also imply that the virtual is a virtual node
|
||||||
attr("virtual_node", Virtual)
|
attr("virtual_node", Virtual)
|
||||||
:- dependency_holds(Package, Virtual, Type),
|
:- dependency_holds(Package, Virtual, Type),
|
||||||
|
@ -170,7 +170,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
#: specfile format version. Must increase monotonically
|
#: specfile format version. Must increase monotonically
|
||||||
SPECFILE_FORMAT_VERSION = 3
|
SPECFILE_FORMAT_VERSION = 4
|
||||||
|
|
||||||
|
|
||||||
def colorize_spec(spec):
|
def colorize_spec(spec):
|
||||||
@ -714,47 +714,81 @@ class DependencySpec:
|
|||||||
parent: starting node of the edge
|
parent: starting node of the edge
|
||||||
spec: ending node of the edge.
|
spec: ending node of the edge.
|
||||||
deptypes: list of strings, representing dependency relationships.
|
deptypes: list of strings, representing dependency relationships.
|
||||||
|
virtuals: virtual packages provided from child to parent node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = "parent", "spec", "deptypes"
|
__slots__ = "parent", "spec", "parameters"
|
||||||
|
|
||||||
def __init__(self, parent: "Spec", spec: "Spec", *, deptypes: dp.DependencyArgument):
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: "Spec",
|
||||||
|
spec: "Spec",
|
||||||
|
*,
|
||||||
|
deptypes: dp.DependencyArgument,
|
||||||
|
virtuals: Tuple[str, ...],
|
||||||
|
):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.spec = spec
|
self.spec = spec
|
||||||
self.deptypes = dp.canonical_deptype(deptypes)
|
self.parameters = {
|
||||||
|
"deptypes": dp.canonical_deptype(deptypes),
|
||||||
|
"virtuals": tuple(sorted(set(virtuals))),
|
||||||
|
}
|
||||||
|
|
||||||
def update_deptypes(self, deptypes: dp.DependencyArgument) -> bool:
|
@property
|
||||||
deptypes = set(deptypes)
|
def deptypes(self) -> Tuple[str, ...]:
|
||||||
deptypes.update(self.deptypes)
|
return self.parameters["deptypes"]
|
||||||
deptypes = tuple(sorted(deptypes))
|
|
||||||
changed = self.deptypes != deptypes
|
|
||||||
|
|
||||||
self.deptypes = deptypes
|
@property
|
||||||
return changed
|
def virtuals(self) -> Tuple[str, ...]:
|
||||||
|
return self.parameters["virtuals"]
|
||||||
|
|
||||||
|
def _update_edge_multivalued_property(
|
||||||
|
self, property_name: str, value: Tuple[str, ...]
|
||||||
|
) -> bool:
|
||||||
|
current = self.parameters[property_name]
|
||||||
|
update = set(current) | set(value)
|
||||||
|
update = tuple(sorted(update))
|
||||||
|
changed = current != update
|
||||||
|
|
||||||
|
if not changed:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.parameters[property_name] = update
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_deptypes(self, deptypes: Tuple[str, ...]) -> bool:
|
||||||
|
"""Update the current dependency types"""
|
||||||
|
return self._update_edge_multivalued_property("deptypes", deptypes)
|
||||||
|
|
||||||
|
def update_virtuals(self, virtuals: Tuple[str, ...]) -> bool:
|
||||||
|
"""Update the list of provided virtuals"""
|
||||||
|
return self._update_edge_multivalued_property("virtuals", virtuals)
|
||||||
|
|
||||||
def copy(self) -> "DependencySpec":
|
def copy(self) -> "DependencySpec":
|
||||||
return DependencySpec(self.parent, self.spec, deptypes=self.deptypes)
|
"""Return a copy of this edge"""
|
||||||
|
return DependencySpec(
|
||||||
def add_type(self, type: dp.DependencyArgument):
|
self.parent, self.spec, deptypes=self.deptypes, virtuals=self.virtuals
|
||||||
self.deptypes = dp.canonical_deptype(self.deptypes + dp.canonical_deptype(type))
|
)
|
||||||
|
|
||||||
def _cmp_iter(self):
|
def _cmp_iter(self):
|
||||||
yield self.parent.name if self.parent else None
|
yield self.parent.name if self.parent else None
|
||||||
yield self.spec.name if self.spec else None
|
yield self.spec.name if self.spec else None
|
||||||
yield self.deptypes
|
yield self.deptypes
|
||||||
|
yield self.virtuals
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return "%s %s--> %s" % (
|
parent = self.parent.name if self.parent else None
|
||||||
self.parent.name if self.parent else None,
|
child = self.spec.name if self.spec else None
|
||||||
self.deptypes,
|
return f"{parent} {self.deptypes}[virtuals={','.join(self.virtuals)}] --> {child}"
|
||||||
self.spec.name if self.spec else None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def canonical(self) -> Tuple[str, str, Tuple[str, ...]]:
|
def canonical(self) -> Tuple[str, str, Tuple[str, ...], Tuple[str, ...]]:
|
||||||
return self.parent.dag_hash(), self.spec.dag_hash(), self.deptypes
|
return self.parent.dag_hash(), self.spec.dag_hash(), self.deptypes, self.virtuals
|
||||||
|
|
||||||
def flip(self) -> "DependencySpec":
|
def flip(self) -> "DependencySpec":
|
||||||
return DependencySpec(parent=self.spec, spec=self.parent, deptypes=self.deptypes)
|
"""Flip the dependency, and drop virtual information"""
|
||||||
|
return DependencySpec(
|
||||||
|
parent=self.spec, spec=self.parent, deptypes=self.deptypes, virtuals=()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CompilerFlag(str):
|
class CompilerFlag(str):
|
||||||
@ -1575,10 +1609,12 @@ def _set_compiler(self, compiler):
|
|||||||
)
|
)
|
||||||
self.compiler = compiler
|
self.compiler = compiler
|
||||||
|
|
||||||
def _add_dependency(self, spec: "Spec", *, deptypes: dp.DependencyArgument):
|
def _add_dependency(
|
||||||
|
self, spec: "Spec", *, deptypes: dp.DependencyArgument, virtuals: Tuple[str, ...]
|
||||||
|
):
|
||||||
"""Called by the parser to add another spec as a dependency."""
|
"""Called by the parser to add another spec as a dependency."""
|
||||||
if spec.name not in self._dependencies or not spec.name:
|
if spec.name not in self._dependencies or not spec.name:
|
||||||
self.add_dependency_edge(spec, deptypes=deptypes)
|
self.add_dependency_edge(spec, deptypes=deptypes, virtuals=virtuals)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Keep the intersection of constraints when a dependency is added
|
# Keep the intersection of constraints when a dependency is added
|
||||||
@ -1596,34 +1632,58 @@ def _add_dependency(self, spec: "Spec", *, deptypes: dp.DependencyArgument):
|
|||||||
"Cannot depend on incompatible specs '%s' and '%s'" % (dspec.spec, spec)
|
"Cannot depend on incompatible specs '%s' and '%s'" % (dspec.spec, spec)
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_dependency_edge(self, dependency_spec: "Spec", *, deptypes: dp.DependencyArgument):
|
def add_dependency_edge(
|
||||||
|
self,
|
||||||
|
dependency_spec: "Spec",
|
||||||
|
*,
|
||||||
|
deptypes: dp.DependencyArgument,
|
||||||
|
virtuals: Tuple[str, ...],
|
||||||
|
):
|
||||||
"""Add a dependency edge to this spec.
|
"""Add a dependency edge to this spec.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dependency_spec: spec of the dependency
|
dependency_spec: spec of the dependency
|
||||||
deptypes: dependency types for this edge
|
deptypes: dependency types for this edge
|
||||||
|
virtuals: virtuals provided by this edge
|
||||||
"""
|
"""
|
||||||
deptypes = dp.canonical_deptype(deptypes)
|
deptypes = dp.canonical_deptype(deptypes)
|
||||||
|
|
||||||
# Check if we need to update edges that are already present
|
# Check if we need to update edges that are already present
|
||||||
selected = self._dependencies.select(child=dependency_spec.name)
|
selected = self._dependencies.select(child=dependency_spec.name)
|
||||||
for edge in selected:
|
for edge in selected:
|
||||||
|
has_errors, details = False, []
|
||||||
|
msg = f"cannot update the edge from {edge.parent.name} to {edge.spec.name}"
|
||||||
if any(d in edge.deptypes for d in deptypes):
|
if any(d in edge.deptypes for d in deptypes):
|
||||||
msg = (
|
has_errors = True
|
||||||
'cannot add a dependency on "{0.spec}" of {1} type '
|
details.append(
|
||||||
'when the "{0.parent}" has the edge {0!s} already'
|
(
|
||||||
|
f"{edge.parent.name} has already an edge matching any"
|
||||||
|
f" of these types {str(deptypes)}"
|
||||||
)
|
)
|
||||||
raise spack.error.SpecError(msg.format(edge, deptypes))
|
)
|
||||||
|
|
||||||
|
if any(v in edge.virtuals for v in virtuals):
|
||||||
|
has_errors = True
|
||||||
|
details.append(
|
||||||
|
(
|
||||||
|
f"{edge.parent.name} has already an edge matching any"
|
||||||
|
f" of these virtuals {str(virtuals)}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if has_errors:
|
||||||
|
raise spack.error.SpecError(msg, "\n".join(details))
|
||||||
|
|
||||||
for edge in selected:
|
for edge in selected:
|
||||||
if id(dependency_spec) == id(edge.spec):
|
if id(dependency_spec) == id(edge.spec):
|
||||||
# If we are here, it means the edge object was previously added to
|
# If we are here, it means the edge object was previously added to
|
||||||
# both the parent and the child. When we update this object they'll
|
# both the parent and the child. When we update this object they'll
|
||||||
# both see the deptype modification.
|
# both see the deptype modification.
|
||||||
edge.add_type(deptypes)
|
edge.update_deptypes(deptypes=deptypes)
|
||||||
|
edge.update_virtuals(virtuals=virtuals)
|
||||||
return
|
return
|
||||||
|
|
||||||
edge = DependencySpec(self, dependency_spec, deptypes=deptypes)
|
edge = DependencySpec(self, dependency_spec, deptypes=deptypes, virtuals=virtuals)
|
||||||
self._dependencies.add(edge)
|
self._dependencies.add(edge)
|
||||||
dependency_spec._dependents.add(edge)
|
dependency_spec._dependents.add(edge)
|
||||||
|
|
||||||
@ -1896,12 +1956,12 @@ def lookup_hash(self):
|
|||||||
for node in self.traverse(root=False):
|
for node in self.traverse(root=False):
|
||||||
if node.abstract_hash:
|
if node.abstract_hash:
|
||||||
new = node._lookup_hash()
|
new = node._lookup_hash()
|
||||||
spec._add_dependency(new, deptypes=())
|
spec._add_dependency(new, deptypes=(), virtuals=())
|
||||||
|
|
||||||
# reattach nodes that were not otherwise satisfied by new dependencies
|
# reattach nodes that were not otherwise satisfied by new dependencies
|
||||||
for node in self.traverse(root=False):
|
for node in self.traverse(root=False):
|
||||||
if not any(n._satisfies(node) for n in spec.traverse()):
|
if not any(n._satisfies(node) for n in spec.traverse()):
|
||||||
spec._add_dependency(node.copy(), deptypes=())
|
spec._add_dependency(node.copy(), deptypes=(), virtuals=())
|
||||||
|
|
||||||
return spec
|
return spec
|
||||||
|
|
||||||
@ -2036,8 +2096,14 @@ def to_node_dict(self, hash=ht.dag_hash):
|
|||||||
name_tuple = ("name", name)
|
name_tuple = ("name", name)
|
||||||
for dspec in edges_for_name:
|
for dspec in edges_for_name:
|
||||||
hash_tuple = (hash.name, dspec.spec._cached_hash(hash))
|
hash_tuple = (hash.name, dspec.spec._cached_hash(hash))
|
||||||
type_tuple = ("type", sorted(str(s) for s in dspec.deptypes))
|
parameters_tuple = (
|
||||||
deps_list.append(syaml.syaml_dict([name_tuple, hash_tuple, type_tuple]))
|
"parameters",
|
||||||
|
syaml.syaml_dict(
|
||||||
|
(key, dspec.parameters[key]) for key in sorted(dspec.parameters)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ordered_entries = [name_tuple, hash_tuple, parameters_tuple]
|
||||||
|
deps_list.append(syaml.syaml_dict(ordered_entries))
|
||||||
d["dependencies"] = deps_list
|
d["dependencies"] = deps_list
|
||||||
|
|
||||||
# Name is included in case this is replacing a virtual.
|
# Name is included in case this is replacing a virtual.
|
||||||
@ -2361,7 +2427,7 @@ def spec_and_dependency_types(s):
|
|||||||
dag_node, dependency_types = spec_and_dependency_types(s)
|
dag_node, dependency_types = spec_and_dependency_types(s)
|
||||||
|
|
||||||
dependency_spec = spec_builder({dag_node: s_dependencies})
|
dependency_spec = spec_builder({dag_node: s_dependencies})
|
||||||
spec._add_dependency(dependency_spec, deptypes=dependency_types)
|
spec._add_dependency(dependency_spec, deptypes=dependency_types, virtuals=())
|
||||||
|
|
||||||
return spec
|
return spec
|
||||||
|
|
||||||
@ -2379,8 +2445,10 @@ def from_dict(data):
|
|||||||
spec = SpecfileV1.load(data)
|
spec = SpecfileV1.load(data)
|
||||||
elif int(data["spec"]["_meta"]["version"]) == 2:
|
elif int(data["spec"]["_meta"]["version"]) == 2:
|
||||||
spec = SpecfileV2.load(data)
|
spec = SpecfileV2.load(data)
|
||||||
else:
|
elif int(data["spec"]["_meta"]["version"]) == 3:
|
||||||
spec = SpecfileV3.load(data)
|
spec = SpecfileV3.load(data)
|
||||||
|
else:
|
||||||
|
spec = SpecfileV4.load(data)
|
||||||
|
|
||||||
# Any git version should
|
# Any git version should
|
||||||
for s in spec.traverse():
|
for s in spec.traverse():
|
||||||
@ -2529,6 +2597,7 @@ def _concretize_helper(self, concretizer, presets=None, visited=None):
|
|||||||
def _replace_with(self, concrete):
|
def _replace_with(self, concrete):
|
||||||
"""Replace this virtual spec with a concrete spec."""
|
"""Replace this virtual spec with a concrete spec."""
|
||||||
assert self.virtual
|
assert self.virtual
|
||||||
|
virtuals = (self.name,)
|
||||||
for dep_spec in itertools.chain.from_iterable(self._dependents.values()):
|
for dep_spec in itertools.chain.from_iterable(self._dependents.values()):
|
||||||
dependent = dep_spec.parent
|
dependent = dep_spec.parent
|
||||||
deptypes = dep_spec.deptypes
|
deptypes = dep_spec.deptypes
|
||||||
@ -2539,7 +2608,11 @@ def _replace_with(self, concrete):
|
|||||||
|
|
||||||
# add the replacement, unless it is already a dep of dependent.
|
# add the replacement, unless it is already a dep of dependent.
|
||||||
if concrete.name not in dependent._dependencies:
|
if concrete.name not in dependent._dependencies:
|
||||||
dependent._add_dependency(concrete, deptypes=deptypes)
|
dependent._add_dependency(concrete, deptypes=deptypes, virtuals=virtuals)
|
||||||
|
else:
|
||||||
|
dependent.edges_to_dependencies(name=concrete.name)[0].update_virtuals(
|
||||||
|
virtuals=virtuals
|
||||||
|
)
|
||||||
|
|
||||||
def _expand_virtual_packages(self, concretizer):
|
def _expand_virtual_packages(self, concretizer):
|
||||||
"""Find virtual packages in this spec, replace them with providers,
|
"""Find virtual packages in this spec, replace them with providers,
|
||||||
@ -3180,7 +3253,9 @@ def _merge_dependency(self, dependency, visited, spec_deps, provider_index, test
|
|||||||
|
|
||||||
# If it's a virtual dependency, try to find an existing
|
# If it's a virtual dependency, try to find an existing
|
||||||
# provider in the spec, and merge that.
|
# provider in the spec, and merge that.
|
||||||
|
virtuals = ()
|
||||||
if spack.repo.path.is_virtual_safe(dep.name):
|
if spack.repo.path.is_virtual_safe(dep.name):
|
||||||
|
virtuals = (dep.name,)
|
||||||
visited.add(dep.name)
|
visited.add(dep.name)
|
||||||
provider = self._find_provider(dep, provider_index)
|
provider = self._find_provider(dep, provider_index)
|
||||||
if provider:
|
if provider:
|
||||||
@ -3236,7 +3311,7 @@ def _merge_dependency(self, dependency, visited, spec_deps, provider_index, test
|
|||||||
# Add merged spec to my deps and recurse
|
# Add merged spec to my deps and recurse
|
||||||
spec_dependency = spec_deps[dep.name]
|
spec_dependency = spec_deps[dep.name]
|
||||||
if dep.name not in self._dependencies:
|
if dep.name not in self._dependencies:
|
||||||
self._add_dependency(spec_dependency, deptypes=dependency.type)
|
self._add_dependency(spec_dependency, deptypes=dependency.type, virtuals=virtuals)
|
||||||
|
|
||||||
changed |= spec_dependency._normalize_helper(visited, spec_deps, provider_index, tests)
|
changed |= spec_dependency._normalize_helper(visited, spec_deps, provider_index, tests)
|
||||||
return changed
|
return changed
|
||||||
@ -3573,15 +3648,20 @@ def _constrain_dependencies(self, other):
|
|||||||
changed |= edges_from_name[0].update_deptypes(
|
changed |= edges_from_name[0].update_deptypes(
|
||||||
other._dependencies[name][0].deptypes
|
other._dependencies[name][0].deptypes
|
||||||
)
|
)
|
||||||
|
changed |= edges_from_name[0].update_virtuals(
|
||||||
|
other._dependencies[name][0].virtuals
|
||||||
|
)
|
||||||
|
|
||||||
# Update with additional constraints from other spec
|
# Update with additional constraints from other spec
|
||||||
# operate on direct dependencies only, because a concrete dep
|
# operate on direct dependencies only, because a concrete dep
|
||||||
# represented by hash may have structure that needs to be preserved
|
# represented by hash may have structure that needs to be preserved
|
||||||
for name in other.direct_dep_difference(self):
|
for name in other.direct_dep_difference(self):
|
||||||
dep_spec_copy = other._get_dependency(name)
|
dep_spec_copy = other._get_dependency(name)
|
||||||
dep_copy = dep_spec_copy.spec
|
self._add_dependency(
|
||||||
deptypes = dep_spec_copy.deptypes
|
dep_spec_copy.spec.copy(),
|
||||||
self._add_dependency(dep_copy.copy(), deptypes=deptypes)
|
deptypes=dep_spec_copy.deptypes,
|
||||||
|
virtuals=dep_spec_copy.virtuals,
|
||||||
|
)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
@ -3965,7 +4045,7 @@ def spid(spec):
|
|||||||
new_specs[spid(edge.spec)] = edge.spec.copy(deps=False)
|
new_specs[spid(edge.spec)] = edge.spec.copy(deps=False)
|
||||||
|
|
||||||
new_specs[spid(edge.parent)].add_dependency_edge(
|
new_specs[spid(edge.parent)].add_dependency_edge(
|
||||||
new_specs[spid(edge.spec)], deptypes=edge.deptypes
|
new_specs[spid(edge.spec)], deptypes=edge.deptypes, virtuals=edge.virtuals
|
||||||
)
|
)
|
||||||
|
|
||||||
def copy(self, deps=True, **kwargs):
|
def copy(self, deps=True, **kwargs):
|
||||||
@ -4635,12 +4715,16 @@ def from_self(name, transitive):
|
|||||||
if name in self_nodes:
|
if name in self_nodes:
|
||||||
for edge in self[name].edges_to_dependencies():
|
for edge in self[name].edges_to_dependencies():
|
||||||
dep_name = deps_to_replace.get(edge.spec, edge.spec).name
|
dep_name = deps_to_replace.get(edge.spec, edge.spec).name
|
||||||
nodes[name].add_dependency_edge(nodes[dep_name], deptypes=edge.deptypes)
|
nodes[name].add_dependency_edge(
|
||||||
|
nodes[dep_name], deptypes=edge.deptypes, virtuals=edge.virtuals
|
||||||
|
)
|
||||||
if any(dep not in self_nodes for dep in self[name]._dependencies):
|
if any(dep not in self_nodes for dep in self[name]._dependencies):
|
||||||
nodes[name].build_spec = self[name].build_spec
|
nodes[name].build_spec = self[name].build_spec
|
||||||
else:
|
else:
|
||||||
for edge in other[name].edges_to_dependencies():
|
for edge in other[name].edges_to_dependencies():
|
||||||
nodes[name].add_dependency_edge(nodes[edge.spec.name], deptypes=edge.deptypes)
|
nodes[name].add_dependency_edge(
|
||||||
|
nodes[edge.spec.name], deptypes=edge.deptypes, virtuals=edge.virtuals
|
||||||
|
)
|
||||||
if any(dep not in other_nodes for dep in other[name]._dependencies):
|
if any(dep not in other_nodes for dep in other[name]._dependencies):
|
||||||
nodes[name].build_spec = other[name].build_spec
|
nodes[name].build_spec = other[name].build_spec
|
||||||
|
|
||||||
@ -4730,11 +4814,40 @@ def merge_abstract_anonymous_specs(*abstract_specs: Spec):
|
|||||||
# Update with additional constraints from other spec
|
# Update with additional constraints from other spec
|
||||||
for name in current_spec_constraint.direct_dep_difference(merged_spec):
|
for name in current_spec_constraint.direct_dep_difference(merged_spec):
|
||||||
edge = next(iter(current_spec_constraint.edges_to_dependencies(name)))
|
edge = next(iter(current_spec_constraint.edges_to_dependencies(name)))
|
||||||
merged_spec._add_dependency(edge.spec.copy(), deptypes=edge.deptypes)
|
merged_spec._add_dependency(
|
||||||
|
edge.spec.copy(), deptypes=edge.deptypes, virtuals=edge.virtuals
|
||||||
|
)
|
||||||
|
|
||||||
return merged_spec
|
return merged_spec
|
||||||
|
|
||||||
|
|
||||||
|
def reconstruct_virtuals_on_edges(spec):
|
||||||
|
"""Reconstruct virtuals on edges. Used to read from old DB and reindex.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
spec: spec on which we want to reconstruct virtuals
|
||||||
|
"""
|
||||||
|
# Collect all possible virtuals
|
||||||
|
possible_virtuals = set()
|
||||||
|
for node in spec.traverse():
|
||||||
|
try:
|
||||||
|
possible_virtuals.update({x for x in node.package.dependencies if Spec(x).virtual})
|
||||||
|
except Exception as e:
|
||||||
|
warnings.warn(f"cannot reconstruct virtual dependencies on package {node.name}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Assume all incoming edges to provider are marked with virtuals=
|
||||||
|
for vspec in possible_virtuals:
|
||||||
|
try:
|
||||||
|
provider = spec[vspec]
|
||||||
|
except KeyError:
|
||||||
|
# Virtual not in the DAG
|
||||||
|
continue
|
||||||
|
|
||||||
|
for edge in provider.edges_from_dependents():
|
||||||
|
edge.update_virtuals([vspec])
|
||||||
|
|
||||||
|
|
||||||
class SpecfileReaderBase:
|
class SpecfileReaderBase:
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_node_dict(cls, node):
|
def from_node_dict(cls, node):
|
||||||
@ -4818,7 +4931,7 @@ def _load(cls, data):
|
|||||||
|
|
||||||
# Pass 0: Determine hash type
|
# Pass 0: Determine hash type
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
for _, _, _, dhash_type in cls.dependencies_from_node_dict(node):
|
for _, _, _, dhash_type, _ in cls.dependencies_from_node_dict(node):
|
||||||
any_deps = True
|
any_deps = True
|
||||||
if dhash_type:
|
if dhash_type:
|
||||||
hash_type = dhash_type
|
hash_type = dhash_type
|
||||||
@ -4849,8 +4962,10 @@ def _load(cls, data):
|
|||||||
# Pass 2: Finish construction of all DAG edges (including build specs)
|
# Pass 2: Finish construction of all DAG edges (including build specs)
|
||||||
for node_hash, node in hash_dict.items():
|
for node_hash, node in hash_dict.items():
|
||||||
node_spec = node["node_spec"]
|
node_spec = node["node_spec"]
|
||||||
for _, dhash, dtypes, _ in cls.dependencies_from_node_dict(node):
|
for _, dhash, dtypes, _, virtuals in cls.dependencies_from_node_dict(node):
|
||||||
node_spec._add_dependency(hash_dict[dhash]["node_spec"], deptypes=dtypes)
|
node_spec._add_dependency(
|
||||||
|
hash_dict[dhash]["node_spec"], deptypes=dtypes, virtuals=virtuals
|
||||||
|
)
|
||||||
if "build_spec" in node.keys():
|
if "build_spec" in node.keys():
|
||||||
_, bhash, _ = cls.build_spec_from_node_dict(node, hash_type=hash_type)
|
_, bhash, _ = cls.build_spec_from_node_dict(node, hash_type=hash_type)
|
||||||
node_spec._build_spec = hash_dict[bhash]["node_spec"]
|
node_spec._build_spec = hash_dict[bhash]["node_spec"]
|
||||||
@ -4884,9 +4999,10 @@ def load(cls, data):
|
|||||||
for node in nodes:
|
for node in nodes:
|
||||||
# get dependency dict from the node.
|
# get dependency dict from the node.
|
||||||
name, data = cls.name_and_data(node)
|
name, data = cls.name_and_data(node)
|
||||||
for dname, _, dtypes, _ in cls.dependencies_from_node_dict(data):
|
for dname, _, dtypes, _, virtuals in cls.dependencies_from_node_dict(data):
|
||||||
deps[name]._add_dependency(deps[dname], deptypes=dtypes)
|
deps[name]._add_dependency(deps[dname], deptypes=dtypes, virtuals=virtuals)
|
||||||
|
|
||||||
|
reconstruct_virtuals_on_edges(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -4915,18 +5031,20 @@ def read_specfile_dep_specs(cls, deps, hash_type=ht.dag_hash.name):
|
|||||||
if h.name in elt:
|
if h.name in elt:
|
||||||
dep_hash, deptypes = elt[h.name], elt["type"]
|
dep_hash, deptypes = elt[h.name], elt["type"]
|
||||||
hash_type = h.name
|
hash_type = h.name
|
||||||
|
virtuals = []
|
||||||
break
|
break
|
||||||
else: # We never determined a hash type...
|
else: # We never determined a hash type...
|
||||||
raise spack.error.SpecError("Couldn't parse dependency spec.")
|
raise spack.error.SpecError("Couldn't parse dependency spec.")
|
||||||
else:
|
else:
|
||||||
raise spack.error.SpecError("Couldn't parse dependency types in spec.")
|
raise spack.error.SpecError("Couldn't parse dependency types in spec.")
|
||||||
yield dep_name, dep_hash, list(deptypes), hash_type
|
yield dep_name, dep_hash, list(deptypes), hash_type, list(virtuals)
|
||||||
|
|
||||||
|
|
||||||
class SpecfileV2(SpecfileReaderBase):
|
class SpecfileV2(SpecfileReaderBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, data):
|
def load(cls, data):
|
||||||
result = cls._load(data)
|
result = cls._load(data)
|
||||||
|
reconstruct_virtuals_on_edges(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -4960,7 +5078,7 @@ def read_specfile_dep_specs(cls, deps, hash_type=ht.dag_hash.name):
|
|||||||
raise spack.error.SpecError("Couldn't parse dependency spec.")
|
raise spack.error.SpecError("Couldn't parse dependency spec.")
|
||||||
else:
|
else:
|
||||||
raise spack.error.SpecError("Couldn't parse dependency types in spec.")
|
raise spack.error.SpecError("Couldn't parse dependency types in spec.")
|
||||||
result.append((dep_name, dep_hash, list(deptypes), hash_type))
|
result.append((dep_name, dep_hash, list(deptypes), hash_type, list(virtuals)))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -4980,6 +5098,20 @@ class SpecfileV3(SpecfileV2):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SpecfileV4(SpecfileV2):
|
||||||
|
@classmethod
|
||||||
|
def extract_info_from_dep(cls, elt, hash):
|
||||||
|
dep_hash = elt[hash.name]
|
||||||
|
deptypes = elt["parameters"]["deptypes"]
|
||||||
|
hash_type = hash.name
|
||||||
|
virtuals = elt["parameters"]["virtuals"]
|
||||||
|
return dep_hash, deptypes, hash_type, virtuals
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, data):
|
||||||
|
return cls._load(data)
|
||||||
|
|
||||||
|
|
||||||
class LazySpecCache(collections.defaultdict):
|
class LazySpecCache(collections.defaultdict):
|
||||||
"""Cache for Specs that uses a spec_like as key, and computes lazily
|
"""Cache for Specs that uses a spec_like as key, and computes lazily
|
||||||
the corresponding value ``Spec(spec_like``.
|
the corresponding value ``Spec(spec_like``.
|
||||||
|
@ -2170,3 +2170,14 @@ def test_concretization_with_compilers_supporting_target_any(self):
|
|||||||
with spack.config.override("compilers", compiler_configuration):
|
with spack.config.override("compilers", compiler_configuration):
|
||||||
s = spack.spec.Spec("a").concretized()
|
s = spack.spec.Spec("a").concretized()
|
||||||
assert s.satisfies("%gcc@12.1.0")
|
assert s.satisfies("%gcc@12.1.0")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("spec_str", ["mpileaks", "mpileaks ^mpich"])
|
||||||
|
def test_virtuals_are_annotated_on_edges(self, spec_str, default_mock_concretization):
|
||||||
|
"""Tests that information on virtuals is annotated on DAG edges"""
|
||||||
|
spec = default_mock_concretization(spec_str)
|
||||||
|
mpi_provider = spec["mpi"].name
|
||||||
|
|
||||||
|
edges = spec.edges_to_dependencies(name=mpi_provider)
|
||||||
|
assert len(edges) == 1 and edges[0].virtuals == ("mpi",)
|
||||||
|
edges = spec.edges_to_dependencies(name="callpath")
|
||||||
|
assert len(edges) == 1 and edges[0].virtuals == ()
|
||||||
|
BIN
lib/spack/spack/test/data/specfiles/hdf5.v020.json.gz
Normal file
BIN
lib/spack/spack/test/data/specfiles/hdf5.v020.json.gz
Normal file
Binary file not shown.
@ -125,7 +125,7 @@ def _mock_installed(self):
|
|||||||
# use the installed C. It should *not* force A to use the installed D
|
# use the installed C. It should *not* force A to use the installed D
|
||||||
# *if* we're doing a fresh installation.
|
# *if* we're doing a fresh installation.
|
||||||
a_spec = Spec(a)
|
a_spec = Spec(a)
|
||||||
a_spec._add_dependency(c_spec, deptypes=("build", "link"))
|
a_spec._add_dependency(c_spec, deptypes=("build", "link"), virtuals=())
|
||||||
a_spec.concretize()
|
a_spec.concretize()
|
||||||
assert spack.version.Version("2") == a_spec[c][d].version
|
assert spack.version.Version("2") == a_spec[c][d].version
|
||||||
assert spack.version.Version("2") == a_spec[e].version
|
assert spack.version.Version("2") == a_spec[e].version
|
||||||
@ -148,7 +148,7 @@ def test_specify_preinstalled_dep(tmpdir, monkeypatch):
|
|||||||
monkeypatch.setattr(Spec, "installed", property(lambda x: x.name != "a"))
|
monkeypatch.setattr(Spec, "installed", property(lambda x: x.name != "a"))
|
||||||
|
|
||||||
a_spec = Spec("a")
|
a_spec = Spec("a")
|
||||||
a_spec._add_dependency(b_spec, deptypes=("build", "link"))
|
a_spec._add_dependency(b_spec, deptypes=("build", "link"), virtuals=())
|
||||||
a_spec.concretize()
|
a_spec.concretize()
|
||||||
|
|
||||||
assert set(x.name for x in a_spec.traverse()) == set(["a", "b", "c"])
|
assert set(x.name for x in a_spec.traverse()) == set(["a", "b", "c"])
|
||||||
@ -989,9 +989,9 @@ def test_synthetic_construction_of_split_dependencies_from_same_package(mock_pac
|
|||||||
link_run_spec = Spec("c@=1.0").concretized()
|
link_run_spec = Spec("c@=1.0").concretized()
|
||||||
build_spec = Spec("c@=2.0").concretized()
|
build_spec = Spec("c@=2.0").concretized()
|
||||||
|
|
||||||
root.add_dependency_edge(link_run_spec, deptypes="link")
|
root.add_dependency_edge(link_run_spec, deptypes="link", virtuals=())
|
||||||
root.add_dependency_edge(link_run_spec, deptypes="run")
|
root.add_dependency_edge(link_run_spec, deptypes="run", virtuals=())
|
||||||
root.add_dependency_edge(build_spec, deptypes="build")
|
root.add_dependency_edge(build_spec, deptypes="build", virtuals=())
|
||||||
|
|
||||||
# Check dependencies from the perspective of root
|
# Check dependencies from the perspective of root
|
||||||
assert len(root.dependencies()) == 2
|
assert len(root.dependencies()) == 2
|
||||||
@ -1017,7 +1017,7 @@ def test_synthetic_construction_bootstrapping(mock_packages, config):
|
|||||||
root = Spec("b@=2.0").concretized()
|
root = Spec("b@=2.0").concretized()
|
||||||
bootstrap = Spec("b@=1.0").concretized()
|
bootstrap = Spec("b@=1.0").concretized()
|
||||||
|
|
||||||
root.add_dependency_edge(bootstrap, deptypes="build")
|
root.add_dependency_edge(bootstrap, deptypes="build", virtuals=())
|
||||||
|
|
||||||
assert len(root.dependencies()) == 1
|
assert len(root.dependencies()) == 1
|
||||||
assert root.dependencies()[0].name == "b"
|
assert root.dependencies()[0].name == "b"
|
||||||
@ -1036,7 +1036,7 @@ def test_addition_of_different_deptypes_in_multiple_calls(mock_packages, config)
|
|||||||
bootstrap = Spec("b@=1.0").concretized()
|
bootstrap = Spec("b@=1.0").concretized()
|
||||||
|
|
||||||
for current_deptype in ("build", "link", "run"):
|
for current_deptype in ("build", "link", "run"):
|
||||||
root.add_dependency_edge(bootstrap, deptypes=current_deptype)
|
root.add_dependency_edge(bootstrap, deptypes=current_deptype, virtuals=())
|
||||||
|
|
||||||
# Check edges in dependencies
|
# Check edges in dependencies
|
||||||
assert len(root.edges_to_dependencies()) == 1
|
assert len(root.edges_to_dependencies()) == 1
|
||||||
@ -1063,9 +1063,9 @@ def test_adding_same_deptype_with_the_same_name_raises(
|
|||||||
c1 = Spec("b@=1.0").concretized()
|
c1 = Spec("b@=1.0").concretized()
|
||||||
c2 = Spec("b@=2.0").concretized()
|
c2 = Spec("b@=2.0").concretized()
|
||||||
|
|
||||||
p.add_dependency_edge(c1, deptypes=c1_deptypes)
|
p.add_dependency_edge(c1, deptypes=c1_deptypes, virtuals=())
|
||||||
with pytest.raises(spack.error.SpackError):
|
with pytest.raises(spack.error.SpackError):
|
||||||
p.add_dependency_edge(c2, deptypes=c2_deptypes)
|
p.add_dependency_edge(c2, deptypes=c2_deptypes, virtuals=())
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.regression("33499")
|
@pytest.mark.regression("33499")
|
||||||
@ -1084,16 +1084,16 @@ def test_indexing_prefers_direct_or_transitive_link_deps():
|
|||||||
z3_flavor_1 = Spec("z3 +through_a1")
|
z3_flavor_1 = Spec("z3 +through_a1")
|
||||||
z3_flavor_2 = Spec("z3 +through_z1")
|
z3_flavor_2 = Spec("z3 +through_z1")
|
||||||
|
|
||||||
root.add_dependency_edge(a1, deptypes=("build", "run", "test"))
|
root.add_dependency_edge(a1, deptypes=("build", "run", "test"), virtuals=())
|
||||||
|
|
||||||
# unique package as a dep of a build/run/test type dep.
|
# unique package as a dep of a build/run/test type dep.
|
||||||
a1.add_dependency_edge(a2, deptypes="all")
|
a1.add_dependency_edge(a2, deptypes="all", virtuals=())
|
||||||
a1.add_dependency_edge(z3_flavor_1, deptypes="all")
|
a1.add_dependency_edge(z3_flavor_1, deptypes="all", virtuals=())
|
||||||
|
|
||||||
# chain of link type deps root -> z1 -> z2 -> z3
|
# chain of link type deps root -> z1 -> z2 -> z3
|
||||||
root.add_dependency_edge(z1, deptypes="link")
|
root.add_dependency_edge(z1, deptypes="link", virtuals=())
|
||||||
z1.add_dependency_edge(z2, deptypes="link")
|
z1.add_dependency_edge(z2, deptypes="link", virtuals=())
|
||||||
z2.add_dependency_edge(z3_flavor_2, deptypes="link")
|
z2.add_dependency_edge(z3_flavor_2, deptypes="link", virtuals=())
|
||||||
|
|
||||||
# Indexing should prefer the link-type dep.
|
# Indexing should prefer the link-type dep.
|
||||||
assert "through_z1" in root["z3"].variants
|
assert "through_z1" in root["z3"].variants
|
||||||
|
@ -971,7 +971,7 @@ def test_error_message_unknown_variant(self):
|
|||||||
def test_satisfies_dependencies_ordered(self):
|
def test_satisfies_dependencies_ordered(self):
|
||||||
d = Spec("zmpi ^fake")
|
d = Spec("zmpi ^fake")
|
||||||
s = Spec("mpileaks")
|
s = Spec("mpileaks")
|
||||||
s._add_dependency(d, deptypes=())
|
s._add_dependency(d, deptypes=(), virtuals=())
|
||||||
assert s.satisfies("mpileaks ^zmpi ^fake")
|
assert s.satisfies("mpileaks ^zmpi ^fake")
|
||||||
|
|
||||||
@pytest.mark.parametrize("transitive", [True, False])
|
@pytest.mark.parametrize("transitive", [True, False])
|
||||||
@ -1018,6 +1018,7 @@ def test_is_extension_after_round_trip_to_dict(config, mock_packages, spec_str):
|
|||||||
|
|
||||||
|
|
||||||
def test_malformed_spec_dict():
|
def test_malformed_spec_dict():
|
||||||
|
# FIXME: This test was really testing the specific implementation with an ad-hoc test
|
||||||
with pytest.raises(SpecError, match="malformed"):
|
with pytest.raises(SpecError, match="malformed"):
|
||||||
Spec.from_dict(
|
Spec.from_dict(
|
||||||
{"spec": {"_meta": {"version": 2}, "nodes": [{"dependencies": {"name": "foo"}}]}}
|
{"spec": {"_meta": {"version": 2}, "nodes": [{"dependencies": {"name": "foo"}}]}}
|
||||||
@ -1025,6 +1026,7 @@ def test_malformed_spec_dict():
|
|||||||
|
|
||||||
|
|
||||||
def test_spec_dict_hashless_dep():
|
def test_spec_dict_hashless_dep():
|
||||||
|
# FIXME: This test was really testing the specific implementation with an ad-hoc test
|
||||||
with pytest.raises(SpecError, match="Couldn't parse"):
|
with pytest.raises(SpecError, match="Couldn't parse"):
|
||||||
Spec.from_dict(
|
Spec.from_dict(
|
||||||
{
|
{
|
||||||
@ -1118,7 +1120,7 @@ def test_concretize_partial_old_dag_hash_spec(mock_packages, config):
|
|||||||
|
|
||||||
# add it to an abstract spec as a dependency
|
# add it to an abstract spec as a dependency
|
||||||
top = Spec("dt-diamond")
|
top = Spec("dt-diamond")
|
||||||
top.add_dependency_edge(bottom, deptypes=())
|
top.add_dependency_edge(bottom, deptypes=(), virtuals=())
|
||||||
|
|
||||||
# concretize with the already-concrete dependency
|
# concretize with the already-concrete dependency
|
||||||
top.concretize()
|
top.concretize()
|
||||||
|
@ -43,12 +43,6 @@ def check_json_round_trip(spec):
|
|||||||
assert spec.eq_dag(spec_from_json)
|
assert spec.eq_dag(spec_from_json)
|
||||||
|
|
||||||
|
|
||||||
def test_simple_spec():
|
|
||||||
spec = Spec("mpileaks")
|
|
||||||
check_yaml_round_trip(spec)
|
|
||||||
check_json_round_trip(spec)
|
|
||||||
|
|
||||||
|
|
||||||
def test_read_spec_from_signed_json():
|
def test_read_spec_from_signed_json():
|
||||||
spec_dir = os.path.join(spack.paths.test_path, "data", "mirrors", "signed_json")
|
spec_dir = os.path.join(spack.paths.test_path, "data", "mirrors", "signed_json")
|
||||||
file_name = (
|
file_name = (
|
||||||
@ -70,13 +64,6 @@ def check_spec(spec_to_check):
|
|||||||
check_spec(s)
|
check_spec(s)
|
||||||
|
|
||||||
|
|
||||||
def test_normal_spec(mock_packages):
|
|
||||||
spec = Spec("mpileaks+debug~opt")
|
|
||||||
spec.normalize()
|
|
||||||
check_yaml_round_trip(spec)
|
|
||||||
check_json_round_trip(spec)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"invalid_yaml", ["playing_playlist: {{ action }} playlist {{ playlist_name }}"]
|
"invalid_yaml", ["playing_playlist: {{ action }} playlist {{ playlist_name }}"]
|
||||||
)
|
)
|
||||||
@ -95,37 +82,28 @@ def test_invalid_json_spec(invalid_json, error_message):
|
|||||||
assert error_message in exc_msg
|
assert error_message in exc_msg
|
||||||
|
|
||||||
|
|
||||||
def test_external_spec(config, mock_packages):
|
@pytest.mark.parametrize(
|
||||||
spec = Spec("externaltool")
|
"abstract_spec",
|
||||||
spec.concretize()
|
[
|
||||||
check_yaml_round_trip(spec)
|
# Externals
|
||||||
check_json_round_trip(spec)
|
"externaltool",
|
||||||
|
"externaltest",
|
||||||
spec = Spec("externaltest")
|
# Ambiguous version spec
|
||||||
spec.concretize()
|
"mpileaks@1.0:5.0,6.1,7.3+debug~opt",
|
||||||
check_yaml_round_trip(spec)
|
# Variants
|
||||||
check_json_round_trip(spec)
|
"mpileaks+debug~opt",
|
||||||
|
'multivalue-variant foo="bar,baz"',
|
||||||
|
# Virtuals on edges
|
||||||
def test_ambiguous_version_spec(mock_packages):
|
"callpath",
|
||||||
spec = Spec("mpileaks@1.0:5.0,6.1,7.3+debug~opt")
|
"mpileaks",
|
||||||
spec.normalize()
|
],
|
||||||
check_yaml_round_trip(spec)
|
)
|
||||||
check_json_round_trip(spec)
|
def test_roundtrip_concrete_specs(abstract_spec, default_mock_concretization):
|
||||||
|
check_yaml_round_trip(Spec(abstract_spec))
|
||||||
|
check_json_round_trip(Spec(abstract_spec))
|
||||||
def test_concrete_spec(config, mock_packages):
|
concrete_spec = default_mock_concretization(abstract_spec)
|
||||||
spec = Spec("mpileaks+debug~opt")
|
check_yaml_round_trip(concrete_spec)
|
||||||
spec.concretize()
|
check_json_round_trip(concrete_spec)
|
||||||
check_yaml_round_trip(spec)
|
|
||||||
check_json_round_trip(spec)
|
|
||||||
|
|
||||||
|
|
||||||
def test_yaml_multivalue(config, mock_packages):
|
|
||||||
spec = Spec('multivalue-variant foo="bar,baz"')
|
|
||||||
spec.concretize()
|
|
||||||
check_yaml_round_trip(spec)
|
|
||||||
check_json_round_trip(spec)
|
|
||||||
|
|
||||||
|
|
||||||
def test_yaml_subdag(config, mock_packages):
|
def test_yaml_subdag(config, mock_packages):
|
||||||
@ -506,6 +484,8 @@ def test_legacy_yaml(tmpdir, install_mockery, mock_packages):
|
|||||||
("specfiles/hdf5.v017.json.gz", "xqh5iyjjtrp2jw632cchacn3l7vqzf3m", spack.spec.SpecfileV2),
|
("specfiles/hdf5.v017.json.gz", "xqh5iyjjtrp2jw632cchacn3l7vqzf3m", spack.spec.SpecfileV2),
|
||||||
# Use "full hash" everywhere, see https://github.com/spack/spack/pull/28504
|
# Use "full hash" everywhere, see https://github.com/spack/spack/pull/28504
|
||||||
("specfiles/hdf5.v019.json.gz", "iulacrbz7o5v5sbj7njbkyank3juh6d3", spack.spec.SpecfileV3),
|
("specfiles/hdf5.v019.json.gz", "iulacrbz7o5v5sbj7njbkyank3juh6d3", spack.spec.SpecfileV3),
|
||||||
|
# Add properties on edges, see https://github.com/spack/spack/pull/34821
|
||||||
|
("specfiles/hdf5.v020.json.gz", "vlirlcgazhvsvtundz4kug75xkkqqgou", spack.spec.SpecfileV4),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_load_json_specfiles(specfile, expected_hash, reader_cls):
|
def test_load_json_specfiles(specfile, expected_hash, reader_cls):
|
||||||
|
@ -19,7 +19,7 @@ def create_dag(nodes, edges):
|
|||||||
"""
|
"""
|
||||||
specs = {name: Spec(name) for name in nodes}
|
specs = {name: Spec(name) for name in nodes}
|
||||||
for parent, child, deptypes in edges:
|
for parent, child, deptypes in edges:
|
||||||
specs[parent].add_dependency_edge(specs[child], deptypes=deptypes)
|
specs[parent].add_dependency_edge(specs[child], deptypes=deptypes, virtuals=())
|
||||||
return specs
|
return specs
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,7 +211,9 @@ def get_visitor_from_args(cover, direction, deptype, key=id, visited=None, visit
|
|||||||
def with_artificial_edges(specs):
|
def with_artificial_edges(specs):
|
||||||
"""Initialize a list of edges from an imaginary root node to the root specs."""
|
"""Initialize a list of edges from an imaginary root node to the root specs."""
|
||||||
return [
|
return [
|
||||||
EdgeAndDepth(edge=spack.spec.DependencySpec(parent=None, spec=s, deptypes=()), depth=0)
|
EdgeAndDepth(
|
||||||
|
edge=spack.spec.DependencySpec(parent=None, spec=s, deptypes=(), virtuals=()), depth=0
|
||||||
|
)
|
||||||
for s in specs
|
for s in specs
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user