I’m running into an issue where multiple build invocations are required to complete compilation (because modules are not being compiled in the correct order). I’m using CMake 3.27.7 and Ninja 1.11.1 on Windows with the latest OneAPI Intel classic Fortran compiler (ifort).
I’m new to CMake, so this is almost certainly a PEBCAK issue, but I’m at a loss as to how best to resolve it. I’ve made a MWE, but unfortunately lack the permissions to attach it. But the primary structure is:
src
library1
CMakeLists.txt
module1A.f90
module1B.f90
library2
CMakeLists.txt
module2A.f90
test
library1
CMakeLists.txt
test_library1.f90
library2
CMakeLists.txt
test_library1.f90
CMakeLists.txt
The library1 CMakeLists.txt file looks like:
add_library(${library1Name} module1A.f90 module1B.f90)
set_target_properties(${library1Name} PROPERTIES Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(${library1Name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
The library2 CMakeLists.txt is basically identical:
add_library(${library2Name} module2A.f90)
set_target_properties(${library2Name} PROPERTIES Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/library2)
target_include_directories(${library2Name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/library2)
There are dependencies between the libraries via modules as (module1A.f90):
module module1A
implicit none
integer, parameter :: MODULE_1A_PARAMETER = 1
end module module1A
and module1B.f90:
module module1B
use module2A, only : MODULE_2A_PARAMETER
implicit none
integer, parameter :: MODULE_1B_PARAMETER = MODULE_2A_PARAMETER + 2
end module module1B
and module2A.f90:
module module2A
use module1A, only : MODULE_1A_PARAMETER
implicit none
integer, parameter :: MODULE_2A_PARAMETER = MODULE_1A_PARAMETER + 1
end module module2A
The test\library1\CMakeLists.txt only adds library1:
add_executable(test_library1 test_library1.f90)
target_link_libraries(test_library1 PUBLIC ${library1Name})
And the test_library1.f90 is simply:
program test_library1
use module1A, only : MODULE_1A_PARAMETER
use module1B, only : MODULE_1B_PARAMETER
implicit none
write(*,*) "MODULE_1A_PARAMETER = ", MODULE_1A_PARAMETER
write(*,*) "MODULE_1B_PARAMETER = ", MODULE_1B_PARAMETER
end program test_library1
The test\library2 CMakeLists.txt is basically identical:
add_executable(test_library2 test_library2.f90)
target_link_libraries(test_library2 ${library2Name})
As is test_library2.f90:
program test_library2
use module2A, only : MODULE_2A_PARAMETER
implicit none
write(*,*) "MODULE_2A_PARAMETER = ", MODULE_2A_PARAMETER
end program test_library2
The issue is obviously that library1
and library2
depend on each other. The root CMakeLists.txt attempts to resolve this as:
cmake_minimum_required(VERSION 3.27.7)
project(moduleTest LANGUAGES Fortran)
set(library1Name library1)
set(library2Name library2)
add_subdirectory(src)
add_subdirectory(test)
target_link_libraries(${library1Name} ${library2Name})
target_link_libraries(${library2Name} ${library1Name})
Obviously this is wrong, since although it can compile, it requires multiple build invocations to successfully do so (the first build invocation will complain that MODULE_1A_PARAMETER
in module2A.f90
does not exist).
Is there a canonical or “best practices” way of handling this kind of circular dependency?