# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) import pytest import spack.bootstrap import spack.bootstrap.config import spack.bootstrap.core import spack.compilers import spack.environment import spack.store import spack.util.path @pytest.fixture def active_mock_environment(mutable_config, mutable_mock_env_path): with spack.environment.create("bootstrap-test") as env: yield env @pytest.mark.regression("22294") def test_store_is_restored_correctly_after_bootstrap(mutable_config, tmpdir): """Tests that the store is correctly swapped during bootstrapping, and restored afterward.""" user_path = str(tmpdir.join("store")) with spack.store.use_store(user_path): assert spack.store.STORE.root == user_path assert spack.config.CONFIG.get("config:install_tree:root") == user_path with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.store.STORE.root == spack.bootstrap.config.store_path() assert spack.store.STORE.root == user_path assert spack.config.CONFIG.get("config:install_tree:root") == user_path @pytest.mark.regression("38963") def test_store_padding_length_is_zero_during_bootstrapping(mutable_config, tmpdir): """Tests that, even though padded length is set in user config, the bootstrap store maintains a padded length of zero. """ user_path = str(tmpdir.join("store")) with spack.store.use_store(user_path, extra_data={"padded_length": 512}): assert spack.config.CONFIG.get("config:install_tree:padded_length") == 512 with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.store.STORE.root == spack.bootstrap.config.store_path() assert spack.config.CONFIG.get("config:install_tree:padded_length") == 0 assert spack.config.CONFIG.get("config:install_tree:padded_length") == 512 @pytest.mark.regression("38963") def test_install_tree_customization_is_respected(mutable_config, tmp_path): """Tests that a custom user store is respected when we exit the bootstrapping environment. """ spack.store.reinitialize() store_dir = tmp_path / "store" spack.config.CONFIG.set("config:install_tree:root", str(store_dir)) with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.store.STORE.root == spack.bootstrap.config.store_path() assert ( spack.config.CONFIG.get("config:install_tree:root") == spack.bootstrap.config.store_path() ) assert spack.config.CONFIG.get("config:install_tree:padded_length") == 0 assert spack.config.CONFIG.get("config:install_tree:root") == str(store_dir) assert spack.store.STORE.root == str(store_dir) @pytest.mark.parametrize( "config_value,expected", [ # Absolute path without expansion ("/opt/spack/bootstrap", "/opt/spack/bootstrap/store"), # Path with placeholder ("$spack/opt/bootstrap", "$spack/opt/bootstrap/store"), ], ) def test_store_path_customization(config_value, expected, mutable_config): # Set the current configuration to a specific value spack.config.set("bootstrap:root", config_value) # Check the store path current = spack.bootstrap.config.store_path() assert current == spack.util.path.canonicalize_path(expected) def test_raising_exception_if_bootstrap_disabled(mutable_config): # Disable bootstrapping in config.yaml spack.config.set("bootstrap:enable", False) # Check the correct exception is raised with pytest.raises(RuntimeError, match="bootstrapping is currently disabled"): spack.bootstrap.config.store_path() def test_raising_exception_module_importable(): with pytest.raises(ImportError, match='cannot bootstrap the "asdf" Python module'): spack.bootstrap.core.ensure_module_importable_or_raise("asdf") def test_raising_exception_executables_in_path(): with pytest.raises(RuntimeError, match="cannot bootstrap any of the asdf, fdsa executables"): spack.bootstrap.core.ensure_executables_in_path_or_raise(["asdf", "fdsa"], "python") @pytest.mark.regression("25603") def test_bootstrap_deactivates_environments(active_mock_environment): assert spack.environment.active_environment() == active_mock_environment with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.environment.active_environment() is None assert spack.environment.active_environment() == active_mock_environment @pytest.mark.regression("25805") def test_bootstrap_disables_modulefile_generation(mutable_config): # Be sure to enable both lmod and tcl in modules.yaml spack.config.set("modules:default:enable", ["tcl", "lmod"]) assert "tcl" in spack.config.get("modules:default:enable") assert "lmod" in spack.config.get("modules:default:enable") with spack.bootstrap.ensure_bootstrap_configuration(): assert "tcl" not in spack.config.get("modules:default:enable") assert "lmod" not in spack.config.get("modules:default:enable") assert "tcl" in spack.config.get("modules:default:enable") assert "lmod" in spack.config.get("modules:default:enable") @pytest.mark.regression("25992") @pytest.mark.requires_executables("gcc") def test_bootstrap_search_for_compilers_with_no_environment(no_compilers_yaml): assert not spack.compilers.all_compiler_specs(init_config=False) with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.compilers.all_compiler_specs(init_config=False) assert not spack.compilers.all_compiler_specs(init_config=False) @pytest.mark.regression("25992") @pytest.mark.requires_executables("gcc") def test_bootstrap_search_for_compilers_with_environment_active( no_compilers_yaml, active_mock_environment ): assert not spack.compilers.all_compiler_specs(init_config=False) with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.compilers.all_compiler_specs(init_config=False) assert not spack.compilers.all_compiler_specs(init_config=False) @pytest.mark.regression("26189") def test_config_yaml_is_preserved_during_bootstrap(mutable_config): expected_dir = "/tmp/test" spack.config.set("config:test_stage", expected_dir, scope="command_line") assert spack.config.get("config:test_stage") == expected_dir 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_bootstrap_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") install_root = tmpdir.join("store") spack_yaml.write( """ spack: specs: - libelf config: install_tree: root: {0} """.format( install_root ) ) with spack.environment.Environment(str(tmpdir)): assert spack.environment.active_environment() assert spack.config.get("config:install_tree:root") == install_root # Don't trigger evaluation here with spack.bootstrap.ensure_bootstrap_configuration(): pass assert str(spack.store.STORE.root) == install_root def test_nested_use_of_context_manager(mutable_config): """Test nested use of the context manager""" user_config = spack.config.CONFIG with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.config.CONFIG != user_config with spack.bootstrap.ensure_bootstrap_configuration(): assert spack.config.CONFIG != user_config assert spack.config.CONFIG == user_config @pytest.mark.parametrize("expected_missing", [False, True]) def test_status_function_find_files( mutable_config, mock_executable, tmpdir, monkeypatch, expected_missing ): import spack.bootstrap.status if not expected_missing: mock_executable("foo", "echo Hello WWorld!") monkeypatch.setattr( spack.bootstrap.status, "_optional_requirements", lambda: [spack.bootstrap.status._required_system_executable("foo", "NOT FOUND")], ) monkeypatch.setenv("PATH", str(tmpdir.join("bin"))) _, missing = spack.bootstrap.status_message("optional") assert missing is expected_missing @pytest.mark.regression("31042") def test_source_is_disabled(mutable_config): # Get the configuration dictionary of the current bootstrapping source conf = next(iter(spack.bootstrap.core.bootstrapping_sources())) # The source is not explicitly enabled or disabled, so the following # call should raise to skip using it for bootstrapping with pytest.raises(ValueError): spack.bootstrap.core.source_is_enabled_or_raise(conf) # Try to explicitly disable the source and verify that the behavior # is the same as above spack.config.add("bootstrap:trusted:{0}:{1}".format(conf["name"], False)) with pytest.raises(ValueError): spack.bootstrap.core.source_is_enabled_or_raise(conf)