I’m using CMake to build a moderate-sized Fortran project. I noticed that sometimes when I refactor modules I ran into weird compilation errors that are gone after clean rebuild in a fresh build directory.
I’ve managed to reproduce the problem in the following minimal example. Consider a project with two libraries and a single executable
├── build
├── CMakeLists.txt
├── liba
│ ├── CMakeLists.txt
│ ├── first.f90
│ └── second.f90
├── libb
│ ├── CMakeLists.txt
│ └── third.f90
└── main.f90
The main.f90
is simply
program test
use first, only: x
use second, only: y
use third, only: z
implicit none
print *, x, y, z
end program
and the root CMakeLists.txt
is
project(test)
enable_language(Fortran)
add_subdirectory(liba)
add_subdirectory(libb)
add_executable(main main.f90)
target_link_libraries(main a b)
Each module just exports a single parameter value
! first.f90
module first
integer, parameter :: x = 1
end module first
! second.f90
module second
integer, parameter :: y = 2
end module second
! third.f90
module third
integer, parameter :: z = 3
end module third
The libraries are described similarly as
# liba/CMakeLists.txt
add_library(a
first.f90
second.f90
)
target_include_directories(a PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
# libb/CMakeLists.txt
add_library(b
third.f90
)
target_include_directories(b PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
Building and running I get 1 2 3
, as expected.
Now the problem. If I move the second.f90
to libb
and change y = 2
to y = 42
I still have 1 2 3
as the output, even if I do full rebuild using make -C build clean all
. This is because liba
output directory contains second.mod
with outdated parameter value y = 2
and it is used instead of the correct module in the libb
build directory. The Ninja generator behaves just the same.
I greatly appreciate any suggestions how to fix my build scripts to avoid this kind of errors.
Just in case you want to try it yourself, here is the example on GitHub:
$ git clone https://github.com/uranix/cmake-fortran-modules
$ cd cmake-fortran-modules
$ git checkout a90ddfc
$ cmake -B build
$ make -C build
$ ./build/main
1 2 3
$ git checkout 736b738
$ make -C build
$ ./build/main
1 2 3
$ make -C build clean all
$ ./build/main
1 2 3
$ cmake -B build2
$ make -C build2
$ ./build2/main
1 42 3