From d06c9ce6c93e982b39c1d5f484c69141e1579b8c Mon Sep 17 00:00:00 2001 From: Joerg Henrichs Date: Mon, 2 Dec 2024 23:51:31 +1100 Subject: [PATCH] Created linker wrapper in ToolRepository. --- source/fab/tools/compiler.py | 5 ++++ source/fab/tools/linker.py | 10 +++++--- source/fab/tools/tool_repository.py | 34 +++++++++++++++++++------ tests/unit_tests/tools/test_compiler.py | 4 +-- tests/unit_tests/tools/test_linker.py | 21 +++++++++++++-- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/source/fab/tools/compiler.py b/source/fab/tools/compiler.py index 669bb292..b937e9d1 100644 --- a/source/fab/tools/compiler.py +++ b/source/fab/tools/compiler.py @@ -83,6 +83,11 @@ def openmp_flag(self) -> str: '''Returns the flag to enable OpenMP.''' return self._openmp_flag + @property + def output_flag(self) -> str: + '''Returns the flag that specifies the output flag.''' + return self._output_flag + def get_hash(self) -> int: ''':returns: a hash based on the compiler name and version. ''' diff --git a/source/fab/tools/linker.py b/source/fab/tools/linker.py index 15a1c28a..bf2eda2b 100644 --- a/source/fab/tools/linker.py +++ b/source/fab/tools/linker.py @@ -21,19 +21,21 @@ class Linker(CompilerWrapper): '''This is the base class for any Linker. :param compiler: a compiler or linker instance + :param name: name of the linker :param output_flag: flag to use to specify the output name. ''' - def __init__(self, compiler: Compiler, output_flag: str = "-o"): + def __init__(self, compiler: Compiler, name: Optional[str] = None, + output_flag: Optional[str] = None): super().__init__( - name=f"linker-{compiler.name}", + name=name or f"linker-{compiler.name}", exec_name=compiler.exec_name, compiler=compiler, category=Category.LINKER, mpi=compiler.mpi) - self._output_flag = output_flag + self._output_flag = output_flag or "" self.add_flags(os.getenv("LDFLAGS", "").split()) # Maintain a set of flags for common libraries. @@ -48,7 +50,7 @@ def get_output_flag(self) -> str: if self._output_flag: return self._output_flag if not self.compiler.category == Category.LINKER: - raise RuntimeError(f"No output flag found for linker {self.name}.") + return self.compiler.output_flag linker = cast(Linker, self.compiler) return linker.get_output_flag() diff --git a/source/fab/tools/tool_repository.py b/source/fab/tools/tool_repository.py index 62046712..d93c61c0 100644 --- a/source/fab/tools/tool_repository.py +++ b/source/fab/tools/tool_repository.py @@ -17,8 +17,8 @@ from fab.tools.tool import Tool from fab.tools.category import Category from fab.tools.compiler import Compiler -from fab.tools.compiler_wrapper import (CrayCcWrapper, CrayFtnWrapper, - Mpif90, Mpicc) +from fab.tools.compiler_wrapper import (CompilerWrapper, CrayCcWrapper, + CrayFtnWrapper, Mpif90, Mpicc) from fab.tools.linker import Linker from fab.tools.versioning import Fcm, Git, Subversion from fab.tools import (Ar, Cpp, CppFortran, Craycc, Crayftn, @@ -81,12 +81,12 @@ def __init__(self): # Now create the potential mpif90 and Cray ftn wrapper all_fc = self[Category.FORTRAN_COMPILER][:] for fc in all_fc: - mpif90 = Mpif90(fc) - self.add_tool(mpif90) + if not fc.mpi: + mpif90 = Mpif90(fc) + self.add_tool(mpif90) # I assume cray has (besides cray) only support for Intel and GNU if fc.name in ["gfortran", "ifort"]: crayftn = CrayFtnWrapper(fc) - print("NEW NAME", crayftn, crayftn.name) self.add_tool(crayftn) # Now create the potential mpicc and Cray cc wrapper @@ -114,9 +114,27 @@ def add_tool(self, tool: Tool): # If we have a compiler, add the compiler as linker as well if tool.is_compiler: - tool = cast(Compiler, tool) - linker = Linker(compiler=tool) - self[linker.category].append(linker) + compiler = cast(Compiler, tool) + if isinstance(compiler, CompilerWrapper): + # If we have a compiler wrapper, create a new linker using + # the linker based on the wrappped compiler. For example, when + # creating linker-mpif90-gfortran, we want this to be based on + # linker-gfortran (and not on the compiler mpif90-gfortran), + # since the linker-gfortran might have library definitions + # that should be reused. So we first get the existing linker + # (since the compiler exists, a linker for this compiler was + # already created and must exist). + other_linker = self.get_tool( + category=Category.LINKER, + name=f"linker-{compiler.compiler.name}") + other_linker = cast(Linker, other_linker) + linker = Linker(compiler=other_linker, + name=f"linker-{compiler.name}") + self[linker.category].append(linker) + else: + linker = Linker(compiler=compiler, + name=f"linker-{compiler.name}") + self[linker.category].append(linker) def get_tool(self, category: Category, name: str) -> Tool: ''':returns: the tool with a given name in the specified category. diff --git a/tests/unit_tests/tools/test_compiler.py b/tests/unit_tests/tools/test_compiler.py index f6c7c158..60c18d78 100644 --- a/tests/unit_tests/tools/test_compiler.py +++ b/tests/unit_tests/tools/test_compiler.py @@ -25,7 +25,7 @@ def test_compiler(): category=Category.C_COMPILER, openmp_flag="-fopenmp") assert cc.category == Category.C_COMPILER assert cc._compile_flag == "-c" - assert cc._output_flag == "-o" + assert cc.output_flag == "-o" # pylint: disable-next=use-implicit-booleaness-not-comparison assert cc.flags == [] assert cc.suite == "gnu" @@ -35,7 +35,7 @@ def test_compiler(): fc = FortranCompiler("gfortran", "gfortran", "gnu", openmp_flag="-fopenmp", version_regex="something", module_folder_flag="-J") assert fc._compile_flag == "-c" - assert fc._output_flag == "-o" + assert fc.output_flag == "-o" assert fc.category == Category.FORTRAN_COMPILER assert fc.suite == "gnu" # pylint: disable-next=use-implicit-booleaness-not-comparison diff --git a/tests/unit_tests/tools/test_linker.py b/tests/unit_tests/tools/test_linker.py index 344243a5..cb94a039 100644 --- a/tests/unit_tests/tools/test_linker.py +++ b/tests/unit_tests/tools/test_linker.py @@ -13,7 +13,7 @@ import pytest -from fab.tools import (Category, Linker) +from fab.tools import (Category, Linker, ToolRepository) def test_linker(mock_c_compiler, mock_fortran_compiler): @@ -22,12 +22,13 @@ def test_linker(mock_c_compiler, mock_fortran_compiler): assert mock_c_compiler.category == Category.C_COMPILER assert mock_c_compiler.name == "mock_c_compiler" - linker = Linker(mock_c_compiler) + linker = Linker(mock_c_compiler, output_flag="-o") assert linker.category == Category.LINKER assert linker.name == "linker-mock_c_compiler" assert linker.exec_name == "mock_c_compiler.exe" assert linker.suite == "suite" assert linker.flags == [] + assert linker.get_output_flag() == "-o" assert mock_fortran_compiler.category == Category.FORTRAN_COMPILER assert mock_fortran_compiler.name == "mock_fortran_compiler" @@ -313,3 +314,19 @@ def test_linker_nesting(mock_c_compiler): "b_from_2", "c_from_2", '-o', 'a.out'], capture_output=True, env=None, cwd=None, check=False) + + +def test_linker_inheriting(): + '''Make sure that libraries from a wrapper compiler will be + available for a wrapper. + ''' + tr = ToolRepository() + linker_gfortran = tr.get_tool(Category.LINKER, "linker-gfortran") + linker_mpif90 = tr.get_tool(Category.LINKER, "linker-mpif90-gfortran") + + linker_gfortran.add_lib_flags("lib_a", ["a_from_1"]) + assert linker_mpif90.get_lib_flags("lib_a") == ["a_from_1"] + + with pytest.raises(RuntimeError) as err: + linker_mpif90.get_lib_flags("does_not_exist") + assert "Unknown library name: 'does_not_exist'" in str(err.value)