CMake FortranCInterface

I just encountered a code that uses what appears to be a CMake utility that builds C prototypes for Fortran functions. I presume this automates the old compiler dependent Fortran - C name mangling that you had to use prior to the introduction of the the F2003 C-Interp facility. Anyone have any experience using this. I tend to avoid CMake like the plague so I’ll admit to being clueless about a lot of what CMake can do. As best as I can tell it also builds a FCMangle.h C include file that provides some macros for defining global and module names.

The code is the PHASTA Finite Element CFD code described here:

https://fluid.colorado.edu/wiki/index.php/PHASTA

I’m just looking for feedback on what it actually does, how reliable it is and if it might be worth the effort to write proper C-interop interfaces.

1 Like

AFAICU, it doesn’t build prototypes, but only helps you define the correct mangled symbol names. You are correct that this CMake module is only needed if you want to do things the old way, before standard interoperability with C was introduced.

That’s correct. Depending on the Fortran compiler selected at configuration time, it will generate a header (here for gfortran on Mac):

#ifndef FC_HEADER_INCLUDED
#define FC_HEADER_INCLUDED

/* Mangling for Fortran global symbols without underscores. */
#define FC_GLOBAL(name,NAME) name##_

/* Mangling for Fortran global symbols with underscores. */
#define FC_GLOBAL_(name,NAME) name##_

/* Mangling for Fortran module symbols without underscores. */
#define FC_MODULE(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name

/* Mangling for Fortran module symbols with underscores. */
#define FC_MODULE_(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name

#endif

Judging by the contents of the CMake module, it infers the name-mangling by compiling some small test programs. Annoyingly, the macros expect you to provide the names in both upper- and lower-case (the C preprocessor cannot change case, and Intel on Windows uses upper-case names for Fortran symbols). For example if there is an external Fortran procedure named mysub, you’d use it like this:

#define mysub FC_GLOBAL(mysub, MYSUB)

It remains up to the user to specify the prototypes with the correct argument types:

// my_fortran_lib.h
#include "fc.h"
#define mysub FC_GLOBAL(mysub, MYSUB)
void mysub(int * /* n */, float * /* a */);

The CMake module can do the symbol wrapping step for you via the SYMBOLS argument:

FortranCInterface_HEADER(FCMangle.h
                         MACRO_NAMESPACE "FC_"
                         SYMBOLS mysub)

(You can control the prefix of symbols using SYMBOL_NAMESPACE.)

When to use it?

  • The Fortran compiler doesn’t support C binding.
  • The Fortran side doesn’t use bind(c) and you cannot change the source (perhaps the Fortran code is distributed in compiled form).
  • Using the standard approach would be too expensive or time-consuming.

As I see it, the CMake module only helps with names, but it has no knowledge of argument passing conventions (say assumed-length character strings, callbacks, structs/derived types, etc.), where you also need to know the type layout.

So in my eyes it’s really just “duct tape” for old F77-like libraries of external routines with arguments of the intrinsic types integer and real.

I’ve tried to imagine two examples of how this might be used:

Calling Fortran from C

! flib.f90
subroutine mysub(n, a)
    integer, intent(in) :: n
    real, intent(in) :: a(n)
    print *, a
end subroutine
// c_flib.h
// (!) ONLY FOR DEMONSTRATION PURPOSES (!)
// WHEN POSSIBLE, USE THE STANDARD C INTEROPERABILITY
// WITH THE BIND(C) ATTRIBUTE ON THE FORTRAN SIDE INSTEAD
#include "c_flib_symbols.h"
void mysub(int *n, float *a);     // <-- mangled name
# Fortran library (defines subroutine 'mysub')
add_library(flib flib.f90)

include(FortranCInterface)
FortranCInterface_HEADER(c_flib_symbols.h 
    MACRO_NAMESPACE "FC_"
    SYMBOLS mysub)

# C Interface of the Fortran library
add_library(c_flib INTERFACE c_flib.h)
target_link_libraries(c_flib INTERFACE flib)
target_include_directories(c_flib INTERFACE ${CMAKE_BINARY_DIR})

Calling C from Fortran

// clib.c
#include "c_flib_symbols.h"   // reusing same header as in previous example
#include <stdio.h>

void mysub(int *n, float *a)  // <-- mangled name
{
    for (int i = 0; i < *n; i++) {
        printf("%f\n", a[i]);
    }
}

In the CMake file you’d then build the library like this:

