多项目间共享CMake脚本的实现方案咨询
问题原因
你遇到的报错根因是CMake执行include指令时,相对路径默认基于当前正在执行的CMakeLists.txt所在的源目录(即CMAKE_CURRENT_SOURCE_DIR变量值)。你在主项目中引入somelib的根CMakeLists.txt时,此时的CMAKE_CURRENT_SOURCE_DIR是主项目的根目录,不是somelib的根目录,因此somelib内部写的相对路径cmake/xxx.cmake会去主项目的cmake目录查找,自然找不到。
另外你在FetchContent_MakeAvailable(somelib)之后再手动include somelib的根CMakeLists.txt属于冗余操作,FetchContent_MakeAvailable已经完成了somelib的配置、目标声明等流程,重复执行会导致目标重定义、配置重复生效等问题。
最优解决方案(仅需修改somelib侧代码,下游无感知)
在somelib的cmake目录下新增一个统一的导出入口脚本,比如命名为somelib.cmake,所有内部脚本的引入都基于CMAKE_CURRENT_LIST_DIR变量(该变量永远指向当前正在执行的cmake脚本所在的目录,和调用方的路径无关):
# somelib/cmake/somelib.cmake 内容 include(${CMAKE_CURRENT_LIST_DIR}/Cache.cmake) include(${CMAKE_CURRENT_LIST_DIR}/Linker.cmake) include(${CMAKE_CURRENT_LIST_DIR}/CompilerWarnings.cmake) # 后续新增的cmake脚本统一在这里添加即可
然后修改somelib自身的根CMakeLists.txt,原来逐个include的部分改成引入这个统一入口:
# somelib/CMakeLists.txt 中引入脚本的部分改为 include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/somelib.cmake)
下游项目使用时,拉取完somelib后仅需一行代码就能引入所有脚本:
include(FetchContent) FetchContent_Declare( somelib GIT_REPOSITORY git@git.somewhere.com:somewhere/somelib.git GIT_TAG 【建议替换为固定的commit哈希,避免分支更新导致构建不稳定】 ) FetchContent_MakeAvailable(somelib) # 仅需这一行即可引入所有somelib提供的cmake脚本 include(${somelib_SOURCE_DIR}/cmake/somelib.cmake)
后续somelib新增cmake脚本时,仅需在自己的somelib.cmake入口中添加一行即可,所有下游项目无需修改任何代码就能自动用上新脚本,完全没有冗余。
临时兼容方案(无需修改somelib,仅改下游代码)
如果暂时无法修改somelib的代码,可以在下游项目中把somelib的cmake目录加入CMake的模块搜索路径,之后直接用文件名引入即可:
FetchContent_MakeAvailable(somelib) list(APPEND CMAKE_MODULE_PATH ${somelib_SOURCE_DIR}/cmake) # 之后直接用文件名引入脚本即可,不需要写全路径 include(Cache.cmake) include(Linker.cmake) include(CompilerWarnings.cmake)
关于FetchContent使用的建议
你当前使用FetchContent引入依赖的方式是CMake官方推荐的最佳实践之一,唯一可以优化的点是GIT_TAG尽量使用固定的commit哈希值,而不是origin/master这类分支名,避免上游仓库分支更新后,下游未经测试就拉到新代码导致构建失败。
内容的提问来源于stack exchange,提问作者ShellCode

