bugfix: handle hashes that only exist in input specs
In our tests, we use concrete specs generated from mock packages, which *only* occur as inputs to the solver. This fixes two problems: 1. We weren't previously adding facts to encode the necessary `depends_on()` relationships, and specs were unsatisfiable on reachability. 2. Our hash lookup for reconstructing the DAG does not consider that a hash may have come from the inputs.
This commit is contained in:
parent
a4a2ed3c34
commit
e0c3d074c0
@ -631,6 +631,9 @@ def __init__(self):
|
|||||||
self.compiler_version_constraints = set()
|
self.compiler_version_constraints = set()
|
||||||
self.post_facts = []
|
self.post_facts = []
|
||||||
|
|
||||||
|
# hashes we've already added facts for
|
||||||
|
self.seen_hashes = set()
|
||||||
|
|
||||||
# id for dummy variables
|
# id for dummy variables
|
||||||
self._condition_id_counter = itertools.count()
|
self._condition_id_counter = itertools.count()
|
||||||
|
|
||||||
@ -1569,43 +1572,48 @@ def define_variant_values(self):
|
|||||||
for pkg, variant, value in sorted(self.variant_values_from_specs):
|
for pkg, variant, value in sorted(self.variant_values_from_specs):
|
||||||
self.gen.fact(fn.variant_possible_value(pkg, variant, value))
|
self.gen.fact(fn.variant_possible_value(pkg, variant, value))
|
||||||
|
|
||||||
def define_installed_packages(self, possible):
|
def _facts_from_concrete_spec(self, spec, possible):
|
||||||
|
# tell the solver about any installed packages that could
|
||||||
|
# be dependencies (don't tell it about the others)
|
||||||
|
h = spec.dag_hash()
|
||||||
|
if spec.name in possible and h not in self.seen_hashes:
|
||||||
|
# this indicates that there is a spec like this installed
|
||||||
|
self.gen.fact(fn.installed_hash(spec.name, h))
|
||||||
|
|
||||||
|
# this describes what constraints it imposes on the solve
|
||||||
|
self.impose(h, spec, body=True)
|
||||||
|
self.gen.newline()
|
||||||
|
|
||||||
|
# add OS to possible OS's
|
||||||
|
self.possible_oses.add(spec.os)
|
||||||
|
|
||||||
|
# add the hash to the one seen so far
|
||||||
|
self.seen_hashes.add(h)
|
||||||
|
|
||||||
|
def define_concrete_input_specs(self, specs, possible):
|
||||||
|
# any concrete specs in the input spec list
|
||||||
|
for input_spec in specs:
|
||||||
|
for spec in input_spec.traverse():
|
||||||
|
if spec.concrete:
|
||||||
|
self._facts_from_concrete_spec(spec, possible)
|
||||||
|
|
||||||
|
def define_installed_packages(self, specs, possible):
|
||||||
"""Add facts about all specs already in the database.
|
"""Add facts about all specs already in the database.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
possible (dict): result of Package.possible_dependencies() for
|
possible (dict): result of Package.possible_dependencies() for
|
||||||
specs in this solve.
|
specs in this solve.
|
||||||
"""
|
"""
|
||||||
seen = set()
|
|
||||||
|
|
||||||
def _facts_from_concrete_spec(spec):
|
|
||||||
# tell the solver about any installed packages that could
|
|
||||||
# be dependencies (don't tell it about the others)
|
|
||||||
h = spec.dag_hash()
|
|
||||||
if spec.name in possible and h not in seen:
|
|
||||||
# this indicates that there is a spec like this installed
|
|
||||||
self.gen.fact(fn.installed_hash(spec.name, h))
|
|
||||||
|
|
||||||
# this describes what constraints it imposes on the solve
|
|
||||||
self.impose(h, spec, body=True)
|
|
||||||
self.gen.newline()
|
|
||||||
|
|
||||||
# add OS to possible OS's
|
|
||||||
self.possible_oses.add(spec.os)
|
|
||||||
|
|
||||||
# add the hash to the one seen so far
|
|
||||||
seen.add(h)
|
|
||||||
|
|
||||||
# Specs from local store
|
# Specs from local store
|
||||||
with spack.store.db.read_transaction():
|
with spack.store.db.read_transaction():
|
||||||
for spec in spack.store.db.query(installed=True):
|
for spec in spack.store.db.query(installed=True):
|
||||||
_facts_from_concrete_spec(spec)
|
self._facts_from_concrete_spec(spec, possible)
|
||||||
|
|
||||||
# Specs from configured buildcaches
|
# Specs from configured buildcaches
|
||||||
try:
|
try:
|
||||||
index = spack.binary_distribution.update_cache_and_get_specs()
|
index = spack.binary_distribution.update_cache_and_get_specs()
|
||||||
for spec in index:
|
for spec in index:
|
||||||
_facts_from_concrete_spec(spec)
|
self._facts_from_concrete_spec(spec, possible)
|
||||||
except spack.binary_distribution.FetchCacheError:
|
except spack.binary_distribution.FetchCacheError:
|
||||||
# this is raised when no mirrors had indices.
|
# this is raised when no mirrors had indices.
|
||||||
# TODO: update mirror configuration so it can indicate that the source cache
|
# TODO: update mirror configuration so it can indicate that the source cache
|
||||||
@ -1656,11 +1664,14 @@ def setup(self, driver, specs, tests=False, reuse=False):
|
|||||||
# traverse all specs and packages to build dict of possible versions
|
# traverse all specs and packages to build dict of possible versions
|
||||||
self.build_version_dict(possible, specs)
|
self.build_version_dict(possible, specs)
|
||||||
|
|
||||||
|
self.gen.h1("Concrete input spec definitions")
|
||||||
|
self.define_concrete_input_specs(specs, possible)
|
||||||
|
|
||||||
if reuse:
|
if reuse:
|
||||||
self.gen.h1("Installed packages")
|
self.gen.h1("Installed packages")
|
||||||
self.gen.fact(fn.optimize_for_reuse())
|
self.gen.fact(fn.optimize_for_reuse())
|
||||||
self.gen.newline()
|
self.gen.newline()
|
||||||
self.define_installed_packages(possible)
|
self.define_installed_packages(specs, possible)
|
||||||
|
|
||||||
self.gen.h1('General Constraints')
|
self.gen.h1('General Constraints')
|
||||||
self.available_compilers()
|
self.available_compilers()
|
||||||
@ -1736,12 +1747,22 @@ def hash(self, pkg, h):
|
|||||||
if pkg not in self._specs:
|
if pkg not in self._specs:
|
||||||
try:
|
try:
|
||||||
# try to get the candidate from the store
|
# try to get the candidate from the store
|
||||||
self._specs[pkg] = spack.store.db.get_by_hash(h)[0]
|
concrete_spec = spack.store.db.get_by_hash(h)[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# the dag hash was not in the DB, try buildcache
|
# the dag hash was not in the DB, try buildcache
|
||||||
s = spack.binary_distribution.binary_index.find_by_hash(h)
|
s = spack.binary_distribution.binary_index.find_by_hash(h)
|
||||||
# see specifications in spack.binary_distribution.BinaryCacheIndex
|
if s:
|
||||||
self._specs[pkg] = s[0]['spec']
|
concrete_spec = s[0]['spec']
|
||||||
|
else:
|
||||||
|
# last attempt: maybe the hash comes from a particular input spec
|
||||||
|
# this only occurs in tests (so far)
|
||||||
|
for clspec in self._command_line_specs:
|
||||||
|
for spec in clspec.traverse():
|
||||||
|
if spec.concrete and spec.dag_hash() == h:
|
||||||
|
concrete_spec = spec
|
||||||
|
|
||||||
|
assert concrete_spec, "Unable to look up concrete spec with hash %s" % h
|
||||||
|
self._specs[pkg] = concrete_spec
|
||||||
else:
|
else:
|
||||||
# ensure that if it's already there, it's correct
|
# ensure that if it's already there, it's correct
|
||||||
spec = self._specs[pkg]
|
spec = self._specs[pkg]
|
||||||
|
Loading…
Reference in New Issue
Block a user