How to prevent CMake usage of an outdated fortran module

Ivan Tsybulin
Thu, 10 Aug 2023 08:05:14 +0000
Code

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

7 posts - 2 participants