多模块CMake项目中Target命名冲突问题求助
doc Target冲突的方案 这问题我之前帮团队处理过好几次,几个方案供你参考,都能避开手动输入带前缀的命令:
方案1:创建超级项目统一入口目标(推荐)
这个方案最贴合你的需求——用户只需要执行make doc就能生成所有子项目的文档,同时彻底解决目标名冲突:
子项目侧调整:
每个子项目保留内部可用的doc别名,但实际执行文档生成的目标用带项目前缀的名称。在子项目的CMakeLists.txt里添加:# 创建带项目前缀的实际文档生成目标 add_custom_target(${PROJECT_NAME}_doc COMMAND 你的文档生成命令(比如doxygen ./Doxyfile) COMMENT "生成${PROJECT_NAME}项目文档" ) # 为子项目内部保留`doc`别名,单独构建子项目时仍能使用`make doc` add_custom_target(doc ALIAS ${PROJECT_NAME}_doc)超级项目侧整合:
在超级项目的CMakeLists.txt里,定义一个全局的doc目标,让它依赖所有子项目的带前缀文档目标:# 定义全局统一的doc目标 add_custom_target(doc COMMENT "生成所有子项目文档") # 遍历所有子项目,添加依赖关系 foreach(subproj IN ITEMS ProjectA ProjectB ProjectC) add_dependencies(doc ${subproj}_doc) endforeach()这样做的好处:
- 超级项目下执行
make doc会自动批量生成所有子项目文档 - 单独进入某个子项目的构建目录时,依然可以用
make doc生成该项目的文档 - 完全规避了目标名冲突问题
- 超级项目下执行
方案2:利用CMake的目标路径引用(无需修改子项目)
如果不想改动子项目的现有CMake配置,可以直接用CMake支持的子目录/目标名语法来指定目标:
比如你的子项目分别放在ProjectA、ProjectB目录下,超级项目构建时:
# 生成ProjectA的文档 make ProjectA/doc # 生成ProjectB的文档 make ProjectB/doc
要是想一键生成所有文档,可以在超级项目里加个简单的自定义目标来批量触发:
add_custom_target(all_docs COMMAND ${CMAKE_MAKE_PROGRAM} ProjectA/doc COMMAND ${CMAKE_MAKE_PROGRAM} ProjectB/doc COMMENT "生成所有子项目文档" )
这个方案的优点是不用改子项目代码,但用户需要记住子项目的目录名,便捷性不如方案1。
方案3:使用CMake命名空间(CMake 3.21+)
如果你的CMake版本在3.21及以上,可以利用CMAKE_TARGET_NAMESPACE自动给子项目目标加上规范的命名空间前缀:
在每个子项目的CMakeLists.txt开头设置:
set(CMAKE_TARGET_NAMESPACE ${PROJECT_NAME}::)子项目里原本的
doc目标会自动带上命名空间,变成ProjectA::doc、ProjectB::doc超级项目里创建全局
doc目标,依赖所有命名空间下的文档目标:add_custom_target(doc COMMENT "生成所有子项目文档") add_dependencies(doc ProjectA::doc ProjectB::doc)这种方式符合CMake的现代规范,用户既可以用
make doc生成所有文档,也可以用make ProjectA::doc单独生成某个项目的文档,比生硬的下划线前缀更优雅。
内容的提问来源于stack exchange,提问作者Marco Stramezzi

