diff --git a/Documentation/source/site-specific-config.rst b/Documentation/source/site-specific-config.rst index 39033695..6753e38b 100644 --- a/Documentation/source/site-specific-config.rst +++ b/Documentation/source/site-specific-config.rst @@ -171,8 +171,8 @@ part of Fab, there is no need to explicitly add this yourself): The tool system allows several different tools to use the same name for the executable, as long as the Fab name is different, i.e. the `mpicc-{compiler.name}`. The tool -repository will automatically add compiler wrapper for `mpicc` and -`mpif90` for any compiler that is added by Fab. If you want to add +repository will automatically add compiler wrapper for `mpicc`, `mpif90` +and `mpifort` for any compiler that is added by Fab. If you want to add a new compiler, which can also be invoked using `mpicc`, you need to add a compiler wrapper as follows: diff --git a/source/fab/tools/compiler_wrapper.py b/source/fab/tools/compiler_wrapper.py index e8b93499..7add7ea5 100644 --- a/source/fab/tools/compiler_wrapper.py +++ b/source/fab/tools/compiler_wrapper.py @@ -5,7 +5,7 @@ ############################################################################## """This file contains the base class for any compiler-wrapper, including -the derived classes for mpif90, mpicc, and CrayFtnWrapper and CrayCcWrapper. +the derived classes for mpif90, mpifort, mpicc, and the Cray wrappers. """ from pathlib import Path @@ -204,6 +204,21 @@ def __init__(self, compiler: Compiler): exec_name="mpif90", compiler=compiler, mpi=True) +# ============================================================================ +class Mpifort(CompilerWrapper): + '''Class for a simple wrapper for using a compiler driver (like mpifort). + It will be using the name "mpifort-COMPILER_NAME" and calls `mpifort`. + All flags from the original compiler will be used when using the wrapper + as compiler. + + :param compiler: the compiler that the mpifort wrapper will use. + ''' + + def __init__(self, compiler: Compiler): + super().__init__(name=f"mpifort-{compiler.name}", + exec_name="mpifort", compiler=compiler, mpi=True) + + # ============================================================================ class Mpicc(CompilerWrapper): '''Class for a simple wrapper for using a compiler driver (like mpicc) diff --git a/source/fab/tools/tool_repository.py b/source/fab/tools/tool_repository.py index d476f957..fde1b0e5 100644 --- a/source/fab/tools/tool_repository.py +++ b/source/fab/tools/tool_repository.py @@ -19,7 +19,8 @@ from fab.tools.category import Category from fab.tools.compiler import Compiler, FortranCompiler from fab.tools.compiler_wrapper import (CompilerWrapper, CrayCcWrapper, - CrayFtnWrapper, Mpif90, Mpicc) + CrayFtnWrapper, Mpif90, Mpifort, + Mpicc) from fab.tools.linker import Linker from fab.tools.versioning import Fcm, Git, Subversion from fab.tools.ar import Ar @@ -86,12 +87,14 @@ def __init__(self): for shell_name in ["sh"]: self.add_tool(Shell(shell_name)) - # Now create the potential mpif90 and Cray ftn wrapper + # Now create the potential MPI Fortran and Cray ftn wrappers all_fc = self[Category.FORTRAN_COMPILER][:] for fc in all_fc: if not fc.mpi: mpif90 = Mpif90(fc) self.add_tool(mpif90) + mpifort = Mpifort(fc) + self.add_tool(mpifort) # I assume cray has (besides cray) only support for Intel and GNU if fc.name in ["gfortran", "ifort", "ifx"]: crayftn = CrayFtnWrapper(fc) diff --git a/tests/unit_tests/tools/test_tool_repository.py b/tests/unit_tests/tools/test_tool_repository.py index 5127f66b..189cbe85 100644 --- a/tests/unit_tests/tools/test_tool_repository.py +++ b/tests/unit_tests/tools/test_tool_repository.py @@ -15,7 +15,7 @@ from fab.tools.ar import Ar from fab.tools.category import Category from fab.tools.compiler import Compiler, FortranCompiler, Gfortran, Ifort -from fab.tools.compiler_wrapper import Mpicc, Mpif90 +from fab.tools.compiler_wrapper import Mpicc, Mpif90, Mpifort from fab.tools.linker import Linker from fab.tools.tool_repository import ToolRepository @@ -95,6 +95,48 @@ def test_tool_repository_get_tool_with_exec_name(stub_fortran_compiler): ToolRepository._singleton = None +def test_tool_repository_get_mpifort_with_exec_name(stub_fortran_compiler): + '''Tests get_tool when the mpifort executable name is specified.''' + tr = ToolRepository() + # Keep a copy of gfortran for later + gfortran = tr.get_tool(Category.FORTRAN_COMPILER, "gfortran") + + # First add just one unavailable Fortran compiler and an mpifort wrapper: + tr[Category.FORTRAN_COMPILER] = [] + tr.add_tool(stub_fortran_compiler) + mpifort = Mpifort(stub_fortran_compiler) + tr.add_tool(mpifort) + + # If mpifort is not available, an error is raised: + mpifort._is_available = False + try: + tr.get_tool(Category.FORTRAN_COMPILER, "mpifort") + except KeyError as err: + assert "Unknown tool 'mpifort' in category" in str(err) + + # When using the exec name, the compiler must be available: + mpifort._is_available = True + ftn = tr.get_tool(Category.FORTRAN_COMPILER, "mpifort") + assert ftn is mpifort + + # Now add mpifort-gfortran, set mpifort-gfortran as available, + # and mpifort-stub-fortran as unavailable. We need to make sure + # we then get mpifort-gfortran: + mpifort_gfortran = Mpifort(gfortran) + tr.add_tool(mpifort_gfortran) + mpifort._is_available = False + mpifort_gfortran._is_available = True + ftn = tr.get_tool(Category.FORTRAN_COMPILER, "mpifort") + assert ftn is mpifort_gfortran + + # Then verify using the full path + ftn = tr.get_tool(Category.FORTRAN_COMPILER, "/some/where/mpifort") + assert ftn is mpifort_gfortran + assert ftn.exec_path == Path("/some/where/mpifort") + # Reset the repository, since this test messed up the compilers. + ToolRepository._singleton = None + + def test_get_tool_error(): """ Tests error handling during tet_tool.