compiler.py: simplify implicit link dir bits (#43078)

This commit is contained in:
Harmen Stoppels 2024-03-14 08:54:47 +01:00 committed by GitHub
parent 7d67d9ece4
commit c38ef72b06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 50 deletions

View File

@ -389,8 +389,7 @@ def implicit_rpaths(self):
# Put CXX first since it has the most linking issues # Put CXX first since it has the most linking issues
# And because it has flags that affect linking # And because it has flags that affect linking
exe_paths = [x for x in [self.cxx, self.cc, self.fc, self.f77] if x] link_dirs = self._get_compiler_link_paths()
link_dirs = self._get_compiler_link_paths(exe_paths)
all_required_libs = list(self.required_libs) + Compiler._all_compiler_rpath_libraries all_required_libs = list(self.required_libs) + Compiler._all_compiler_rpath_libraries
return list(paths_containing_libs(link_dirs, all_required_libs)) return list(paths_containing_libs(link_dirs, all_required_libs))
@ -403,43 +402,33 @@ def required_libs(self):
# By default every compiler returns the empty list # By default every compiler returns the empty list
return [] return []
def _get_compiler_link_paths(self, paths): def _get_compiler_link_paths(self):
first_compiler = next((c for c in paths if c), None) cc = self.cc if self.cc else self.cxx
if not first_compiler: if not cc or not self.verbose_flag:
return [] # Cannot determine implicit link paths without a compiler / verbose flag
if not self.verbose_flag:
# In this case there is no mechanism to learn what link directories
# are used by the compiler
return [] return []
# What flag types apply to first_compiler, in what order # What flag types apply to first_compiler, in what order
flags = ["cppflags", "ldflags"] if cc == self.cc:
if first_compiler == self.cc: flags = ["cflags", "cppflags", "ldflags"]
flags = ["cflags"] + flags
elif first_compiler == self.cxx:
flags = ["cxxflags"] + flags
else: else:
flags.append("fflags") flags = ["cxxflags", "cppflags", "ldflags"]
try: try:
tmpdir = tempfile.mkdtemp(prefix="spack-implicit-link-info") tmpdir = tempfile.mkdtemp(prefix="spack-implicit-link-info")
fout = os.path.join(tmpdir, "output") fout = os.path.join(tmpdir, "output")
fin = os.path.join(tmpdir, "main.c") fin = os.path.join(tmpdir, "main.c")
with open(fin, "w+") as csource: with open(fin, "w") as csource:
csource.write( csource.write(
"int main(int argc, char* argv[]) { " "(void)argc; (void)argv; return 0; }\n" "int main(int argc, char* argv[]) { (void)argc; (void)argv; return 0; }\n"
) )
compiler_exe = spack.util.executable.Executable(first_compiler) cc_exe = spack.util.executable.Executable(cc)
for flag_type in flags: for flag_type in flags:
for flag in self.flags.get(flag_type, []): cc_exe.add_default_arg(*self.flags.get(flag_type, []))
compiler_exe.add_default_arg(flag)
output = ""
with self.compiler_environment(): with self.compiler_environment():
output = str( output = cc_exe(self.verbose_flag, fin, "-o", fout, output=str, error=str)
compiler_exe(self.verbose_flag, fin, "-o", fout, output=str, error=str)
) # str for py2
return _parse_non_system_link_dirs(output) return _parse_non_system_link_dirs(output)
except spack.util.executable.ProcessError as pe: except spack.util.executable.ProcessError as pe:
tty.debug("ProcessError: Command exited with non-zero status: " + pe.long_message) tty.debug("ProcessError: Command exited with non-zero status: " + pe.long_message)

View File

@ -15,7 +15,7 @@
import spack.spec import spack.spec
import spack.util.environment import spack.util.environment
from spack.compiler import Compiler from spack.compiler import Compiler
from spack.util.executable import ProcessError from spack.util.executable import Executable, ProcessError
@pytest.fixture() @pytest.fixture()
@ -138,11 +138,11 @@ def __init__(self):
environment={}, environment={},
) )
def _get_compiler_link_paths(self, paths): def _get_compiler_link_paths(self):
# Mock os.path.isdir so the link paths don't have to exist # Mock os.path.isdir so the link paths don't have to exist
old_isdir = os.path.isdir old_isdir = os.path.isdir
os.path.isdir = lambda x: True os.path.isdir = lambda x: True
ret = super()._get_compiler_link_paths(paths) ret = super()._get_compiler_link_paths()
os.path.isdir = old_isdir os.path.isdir = old_isdir
return ret return ret
@ -197,37 +197,37 @@ def call_compiler(exe, *args, **kwargs):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"exe,flagname", "exe,flagname",
[ [
("cxx", ""),
("cxx", "cxxflags"), ("cxx", "cxxflags"),
("cxx", "cppflags"), ("cxx", "cppflags"),
("cxx", "ldflags"), ("cxx", "ldflags"),
("cc", ""),
("cc", "cflags"), ("cc", "cflags"),
("cc", "cppflags"), ("cc", "cppflags"),
("fc", ""),
("fc", "fflags"),
("f77", "fflags"),
("f77", "cppflags"),
], ],
) )
@pytest.mark.enable_compiler_link_paths @pytest.mark.enable_compiler_link_paths
def test_get_compiler_link_paths(monkeypatch, exe, flagname): def test_get_compiler_link_paths(monkeypatch, exe, flagname):
# create fake compiler that emits mock verbose output # create fake compiler that emits mock verbose output
compiler = MockCompiler() compiler = MockCompiler()
monkeypatch.setattr(spack.util.executable.Executable, "__call__", call_compiler) monkeypatch.setattr(Executable, "__call__", call_compiler)
# Grab executable path to test if exe == "cxx":
paths = [getattr(compiler, exe)] compiler.cc = None
compiler.fc = None
compiler.f77 = None
elif exe == "cc":
compiler.cxx = None
compiler.fc = None
compiler.f77 = None
else:
assert False
# Test without flags # Test without flags
dirs = compiler._get_compiler_link_paths(paths) assert compiler._get_compiler_link_paths() == no_flag_dirs
assert dirs == no_flag_dirs
if flagname: if flagname:
# set flags and test # set flags and test
setattr(compiler, "flags", {flagname: ["--correct-flag"]}) compiler.flags = {flagname: ["--correct-flag"]}
dirs = compiler._get_compiler_link_paths(paths) assert compiler._get_compiler_link_paths() == flag_dirs
assert dirs == flag_dirs
def test_get_compiler_link_paths_no_path(): def test_get_compiler_link_paths_no_path():
@ -236,17 +236,13 @@ def test_get_compiler_link_paths_no_path():
compiler.cxx = None compiler.cxx = None
compiler.f77 = None compiler.f77 = None
compiler.fc = None compiler.fc = None
assert compiler._get_compiler_link_paths() == []
dirs = compiler._get_compiler_link_paths([compiler.cxx])
assert dirs == []
def test_get_compiler_link_paths_no_verbose_flag(): def test_get_compiler_link_paths_no_verbose_flag():
compiler = MockCompiler() compiler = MockCompiler()
compiler._verbose_flag = None compiler._verbose_flag = None
assert compiler._get_compiler_link_paths() == []
dirs = compiler._get_compiler_link_paths([compiler.cxx])
assert dirs == []
@pytest.mark.not_on_windows("Not supported on Windows (yet)") @pytest.mark.not_on_windows("Not supported on Windows (yet)")
@ -275,11 +271,11 @@ def module(*args):
monkeypatch.setattr(spack.util.module_cmd, "module", module) monkeypatch.setattr(spack.util.module_cmd, "module", module)
compiler = MockCompiler() compiler = MockCompiler()
compiler.cc = gcc
compiler.environment = {"set": {"ENV_SET": "1"}} compiler.environment = {"set": {"ENV_SET": "1"}}
compiler.modules = ["turn_on"] compiler.modules = ["turn_on"]
dirs = compiler._get_compiler_link_paths([gcc]) assert compiler._get_compiler_link_paths() == no_flag_dirs
assert dirs == no_flag_dirs
# Get the desired flag from the specified compiler spec. # Get the desired flag from the specified compiler spec.
@ -824,7 +820,7 @@ def module(*args):
def _call(*args, **kwargs): def _call(*args, **kwargs):
raise ProcessError("Failed intentionally") raise ProcessError("Failed intentionally")
monkeypatch.setattr(spack.util.executable.Executable, "__call__", _call) monkeypatch.setattr(Executable, "__call__", _call)
# Run and no change to environment # Run and no change to environment
compilers = spack.compilers.get_compilers([compiler_dict]) compilers = spack.compilers.get_compilers([compiler_dict])