Search code examples
cmakemakefilefortranparallel-builds

CMake parallel build for Fortran-90+ with modules


I have a problem building multiple Fortran-90+ executable targets using CMake, when I use Fortran modules that are shared by all executables, and build in parallel using make -j. The issue seems to be that the compiled object files are placed in different subdirectories for each target, CMakeFiles/targetName.dir/src/file.f90.o, while the module files are placed in the same directory for every target (I can change this directory by setting Fortran_MODULE_DIRECTORY, but it will still be the same dir for all module files). The problem is then that all targets start writing these module files in parallel, and I get a Fatal Error: Can't rename module file 'module.mod0' to 'module.mod': No such file or directory when using gfortran (which apparently creates a .mod0 file and then renames it to .mod). The problem does not arise when I issue make without the -j option (serial build).

I can see two solutions, but I don't know how to implement them. Firstly, put the object files for all targets in the same directory rather than target-specific directories. This may be the preferred option, since I won't have to compile the shared source files N times for N targets. The make process will then recognise that the object files exist, and not compile the corresponding source files again, hence not touching the .mod(0) files again (I may need to make all following targets depend on the first).

The second solution would be to put the .mod(0) files in the target-specific directories, so that they are not overwritten or removed by the other targets. This would solve my problem, even though it would still involve more compiling than necessary. I have no idea how to accomplish either option, so any hint there or alternative solution is welcome.


Solution

  • The answer I was looking for was provided in the comments to the question by @RaulLaasner:

    I would create an additional target in the form of a core library of the relevant source files, which can then be linked to all executables. This should work in parallel. The mod files can still be in put into a single directory.

    I used add_library() and target_link_libraries() to achieve this.

    Note that e.g. Gentoo ebuild scripts add --as-needed to the linker, which may cause undefined references in your core library when you link it and external libraries to form the executable. To prevent this, make sure you link the external libraries to your core library first. To this end, my CMakeListst.txt contains:

    add_library( "Core" STATIC src/functions.f90 src/routines.f90 )  # creates libCore.a
    target_link_libraries( Core ${EXTERNAL_LIBRARIES} )              # link external libraries to libCore.a
    ...
    add_executable( myProgram1 src/myProgram1.f90 )                  # creates the first executable
    target_link_libraries( myProgram1 Core )                         # links libCore.a to myProgram1 
    

    The last two lines can be repeated to build the other executables (e.g. using foreach()).