Skip to content

Commit 9da34f2

Browse files
authored
Spec.installed_upstream fix (spack#50938)
`Database.query_by_spec_hash` returns `upstream, record`, and claims in its docstring that `upstream = True` if the spec is installed upstream. However, the record only needs to be *present* upstream for this to be true. `Spec.installed_upstream` was going by the docstring (which meant it could be wrong in circumstances where the upstream had a record, but the spec wasn't actually installed there). I think it's useful for `query_by_spec_hash` to preserve its behavior, so I * Updated the docstring * Made `Spec.installed_upstream` check the record I also added a test. More than just the behavior here, I didn't see an explicit test for the case where a spec is added to the DB with no prefix - something that can now happen fairly frequently with the compiler wrappers when installing from a binary cache (which is also how a user found this problem).
1 parent 89cf3da commit 9da34f2

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

lib/spack/spack/database.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -727,8 +727,13 @@ def query_by_spec_hash(
727727
728728
Return:
729729
(tuple): (bool, optional InstallRecord): bool tells us whether
730-
the spec is installed upstream. Its InstallRecord is also
731-
returned if it's installed at all; otherwise None.
730+
the record is from an upstream. Its InstallRecord is also
731+
returned if available (the record must be checked to know
732+
whether the hash is installed).
733+
734+
If the record is available locally, this function will always have
735+
a preference for returning that, even if it is not installed locally
736+
and is installed upstream.
732737
"""
733738
if data and hash_key in data:
734739
return False, data[hash_key]
@@ -1175,7 +1180,7 @@ def _add(
11751180
key = spec.dag_hash()
11761181
spec_pkg_hash = spec._package_hash # type: ignore[attr-defined]
11771182
upstream, record = self.query_by_spec_hash(key)
1178-
if upstream:
1183+
if upstream and record and record.installed:
11791184
return
11801185

11811186
installation_time = installation_time or _now()
@@ -1264,7 +1269,7 @@ def add(self, spec: "spack.spec.Spec", *, explicit: bool = False, allow_missing=
12641269
def _get_matching_spec_key(self, spec: "spack.spec.Spec", **kwargs) -> str:
12651270
"""Get the exact spec OR get a single spec that matches."""
12661271
key = spec.dag_hash()
1267-
upstream, record = self.query_by_spec_hash(key)
1272+
_, record = self.query_by_spec_hash(key)
12681273
if not record:
12691274
match = self.query_one(spec, **kwargs)
12701275
if match:
@@ -1275,7 +1280,7 @@ def _get_matching_spec_key(self, spec: "spack.spec.Spec", **kwargs) -> str:
12751280
@_autospec
12761281
def get_record(self, spec: "spack.spec.Spec", **kwargs) -> Optional[InstallRecord]:
12771282
key = self._get_matching_spec_key(spec, **kwargs)
1278-
upstream, record = self.query_by_spec_hash(key)
1283+
_, record = self.query_by_spec_hash(key)
12791284
return record
12801285

12811286
def _decrement_ref_count(self, spec: "spack.spec.Spec") -> None:
@@ -1783,7 +1788,7 @@ def query_one(
17831788

17841789
def missing(self, spec):
17851790
key = spec.dag_hash()
1786-
upstream, record = self.query_by_spec_hash(key)
1791+
_, record = self.query_by_spec_hash(key)
17871792
return record and not record.installed
17881793

17891794
def is_occupied_install_prefix(self, path):

lib/spack/spack/spec.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,8 +2019,8 @@ def installed_upstream(self):
20192019
if not self.concrete:
20202020
return False
20212021

2022-
upstream, _ = spack.store.STORE.db.query_by_spec_hash(self.dag_hash())
2023-
return upstream
2022+
upstream, record = spack.store.STORE.db.query_by_spec_hash(self.dag_hash())
2023+
return upstream and record and record.installed
20242024

20252025
@overload
20262026
def traverse(

lib/spack/spack/test/database.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import pytest
1717

1818
import spack.subprocess_context
19+
from spack.directory_layout import DirectoryLayoutError
1920

2021
try:
2122
import uuid
@@ -186,6 +187,53 @@ def test_installed_upstream(upstream_and_downstream_db, tmpdir):
186187
downstream_db._check_ref_counts()
187188

188189

190+
def test_missing_upstream_build_dep(upstream_and_downstream_db, tmpdir, monkeypatch, config):
191+
upstream_db, downstream_db = upstream_and_downstream_db
192+
193+
z_y_prefix = str(tmpdir.join("z-y"))
194+
195+
def fail_for_z(spec):
196+
if spec.prefix == z_y_prefix:
197+
raise DirectoryLayoutError("Fake layout error for z")
198+
199+
upstream_db.layout.ensure_installed = fail_for_z
200+
201+
builder = spack.repo.MockRepositoryBuilder(tmpdir.mkdir("mock.repo"))
202+
builder.add_package("z")
203+
builder.add_package("y", dependencies=[("z", "build", None)])
204+
205+
monkeypatch.setattr(spack.store.STORE, "db", downstream_db)
206+
207+
with spack.repo.use_repositories(builder.root):
208+
y = spack.concretize.concretize_one("y")
209+
z_y = y["z"]
210+
z_y.set_prefix(z_y_prefix)
211+
212+
with writable(upstream_db):
213+
upstream_db.add(y)
214+
upstream_db._read()
215+
216+
upstream, record = downstream_db.query_by_spec_hash(z_y.dag_hash())
217+
assert upstream
218+
assert not record.installed
219+
220+
assert y.installed
221+
assert y.installed_upstream
222+
223+
assert not z_y.installed
224+
assert not z_y.installed_upstream
225+
226+
# Now add z to downstream with non-triggering prefix
227+
# and make sure z *is* installed
228+
229+
z_new = z_y.copy()
230+
z_new.set_prefix(str(tmpdir.join("z-new")))
231+
downstream_db.add(z_new)
232+
233+
assert z_new.installed
234+
assert not z_new.installed_upstream
235+
236+
189237
def test_removed_upstream_dep(upstream_and_downstream_db, tmpdir, capsys, config):
190238
upstream_db, downstream_db = upstream_and_downstream_db
191239

0 commit comments

Comments
 (0)