CMake, Ninja, Fortran, and inconsistent `Cannot open module file` errors

golmschenk
Wed, 25 Oct 2023 14:50:01 +0000
Code

Hello! I’m running into an issue where, for some system configurations, I encounter a Cannot open module file (for a Fortran module file) during the build. From what I can tell, this is occurring because the way I’ve set up my CMake code and the related the implicit building of the Fortran module files results in Ninja attempting to use the module files before they’ve been built. However, I’ve not particularly knowledgeable about any of these components, so I may be wrong. This project is refurbishing a legacy Fortran codebase.

Notably, encountering the error seems somewhat inconsistent, and I’m having difficulties reducing the problem to a smaller minimal working example. As such, I apologize for this large MWE, but I’ve been unable to shrink it further while still representing the issue. I also cannot reproduce the error on my local machine. I’m using cibuildwheel, a tool to run the build on various CI runners (e.g., GitHub Actions) to produce precompiled binaries for various systems. I only encounter the issue on some CI runner configurations. I’ve found a few tangentially related issues where people have encountered difficulties with Fortran module files and the Ninja build system. Also, if I switch CMake from using Ninja to using Unix Makefiles, I don’t seem to encounter the issue.

For my actual set up, I’m using CMake to build both a shared library and an executable. For components used by both, I create OBJECT libraries. It is one of the OBJECT libraries whose mod files seem to have the issue when used as a component in building the executable. Concretely, in the below CMake code, eesunhong_recipes_replacements is the OBJECT library whose mod file is not found when building the eesunhong_main executable.

I’m guessing what I would need to do is enforce that eesunhong_recipes_replacements produces its mod files before anything in eesunhong_main is attempted to be built. However, I’ve tried various solutions such as add_dependencies(eesunhong_main eesunhong_recipes_replacements) without success. Of course, the issue might be something else entirely. For example, the build location of the Fortran module files. I’ve attempted various uses of Fortran_MODULE_DIRECTORY to specify the module file location and add include that in later build steps, but also haven’t had any success there either. Does anyone have suggestions as to how I can resolve this? Does it seem I’m on the right track, that it’s happening because of the build order from Ninja and the implicit building of the Fortran module files? If so, is there a way to enforce the order for those module files? Any advice would be greatly appreciated. Thank you so much for your time.

CMake

cmake_minimum_required(VERSION 3.17)

project(src/eesunhong LANGUAGES Fortran)

set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Build the Fortran standard library.
set(BUILD_TESTING OFF)
include(FetchContent)
FetchContent_Declare(
        fortran_stdlib
        GIT_REPOSITORY https://github.com/fortran-lang/stdlib.git
        GIT_TAG df1e2f0ed0cbe2fbd9c1f20dcb5bd1a4bba95bb2  # v3.0.0
)
FetchContent_MakeAvailable(fortran_stdlib)
set(BUILD_TESTING ON)

# Create the object libraries to be used in both static and shared libraries.
add_library(polyroots OBJECT third_party/polyroots-fortran/polyroots_cmplx_roots_gen.f90)
add_library(roots OBJECT third_party/roots-fortran/root_module.F90)
add_library(eesunhong_recipes_replacements OBJECT src/eesunhong_recipes_replacements.f90)
target_link_libraries(eesunhong_recipes_replacements PUBLIC fortran_stdlib)

# Create the shared library.
add_library(eesunhong_fortran_library SHARED $<TARGET_OBJECTS:eesunhong_recipes_replacements> $<TARGET_OBJECTS:roots>)
target_link_libraries(eesunhong_fortran_library PUBLIC fortran_stdlib)

# Create the static version of the object libraries.
add_library(eesunhong_complete_static STATIC $<TARGET_OBJECTS:eesunhong_recipes_replacements> $<TARGET_OBJECTS:polyroots> $<TARGET_OBJECTS:roots>)
target_link_libraries(eesunhong_complete_static PUBLIC fortran_stdlib)

# Create the main executable.
add_executable(eesunhong_main src/main.f third_party/minuit/minuit_94a_dblb.f src/fcnrvg4_Ctpar.f src/bilens.f src/critical.f
        src/microcurve_rvg4Ctpar.f src/hexadec_only.f src/geo_par.f src/eesunhong_real_complex_conversion.f90)
target_link_libraries(eesunhong_main PUBLIC eesunhong_complete_static)

# Install
if(SKBUILD)
    set(library_directory "${SKBUILD_PLATLIB_DIR}")
    set(binary_directory "${SKBUILD_PLATLIB_DIR}")
else()  # Calling CMake directly instead of through scikit-build means this is a developer build.
    set(library_directory ".")
    set(binary_directory ".")
    set(CMAKE_INSTALL_PREFIX .)
endif()

install(TARGETS eesunhong_fortran_library DESTINATION ${library_directory})
install(TARGETS eesunhong_main DESTINATION ${binary_directory})

Error

The full output of the build can be seen at this CI run (after clicking the dropdown for “Building wheel…” inside the output window): https://github.com/golmschenk/eesunhong/actions/runs/6641510632/job/18044212891

The snippet of the main error is:

[179/241] Building Fortran object CMakeFiles/eesunhong_main.dir/src/microcurve_rvg4Ctpar.f.o
    FAILED: CMakeFiles/eesunhong_main.dir/src/microcurve_rvg4Ctpar.f.o
    /opt/rh/devtoolset-10/root/usr/bin/gfortran -I/project/src -I/project/build/_deps/fortran_stdlib-build/src/mod_files -O3 -DNDEBUG -O3 -fPIE -fpreprocessed -c CMakeFiles/eesunhong_main.dir/src/microcurve_rvg4Ctpar.f-pp.f -o CMakeFiles/eesunhong_main.dir/src/microcurve_rvg4Ctpar.f.o

...

    /project/src/microcurve_rvg4Ctpar.f:2537:11:

     2537 |        use eesunhong_recipes_replacements,
          |           1
    Fatal Error: Cannot open module file ‘eesunhong_recipes_replacements.mod’ for reading at (1): No such file or directory
    compilation terminated.

11 posts - 2 participants