使用模块为 Fortran-90+ 进行 CMake 并行构建
CMake parallel build for Fortran-90+ with modules
我在使用 CMake 构建多个 Fortran-90+ 可执行目标时遇到问题,当我使用由所有可执行文件共享的 Fortran mod 规则并使用 make -j
并行构建时。问题似乎是编译后的目标文件被放置在每个目标的不同子目录中,CMakeFiles/targetName.dir/src/file.f90.o
,而 module 文件被放置在每个目标的同一目录中(我可以通过更改此目录设置 Fortran_MODULE_DIRECTORY
,但它仍然是所有 module 文件的相同目录)。问题是所有目标都开始并行写入这些 module 文件,我得到一个
Fatal Error: Can't rename module file 'module.mod0' to 'module.mod': No such file or directory
使用 gfortran 时(显然会创建一个 .mod0 文件,然后将其重命名为 .mod)。当我在没有 -j 选项(串行构建)的情况下发出 make 时,问题不会出现。
我可以看到两个解决方案,但我不知道如何实现它们。首先,将所有目标的目标文件放在同一目录中,而不是特定于目标的目录中。这可能是首选选项,因为我不必为 N 个目标编译 N 次共享源文件。然后 make 进程将识别目标文件存在,并且不会再次编译相应的源文件,因此不会再次触及 .mod(0) 文件(我可能需要使所有以下目标都依赖于第一个) .
第二种解决方案是将 .mod(0) 文件放在特定于目标的目录中,这样它们就不会被其他目标覆盖或删除。这将解决我的问题,即使它仍然涉及比必要更多的编译。我不知道如何完成这两个选项,所以欢迎任何提示或替代解决方案。
@RaulLaasner 对问题的评论中提供了我正在寻找的答案:
I would create an additional target in the form of a core library of the relevant source files, which can then be linked to all executables. This should work in parallel. The mod files can still be in put into a single directory.
我用 add_library()
和 target_link_libraries()
来实现这个。
请注意,例如Gentoo ebuild 脚本将 --as-needed
添加到 linker,当您 link 它和外部库形成可执行文件时,这可能会导致您的核心库中 undefined references 。为防止这种情况,请确保先 link 将外部库添加到核心库中。为此,我的 CMakeListst.txt 包含:
add_library( "Core" STATIC src/functions.f90 src/routines.f90 ) # creates libCore.a
target_link_libraries( Core ${EXTERNAL_LIBRARIES} ) # link external libraries to libCore.a
...
add_executable( myProgram1 src/myProgram1.f90 ) # creates the first executable
target_link_libraries( myProgram1 Core ) # links libCore.a to myProgram1
可以重复最后两行来构建其他可执行文件(例如使用 foreach())。
我在使用 CMake 构建多个 Fortran-90+ 可执行目标时遇到问题,当我使用由所有可执行文件共享的 Fortran mod 规则并使用 make -j
并行构建时。问题似乎是编译后的目标文件被放置在每个目标的不同子目录中,CMakeFiles/targetName.dir/src/file.f90.o
,而 module 文件被放置在每个目标的同一目录中(我可以通过更改此目录设置 Fortran_MODULE_DIRECTORY
,但它仍然是所有 module 文件的相同目录)。问题是所有目标都开始并行写入这些 module 文件,我得到一个
Fatal Error: Can't rename module file 'module.mod0' to 'module.mod': No such file or directory
使用 gfortran 时(显然会创建一个 .mod0 文件,然后将其重命名为 .mod)。当我在没有 -j 选项(串行构建)的情况下发出 make 时,问题不会出现。
我可以看到两个解决方案,但我不知道如何实现它们。首先,将所有目标的目标文件放在同一目录中,而不是特定于目标的目录中。这可能是首选选项,因为我不必为 N 个目标编译 N 次共享源文件。然后 make 进程将识别目标文件存在,并且不会再次编译相应的源文件,因此不会再次触及 .mod(0) 文件(我可能需要使所有以下目标都依赖于第一个) .
第二种解决方案是将 .mod(0) 文件放在特定于目标的目录中,这样它们就不会被其他目标覆盖或删除。这将解决我的问题,即使它仍然涉及比必要更多的编译。我不知道如何完成这两个选项,所以欢迎任何提示或替代解决方案。
@RaulLaasner 对问题的评论中提供了我正在寻找的答案:
I would create an additional target in the form of a core library of the relevant source files, which can then be linked to all executables. This should work in parallel. The mod files can still be in put into a single directory.
我用 add_library()
和 target_link_libraries()
来实现这个。
请注意,例如Gentoo ebuild 脚本将 --as-needed
添加到 linker,当您 link 它和外部库形成可执行文件时,这可能会导致您的核心库中 undefined references 。为防止这种情况,请确保先 link 将外部库添加到核心库中。为此,我的 CMakeListst.txt 包含:
add_library( "Core" STATIC src/functions.f90 src/routines.f90 ) # creates libCore.a
target_link_libraries( Core ${EXTERNAL_LIBRARIES} ) # link external libraries to libCore.a
...
add_executable( myProgram1 src/myProgram1.f90 ) # creates the first executable
target_link_libraries( myProgram1 Core ) # links libCore.a to myProgram1
可以重复最后两行来构建其他可执行文件(例如使用 foreach())。