CMake子目录链接Fortran库时模块查找失败及单独编译问题
问题描述
项目目录结构如下:
└── Project/ ├── build/ │ ├── common/ │ ├── case/ │ └── case2/ ├── common/ │ ├── CMakeLists.txt │ └── Mod*.F90 ├── examples/ │ ├── case/ │ │ ├── CMakeLists.txt │ │ ├── exe1.F90 │ │ └── exe2.F90 │ └── case2/ │ ├── exe3.F90 │ └── exe4.F90 └── CMakeLists.txt
需求是在Project/build目录下执行make common和make case单独编译对应模块,但当前遇到两个问题:
- 执行
make时,common库编译成功,但case下的Fortran文件无法找到common模块 make common可正常运行,但make case无法执行
相关CMake配置如下:
根目录CMakeLists.txt
add_subdirectory(common) install(TARGETS common LIBRARY DESTINATION "bin") add_subdirectory(examples/case case)
Project/common/CMakeLists.txt
file(GLOB common_F90s "*.F90") add_library(common "${common_F90s}") target_include_directories(common PRIVATE ${PETSC_INCLUDE_DIRS} ${NETCDF_INCLUDE}) target_link_libraries(common PRIVATE ${PETSC_LINK_LIBRARIES} ${SPHPK_LIB} ${FFTW_LIB} ${NETCDF_LIB})
Project/examples/case/CMakeLists.txt
file(GLOB case_F90s "*.F90") cmake_print_variables(case_F90s) foreach(F90_filepath ${case_F90s}) get_filename_component(file_target ${F90_filepath} NAME_WE) add_executable(${file_target} ${F90_filepath}) cmake_print_variables(file_target) target_include_directories(${file_target} PRIVATE ${PETSC_INCLUDE_DIRS}) target_link_libraries(${file_target} PRIVATE common ${PETSC_LINK_LIBRARIES}) message("HERE") endforeach()
错误信息示例:
[ 90%] Building Fortran object CMakeFiles/initcond.dir/examples/case/initcond.F90.o ~Project/examples/case/initcond.F90:4:7: 4 | use ModDataTypes | 1 Fatal Error: Cannot open module file 'moddatatypes.mod' for reading at (1): No such file or directory compilation terminated. make[2]: *** [CMakeFiles/initcond.dir/examples/case/initcond.F90.o] Error 1 make[1]: *** [CMakeFiles/initcond.dir/all] Error 2
尝试过将common库改为STATIC/SHARED类型,SHARED报错,STATIC无改善。旧版本无嵌套CMakeLists可正常编译case,但无法单独编译模块。
解决方案
1. 修复模块文件路径问题
Fortran模块文件(.mod)默认生成在库的编译目录中,case下的可执行文件找不到这些模块,是因为未将common库的模块输出目录添加到可执行文件的包含路径。
修改Project/common/CMakeLists.txt:
file(GLOB common_F90s "*.F90") add_library(common "${common_F90s}") # 统一设置模块文件输出目录 set_target_properties(common PROPERTIES Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/modules" ) # 将模块目录设为PUBLIC,依赖common的目标会自动继承该路径 target_include_directories(common PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/modules" PRIVATE ${PETSC_INCLUDE_DIRS} ${NETCDF_INCLUDE} ) target_link_libraries(common PRIVATE ${PETSC_LINK_LIBRARIES} ${SPHPK_LIB} ${FFTW_LIB} ${NETCDF_LIB})
这样case下的可执行文件通过target_link_libraries依赖common时,会自动获取模块文件的包含路径,无需手动添加。
2. 创建case虚拟目标,支持make case单独编译
当前make case无法执行,是因为没有定义名为case的目标。用add_custom_target创建虚拟目标,将case下所有可执行文件设为它的依赖:
修改Project/examples/case/CMakeLists.txt:
file(GLOB case_F90s "*.F90") cmake_print_variables(case_F90s) # 定义空的case虚拟目标,用于统一编译所有case可执行文件 add_custom_target(case) foreach(F90_filepath ${case_F90s}) get_filename_component(file_target ${F90_filepath} NAME_WE) add_executable(${file_target} ${F90_filepath}) cmake_print_variables(file_target) target_include_directories(${file_target} PRIVATE ${PETSC_INCLUDE_DIRS}) target_link_libraries(${file_target} PRIVATE common ${PETSC_LINK_LIBRARIES}) # 将每个可执行文件添加为case目标的依赖 add_dependencies(case ${file_target}) message("HERE") endforeach()
现在执行make case时,CMake会自动编译未完成的common库,再编译所有case下的可执行文件。
3. 验证步骤
- 清空build目录,重新执行
cmake .. - 执行
make common:仅编译common库 - 执行
make case:编译common(若未编译)和所有case可执行文件 - 执行
make:编译所有目标
内容的提问来源于stack exchange,提问作者suzm10
相关产品推荐
相关产品推荐

