summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Wittenburg <scott.wittenburg@kitware.com>2022-03-03 16:23:52 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2022-05-13 10:45:12 -0700
commit9de61c019738c0165b41c6d2ba4dc519b16f8fd5 (patch)
tree7973b1b5c4f5c3a672614f54bbb7f75f4b9fcd89
parent84cfb3b7fea78f81af5c6c1345a3b3db2d59b9dd (diff)
downloadspack-9de61c019738c0165b41c6d2ba4dc519b16f8fd5.tar.gz
spack-9de61c019738c0165b41c6d2ba4dc519b16f8fd5.tar.bz2
spack-9de61c019738c0165b41c6d2ba4dc519b16f8fd5.tar.xz
spack-9de61c019738c0165b41c6d2ba4dc519b16f8fd5.zip
env: enforce predictable ordering when reading lockfile
Without some enforcement of spec ordering, python 2 produced different results in the affected test than did python 3. This change makes the arbitrary but reproducible decision to sort the specs by their lockfile key alphabetically.
-rw-r--r--lib/spack/spack/environment/environment.py6
-rw-r--r--lib/spack/spack/test/cmd/env.py22
2 files changed, 19 insertions, 9 deletions
diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py
index 35e2956fdd..18a39dc1ad 100644
--- a/lib/spack/spack/environment/environment.py
+++ b/lib/spack/spack/environment/environment.py
@@ -1817,9 +1817,11 @@ class Environment(object):
roots = d['roots']
self.concretized_user_specs = [Spec(r['spec']) for r in roots]
self.concretized_order = [r['hash'] for r in roots]
- json_specs_by_hash = d['concrete_specs']
+ json_specs_by_hash = collections.OrderedDict()
+ for lockfile_key in sorted(d['concrete_specs']):
+ json_specs_by_hash[lockfile_key] = d['concrete_specs'][lockfile_key]
- specs_by_hash = {}
+ specs_by_hash = collections.OrderedDict()
for lockfile_key, node_dict in json_specs_by_hash.items():
specs_by_hash[lockfile_key] = Spec.from_node_dict(node_dict)
diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py
index a0608fecea..48ef699f33 100644
--- a/lib/spack/spack/test/cmd/env.py
+++ b/lib/spack/spack/test/cmd/env.py
@@ -2868,16 +2868,24 @@ def test_environment_query_spec_by_hash(mock_stage, mock_fetch, install_mockery)
def test_read_legacy_lockfile_and_reconcretize(mock_stage, mock_fetch, install_mockery):
"""Make sure that when we read a legacy environment lock file with a hash
conflict (one from before we switched to full hash), the behavior as to
- which of the conflicting specs we pick is deterministic. When we read
- the lockfile, we process root specs in the order they appear in 'roots',
- so we expect the dependencies of the last root in that list to be the
- ones that appear in the environment before we forcefully re-concretize
- the environment. After we force reconcretization, we should see all
- the dependencies again."""
+ which of the conflicting specs we pick is deterministic and reproducible.
+ When we read the lockfile, we (somewhat arbitrarily) process specs in
+ alphabetical order of their lockfile key. Consequently, when reading an
+ old lockfile where two specs have a dag hash conflict we expect to keep the
+ second one we encounter. After we force reconcretization, we should both of
+ the specs that originally conflicted present in the environment again."""
legacy_lockfile_path = os.path.join(
spack.paths.test_path, 'data', 'legacy_env', 'spack.lock'
)
+ # In the legacy lockfile, we have two conflicting specs that differ only
+ # in a build-only dependency. The lockfile keys and conflicting specs
+ # are:
+ # wci7a3a -> dttop ^dtbuild1@0.5
+ # 5zg6wxw -> dttop ^dtbuild1@1.0
+ # So when we initially read the legacy lockfile, we expect to have kept
+ # the version of dttop that depends on dtbuild1@0.5
+
env('create', 'test', legacy_lockfile_path)
test = ev.read('test')
@@ -2888,7 +2896,7 @@ def test_read_legacy_lockfile_and_reconcretize(mock_stage, mock_fetch, install_m
single_root = next(iter(test.specs_by_hash.values()))
- assert single_root['dtbuild1'].version == Version('1.0')
+ assert single_root['dtbuild1'].version == Version('0.5')
# Now forcefully reconcretize
with ev.read('test'):