Skip to content

Commit 0b2db30

Browse files
committed
Reconstruct virtual attributes on edges
1 parent f19daa8 commit 0b2db30

3 files changed

Lines changed: 83 additions & 56 deletions

File tree

lib/spack/spack/spec.py

Lines changed: 78 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4805,6 +4805,18 @@ def merge_abstract_anonymous_specs(*abstract_specs: Spec):
48054805
return merged_spec
48064806

48074807

4808+
def _reconstruct_virtuals_on_edges(spec):
4809+
"""Reconstruct virtuals on edges. Used to read from old DB
4810+
and reindex.
4811+
"""
4812+
possible_virtuals = [x for x in spec.package.dependencies if Spec(x).virtual]
4813+
for vspec in possible_virtuals:
4814+
if vspec in spec:
4815+
name = spec[vspec].name
4816+
for edge in spec.edges_to_dependencies(name=name):
4817+
edge.update_virtuals([vspec])
4818+
4819+
48084820
class SpecfileReaderBase:
48094821
@classmethod
48104822
def from_node_dict(cls, node):
@@ -4869,6 +4881,64 @@ def from_node_dict(cls, node):
48694881

48704882
return spec
48714883

4884+
@classmethod
4885+
def _load(cls, data):
4886+
"""Construct a spec from JSON/YAML using the format version 2.
4887+
4888+
This format is used in Spack v0.17, was introduced in
4889+
https://github.com/spack/spack/pull/22845
4890+
4891+
Args:
4892+
data: a nested dict/list data structure read from YAML or JSON.
4893+
"""
4894+
# Current specfile format
4895+
nodes = data["spec"]["nodes"]
4896+
hash_type = None
4897+
any_deps = False
4898+
4899+
# Pass 0: Determine hash type
4900+
for node in nodes:
4901+
for _, _, _, dhash_type, _ in cls.dependencies_from_node_dict(node):
4902+
any_deps = True
4903+
if dhash_type:
4904+
hash_type = dhash_type
4905+
break
4906+
4907+
if not any_deps: # If we never see a dependency...
4908+
hash_type = ht.dag_hash.name
4909+
elif not hash_type: # Seen a dependency, still don't know hash_type
4910+
raise spack.error.SpecError(
4911+
"Spec dictionary contains malformed dependencies. Old format?"
4912+
)
4913+
4914+
hash_dict = {}
4915+
root_spec_hash = None
4916+
4917+
# Pass 1: Create a single lookup dictionary by hash
4918+
for i, node in enumerate(nodes):
4919+
node_hash = node[hash_type]
4920+
node_spec = cls.from_node_dict(node)
4921+
hash_dict[node_hash] = node
4922+
hash_dict[node_hash]["node_spec"] = node_spec
4923+
if i == 0:
4924+
root_spec_hash = node_hash
4925+
4926+
if not root_spec_hash:
4927+
raise spack.error.SpecError("Spec dictionary contains no nodes.")
4928+
4929+
# Pass 2: Finish construction of all DAG edges (including build specs)
4930+
for node_hash, node in hash_dict.items():
4931+
node_spec = node["node_spec"]
4932+
for _, dhash, dtypes, _, virtuals in cls.dependencies_from_node_dict(node):
4933+
node_spec._add_dependency(
4934+
hash_dict[dhash]["node_spec"], deptypes=dtypes, virtuals=virtuals
4935+
)
4936+
if "build_spec" in node.keys():
4937+
_, bhash, _ = cls.build_spec_from_node_dict(node, hash_type=hash_type)
4938+
node_spec._build_spec = hash_dict[bhash]["node_spec"]
4939+
4940+
return hash_dict[root_spec_hash]["node_spec"]
4941+
48724942

48734943
class SpecfileV1(SpecfileReaderBase):
48744944
@classmethod
@@ -4899,6 +4969,7 @@ def load(cls, data):
48994969
for dname, _, dtypes, _, virtuals in cls.dependencies_from_node_dict(data):
49004970
deps[name]._add_dependency(deps[dname], deptypes=dtypes, virtuals=virtuals)
49014971

4972+
_reconstruct_virtuals_on_edges(result)
49024973
return result
49034974

