7
votes

I have a big Fortran program which contains many directories. Each directory is compiled separately in a pseudo-library, but there is still an interdependency mess, so at the end all pseudo-libraries are combined in a single usable library. I'd like to use Fortran modules, but it's very fragile, since I cannot rely on automatic dependency checking, and compilation may fail depending on the order.

For instance, consider the following CMakeLists.txt file:

project (test Fortran)
add_library (lib1 dir1/lib1.f90)
add_library (lib2 dir2/lib2.f90 dir2/mod.f90)
add_executable (exe dir3/exe.f90)
target_link_libraries (exe lib1 lib2)

With the sources:

dir1/lib1.f90:

subroutine bar
use foo, only: foofoo
implicit none
write(6,*) foofoo
end subroutine bar

dir2/lib2.f90:

subroutine bar2
use foo, only: foofoo
implicit none
write(6,*) foofoo,' again'
end subroutine bar2

dir2/mod.f90:

module foo
implicit none
integer :: foofoo=3
end module foo

dir3/exe.f90:

program meh
implicit none
call bar()
call bar2()
end program meh

Compiling from scratch fails:

$ make
[ 25%] Building Fortran object CMakeFiles/lib1.dir/dir1/lib1.f90.o
/home/user/cmake/dir1/lib1.f90:2.4:

use foo, only: foofoo
    1
Fatal Error: Can't open module file 'foo.mod' for reading at (1): No such file or directory
make[2]: *** [CMakeFiles/lib1.dir/dir1/lib1.f90.o] Error 1
make[1]: *** [CMakeFiles/lib1.dir/all] Error 2
make: *** [all] Error 2

but doing it in the right order works:

$ make lib2
Scanning dependencies of target lib2
[ 50%] Building Fortran object CMakeFiles/lib2.dir/dir2/mod.f90.o
[100%] Building Fortran object CMakeFiles/lib2.dir/dir2/lib2.f90.o
Linking Fortran static library liblib2.a
[100%] Built target lib2
$ make
[ 25%] Building Fortran object CMakeFiles/lib1.dir/dir1/lib1.f90.o
Linking Fortran static library liblib1.a
[ 25%] Built target lib1
[ 75%] Built target lib2
Scanning dependencies of target exe
[100%] Building Fortran object CMakeFiles/exe.dir/dir3/exe.f90.o
Linking Fortran executable exe
[100%] Built target exe

Is there any way CMake can figure out the dependency and compile lib2 (or at least mod.f90) before lib1?

ETA: A robust solution should work regardless of the order in which lib1 and lib2 are defined in the CMakeLists.txt file and, once the program has been compiled, after running rm foo.mod ; touch ../dir1/lib1.f90.

2
What is your version of cmake? cmake --version - Pierre de Buyl
For info, "cmake version 3.13.4" resolves the dependency properly. - Pierre de Buyl
@PierredeBuyl I've tried 2.8.12.2 and 3.8.1. - Jellby
Is it feasible for you to update? If you don't have an up-to-date package on your system's package manager installing from source is quite painless :-) cmake.org/download - Pierre de Buyl
@PierredeBuyl It should be feasible, since that's the way I got 3.8.1. In the meantime, I tried in another machine where I have 3.13.2, and got the same error. Did something relevant change from 3.13.2 to 3.13.4? Did you somehow compile mod.f90 first? Or is this a random/system dependent behaviour? - Jellby

2 Answers

4
votes

The upcoming ninja build system version (1.10.0) has support for dynamic dependencies, which will resolve modules compilation order correctly.

To use it with CMake, you have to specify the Ninja generator:

cmake .. -DCMAKE_GENERATOR=Ninja # generate project
ninja # build the project
4
votes

Here problem is that lib1 target required object file for mod.f90. But there is no rule mention in CMakeLists.txt to create mod.f90.o while creating liblib1.a. For lib2 target mod.f90.o is created.

There can be two possible solution as below.

Solution-1

Add mod.f90 to both library.

project (test Fortran)
add_library (lib1 dir1/lib1.f90 dir2/mod.f90)
add_library (lib2 dir2/lib2.f90 dir2/mod.f90)
add_executable (exe dir3/exe.f90)
target_link_libraries (exe lib1 lib2)

Solution-2

Link library with mod.f90.o to other one.

project (test Fortran)
add_library (lib1 dir1/lib1.f90)
add_library (lib2 dir2/lib2.f90 dir2/mod.f90)
add_dependencies(lib1 lib2)
add_executable (exe dir3/exe.f90)
target_link_libraries (exe lib1 lib2)