ASP-based solver: always consider version of installed packages (#29933)
* ASP-based solver: always consider version of installed packages fixes #29201 Explicitly add facts for versions of installed software when using the --reuse option, so that we could consider versions that are not declared in package.py
This commit is contained in:
parent
3dd4999fd7
commit
268c671dc8
@ -80,16 +80,18 @@ def getter(node):
|
|||||||
|
|
||||||
#: Enumeration like object to mark version provenance
|
#: Enumeration like object to mark version provenance
|
||||||
version_provenance = collections.namedtuple( # type: ignore
|
version_provenance = collections.namedtuple( # type: ignore
|
||||||
'VersionProvenance', ['external', 'packages_yaml', 'package_py', 'spec']
|
'VersionProvenance',
|
||||||
)(spec=0, external=1, packages_yaml=2, package_py=3)
|
['external', 'packages_yaml', 'package_py', 'spec', 'installed']
|
||||||
|
)(installed=0, spec=1, external=2, packages_yaml=3, package_py=4)
|
||||||
|
|
||||||
#: String representation of version origins, to emit legible
|
#: String representation of version origins, to emit legible
|
||||||
# facts for the ASP solver
|
# facts for the ASP solver
|
||||||
version_origin_str = {
|
version_origin_str = {
|
||||||
0: 'spec',
|
0: 'installed',
|
||||||
1: 'external',
|
1: 'spec',
|
||||||
2: 'packages_yaml',
|
2: 'external',
|
||||||
3: 'package_py'
|
3: 'packages_yaml',
|
||||||
|
4: 'package_py'
|
||||||
}
|
}
|
||||||
|
|
||||||
#: Named tuple to contain information on declared versions
|
#: Named tuple to contain information on declared versions
|
||||||
@ -1652,8 +1654,16 @@ def _facts_from_concrete_spec(self, spec, possible):
|
|||||||
self.impose(h, spec, body=True)
|
self.impose(h, spec, body=True)
|
||||||
self.gen.newline()
|
self.gen.newline()
|
||||||
|
|
||||||
# add OS to possible OS's
|
# Declare as possible parts of specs that are not in package.py
|
||||||
|
# - Add versions to possible versions
|
||||||
|
# - Add OS to possible OS's
|
||||||
for dep in spec.traverse():
|
for dep in spec.traverse():
|
||||||
|
self.possible_versions[dep.name].add(dep.version)
|
||||||
|
self.declared_versions[dep.name].append(DeclaredVersion(
|
||||||
|
version=dep.version,
|
||||||
|
idx=0,
|
||||||
|
origin=version_provenance.installed
|
||||||
|
))
|
||||||
self.possible_oses.add(dep.os)
|
self.possible_oses.add(dep.os)
|
||||||
|
|
||||||
# add the hash to the one seen so far
|
# add the hash to the one seen so far
|
||||||
|
@ -37,6 +37,12 @@ version_declared(Package, Version, Weight) :- version_declared(Package, Version,
|
|||||||
Origin1 < Origin2,
|
Origin1 < Origin2,
|
||||||
error("Internal error: two versions with identical weights").
|
error("Internal error: two versions with identical weights").
|
||||||
|
|
||||||
|
% We cannot use a version declared for an installed package if we end up building it
|
||||||
|
:- version_declared(Package, Version, Weight, "installed"),
|
||||||
|
version(Package, Version),
|
||||||
|
version_weight(Package, Weight),
|
||||||
|
not hash(Package, _).
|
||||||
|
|
||||||
% versions are declared w/priority -- declared with priority implies declared
|
% versions are declared w/priority -- declared with priority implies declared
|
||||||
version_declared(Package, Version) :- version_declared(Package, Version, _).
|
version_declared(Package, Version) :- version_declared(Package, Version, _).
|
||||||
|
|
||||||
|
@ -129,6 +129,8 @@ class Root(Package):
|
|||||||
|
|
||||||
version(1.0, sha256='abcde')
|
version(1.0, sha256='abcde')
|
||||||
depends_on('changing')
|
depends_on('changing')
|
||||||
|
|
||||||
|
conflicts('changing~foo')
|
||||||
"""
|
"""
|
||||||
packages_dir.join('root', 'package.py').write(
|
packages_dir.join('root', 'package.py').write(
|
||||||
root_pkg_str, ensure=True
|
root_pkg_str, ensure=True
|
||||||
@ -139,32 +141,65 @@ class Changing(Package):
|
|||||||
homepage = "http://www.example.com"
|
homepage = "http://www.example.com"
|
||||||
url = "http://www.example.com/changing-1.0.tar.gz"
|
url = "http://www.example.com/changing-1.0.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
{% if not delete_version %}
|
||||||
version(1.0, sha256='abcde')
|
version(1.0, sha256='abcde')
|
||||||
|
{% endif %}
|
||||||
|
version(0.9, sha256='abcde')
|
||||||
|
|
||||||
{% if not delete_variant %}
|
{% if not delete_variant %}
|
||||||
variant('fee', default=True, description='nope')
|
variant('fee', default=True, description='nope')
|
||||||
{% endif %}
|
{% endif %}
|
||||||
variant('foo', default=True, description='nope')
|
variant('foo', default=True, description='nope')
|
||||||
{% if add_variant %}
|
{% if add_variant %}
|
||||||
variant('fum', default=True, description='nope')
|
variant('fum', default=True, description='nope')
|
||||||
|
variant('fum2', default=True, description='nope')
|
||||||
{% endif %}
|
{% endif %}
|
||||||
"""
|
"""
|
||||||
repo = spack.repo.Repo(str(repo_dir))
|
|
||||||
mutable_mock_repo.put_first(repo)
|
|
||||||
|
|
||||||
class _ChangingPackage(object):
|
class _ChangingPackage(object):
|
||||||
def change(self, context):
|
default_context = [
|
||||||
# To ensure we get the changed package we need to
|
('delete_version', True),
|
||||||
# invalidate the cache
|
('delete_variant', False),
|
||||||
repo._modules = {}
|
('add_variant', False)
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, repo_directory):
|
||||||
|
self.repo_dir = repo_directory
|
||||||
|
self.repo = spack.repo.Repo(str(repo_directory))
|
||||||
|
mutable_mock_repo.put_first(self.repo)
|
||||||
|
|
||||||
|
def change(self, changes=None):
|
||||||
|
changes = changes or {}
|
||||||
|
context = dict(self.default_context)
|
||||||
|
context.update(changes)
|
||||||
|
# Remove the repo object and delete Python modules
|
||||||
|
mutable_mock_repo.remove(self.repo)
|
||||||
|
# TODO: this mocks a change in the recipe that should happen in a
|
||||||
|
# TODO: different process space. Leaving this comment as a hint
|
||||||
|
# TODO: in case tests using this fixture start failing.
|
||||||
|
if sys.modules.get('spack.pkg.changing.changing'):
|
||||||
|
del sys.modules['spack.pkg.changing.changing']
|
||||||
|
del sys.modules['spack.pkg.changing.root']
|
||||||
|
del sys.modules['spack.pkg.changing']
|
||||||
|
|
||||||
|
# Change the recipe
|
||||||
t = jinja2.Template(changing_template)
|
t = jinja2.Template(changing_template)
|
||||||
changing_pkg_str = t.render(**context)
|
changing_pkg_str = t.render(**context)
|
||||||
packages_dir.join('changing', 'package.py').write(
|
packages_dir.join('changing', 'package.py').write(
|
||||||
changing_pkg_str, ensure=True
|
changing_pkg_str, ensure=True
|
||||||
)
|
)
|
||||||
|
|
||||||
_changing_pkg = _ChangingPackage()
|
# Re-add the repository
|
||||||
_changing_pkg.change({'delete_variant': False, 'add_variant': False})
|
self.repo = spack.repo.Repo(str(self.repo_dir))
|
||||||
|
mutable_mock_repo.put_first(self.repo)
|
||||||
|
|
||||||
|
_changing_pkg = _ChangingPackage(repo_dir)
|
||||||
|
_changing_pkg.change({
|
||||||
|
'delete_version': False,
|
||||||
|
'delete_variant': False,
|
||||||
|
'add_variant': False
|
||||||
|
})
|
||||||
|
|
||||||
return _changing_pkg
|
return _changing_pkg
|
||||||
|
|
||||||
@ -1210,10 +1245,12 @@ def mock_fn(*args, **kwargs):
|
|||||||
{'add_variant': False, 'delete_variant': True},
|
{'add_variant': False, 'delete_variant': True},
|
||||||
{'add_variant': True, 'delete_variant': True}
|
{'add_variant': True, 'delete_variant': True}
|
||||||
])
|
])
|
||||||
@pytest.mark.xfail()
|
|
||||||
def test_reuse_installed_packages_when_package_def_changes(
|
def test_reuse_installed_packages_when_package_def_changes(
|
||||||
self, context, mutable_database, repo_with_changing_recipe
|
self, context, mutable_database, repo_with_changing_recipe
|
||||||
):
|
):
|
||||||
|
if spack.config.get('config:concretizer') == 'original':
|
||||||
|
pytest.xfail('Known failure of the original concretizer')
|
||||||
|
|
||||||
# Install a spec
|
# Install a spec
|
||||||
root = Spec('root').concretized()
|
root = Spec('root').concretized()
|
||||||
dependency = root['changing'].copy()
|
dependency = root['changing'].copy()
|
||||||
@ -1223,11 +1260,14 @@ def test_reuse_installed_packages_when_package_def_changes(
|
|||||||
repo_with_changing_recipe.change(context)
|
repo_with_changing_recipe.change(context)
|
||||||
|
|
||||||
# Try to concretize with the spec installed previously
|
# Try to concretize with the spec installed previously
|
||||||
new_root = Spec('root ^/{0}'.format(
|
new_root_with_reuse = Spec('root ^/{0}'.format(
|
||||||
dependency.dag_hash())
|
dependency.dag_hash())
|
||||||
).concretized()
|
).concretized()
|
||||||
|
|
||||||
assert root.dag_hash() == new_root.dag_hash()
|
new_root_without_reuse = Spec('root').concretized()
|
||||||
|
|
||||||
|
assert root.dag_hash() == new_root_with_reuse.dag_hash()
|
||||||
|
assert root.dag_hash() != new_root_without_reuse.dag_hash()
|
||||||
|
|
||||||
@pytest.mark.regression('20784')
|
@pytest.mark.regression('20784')
|
||||||
def test_concretization_of_test_dependencies(self):
|
def test_concretization_of_test_dependencies(self):
|
||||||
@ -1485,3 +1525,43 @@ def test_conditional_values_in_conditional_variant(self):
|
|||||||
|
|
||||||
s = Spec('conditional-values-in-variant@1.60.0').concretized()
|
s = Spec('conditional-values-in-variant@1.60.0').concretized()
|
||||||
assert 'cxxstd' in s.variants
|
assert 'cxxstd' in s.variants
|
||||||
|
|
||||||
|
@pytest.mark.regression('29201')
|
||||||
|
def test_delete_version_and_reuse(
|
||||||
|
self, mutable_database, repo_with_changing_recipe
|
||||||
|
):
|
||||||
|
"""Test that we can reuse installed specs with versions not
|
||||||
|
declared in package.py
|
||||||
|
"""
|
||||||
|
if spack.config.get('config:concretizer') == 'original':
|
||||||
|
pytest.xfail('Known failure of the original concretizer')
|
||||||
|
|
||||||
|
root = Spec('root').concretized()
|
||||||
|
root.package.do_install(fake=True, explicit=True)
|
||||||
|
repo_with_changing_recipe.change({'delete_version': True})
|
||||||
|
|
||||||
|
with spack.config.override("concretizer:reuse", True):
|
||||||
|
new_root = Spec('root').concretized()
|
||||||
|
|
||||||
|
assert root.dag_hash() == new_root.dag_hash()
|
||||||
|
|
||||||
|
@pytest.mark.regression('29201')
|
||||||
|
def test_installed_version_is_selected_only_for_reuse(
|
||||||
|
self, mutable_database, repo_with_changing_recipe
|
||||||
|
):
|
||||||
|
"""Test that a version coming from an installed spec is a possible
|
||||||
|
version only for reuse
|
||||||
|
"""
|
||||||
|
if spack.config.get('config:concretizer') == 'original':
|
||||||
|
pytest.xfail('Known failure of the original concretizer')
|
||||||
|
|
||||||
|
# Install a dependency that cannot be reused with "root"
|
||||||
|
# because of a conflict a variant, then delete its version
|
||||||
|
dependency = Spec('changing@1.0~foo').concretized()
|
||||||
|
dependency.package.do_install(fake=True, explicit=True)
|
||||||
|
repo_with_changing_recipe.change({'delete_version': True})
|
||||||
|
|
||||||
|
with spack.config.override("concretizer:reuse", True):
|
||||||
|
new_root = Spec('root').concretized()
|
||||||
|
|
||||||
|
assert not new_root['changing'].satisfies('@1.0')
|
||||||
|
Loading…
Reference in New Issue
Block a user