Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linker wrapper new #382

Closed
wants to merge 77 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
282f068
Merge pull request #19 from hiker/linker-lib-flags
hiker Sep 24, 2024
3b6e0bd
Support new and old style of PSyclone command line (no more nemo api …
hiker Sep 26, 2024
16d3ff5
Fix mypy errors.
hiker Sep 26, 2024
71fd1ae
Added missing tests for calling psyclone, and converting old style to…
hiker Sep 30, 2024
ec4c0f6
Updated comment.
hiker Sep 30, 2024
b9aabf8
Removed mixing, use a simple regex instead.
hiker Oct 17, 2024
8ee10e8
Added support for ifx/icx compiler as intel-llvm class.
hiker Oct 18, 2024
d7b2008
Added support for nvidia compiler.
hiker Oct 18, 2024
9005b3b
Add preliminary support for Cray compiler.
hiker Oct 18, 2024
8771e80
Added Cray compiler wrapper ftn and cc.
hiker Oct 18, 2024
0188050
Follow a more consistent naming scheme for crays, even though the nat…
hiker Oct 22, 2024
3c569bd
Changed names again.
hiker Oct 22, 2024
edc5fcd
Renamed cray compiler wrapper to be CrayCcWrapper and CrayFtnWrapper,…
hiker Nov 11, 2024
f6a70c8
Fixed incorrect name in comments.
hiker Nov 11, 2024
4f0e70f
Merge pull request #28 from hiker/additional_compilers
lukehoffmann Nov 11, 2024
58caecf
Merge branch 'compiler_wrapper' into bom_master
hiker Nov 11, 2024
5452d70
Merge branch 'linker-lib-flags' into additional_compilers
hiker Nov 12, 2024
d54d94c
Merge branch 'linker-lib-flags' into bom_master
hiker Nov 12, 2024
605e7e5
Merge branch 'additional_compilers' into bom_master
hiker Nov 12, 2024
70ad4b1
Merge branch 'linker-lib-flags' into additional_compilers
hiker Nov 12, 2024
b70f98f
Merge branch 'linker-lib-flags' into additional_compilers
hiker Nov 12, 2024
2f7e3ba
Merge branch 'linker-lib-flags' into additional_compilers
hiker Nov 12, 2024
7a2eb59
Additional compilers (#349)
hiker Nov 12, 2024
bd1d318
Merge branch 'dev' into additional_compilers
hiker Nov 12, 2024
2148fb7
Merge branch 'compiler_wrapper' into update_psyclone_to_support_next_…
hiker Nov 19, 2024
20fe928
Merge branch 'update_psyclone_to_support_next_release_syntax' into ad…
hiker Nov 19, 2024
f7b49e0
Merge branch 'linker-lib-flags' into additional_compilers
hiker Nov 21, 2024
a493c53
Support new and old style of PSyclone command line (no more nemo api …
hiker Sep 26, 2024
824851d
Fix mypy errors.
hiker Sep 26, 2024
16a125c
Added missing tests for calling psyclone, and converting old style to…
hiker Sep 30, 2024
fc19283
Added shell tool.
hiker Oct 23, 2024
730a824
Try to make mypy happy.
hiker Oct 23, 2024
6e280d9
Removed debug code.
hiker Oct 23, 2024
6c3f1c2
ToolRepository now only returns default that are available. Updated t…
hiker Oct 23, 2024
ae61d4a
Fixed typos and coding style.
hiker Nov 21, 2024
e7c2c83
Support new and old style of PSyclone command line (no more nemo api …
hiker Sep 26, 2024
e2051f2
Fix mypy errors.
hiker Sep 26, 2024
0ad85ee
Added missing tests for calling psyclone, and converting old style to…
hiker Sep 30, 2024
890b50d
Updated comment.
hiker Sep 30, 2024
032ab26
Fixed failing tests.
hiker Nov 21, 2024
7168f42
Merge branch 'additional_compilers_clean' into psyclone_3_support_clean
hiker Nov 21, 2024
70c083e
Merge branch 'psyclone_3_support_clean' into add_shell_tool_clean
hiker Nov 21, 2024
8753d0c
Updated fparser dependency to version 0.2.
hiker Nov 28, 2024
634d28c
Replace old code for handling sentinels with triggering this behaviou…
hiker Nov 29, 2024
78697bf
Fixed tests for latest changes.
hiker Nov 29, 2024
c82cedf
Removed invalid openmp continuation line - since now fparser fails wh…
hiker Nov 29, 2024
652db98
Added test for disabled openmp parsing. Updated test to work with new…
hiker Nov 29, 2024
ea7e428
Coding style changes.
hiker Nov 29, 2024
137d346
Fix flake issues.
hiker Nov 29, 2024
fa0cb5d
Fixed double _.
hiker Nov 29, 2024
63e77e5
Merge branch 'psyclone_3_support_clean' into add_shell_tool_clean
hiker Nov 29, 2024
810da77
Merge branch 'add_shell_tool_clean' into update_to_fparser_0_2
hiker Nov 29, 2024
ada81c9
Make Linker inherit CompilerWrapper
lukehoffmann Sep 9, 2024
3fb4018
Fix up tests for new Linker inheritence
lukehoffmann Sep 25, 2024
0f1bd00
Fix a flake error
lukehoffmann Sep 25, 2024
239f417
Use linker wrapping to combine flags from the wrapped linker with the…
hiker Dec 2, 2024
6b07056
Minor code cleanup.
hiker Dec 2, 2024
bbdb380
Merge branch 'develop' into add_shell_tool_clean
hiker Dec 2, 2024
1335878
Merge branch 'add_shell_tool_clean' into update_to_fparser_0_2
hiker Dec 2, 2024
aa76cf9
Merge branch 'update_to_fparser_0_2' into linker_wrapper_new
hiker Dec 2, 2024
d06c9ce
Created linker wrapper in ToolRepository.
hiker Dec 2, 2024
39a8204
Try making linker a CompilerSuiteTool instead of a CompilerWrapper.
hiker Dec 2, 2024
7b189e8
Updated tests.
hiker Dec 3, 2024
d084f20
Fix support for post-libs.
hiker Dec 3, 2024
fe93339
Fixed mypy.
hiker Dec 3, 2024
d032d8a
Merge branch 'develop' into update_to_fparser_0_2
hiker Jan 9, 2025
ccc8a39
Removed more accesses to private members.
hiker Jan 9, 2025
34f4985
Added missing type hint.
hiker Jan 9, 2025
686f990
Make flake8 happy.
hiker Jan 9, 2025
8e67176
Merge branch 'update_to_fparser_0_2' into linker_wrapper_new
hiker Jan 9, 2025
b7ad78a
Merge branch 'develop' into linker_wrapper_new
hiker Jan 17, 2025
439571d
Added missing openmp handling in linker.
hiker Jan 17, 2025
3501e59
Addressed issues raised in review.
hiker Jan 29, 2025
a0c135e
Merge branch 'develop' into linker_wrapper_new
hiker Jan 29, 2025
ddf240a
Forgot that file in previous commit.
hiker Jan 29, 2025
4225ddf
More linker wrapper improvements (#381)
hiker Feb 4, 2025
fb3c0e9
Merge branch 'develop' into linker_wrapper_new
hiker Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion source/fab/tools/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ def mpi(self) -> bool:
def openmp(self) -> bool:
''':returns: if the compiler supports openmp or not
'''
return self._openmp_flag != ""
# It is important not to use `_openmp_flag` directly, since a compiler
# wrapper overwrites `openmp_flag`.
return self.openmp_flag != ""

@property
def openmp_flag(self) -> str:
Expand Down
88 changes: 24 additions & 64 deletions source/fab/tools/linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import os
from pathlib import Path
from typing import cast, Dict, List, Optional, Union
from typing import Dict, List, Optional, Union
import warnings

from fab.tools.category import Category
Expand All @@ -20,47 +20,35 @@


class Linker(CompilerSuiteTool):
'''This is the base class for any Linker. It takes either another linker
instance, or a compiler instance as parameter in the constructor. Exactly
one of these must be provided.

:param compiler: an optional compiler instance
'''This is the base class for any Linker. It takes an existing compiler
instance as parameter, and optional another linker. The latter is used
to get linker settings - for example, linker-mpif90-gfortran will use
mpif90-gfortran as compiler (i.e. to test if it is available and get
compilation flags), and linker-gfortran as linker. This way a user
only has to specify linker flags in the most basic class (gfortran),
all other linker wrapper will inherit the settings.

:param compiler: a compiler instance
:param linker: an optional linker instance
:param name: name of the linker

:raises RuntimeError: if both compiler and linker are specified.
:raises RuntimeError: if neither compiler nor linker is specified.
'''

def __init__(self, compiler: Optional[Compiler] = None,
def __init__(self, compiler: Compiler,
linker: Optional[Linker] = None,
exec_name: Optional[str] = None,
name: Optional[str] = None):

if linker and compiler:
raise RuntimeError("Both compiler and linker is specified in "
"linker constructor.")
if not linker and not compiler:
raise RuntimeError("Neither compiler nor linker is specified in "
"linker constructor.")
self._compiler = compiler
self._linker = linker

search_linker = self
while search_linker._linker:
search_linker = search_linker._linker
final_compiler = search_linker._compiler
if not name:
assert final_compiler # make mypy happy
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()
name = f"linker-{compiler.name}"

super().__init__(
name=name,
exec_name=exec_name,
exec_name=compiler.exec_name,
suite=self.suite,
category=Category.LINKER)

Expand All @@ -76,51 +64,31 @@ def check_available(self) -> bool:
''':returns: whether this linker is available by asking the wrapped
linker or compiler.
'''
if self._compiler:
return self._compiler.check_available()
assert self._linker # make mypy happy
return self._linker.check_available()

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
return self._linker.exec_name
return self._compiler.check_available()

@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
the wrapped compiler.'''
return self._compiler.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 # make mypy happy
return self._linker.mpi
with the wrapped compiler.'''
return self._compiler.mpi

@property
def openmp(self) -> bool:
''':returns" whether this linker supports OpenMP or not by checking
with the wrapper compiler or linker.'''
if self._compiler:
return self._compiler.openmp
assert self._linker # make mypy happy
return self._linker.openmp
''':returns: whether this linker supports OpenMP or not by checking
with the wrapped compiler.'''
return self._compiler.openmp

@property
def output_flag(self) -> str:
''':returns: the flag that is used to specify the output name.
'''
if self._compiler:
return self._compiler.output_flag
assert self._linker # make mypy happy
return self._linker.output_flag
return self._compiler.output_flag

def get_lib_flags(self, lib: str) -> List[str]:
'''Gets the standard flags for a standard library
Expand Down Expand Up @@ -238,18 +206,10 @@ def link(self, input_files: List[Path], output_file: Path,

params: List[Union[str, Path]] = []

# Find the compiler by following the (potentially
# layered) linker wrapper.
linker = self
while linker._linker:
linker = linker._linker
# Now we must have a compiler
compiler = linker._compiler
assert compiler # make mypy happy
params.extend(compiler.flags)
params.extend(self._compiler.flags)

if openmp:
params.append(compiler.openmp_flag)
params.append(self._compiler.openmp_flag)

# TODO: why are the .o files sorted? That shouldn't matter
params.extend(sorted(map(str, input_files)))
Expand Down
16 changes: 8 additions & 8 deletions source/fab/tools/tool_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,19 @@ def add_tool(self, tool: Tool):
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
# the linker based on the wrapped 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).
# linker-gfortran. The compiler mpif90-gfortran will be the
# wrapper compiler. Reason is that e.g. 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(linker=other_linker,
exec_name=compiler.exec_name,
linker = Linker(compiler,
linker=other_linker,
name=f"linker-{compiler.name}")
self[linker.category].append(linker)
else:
Expand Down
12 changes: 12 additions & 0 deletions tests/unit_tests/tools/test_compiler_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ def test_compiler_wrapper_flags_independent():
assert mpicc.flags == ["-a", "-b"]
assert mpicc.openmp_flag == gcc.openmp_flag

# Test a compiler wrapper correctly queries the wrapper compiler for
# openmp flag: Set the wrapper to have no _openmp_flag (which is
# actually the default, since the wrapper never sets its own flag), but
# gcc does have a flag, so mpicc should report that is supports openmp.
# mpicc.openmp calls openmp of its base class (Compiler), which queries
# if an openmp flag is defined. This query must go to the openmp property,
# since the wrapper overwrites this property to return the wrapped
# compiler's flag (and not the wrapper's flag, which would not be defined)
with mock.patch.object(mpicc, "_openmp_flag", ""):
assert mpicc._openmp_flag == ""
assert mpicc.openmp

# Adding flags to the wrapper should not affect the wrapped compiler:
mpicc.add_flags(["-d", "-e"])
assert gcc.flags == ["-a", "-b"]
Expand Down
23 changes: 5 additions & 18 deletions tests/unit_tests/tools/test_linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,15 @@ def test_linker(mock_c_compiler, mock_fortran_compiler):
assert linker.flags == []


def test_linker_constructor_error(mock_c_compiler):
'''Test the linker constructor with invalid parameters.'''

with pytest.raises(RuntimeError) as err:
Linker()
assert ("Neither compiler nor linker is specified in linker constructor."
in str(err.value))
with pytest.raises(RuntimeError) as err:
Linker(compiler=mock_c_compiler, linker=mock_c_compiler)
assert ("Both compiler and linker is specified in linker constructor."
in str(err.value))


@pytest.mark.parametrize("mpi", [True, False])
def test_linker_mpi(mock_c_compiler, mpi):
'''Test that linker wrappers handle MPI as expected.'''

mock_c_compiler._mpi = mpi
linker = Linker(compiler=mock_c_compiler)
linker = Linker(mock_c_compiler)
assert linker.mpi == mpi

wrapped_linker = Linker(linker=linker)
wrapped_linker = Linker(mock_c_compiler, linker=linker)
assert wrapped_linker.mpi == mpi


Expand All @@ -80,7 +67,7 @@ def test_linker_openmp(mock_c_compiler, openmp):
linker = Linker(compiler=mock_c_compiler)
assert linker.openmp == openmp

wrapped_linker = Linker(linker=linker)
wrapped_linker = Linker(mock_c_compiler, linker=linker)
assert wrapped_linker.openmp == openmp


Expand All @@ -103,7 +90,7 @@ def test_linker_check_available(mock_c_compiler):

# Then test the usage of a linker wrapper. The linker will call the
# corresponding function in the wrapper linker:
wrapped_linker = Linker(linker=linker)
wrapped_linker = Linker(mock_c_compiler, linker=linker)
with mock.patch('fab.tools.compiler.Compiler.get_version',
return_value=(1, 2, 3)):
assert wrapped_linker.check_available()
Expand Down Expand Up @@ -342,7 +329,7 @@ def test_linker_nesting(mock_c_compiler):
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 = Linker(mock_c_compiler, 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"])
Expand Down
6 changes: 1 addition & 5 deletions tests/unit_tests/tools/test_tool_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import pytest

from fab.tools import (Ar, Category, FortranCompiler, Gcc, Gfortran, Ifort,
Linker, ToolRepository)
ToolRepository)


def test_tool_repository_get_singleton_new():
Expand Down Expand Up @@ -62,10 +62,6 @@ def test_tool_repository_get_default():
openmp=False)
assert isinstance(gfortran, Gfortran)

gcc_linker = tr.get_default(Category.LINKER, mpi=False, openmp=False)
assert isinstance(gcc_linker, Linker)
assert gcc_linker.name == "linker-gcc"

gcc = tr.get_default(Category.C_COMPILER, mpi=False, openmp=False)
assert isinstance(gcc, Gcc)

Expand Down