49044975
@classmethod
@@ -4939,61 +5010,9 @@ def read_specfile_dep_specs(cls, deps, hash_type=ht.dag_hash.name):
49395010
class SpecfileV2(SpecfileReaderBase):
49405011
@classmethod
49415012
def load(cls, data):
4942-
"""Construct a spec from JSON/YAML using the format version 2.
4943-
4944-
This format is used in Spack v0.17, was introduced in
4945-
https://github.com/spack/spack/pull/22845
4946-
4947-
Args:
4948-
data: a nested dict/list data structure read from YAML or JSON.
4949-
"""
4950-
# Current specfile format
4951-
nodes = data["spec"]["nodes"]
4952-
hash_type = None
4953-
any_deps = False
4954-
4955-
# Pass 0: Determine hash type
4956-
for node in nodes:
4957-
for _, _, _, dhash_type, _ in cls.dependencies_from_node_dict(node):
4958-
any_deps = True
4959-
if dhash_type:
4960-
hash_type = dhash_type
4961-
break
4962-
4963-
if not any_deps: # If we never see a dependency...
4964-
hash_type = ht.dag_hash.name
4965-
elif not hash_type: # Seen a dependency, still don't know hash_type
4966-
raise spack.error.SpecError(
4967-
"Spec dictionary contains malformed dependencies. Old format?"
4968-
)
4969-
4970-
hash_dict = {}
4971-
root_spec_hash = None
4972-
4973-
# Pass 1: Create a single lookup dictionary by hash
4974-
for i, node in enumerate(nodes):
4975-
node_hash = node[hash_type]
4976-
node_spec = cls.from_node_dict(node)
4977-
hash_dict[node_hash] = node
4978-
hash_dict[node_hash]["node_spec"] = node_spec
4979-
if i == 0:
4980-
root_spec_hash = node_hash
4981-
4982-
if not root_spec_hash:
4983-
raise spack.error.SpecError("Spec dictionary contains no nodes.")
4984-
4985-
# Pass 2: Finish construction of all DAG edges (including build specs)
4986-
for node_hash, node in hash_dict.items():
4987-
node_spec = node["node_spec"]
4988-
for _, dhash, dtypes, _, virtuals in cls.dependencies_from_node_dict(node):
4989-
node_spec._add_dependency(
4990-
hash_dict[dhash]["node_spec"], deptypes=dtypes, virtuals=virtuals
4991-
)
4992-
if "build_spec" in node.keys():
4993-
_, bhash, _ = cls.build_spec_from_node_dict(node, hash_type=hash_type)
4994-
node_spec._build_spec = hash_dict[bhash]["node_spec"]
4995-
4996-
return hash_dict[root_spec_hash]["node_spec"]
5013+
result = cls._load(data)
5014+
_reconstruct_virtuals_on_edges(result)
5015+
return result
49975016

49985017
@classmethod
49995018
def name_and_data(cls, node):
@@ -5055,6 +5074,10 @@ def extract_info_from_dep(cls, elt, hash):
50555074
virtuals = elt["parameters"]["virtuals"]
50565075
return dep_hash, deptypes, hash_type, virtuals
50575076

5077+
@classmethod
5078+
def load(cls, data):
5079+
return cls._load(data)
5080+
50585081

50595082
class LazySpecCache(collections.defaultdict):
50605083
"""Cache for Specs that uses a spec_like as key, and computes lazily

lib/spack/spack/test/cmd/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ def test_has_test_method_fails(capsys):
258258
assert "is not a class" in captured
259259

260260

261-
def test_read_old_results(mock_test_stage):
261+
def test_read_old_results(mock_packages, mock_test_stage):
262262
"""Take test data generated before the switch to full hash everywhere
263263
and make sure we can still read it in"""
264264
# Test data was generated with:

lib/spack/spack/test/spec_yaml.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,3 +515,7 @@ def test_load_json_specfiles(specfile, expected_hash, reader_cls):
515515
assert s1.dag_hash() == s2.dag_hash()
516516
assert s1 == s2
517517
assert Spec.from_json(s2.to_json()).dag_hash() == s2.dag_hash()
518+
519+
openmpi_edges = s2.edges_to_dependencies(name="openmpi")
520+
assert len(openmpi_edges) == 1
521+
assert openmpi_edges[0].virtuals == ("mpi",)

0 commit comments

Comments
 (0)