diff options
author | Massimiliano Culpo <massimiliano.culpo@gmail.com> | 2021-10-06 20:32:26 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-06 11:32:26 -0700 |
commit | 98ee00b9773e1ce4a644c431feddb3e66a28ae6a (patch) | |
tree | 9084957eca7d6b9dfa6cd67dfa13427885abb035 | |
parent | fdf76ecb519a0bfba7f904beee68e71ca8c49fe7 (diff) | |
download | spack-98ee00b9773e1ce4a644c431feddb3e66a28ae6a.tar.gz spack-98ee00b9773e1ce4a644c431feddb3e66a28ae6a.tar.bz2 spack-98ee00b9773e1ce4a644c431feddb3e66a28ae6a.tar.xz spack-98ee00b9773e1ce4a644c431feddb3e66a28ae6a.zip |
Restore the correct computation of stores in environments (#26560)
Environments push/pop scopes upon activation. If some lazily
evaluated value depending on the current configuration was
computed and cached before the scopes are pushed / popped
there will be an inconsistency in the current state.
This PR fixes the issue for stores, but it would be better
to move away from global state.
-rw-r--r-- | lib/spack/spack/environment/environment.py | 12 | ||||
-rw-r--r-- | lib/spack/spack/store.py | 16 | ||||
-rw-r--r-- | lib/spack/spack/test/bootstrap.py | 22 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/env.py | 18 |
4 files changed, 66 insertions, 2 deletions
diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py index 88dd545398..5a271fc6dd 100644 --- a/lib/spack/spack/environment/environment.py +++ b/lib/spack/spack/environment/environment.py @@ -126,7 +126,14 @@ def activate(env, use_env_repo=False): if not isinstance(env, Environment): raise TypeError("`env` should be of type {0}".format(Environment.__name__)) + # Check if we need to reinitialize the store due to pushing the configuration + # below. + store_before_pushing = spack.config.get('config:install_tree') prepare_config_scope(env) + store_after_pushing = spack.config.get('config:install_tree') + if store_before_pushing != store_after_pushing: + # Hack to store the state of the store before activation + env.store_token = spack.store.reinitialize() if use_env_repo: spack.repo.path.put_first(env.repo) @@ -144,6 +151,11 @@ def deactivate(): if not _active_environment: return + # If we attached a store token on activation, restore the previous state + # and consume the token + if hasattr(_active_environment, 'store_token'): + spack.store.restore(_active_environment.store_token) + delattr(_active_environment, 'store_token') deactivate_config_scope(_active_environment) # use _repo so we only remove if a repo was actually constructed diff --git a/lib/spack/spack/store.py b/lib/spack/spack/store.py index c60772e595..b0a418c7e3 100644 --- a/lib/spack/spack/store.py +++ b/lib/spack/spack/store.py @@ -236,17 +236,29 @@ layout = llnl.util.lang.LazyReference(_store_layout) def reinitialize(): - """Restore globals to the same state they would have at start-up""" + """Restore globals to the same state they would have at start-up. Return a token + containing the state of the store before reinitialization. + """ global store global root, unpadded_root, db, layout - store = llnl.util.lang.Singleton(_store) + token = store, root, unpadded_root, db, layout + store = llnl.util.lang.Singleton(_store) root = llnl.util.lang.LazyReference(_store_root) unpadded_root = llnl.util.lang.LazyReference(_store_unpadded_root) db = llnl.util.lang.LazyReference(_store_db) layout = llnl.util.lang.LazyReference(_store_layout) + return token + + +def restore(token): + """Restore the environment from a token returned by reinitialize""" + global store + global root, unpadded_root, db, layout + store, root, unpadded_root, db, layout = token + def retrieve_upstream_dbs(): other_spack_instances = spack.config.get('upstreams', {}) diff --git a/lib/spack/spack/test/bootstrap.py b/lib/spack/spack/test/bootstrap.py index fb84921c7a..99c1a61fd3 100644 --- a/lib/spack/spack/test/bootstrap.py +++ b/lib/spack/spack/test/bootstrap.py @@ -118,3 +118,25 @@ def test_config_yaml_is_preserved_during_bootstrap(mutable_config): with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.config.get('config:test_stage') == expected_dir assert spack.config.get('config:test_stage') == expected_dir + + +@pytest.mark.regression('26548') +def test_custom_store_in_environment(mutable_config, tmpdir): + # Test that the custom store in an environment is taken into account + # during bootstrapping + spack_yaml = tmpdir.join('spack.yaml') + spack_yaml.write(""" +spack: + specs: + - libelf + config: + install_tree: + root: /tmp/store +""") + with spack.environment.Environment(str(tmpdir)): + assert spack.environment.active_environment() + assert spack.config.get('config:install_tree:root') == '/tmp/store' + # Don't trigger evaluation here + with spack.bootstrap.ensure_bootstrap_configuration(): + pass + assert str(spack.store.root) == '/tmp/store' diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py index 3d1725a3f0..5c57035527 100644 --- a/lib/spack/spack/test/cmd/env.py +++ b/lib/spack/spack/test/cmd/env.py @@ -2663,3 +2663,21 @@ def test_activation_and_deactiviation_ambiguities(method, env, no_env, env_dir, method(args) _, err = capsys.readouterr() assert 'is ambiguous' in err + + +@pytest.mark.regression('26548') +def test_custom_store_in_environment(mutable_config, tmpdir): + spack_yaml = tmpdir.join('spack.yaml') + spack_yaml.write(""" +spack: + specs: + - libelf + config: + install_tree: + root: /tmp/store +""") + current_store_root = str(spack.store.root) + assert str(current_store_root) != '/tmp/store' + with spack.environment.Environment(str(tmpdir)): + assert str(spack.store.root) == '/tmp/store' + assert str(spack.store.root) == current_store_root |