diff options
author | Massimiliano Culpo <massimiliano.culpo@gmail.com> | 2021-09-18 02:28:48 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-17 18:28:48 -0600 |
commit | b847bb72f0db53f8741876d7633cc46b5f855759 (patch) | |
tree | a0104edae8306c0f0e093b374bf5afbf0682b04c /lib | |
parent | 4d36c40cfb5225a27d25af444d0fc3dbb9e8791d (diff) | |
download | spack-b847bb72f0db53f8741876d7633cc46b5f855759.tar.gz spack-b847bb72f0db53f8741876d7633cc46b5f855759.tar.bz2 spack-b847bb72f0db53f8741876d7633cc46b5f855759.tar.xz spack-b847bb72f0db53f8741876d7633cc46b5f855759.zip |
Bootstrap should search for compilers after switching config scopes (#26029)
fixes #25992
Currently the bootstrapping process may need a compiler.
When bootstrapping from sources the need is obvious, while
when bootstrapping from binaries it's currently needed in
case patchelf is not on the system (since it will be then
bootstrapped from sources).
Before this PR we were searching for compilers as the
first operation, in case they were not declared in
the configuration. This fails in case we start
bootstrapping from within an environment.
The fix is to defer the search until we have swapped
configuration.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/bootstrap.py | 23 | ||||
-rw-r--r-- | lib/spack/spack/cmd/compiler.py | 28 | ||||
-rw-r--r-- | lib/spack/spack/compilers/__init__.py | 34 | ||||
-rw-r--r-- | lib/spack/spack/test/bootstrap.py | 21 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/compiler.py | 12 | ||||
-rw-r--r-- | lib/spack/spack/test/conftest.py | 9 |
6 files changed, 80 insertions, 47 deletions
diff --git a/lib/spack/spack/bootstrap.py b/lib/spack/spack/bootstrap.py index 5d1a494065..fbfeefe92f 100644 --- a/lib/spack/spack/bootstrap.py +++ b/lib/spack/spack/bootstrap.py @@ -490,18 +490,20 @@ def _bootstrap_config_scopes(): return config_scopes -@contextlib.contextmanager -def ensure_bootstrap_configuration(): - # We may need to compile code from sources, so ensure we have compilers - # for the current platform before switching parts. - arch = spack.architecture.default_arch() +def _add_compilers_if_missing(): + # Do not use spack.architecture.default_arch() since it memoize the result + arch = spack.architecture.Arch( + spack.architecture.real_platform(), 'default_os', 'default_target' + ) arch = spack.spec.ArchSpec(str(arch)) # The call below expects an ArchSpec object if not spack.compilers.compilers_for_arch(arch): - compiler_cmd = spack.main.SpackCommand('compiler') - compiler_cmd( - 'find', output=os.devnull, error=os.devnull, fail_on_error=False - ) + new_compilers = spack.compilers.find_new_compilers() + if new_compilers: + spack.compilers.add_compilers_to_config(new_compilers, init_config=False) + +@contextlib.contextmanager +def ensure_bootstrap_configuration(): bootstrap_store_path = store_path() with spack.environment.deactivate_environment(): with spack.architecture.use_platform(spack.architecture.real_platform()): @@ -511,6 +513,9 @@ def ensure_bootstrap_configuration(): # and builtin but accounting for platform specific scopes config_scopes = _bootstrap_config_scopes() with spack.config.use_configuration(*config_scopes): + # We may need to compile code from sources, so ensure we have + # compilers for the current platform before switching parts. + _add_compilers_if_missing() with spack.modules.disable_modules(): with spack_python_interpreter(): yield diff --git a/lib/spack/spack/cmd/compiler.py b/lib/spack/spack/cmd/compiler.py index 51d3dcced3..3543025cc0 100644 --- a/lib/spack/spack/cmd/compiler.py +++ b/lib/spack/spack/cmd/compiler.py @@ -18,7 +18,6 @@ from llnl.util.tty.color import colorize import spack.compilers import spack.config import spack.spec -from spack.spec import ArchSpec, CompilerSpec description = "manage compilers" section = "system" @@ -78,24 +77,13 @@ def compiler_find(args): # None signals spack.compiler.find_compilers to use its default logic paths = args.add_paths or None - # Don't initialize compilers config via compilers.get_compiler_config. - # Just let compiler_find do the - # entire process and return an empty config from all_compilers - # Default for any other process is init_config=True - compilers = [c for c in spack.compilers.find_compilers(paths)] - new_compilers = [] - for c in compilers: - arch_spec = ArchSpec((None, c.operating_system, c.target)) - same_specs = spack.compilers.compilers_for_spec( - c.spec, arch_spec, init_config=False) - - if not same_specs: - new_compilers.append(c) - + # Below scope=None because we want new compilers that don't appear + # in any other configuration. + new_compilers = spack.compilers.find_new_compilers(paths, scope=None) if new_compilers: - spack.compilers.add_compilers_to_config(new_compilers, - scope=args.scope, - init_config=False) + spack.compilers.add_compilers_to_config( + new_compilers, scope=args.scope, init_config=False + ) n = len(new_compilers) s = 's' if n > 1 else '' @@ -110,7 +98,7 @@ def compiler_find(args): def compiler_remove(args): - cspec = CompilerSpec(args.compiler_spec) + cspec = spack.spec.CompilerSpec(args.compiler_spec) compilers = spack.compilers.compilers_for_spec(cspec, scope=args.scope) if not compilers: tty.die("No compilers match spec %s" % cspec) @@ -128,7 +116,7 @@ def compiler_remove(args): def compiler_info(args): """Print info about all compilers matching a spec.""" - cspec = CompilerSpec(args.compiler_spec) + cspec = spack.spec.CompilerSpec(args.compiler_spec) compilers = spack.compilers.compilers_for_spec(cspec, scope=args.scope) if not compilers: diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py index 7959dad25b..9ecfa69393 100644 --- a/lib/spack/spack/compilers/__init__.py +++ b/lib/spack/spack/compilers/__init__.py @@ -192,15 +192,12 @@ def all_compiler_specs(scope=None, init_config=True): def find_compilers(path_hints=None): - """Returns the list of compilers found in the paths given as arguments. + """Return the list of compilers found in the paths given as arguments. Args: path_hints (list or None): list of path hints where to look for. A sensible default based on the ``PATH`` environment variable will be used if the value is None - - Returns: - List of compilers found """ if path_hints is None: path_hints = get_path('PATH') @@ -242,6 +239,30 @@ def find_compilers(path_hints=None): ) +def find_new_compilers(path_hints=None, scope=None): + """Same as ``find_compilers`` but return only the compilers that are not + already in compilers.yaml. + + Args: + path_hints (list or None): list of path hints where to look for. + A sensible default based on the ``PATH`` environment variable + will be used if the value is None + scope (str): scope to look for a compiler. If None consider the + merged configuration. + """ + compilers = find_compilers(path_hints) + compilers_not_in_config = [] + for c in compilers: + arch_spec = spack.spec.ArchSpec((None, c.operating_system, c.target)) + same_specs = compilers_for_spec( + c.spec, arch_spec, scope=scope, init_config=False + ) + if not same_specs: + compilers_not_in_config.append(c) + + return compilers_not_in_config + + def supported_compilers(): """Return a set of names of compilers supported by Spack. @@ -289,8 +310,9 @@ def all_compilers(scope=None): @_auto_compiler_spec -def compilers_for_spec(compiler_spec, arch_spec=None, scope=None, - use_cache=True, init_config=True): +def compilers_for_spec( + compiler_spec, arch_spec=None, scope=None, use_cache=True, init_config=True +): """This gets all compilers that satisfy the supplied CompilerSpec. Returns an empty list if none are found. """ diff --git a/lib/spack/spack/test/bootstrap.py b/lib/spack/spack/test/bootstrap.py index bb03df3aae..3aed7231f3 100644 --- a/lib/spack/spack/test/bootstrap.py +++ b/lib/spack/spack/test/bootstrap.py @@ -5,6 +5,7 @@ import pytest import spack.bootstrap +import spack.compilers import spack.environment import spack.store import spack.util.path @@ -78,3 +79,23 @@ def test_bootstrap_disables_modulefile_generation(mutable_config): assert 'lmod' not in spack.config.get('modules:enable') assert 'tcl' in spack.config.get('modules:enable') assert 'lmod' in spack.config.get('modules: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) diff --git a/lib/spack/spack/test/cmd/compiler.py b/lib/spack/spack/test/cmd/compiler.py index 2d554c2474..cb04e9bd27 100644 --- a/lib/spack/spack/test/cmd/compiler.py +++ b/lib/spack/spack/test/cmd/compiler.py @@ -17,18 +17,6 @@ compiler = spack.main.SpackCommand('compiler') @pytest.fixture -def no_compilers_yaml(mutable_config): - """Creates a temporary configuration without compilers.yaml""" - - for scope, local_config in mutable_config.scopes.items(): - compilers_yaml = os.path.join( - local_config.path, scope, 'compilers.yaml' - ) - if os.path.exists(compilers_yaml): - os.remove(compilers_yaml) - - -@pytest.fixture def mock_compiler_version(): return '4.5.3' diff --git a/lib/spack/spack/test/conftest.py b/lib/spack/spack/test/conftest.py index 456624127a..7ef3429383 100644 --- a/lib/spack/spack/test/conftest.py +++ b/lib/spack/spack/test/conftest.py @@ -652,6 +652,15 @@ def mutable_empty_config(tmpdir_factory, configuration_dir): yield cfg +@pytest.fixture +def no_compilers_yaml(mutable_config): + """Creates a temporary configuration without compilers.yaml""" + for scope, local_config in mutable_config.scopes.items(): + compilers_yaml = os.path.join(local_config.path, 'compilers.yaml') + if os.path.exists(compilers_yaml): + os.remove(compilers_yaml) + + @pytest.fixture() def mock_low_high_config(tmpdir): """Mocks two configuration scopes: 'low' and 'high'.""" |