diff --git a/source/fab/tools/linker.py b/source/fab/tools/linker.py index cb832c27..4bd962cf 100644 --- a/source/fab/tools/linker.py +++ b/source/fab/tools/linker.py @@ -34,6 +34,7 @@ class Linker(CompilerSuiteTool): def __init__(self, compiler: Optional[Compiler] = None, linker: Optional[Linker] = None, + exec_name: Optional[str] = None, name: Optional[str] = None): if linker and compiler: @@ -53,9 +54,13 @@ def __init__(self, compiler: Optional[Compiler] = None, assert final_compiler name = f"linker-{final_compiler.name}" + if not exec_name: + # This will search for the name in linker or compiler + exec_name = self.get_exec_name() + super().__init__( name=name or f"linker-{name}", - exec_name=self.exec_name, + exec_name=exec_name, suite=self.suite, category=Category.LINKER) @@ -67,11 +72,15 @@ def __init__(self, compiler: Optional[Compiler] = None, self._pre_lib_flags: List[str] = [] self._post_lib_flags: List[str] = [] - def check_available(self): + def check_available(self) -> bool: + ''':returns: whether this linker is available by asking the wrapped + linker or compiler. + ''' return (self._compiler or self._linker).check_available() - @property - def exec_name(self) -> str: + def get_exec_name(self) -> str: + ''':returns: the name of the executable by asking the wrapped + linker or compiler.''' if self._compiler: return self._compiler.exec_name assert self._linker # make mypy happy @@ -79,10 +88,14 @@ def exec_name(self) -> str: @property def suite(self) -> str: + ''':returns: the suite this linker belongs to by getting it from + the wrapper compiler or linker.''' return cast(CompilerSuiteTool, (self._compiler or self._linker)).suite @property def mpi(self) -> bool: + ''':returns" whether this linker supports MPI or not by checking + with the wrapper compiler or linker.''' if self._compiler: return self._compiler.mpi assert self._linker @@ -158,7 +171,7 @@ def add_post_lib_flags(self, flags: List[str]): def get_pre_link_flags(self) -> List[str]: '''Returns the list of pre-link flags. It will concatenate the - flags for this instance with all potentially wrapper linkers. + flags for this instance with all potentially wrapped linkers. This wrapper's flag will come first - the assumption is that the pre-link flags are likely paths, so we need a wrapper to be able to put a search path before the paths from a wrapped @@ -178,6 +191,25 @@ def get_pre_link_flags(self) -> List[str]: params.extend(self._linker.get_pre_link_flags()) return params + def get_post_link_flags(self) -> List[str]: + '''Returns the list of post-link flags. It will concatenate the + flags for this instance with all potentially wrapped linkers. + This wrapper's flag will be added to the end. + + :returns: List of post-link flags of this linker and all + wrapped linkers + ''' + params: List[str] = [] + if self._linker: + # If we are wrapping a linker, get the wrapped linker's + # pre-link flags and append them to the end (so the linker + # wrapper's settings come before the setting from the + # wrapped linker). + params.extend(self._linker.get_post_link_flags()) + if self._post_lib_flags: + params.extend(self._post_lib_flags) + return params + def link(self, input_files: List[Path], output_file: Path, openmp: bool, libs: Optional[List[str]] = None) -> str: @@ -214,8 +246,7 @@ def link(self, input_files: List[Path], output_file: Path, for lib in (libs or []): params.extend(self.get_lib_flags(lib)) - if self._post_lib_flags: - params.extend(self._post_lib_flags) + params.extend(self.get_post_link_flags()) params.extend([self.output_flag, str(output_file)]) return self.run(params) diff --git a/source/fab/tools/tool_repository.py b/source/fab/tools/tool_repository.py index e4b4b256..9f6e7abe 100644 --- a/source/fab/tools/tool_repository.py +++ b/source/fab/tools/tool_repository.py @@ -129,6 +129,7 @@ def add_tool(self, tool: Tool): name=f"linker-{compiler.compiler.name}") other_linker = cast(Linker, other_linker) linker = Linker(linker=other_linker, + exec_name=compiler.exec_name, name=f"linker-{compiler.name}") self[linker.category].append(linker) else: diff --git a/tests/unit_tests/tools/test_linker.py b/tests/unit_tests/tools/test_linker.py index c0e31ad4..3ed82994 100644 --- a/tests/unit_tests/tools/test_linker.py +++ b/tests/unit_tests/tools/test_linker.py @@ -316,10 +316,12 @@ def test_linker_nesting(mock_c_compiler): linker1.add_pre_lib_flags(["pre_lib1"]) linker1.add_lib_flags("lib_a", ["a_from_1"]) linker1.add_lib_flags("lib_c", ["c_from_1"]) + linker1.add_post_lib_flags(["post_lib1"]) linker2 = Linker(linker=linker1) linker2.add_pre_lib_flags(["pre_lib2"]) linker2.add_lib_flags("lib_b", ["b_from_2"]) linker2.add_lib_flags("lib_c", ["c_from_2"]) + linker1.add_post_lib_flags(["post_lib2"]) mock_result = mock.Mock(returncode=0) with mock.patch("fab.tools.tool.subprocess.run", @@ -328,9 +330,10 @@ def test_linker_nesting(mock_c_compiler): [Path("a.o")], Path("a.out"), libs=["lib_a", "lib_b", "lib_c"], openmp=True) - tool_run.assert_called_with(['mock_c_compiler.exe', '-fopenmp', - 'a.o', "pre_lib2", "pre_lib1", "a_from_1", - "b_from_2", "c_from_2", '-o', 'a.out'], + tool_run.assert_called_with(["mock_c_compiler.exe", "-fopenmp", + "a.o", "pre_lib2", "pre_lib1", "a_from_1", + "b_from_2", "c_from_2", + "post_lib1", "post_lib2", "-o", "a.out"], capture_output=True, env=None, cwd=None, check=False)