diff --git a/lib/spack/spack/cmd/compiler.py b/lib/spack/spack/cmd/compiler.py index 563142bc8fa..4bb73e628ca 100644 --- a/lib/spack/spack/cmd/compiler.py +++ b/lib/spack/spack/cmd/compiler.py @@ -53,7 +53,7 @@ def setup_parser(subparser): "--scope", choices=scopes, metavar=scopes_metavar, - default=spack.config.default_modify_scope("compilers"), + default=None, help="configuration scope to modify", ) @@ -106,19 +106,21 @@ def compiler_find(args): def compiler_remove(args): - 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) - elif not args.all and len(compilers) > 1: - tty.error("Multiple compilers match spec %s. Choose one:" % cspec) - colify(reversed(sorted([c.spec.display_str for c in compilers])), indent=4) + compiler_spec = spack.spec.CompilerSpec(args.compiler_spec) + candidate_compilers = spack.compilers.compilers_for_spec(compiler_spec, scope=args.scope) + + if not candidate_compilers: + tty.die("No compilers match spec %s" % compiler_spec) + + if not args.all and len(candidate_compilers) > 1: + tty.error(f"Multiple compilers match spec {compiler_spec}. Choose one:") + colify(reversed(sorted([c.spec.display_str for c in candidate_compilers])), indent=4) tty.msg("Or, use `spack compiler remove -a` to remove all of them.") sys.exit(1) - for compiler in compilers: - spack.compilers.remove_compiler_from_config(compiler.spec, scope=args.scope) - tty.msg("Removed compiler %s" % compiler.spec.display_str) + for current_compiler in candidate_compilers: + spack.compilers.remove_compiler_from_config(current_compiler.spec, scope=args.scope) + tty.msg(f"{current_compiler.spec.display_str} has been removed") def compiler_info(args): diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py index c867f927c0e..f6064a9d3f3 100644 --- a/lib/spack/spack/compilers/__init__.py +++ b/lib/spack/spack/compilers/__init__.py @@ -37,7 +37,6 @@ "implicit_rpaths", "extra_rpaths", ] -_cache_config_file = [] # TODO: Caches at module level make it difficult to mock configurations in # TODO: unit tests. It might be worth reworking their implementation. @@ -155,52 +154,65 @@ def add_compilers_to_config(compilers, scope=None, init_config=True): compiler_config = get_compiler_config(scope, init_config) for compiler in compilers: compiler_config.append(_to_dict(compiler)) - global _cache_config_file - _cache_config_file = compiler_config spack.config.set("compilers", compiler_config, scope=scope) @_auto_compiler_spec def remove_compiler_from_config(compiler_spec, scope=None): - """Remove compilers from the config, by spec. + """Remove compilers from configuration by spec. + + If scope is None, all the scopes are searched for removal. Arguments: - compiler_specs: a list of CompilerSpec objects. - scope: configuration scope to modify. + compiler_spec: compiler to be removed + scope: configuration scope to modify """ - # Need a better way for this - global _cache_config_file + candidate_scopes = [scope] + if scope is None: + candidate_scopes = spack.config.config.scopes.keys() + removal_happened = False + for current_scope in candidate_scopes: + removal_happened |= _remove_compiler_from_scope(compiler_spec, scope=current_scope) + + return removal_happened + + +def _remove_compiler_from_scope(compiler_spec, scope): + """Removes a compiler from a specific configuration scope. + + Args: + compiler_spec: compiler to be removed + scope: configuration scope under consideration + + Returns: + True if one or more compiler entries were actually removed, False otherwise + """ + assert scope is not None, "a specific scope is needed when calling this function" compiler_config = get_compiler_config(scope) - config_length = len(compiler_config) - filtered_compiler_config = [ - comp - for comp in compiler_config + compiler_entry + for compiler_entry in compiler_config if not spack.spec.parse_with_version_concrete( - comp["compiler"]["spec"], compiler=True + compiler_entry["compiler"]["spec"], compiler=True ).satisfies(compiler_spec) ] - # Update the cache for changes - _cache_config_file = filtered_compiler_config - if len(filtered_compiler_config) == config_length: # No items removed - CompilerSpecInsufficientlySpecificError(compiler_spec) - spack.config.set("compilers", filtered_compiler_config, scope=scope) + if len(filtered_compiler_config) == len(compiler_config): + return False + + # We need to preserve the YAML type for comments, hence we are copying the + # items in the list that has just been retrieved + compiler_config[:] = filtered_compiler_config + spack.config.set("compilers", compiler_config, scope=scope) + return True def all_compilers_config(scope=None, init_config=True): """Return a set of specs for all the compiler versions currently available to build with. These are instances of CompilerSpec. """ - # Get compilers for this architecture. - # Create a cache of the config file so we don't load all the time. - global _cache_config_file - if not _cache_config_file: - _cache_config_file = get_compiler_config(scope, init_config) - return _cache_config_file - else: - return _cache_config_file + return get_compiler_config(scope, init_config) def all_compiler_specs(scope=None, init_config=True): diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py index 2153f455896..1bc5b9b9a4a 100644 --- a/lib/spack/spack/config.py +++ b/lib/spack/spack/config.py @@ -1353,17 +1353,11 @@ def use_configuration(*scopes_or_paths): configuration = _config_from(scopes_or_paths) config.clear_caches(), configuration.clear_caches() - # Save and clear the current compiler cache - saved_compiler_cache = spack.compilers._cache_config_file - spack.compilers._cache_config_file = [] - saved_config, config = config, configuration try: yield configuration finally: - # Restore previous config files - spack.compilers._cache_config_file = saved_compiler_cache config = saved_config diff --git a/lib/spack/spack/test/bindist.py b/lib/spack/spack/test/bindist.py index 2941e1896cd..f16459bc7f5 100644 --- a/lib/spack/spack/test/bindist.py +++ b/lib/spack/spack/test/bindist.py @@ -115,9 +115,6 @@ def default_config(tmpdir, config_directory, monkeypatch, install_mockery_mutabl spack.config.config, old_config = cfg, spack.config.config spack.config.config.set("repos", [spack.paths.mock_packages_path]) - # This is essential, otherwise the cache will create weird side effects - # that will compromise subsequent tests if compilers.yaml is modified - monkeypatch.setattr(spack.compilers, "_cache_config_file", []) njobs = spack.config.get("config:build_jobs") if not njobs: spack.config.set("config:build_jobs", 4, scope="user") diff --git a/lib/spack/spack/test/cmd/compiler.py b/lib/spack/spack/test/cmd/compiler.py index bca913f358b..87eb7e4daf1 100644 --- a/lib/spack/spack/test/cmd/compiler.py +++ b/lib/spack/spack/test/cmd/compiler.py @@ -8,8 +8,6 @@ import pytest -import llnl.util.filesystem - import spack.compilers import spack.main import spack.version @@ -18,124 +16,8 @@ @pytest.fixture -def mock_compiler_version(): - return "4.5.3" - - -@pytest.fixture() -def mock_compiler_dir(tmpdir, mock_compiler_version): - """Return a directory containing a fake, but detectable compiler.""" - - tmpdir.ensure("bin", dir=True) - bin_dir = tmpdir.join("bin") - - gcc_path = bin_dir.join("gcc") - gxx_path = bin_dir.join("g++") - gfortran_path = bin_dir.join("gfortran") - - gcc_path.write( - """\ -#!/bin/sh - -for arg in "$@"; do - if [ "$arg" = -dumpversion ]; then - echo '%s' - fi -done -""" - % mock_compiler_version - ) - - # Create some mock compilers in the temporary directory - llnl.util.filesystem.set_executable(str(gcc_path)) - gcc_path.copy(gxx_path, mode=True) - gcc_path.copy(gfortran_path, mode=True) - - return str(tmpdir) - - -@pytest.mark.skipif( - sys.platform == "win32", - reason="Cannot execute bash \ - script on Windows", -) -@pytest.mark.regression("11678,13138") -def test_compiler_find_without_paths(no_compilers_yaml, working_env, tmpdir): - with tmpdir.as_cwd(): - with open("gcc", "w") as f: - f.write( - """\ -#!/bin/sh -echo "0.0.0" -""" - ) - os.chmod("gcc", 0o700) - - os.environ["PATH"] = str(tmpdir) - output = compiler("find", "--scope=site") - - assert "gcc" in output - - -@pytest.mark.regression("17589") -def test_compiler_find_no_apple_gcc(no_compilers_yaml, working_env, tmpdir): - with tmpdir.as_cwd(): - # make a script to emulate apple gcc's version args - with open("gcc", "w") as f: - f.write( - """\ -#!/bin/sh -if [ "$1" = "-dumpversion" ]; then - echo "4.2.1" -elif [ "$1" = "--version" ]; then - echo "Configured with: --prefix=/dummy" - echo "Apple clang version 11.0.0 (clang-1100.0.33.16)" - echo "Target: x86_64-apple-darwin18.7.0" - echo "Thread model: posix" - echo "InstalledDir: /dummy" -else - echo "clang: error: no input files" -fi -""" - ) - os.chmod("gcc", 0o700) - - os.environ["PATH"] = str(tmpdir) - output = compiler("find", "--scope=site") - - assert "gcc" not in output - - -def test_compiler_remove(mutable_config, mock_packages): - assert spack.spec.CompilerSpec("gcc@=4.5.0") in spack.compilers.all_compiler_specs() - args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.5.0", add_paths=[], scope=None) - spack.cmd.compiler.compiler_remove(args) - assert spack.spec.CompilerSpec("gcc@=4.5.0") not in spack.compilers.all_compiler_specs() - - -@pytest.mark.skipif( - sys.platform == "win32", - reason="Cannot execute bash \ - script on Windows", -) -def test_compiler_add(mutable_config, mock_packages, mock_compiler_dir, mock_compiler_version): - # Compilers available by default. - old_compilers = set(spack.compilers.all_compiler_specs()) - - args = spack.util.pattern.Bunch( - all=None, compiler_spec=None, add_paths=[mock_compiler_dir], scope=None - ) - spack.cmd.compiler.compiler_find(args) - - # Ensure new compiler is in there - new_compilers = set(spack.compilers.all_compiler_specs()) - new_compiler = new_compilers - old_compilers - assert any(c.version == spack.version.Version(mock_compiler_version) for c in new_compiler) - - -@pytest.fixture -def clangdir(tmpdir): - """Create a directory with some dummy compiler scripts in it. +def compilers_dir(mock_executable): + """Create a directory with some mock compiler scripts in it. Scripts are: - clang @@ -145,11 +27,9 @@ def clangdir(tmpdir): - gfortran-8 """ - with tmpdir.as_cwd(): - with open("clang", "w") as f: - f.write( - """\ -#!/bin/sh + clang_path = mock_executable( + "clang", + output=""" if [ "$1" = "--version" ]; then echo "clang version 11.0.0 (clang-1100.0.33.16)" echo "Target: x86_64-apple-darwin18.7.0" @@ -159,12 +39,11 @@ def clangdir(tmpdir): echo "clang: error: no input files" exit 1 fi -""" - ) - shutil.copy("clang", "clang++") +""", + ) + shutil.copy(clang_path, clang_path.parent / "clang++") - gcc_script = """\ -#!/bin/sh + gcc_script = """ if [ "$1" = "-dumpversion" ]; then echo "8" elif [ "$1" = "-dumpfullversion" ]; then @@ -178,30 +57,111 @@ def clangdir(tmpdir): exit 1 fi """ - with open("gcc-8", "w") as f: - f.write(gcc_script.format("gcc", "gcc-8")) - with open("g++-8", "w") as f: - f.write(gcc_script.format("g++", "g++-8")) - with open("gfortran-8", "w") as f: - f.write(gcc_script.format("GNU Fortran", "gfortran-8")) - os.chmod("clang", 0o700) - os.chmod("clang++", 0o700) - os.chmod("gcc-8", 0o700) - os.chmod("g++-8", 0o700) - os.chmod("gfortran-8", 0o700) + mock_executable("gcc-8", output=gcc_script.format("gcc", "gcc-8")) + mock_executable("g++-8", output=gcc_script.format("g++", "g++-8")) + mock_executable("gfortran-8", output=gcc_script.format("GNU Fortran", "gfortran-8")) - yield tmpdir + return clang_path.parent -@pytest.mark.skipif( - sys.platform == "win32", - reason="Cannot execute bash \ - script on Windows", -) +@pytest.mark.skipif(sys.platform == "win32", reason="Cannot execute bash script on Windows") +@pytest.mark.regression("11678,13138") +def test_compiler_find_without_paths(no_compilers_yaml, working_env, mock_executable): + """Tests that 'spack compiler find' looks into PATH by default, if no specific path + is given. + """ + gcc_path = mock_executable("gcc", output='echo "0.0.0"') + + os.environ["PATH"] = str(gcc_path.parent) + output = compiler("find", "--scope=site") + + assert "gcc" in output + + +@pytest.mark.regression("17589") +def test_compiler_find_no_apple_gcc(no_compilers_yaml, working_env, mock_executable): + """Tests that Spack won't mistake Apple's GCC as a "real" GCC, since it's really + Clang with a few tweaks. + """ + gcc_path = mock_executable( + "gcc", + output=""" +if [ "$1" = "-dumpversion" ]; then + echo "4.2.1" +elif [ "$1" = "--version" ]; then + echo "Configured with: --prefix=/dummy" + echo "Apple clang version 11.0.0 (clang-1100.0.33.16)" + echo "Target: x86_64-apple-darwin18.7.0" + echo "Thread model: posix" + echo "InstalledDir: /dummy" +else + echo "clang: error: no input files" +fi +""", + ) + + os.environ["PATH"] = str(gcc_path.parent) + output = compiler("find", "--scope=site") + + assert "gcc" not in output + + +@pytest.mark.regression("37996") +def test_compiler_remove(mutable_config, mock_packages): + """Tests that we can remove a compiler from configuration.""" + assert spack.spec.CompilerSpec("gcc@=4.5.0") in spack.compilers.all_compiler_specs() + args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.5.0", add_paths=[], scope=None) + spack.cmd.compiler.compiler_remove(args) + assert spack.spec.CompilerSpec("gcc@=4.5.0") not in spack.compilers.all_compiler_specs() + + +@pytest.mark.regression("37996") +def test_removing_compilers_from_multiple_scopes(mutable_config, mock_packages): + # Duplicate "site" scope into "user" scope + site_config = spack.config.get("compilers", scope="site") + spack.config.set("compilers", site_config, scope="user") + + assert spack.spec.CompilerSpec("gcc@=4.5.0") in spack.compilers.all_compiler_specs() + args = spack.util.pattern.Bunch(all=True, compiler_spec="gcc@4.5.0", add_paths=[], scope=None) + spack.cmd.compiler.compiler_remove(args) + assert spack.spec.CompilerSpec("gcc@=4.5.0") not in spack.compilers.all_compiler_specs() + + +@pytest.mark.skipif(sys.platform == "win32", reason="Cannot execute bash script on Windows") +def test_compiler_add(mutable_config, mock_packages, mock_executable): + """Tests that we can add a compiler to configuration.""" + expected_version = "4.5.3" + gcc_path = mock_executable( + "gcc", + output=f"""\ +for arg in "$@"; do + if [ "$arg" = -dumpversion ]; then + echo '{expected_version}' + fi +done +""", + ) + bin_dir = gcc_path.parent + root_dir = bin_dir.parent + + compilers_before_find = set(spack.compilers.all_compiler_specs()) + args = spack.util.pattern.Bunch( + all=None, compiler_spec=None, add_paths=[str(root_dir)], scope=None + ) + spack.cmd.compiler.compiler_find(args) + compilers_after_find = set(spack.compilers.all_compiler_specs()) + + compilers_added_by_find = compilers_after_find - compilers_before_find + assert len(compilers_added_by_find) == 1 + new_compiler = compilers_added_by_find.pop() + assert new_compiler.version == spack.version.Version(expected_version) + + +@pytest.mark.skipif(sys.platform == "win32", reason="Cannot execute bash script on Windows") @pytest.mark.regression("17590") -def test_compiler_find_mixed_suffixes(no_compilers_yaml, working_env, clangdir): +def test_compiler_find_mixed_suffixes(no_compilers_yaml, working_env, compilers_dir): """Ensure that we'll mix compilers with different suffixes when necessary.""" - os.environ["PATH"] = str(clangdir) + os.environ["PATH"] = str(compilers_dir) output = compiler("find", "--scope=site") assert "clang@11.0.0" in output @@ -211,39 +171,33 @@ def test_compiler_find_mixed_suffixes(no_compilers_yaml, working_env, clangdir): clang = next(c["compiler"] for c in config if c["compiler"]["spec"] == "clang@=11.0.0") gcc = next(c["compiler"] for c in config if c["compiler"]["spec"] == "gcc@=8.4.0") - gfortran_path = str(clangdir.join("gfortran-8")) + gfortran_path = str(compilers_dir / "gfortran-8") assert clang["paths"] == { - "cc": str(clangdir.join("clang")), - "cxx": str(clangdir.join("clang++")), + "cc": str(compilers_dir / "clang"), + "cxx": str(compilers_dir / "clang++"), # we only auto-detect mixed clang on macos "f77": gfortran_path if sys.platform == "darwin" else None, "fc": gfortran_path if sys.platform == "darwin" else None, } assert gcc["paths"] == { - "cc": str(clangdir.join("gcc-8")), - "cxx": str(clangdir.join("g++-8")), + "cc": str(compilers_dir / "gcc-8"), + "cxx": str(compilers_dir / "g++-8"), "f77": gfortran_path, "fc": gfortran_path, } -@pytest.mark.skipif( - sys.platform == "win32", - reason="Cannot execute bash \ - script on Windows", -) +@pytest.mark.skipif(sys.platform == "win32", reason="Cannot execute bash script on Windows") @pytest.mark.regression("17590") -def test_compiler_find_prefer_no_suffix(no_compilers_yaml, working_env, clangdir): +def test_compiler_find_prefer_no_suffix(no_compilers_yaml, working_env, compilers_dir): """Ensure that we'll pick 'clang' over 'clang-gpu' when there is a choice.""" - with clangdir.as_cwd(): - shutil.copy("clang", "clang-gpu") - shutil.copy("clang++", "clang++-gpu") - os.chmod("clang-gpu", 0o700) - os.chmod("clang++-gpu", 0o700) + clang_path = compilers_dir / "clang" + shutil.copy(clang_path, clang_path.parent / "clang-gpu") + shutil.copy(clang_path, clang_path.parent / "clang++-gpu") - os.environ["PATH"] = str(clangdir) + os.environ["PATH"] = str(compilers_dir) output = compiler("find", "--scope=site") assert "clang@11.0.0" in output @@ -252,46 +206,38 @@ def test_compiler_find_prefer_no_suffix(no_compilers_yaml, working_env, clangdir config = spack.compilers.get_compiler_config("site", False) clang = next(c["compiler"] for c in config if c["compiler"]["spec"] == "clang@=11.0.0") - assert clang["paths"]["cc"] == str(clangdir.join("clang")) - assert clang["paths"]["cxx"] == str(clangdir.join("clang++")) + assert clang["paths"]["cc"] == str(compilers_dir / "clang") + assert clang["paths"]["cxx"] == str(compilers_dir / "clang++") -@pytest.mark.skipif( - sys.platform == "win32", - reason="Cannot execute bash \ - script on Windows", -) -def test_compiler_find_path_order(no_compilers_yaml, working_env, clangdir): - """Ensure that we find compilers that come first in the PATH first""" - - with clangdir.as_cwd(): - os.mkdir("first_in_path") - shutil.copy("gcc-8", "first_in_path/gcc-8") - shutil.copy("g++-8", "first_in_path/g++-8") - shutil.copy("gfortran-8", "first_in_path/gfortran-8") - - # the first_in_path folder should be searched first - os.environ["PATH"] = "{0}:{1}".format(str(clangdir.join("first_in_path")), str(clangdir)) +@pytest.mark.skipif(sys.platform == "win32", reason="Cannot execute bash script on Windows") +def test_compiler_find_path_order(no_compilers_yaml, working_env, compilers_dir): + """Ensure that we look for compilers in the same order as PATH, when there are duplicates""" + new_dir = compilers_dir / "first_in_path" + new_dir.mkdir() + for name in ("gcc-8", "g++-8", "gfortran-8"): + shutil.copy(compilers_dir / name, new_dir / name) + # Set PATH to have the new folder searched first + os.environ["PATH"] = "{}:{}".format(str(new_dir), str(compilers_dir)) compiler("find", "--scope=site") config = spack.compilers.get_compiler_config("site", False) - gcc = next(c["compiler"] for c in config if c["compiler"]["spec"] == "gcc@=8.4.0") - assert gcc["paths"] == { - "cc": str(clangdir.join("first_in_path", "gcc-8")), - "cxx": str(clangdir.join("first_in_path", "g++-8")), - "f77": str(clangdir.join("first_in_path", "gfortran-8")), - "fc": str(clangdir.join("first_in_path", "gfortran-8")), + "cc": str(new_dir / "gcc-8"), + "cxx": str(new_dir / "g++-8"), + "f77": str(new_dir / "gfortran-8"), + "fc": str(new_dir / "gfortran-8"), } -def test_compiler_list_empty(no_compilers_yaml, working_env, clangdir): - # Spack should not automatically search for compilers when listing them and none - # are available. And when stdout is not a tty like in tests, there should be no - # output and no error exit code. - os.environ["PATH"] = str(clangdir) +def test_compiler_list_empty(no_compilers_yaml, working_env, compilers_dir): + """Spack should not automatically search for compilers when listing them and none are + available. And when stdout is not a tty like in tests, there should be no output and + no error exit code. + """ + os.environ["PATH"] = str(compilers_dir) out = compiler("list") assert not out assert compiler.returncode == 0 diff --git a/lib/spack/spack/test/cmd/external.py b/lib/spack/spack/test/cmd/external.py index 19c91266123..848e14315de 100644 --- a/lib/spack/spack/test/cmd/external.py +++ b/lib/spack/spack/test/cmd/external.py @@ -44,9 +44,8 @@ def define_plat_exe(exe): def test_find_external_single_package(mock_executable, executables_found, _platform_executables): pkgs_to_check = [spack.repo.path.get_pkg_class("cmake")] - executables_found( - {mock_executable("cmake", output="echo cmake version 1.foo"): define_plat_exe("cmake")} - ) + cmake_path = mock_executable("cmake", output="echo cmake version 1.foo") + executables_found({str(cmake_path): define_plat_exe("cmake")}) pkg_to_entries = spack.detection.by_executable(pkgs_to_check) @@ -71,7 +70,7 @@ def test_find_external_two_instances_same_package( "cmake", output="echo cmake version 3.17.2", subdir=("base2", "bin") ) cmake_exe = define_plat_exe("cmake") - executables_found({cmake_path1: cmake_exe, cmake_path2: cmake_exe}) + executables_found({str(cmake_path1): cmake_exe, str(cmake_path2): cmake_exe}) pkg_to_entries = spack.detection.by_executable(pkgs_to_check) @@ -107,7 +106,7 @@ def test_get_executables(working_env, mock_executable): cmake_path1 = mock_executable("cmake", output="echo cmake version 1.foo") path_to_exe = spack.detection.executables_in_path([os.path.dirname(cmake_path1)]) cmake_exe = define_plat_exe("cmake") - assert path_to_exe[cmake_path1] == cmake_exe + assert path_to_exe[str(cmake_path1)] == cmake_exe external = SpackCommand("external") @@ -334,7 +333,7 @@ def test_packages_yaml_format(mock_executable, mutable_config, monkeypatch, _pla assert "extra_attributes" in external_gcc extra_attributes = external_gcc["extra_attributes"] assert "prefix" not in extra_attributes - assert extra_attributes["compilers"]["c"] == gcc_exe + assert extra_attributes["compilers"]["c"] == str(gcc_exe) def test_overriding_prefix(mock_executable, mutable_config, monkeypatch, _platform_executables): diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index 8ef9b558b17..f1b4ceba6ae 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -337,8 +337,6 @@ def test_compiler_flags_differ_identical_compilers(self): # Get the compiler that matches the spec ( compiler = spack.compilers.compiler_for_spec("clang@=12.2.0", spec.architecture) - # Clear cache for compiler config since it has its own cache mechanism outside of config - spack.compilers._cache_config_file = [] # Configure spack to have two identical compilers with different flags default_dict = spack.compilers._to_dict(compiler) @@ -2137,7 +2135,7 @@ def test_compiler_with_custom_non_numeric_version(self, mock_executable): { "compiler": { "spec": "gcc@foo", - "paths": {"cc": gcc_path, "cxx": gcc_path, "f77": None, "fc": None}, + "paths": {"cc": str(gcc_path), "cxx": str(gcc_path), "f77": None, "fc": None}, "operating_system": "debian6", "modules": [], } diff --git a/lib/spack/spack/test/conftest.py b/lib/spack/spack/test/conftest.py index 3a6b36e85b8..f0e5f7fdb2c 100644 --- a/lib/spack/spack/test/conftest.py +++ b/lib/spack/spack/test/conftest.py @@ -1669,22 +1669,21 @@ def clear_directive_functions(): @pytest.fixture -def mock_executable(tmpdir): +def mock_executable(tmp_path): """Factory to create a mock executable in a temporary directory that output a custom string when run. """ - import jinja2 - shebang = "#!/bin/sh\n" if sys.platform != "win32" else "@ECHO OFF" def _factory(name, output, subdir=("bin",)): - f = tmpdir.ensure(*subdir, dir=True).join(name) + executable_dir = tmp_path.joinpath(*subdir) + executable_dir.mkdir(parents=True, exist_ok=True) + executable_path = executable_dir / name if sys.platform == "win32": - f += ".bat" - t = jinja2.Template("{{ shebang }}{{ output }}\n") - f.write(t.render(shebang=shebang, output=output)) - f.chmod(0o755) - return str(f) + executable_path = executable_dir / (name + ".bat") + executable_path.write_text(f"{ shebang }{ output }\n") + executable_path.chmod(0o755) + return executable_path return _factory diff --git a/lib/spack/spack/test/relocate.py b/lib/spack/spack/test/relocate.py index ed83cc29145..481a6fab454 100644 --- a/lib/spack/spack/test/relocate.py +++ b/lib/spack/spack/test/relocate.py @@ -246,12 +246,12 @@ def test_set_elf_rpaths(mock_patchelf): # the call made to patchelf itself patchelf = mock_patchelf("echo $@") rpaths = ["/usr/lib", "/usr/lib64", "/opt/local/lib"] - output = spack.relocate._set_elf_rpaths(patchelf, rpaths) + output = spack.relocate._set_elf_rpaths(str(patchelf), rpaths) # Assert that the arguments of the call to patchelf are as expected assert "--force-rpath" in output assert "--set-rpath " + ":".join(rpaths) in output - assert patchelf in output + assert str(patchelf) in output @skip_unless_linux @@ -261,7 +261,7 @@ def test_set_elf_rpaths_warning(mock_patchelf): rpaths = ["/usr/lib", "/usr/lib64", "/opt/local/lib"] # To avoid using capfd in order to check if the warning was triggered # here we just check that output is not set - output = spack.relocate._set_elf_rpaths(patchelf, rpaths) + output = spack.relocate._set_elf_rpaths(str(patchelf), rpaths) assert output is None diff --git a/lib/spack/spack/test/versions.py b/lib/spack/spack/test/versions.py index 8b58bf14ee3..7d329336aee 100644 --- a/lib/spack/spack/test/versions.py +++ b/lib/spack/spack/test/versions.py @@ -935,7 +935,7 @@ def test_inclusion_upperbound(): @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") def test_git_version_repo_attached_after_serialization( - mock_git_version_info, mock_packages, monkeypatch + mock_git_version_info, mock_packages, config, monkeypatch ): """Test that a GitVersion instance can be serialized and deserialized without losing its repository reference. @@ -954,7 +954,9 @@ def test_git_version_repo_attached_after_serialization( @pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)") -def test_resolved_git_version_is_shown_in_str(mock_git_version_info, mock_packages, monkeypatch): +def test_resolved_git_version_is_shown_in_str( + mock_git_version_info, mock_packages, config, monkeypatch +): """Test that a GitVersion from a commit without a user supplied version is printed as =, and not just .""" repo_path, _, commits = mock_git_version_info @@ -968,7 +970,7 @@ def test_resolved_git_version_is_shown_in_str(mock_git_version_info, mock_packag assert str(spec.version) == f"{commit}=1.0-git.1" -def test_unresolvable_git_versions_error(mock_packages): +def test_unresolvable_git_versions_error(config, mock_packages): """Test that VersionLookupError is raised when a git prop is not set on a package.""" with pytest.raises(VersionLookupError): # The package exists, but does not have a git property set. When dereferencing