多目标CMake项目如何用doxygen_add_docs()构建时生成统一文档
问题描述
项目结构
myproject/ ├─ build/ ├─ cmake/ │ └─ DoxygenFun.cmake ├─ docs/ ├─ src/ │ ├─ CMakeLists.txt │ ├─ superlib/ │ │ ├─ CMakeLists.txt │ │ ├─ lib1/ │ │ │ ├─ include/ │ │ │ │ └─ lib1.h │ │ │ ├─ lib1.cpp │ │ │ └─ CMakeLists.txt │ │ └─ lib2/ │ │ ├─ include/ │ │ │ └─ lib2.h │ │ ├─ lib2.cpp │ │ └─ CMakeLists.txt │ └─ exec/ │ ├─ main.cpp │ ├─ aux.h │ └─ CMakeLists.txt └─ CMakeLists.txt
项目说明:
superlib包含lib1、lib2两个库(lib2依赖lib1),通过add_library()定义exec是依赖lib2的可执行程序,通过add_executable()定义- 各层级
CMakeLists.txt仅包含add_subdirectory()指令
当前Doxygen配置问题
DoxygenFun.cmake中定义的函数:
function(DoxygenFun target input) set(DOXYGEN_HTML_OUTPUT ${PROJECT_BINARY_DIR}/docs) # ????? set(DOXYGEN_JAVADOC_AUTOBRIEF YES) set(DOXYGEN_GENERATE_HTML YES) set(DOXYGEN_HAVE_DOT YES) # other DOXYGEN_... options doxygen_add_docs(doxygen-${target} ${input} ALL # add to the default build target COMMENT "Generate HTML documentation" ) endfunction()
顶层CMakeLists.txt:
cmake_minimum_required(VERSION 3.24) project(myproject VERSION 0.1.0 LANGUAGES CXX) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") find_package(Doxygen REQUIRED dot) include(DoxygenFun) add_subdirectory(src bin) add_custom_target(doc_doxygen ALL COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/docs WORKING_DIRECTORY ${PROJECT_BINARY_DIR} )
现在每个目标的CMakeLists.txt都调用DoxygenFun(targetname ${CMAKE_CURRENT_SOURCE_DIR}),导致:
- 每个目标在各自的
${CMAKE_CURRENT_BINARY_DIR}生成单独的Doxyfile,自定义的doc_doxygen目标无效 - 手动运行各
Doxyfile时,生成的HTML文档会互相覆盖
用户疑问:
- 是否误解了
doxygen_add_docs()的用法? - 是否需要收集所有源文件和头文件,仅调用一次
doxygen_add_docs()?对应的目标应该是什么? - 是否需要在
docs目录下添加CMakeLists.txt并在顶层文件中调用add_subdirectory(docs)?
解决方案
关于doxygen_add_docs()的用法误解
没错,你确实误解了这个函数的用法:doxygen_add_docs()会为每次调用生成独立的Doxyfile和构建目标,多次调用就会生成多个独立的文档构建流程,这就是为什么会出现多份Doxyfile、文档互相覆盖的问题。
正确的实现方式:单调用生成统一文档
不需要在每个目标里调用DoxygenFun,应该收集全项目的源文件和头文件,仅调用一次doxygen_add_docs(),具体步骤如下:
移除顶层无效的自定义目标
删掉顶层CMakeLists.txt里的doc_doxygen自定义目标,因为doxygen_add_docs()已经可以生成对应的构建目标,无需手动调用DOXYGEN_EXECUTABLE。收集全项目的源文件
在顶层CMakeLists.txt中,添加代码收集所有需要生成文档的文件:
# 收集项目内的所有头文件和源文件,CONFIGURE_DEPENDS让CMake自动检测文件变化 file(GLOB_RECURSE DOC_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/**/*.h ${PROJECT_SOURCE_DIR}/src/**/*.cpp )
如果有不需要包含的文件,可以用list(REMOVE_ITEM)剔除。
- 调用DoxygenFun生成统一文档
在顶层CMakeLists.txt中,调用DoxygenFun一次即可:
DoxygenFun(myproject "${DOC_SOURCES}")
- 调整DoxygenFun函数
确保输出路径正确,还可以添加项目名称和版本的配置,修改后的函数:
function(DoxygenFun target input) set(DOXYGEN_HTML_OUTPUT ${PROJECT_BINARY_DIR}/docs) set(DOXYGEN_JAVADOC_AUTOBRIEF YES) set(DOXYGEN_GENERATE_HTML YES) set(DOXYGEN_HAVE_DOT YES) # 添加项目标识配置 set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME}) set(DOXYGEN_PROJECT_VERSION ${PROJECT_VERSION}) # other DOXYGEN_... options doxygen_add_docs(doxygen-${target} ${input} ALL # 添加到默认构建目标,执行make/all时自动生成文档 COMMENT "Generate unified HTML documentation for ${PROJECT_NAME}" ) endfunction()
关于docs目录的处理
不需要在docs目录添加CMakeLists.txt和调用add_subdirectory(docs),除非你需要在该目录放置自定义的Doxygen配置模板(比如Doxyfile.in),但当前的方式已经足够生成统一文档。
额外优化建议
- 如果项目文件固定,也可以手动列出所有需要的文件,避免
GLOB_RECURSE意外包含临时文件;如果文件经常变动,GLOB_RECURSE配合CONFIGURE_DEPENDS更方便。 - 如果需要单独为某个库生成文档,可以保留局部的
DoxygenFun调用,但要修改DOXYGEN_HTML_OUTPUT为不同的子目录(比如${PROJECT_BINARY_DIR}/docs/lib1),避免覆盖。
内容的提问来源于stack exchange,提问作者Breno
相关产品推荐
相关产品推荐