add_library(clib clib.c)
target_include_directories(clib PRIVATE ${CMAKE_BINARY_DIR})

To use it in a Fortran program, you’d use an interface block (desirable) or simply use the subroutine with external (less desirable).

Thanks. After taking a closer look at the code I figured it was doing the old Fortran-C name mangling. This is something like the old netCDF Fortran interfaces that used the cfortran.h macro package. My very painful experiences trying to get cfortran.h to work on Cray’s last attempt at something like a C90 vector machine (I think it was called an SV1) is why I wrote the F2003 C-interop interfaces that are now part of netCDF. Actually there appears to be only a handfull of routines in PHASTA that need C-interop so I might just go ahead and write proper F2003 C-interop interfaces.

I believe at least some of the Fortran support in CMake is courtesy of the DoE.

According to this presentation,

  • CMake is the standard build system of the Exascale Computing Project
  • the transition of Trilinos from Autotools to CMake cost $500K+ in Sandia National Labs developer time, and $700K+ in contracts to Kitware
  • transitioning Trilinos to a new build system and testing environment is estimated to cost $1M+

The initial evaluation for the switch for Trilinos (from 2008) can be found in this report: Trilinos CMake Evaluation. A newer presentation from the same author is Challenges and Suggested Solutions to Sustainable Build, Test, and Integration Processes in CSE Software Ecosystems (2022).

I’ve found two presentations linked to CMake and Kokkos:

Using CMake (or Autotools) is also one of the conditions to become part of the xSDK packages.

The ECP has funded some CMake trainings (lecture slides and recordings available):

C++20 introduced modules, similar to the ones we have in Fortran. It’s been causing a lot of friction in the C++ community, particularly when it comes build tools. Some of the problems are exposed in a recent blog by Jussi Pakkanen (the author of the Meson build system) - “The road to hell is paved with good intentions and C++ modules”.

2 Likes

I guess the search for a perfect build system will be eternal. I’ve used (forced to use would be more accurate) the old Xwindows Imake system (which was an abomination), Autotools (which I’ve seen referred to as AutoHell) and CMake and keep coming back to just bespoke hand woven Makefiles. The complexity and learning curve (at least for me) of CMake is my biggest issue (plus its initial lack of support for Fortran). To date at least in my codes (some of which are approaching 100K lines of code spread out over several different subdirectories) I’ve not encountered anything that good old (g)make couldn’t handle or moving to CMake would make my life easier.

I mostly agree with this (although my experiences with CMake and other tools is also limited), but it does require some discipline on the part of the programmer to write Makefiles in a portable way and with a portable structure. Also, for modern fortran, the Makefile becomes an essential part of the code. It is difficult, or perhaps even impossible, to reconstruct the correct Makefile from just the fortran source code. It is best to build up the Makefile at the same time the fortran source is created.

Agree completely about it taking a certain amount of discipline and planning when you use Makefiles. I’ve found the biggest issue is the one alluded to by Ivan and thats resolving module dependencies. Submodules goes a long way to fix that but make/gmake (at least the versions I use) is clueless as to what a submodule is so you have to build special rules just for them. However, I’ve been using the same combinations of Makefiles, file directory structures, and initialization shell scripts for about 25 years now so and can stand up a new project just copying existing Makefiles etc in less than a day so its second nature to me now. Also, if I remember correctly, one of the things driving the development of CMake was the need for a cross-platform system that would work the same on WIndows and Linux/Unix systems. I never use Windows so I have no real motivation to learn more CMake than I need to compile other folks codes that rely on it.

Can you elaborate what you mean? That’s exactly what CMake does, discover module dependencies and generate a Makefile. It uses the parser from makedepf90 (originally implemented using yacc and lex), which has been extended by hand to deal with new features of Fortran such as submodules. makedepf90 is a variant of the makedepend program but for Fortran instead of C.

The authors of CMake have explained the whole procedure in an article: How CMake supports Fortran modules and its applicability to C++


Scanners for Fortran module dependencies have been re-implemented several times by now:

The question how to correctly compile source files containing modules pops up constantly on Discourse, StackOverflow, and also on comp.lang.fortran. The following are from Fortran Discourse:

In simple cases, this can be done, of course.

But consider the situation where there several machine-dependent versions of a module, all in different files, and the build system selects the appropriate version based on the OS, or the target hardware, or the choice of external libraries. For example, the correct filename could be a Make macro defined on the command line or with an environment variable. In these cases there are multiple ways to build the program, and an automatic way to build the program cannot possibly choose the correct one.