ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Doxygen 从入门到实践:注释规范与自动化文档生成指南

Doxygen 从入门到实践:注释规范与自动化文档生成指南 1. 项目概述为什么我最后选了 Doxygen项目做久了文档这事迟早躲不掉。以前带过的几个项目明明代码量不小一说到“有没有文档”大家就开始沉默了。后来我开始整理一套适合自己团队的注释规范用的是 Doxygen。Doxygen 是干嘛的简单说它能把代码里的注释抽取出来自动生成 HTML、LaTeX、RTF 这类文档。很多人第一反应是“又要学一个新工具了”其实没那么吓人。Doxygen 不是文档生成器里唯一的选择但它是支持语言最全面、输出格式最灵活、生态也相对成熟的那个。我对它的评价就一句话只要注释写得够规范文档就是免费的时间成本被压到了最低。这篇文章面向三类人一是被“写文档”困扰了很久、想找个自动化方案的个人开发者二是团队里需要统一代码注释规范、减少新人沟通成本的技术负责人三是已经装了 Doxygen 但只会敲doxygen 文件名遇到乱码、没图、目录混乱就不知道怎么调的人。我在这篇文章里把注释规范、配置项、实操流程、踩坑点全部串起来按我自己从零到一使用 Doxygen 的过程来讲。你不需要再翻官方手册也不必去搜一堆碎片化资料按着下面的顺序走完基本就能在自己的项目里跑通。2. 让注释先规范起来Doxygen 注释语法速成Doxygen 能生成文档的前提是代码里有它“认识”的注释。不建议一上来就去研究复杂的配置先把注释格式搞定文档质量就成功了一大半。2.1 JavaDoc 风格注释最常用的一套写法Doxygen 支持多种注释风格最推荐团队用的是 JavaDoc 风格一是因为它的可读性好二是因为大部分用过 Java 的开发者都见过上手成本低。对于函数、类、结构体这类带声明的东西写在声明上方的注释块用/** ... */包裹起来/** * brief 计算两个整数的和 * * param a 第一个加数 * param b 第二个加数 * return 两数之和若溢出则返回-1 */ int add(int a, int b);这里有几个关键命令值得留意brief是简短描述会出现在目录和索引里param描述参数return描述返回值。看文档的人第一眼通常只扫函数名和brief这两行一定要写得像“一句话能看懂这个函数是干嘛的”而不是堆概念。我自己的习惯是在brief里写函数职责在完整描述里写使用场景和边界条件。比如“计算两个整数的和”这种描述太笼统了完整描述可以补充“当相加结果超出 int 范围时返回 -1调用方需要自行处理”。这样文档才不是注释的简单搬运而是真正有信息量的参考。2.2 Qt 风格与其他细节除了 JavaDoc 风格Doxygen 还支持 Qt 风格注释写法上更适合从 Qt 项目转过来的团队。个人觉得没有优劣之分只要团队内部统一就行。Qt 风格是这样的/*! * \brief 设置设备名称 * * \param name 设备名称字符串 * \sa getDeviceName() */ void setDeviceName(const QString name);这里\sa会在文档里生成一个“参考参见”链接指向同类的其他函数适合梳理相关接口。更重要的是Doxygen 不仅处理点和斜杠形式还会解析注释里的 markdown 标题、列表、代码块。这意味着可以在注释里写相对丰富的说明比如使用示例、依赖关系、注意事项。下面这个例子就是我自己常用的写法/** * brief 将字节数组转换为十六进制字符串 * * 使用示例 * cpp * QByteArray bytes ...; * QString hex toHex(bytes); * * * 注意该函数不会修改原数组转换结果全部为小写字母。 * * param data 待转换的字节数组 * return 十六进制字符串 */ QString toHex(const QByteArray data);这样生成的文档里调用示例、注意点都直接跟着函数走。写注释时多用“一段话”而不是“几个词”文档质量会明显提升。2.3 特殊命令bug、todo、deprecated、sinceDoxygen 里还有几个特别实用的“标记型”命令它们不描述函数逻辑而是描述代码状态。todo是待办事项生成文档时会汇总到独立的 Todo 列表里。我习惯在代码里看到还没补全的分支时随手加一行todo 处理空指针情况等版本迭代时打开文档里的 Todo 页没做完的事一目了然。bug和 Todo 类似汇总到 Bug 列表deprecated用来标记废弃接口文档中会显示删除线并注明替代方案since标注引入该接口的版本号对长期维护的开源项目特别有用。这些标记的本质是把代码审查里应该留的批注写进文档。我以前带过的项目里代码评审时总是口头说“这个接口以后要改”改完就没人记得了。有了todo和deprecated这些信息不会散落在聊天记录里而是沉淀在文档中。3. Doxyfile 配置从零开始搞懂关键参数注释规范定了接下来是配置。Doxygen 的运行依赖一个叫 Doxyfile 的配置文件。初次生成配置时先别急着手动写Doxygen 提供了一个向导式生成命令。3.1 配置生成与基础项在项目根目录下执行doxygen -g这会在当前目录生成一个默认的 Doxyfile文件特别长里面每一项都有详细说明。但实际需要动的地方不多。我整理了几个必须要改的项配置项推荐值作用PROJECT_NAME项目名显示在文档首页OUTPUT_DIRECTORYdocs文档输出目录INPUT留空或填写源码目录指定扫描哪些目录RECURSIVEYES是否递归扫描子目录EXTRACT_ALLYES没有注释的成员也会出现在文档里GENERATE_HTMLYES生成 HTML 文档SOURCE_BROWSERYES在文档里显示源码方便阅读我踩过的第一个坑就是EXTRACT_ALL默认是NO。意味着代码里没写注释的类、函数、变量会被直接忽略。新手做完配置后打开文档发现只有寥寥几个文件多半就是忘了开这一项。把它改成YES之后所有符号都会出现在文档里没注释的条目会显示为空至少保证了结构完整性。INPUT如果留空Doxygen 会默认扫描当前目录。我的习惯是显式配置例如INPUT src include tests避免把docs、build这些目录也扫进去。千万别把 build 目录放进 INPUT否则生成的文档里会混进一堆自动生成的代码。3.2 控制文档输出形态除了基础项还有几个参数学会了之后能明显提升观感。EXTRACT_PRIVATE和EXTRACT_STATIC默认也是NO。是否提取私有成员和静态成员取决于文档的用途。如果文档给外部用户看私有成员可以不显示如果团队内部做代码走查建议打开让文档完整呈现内部结构。SHOW_FILES控制是否显示文件列表页。默认YES会导致文档一打开就是一堆.h、.cpp文件列表对使用者来说意义不大。如果生成的是对外 API 文档可以设为NO如果是团队内部代码参考保持YES反而方便按文件检索。FULL_SIDEBAR是 HTML 页面侧边栏默认YES就可以。如果文档内容特别多展开的侧边栏可以在不同页面之间快速跳转比翻页舒服很多。最后还有两个跟“图”有关的配置HAVE_DOT和CALL_GRAPH。要生成类继承关系图、调用关系图需要先安装 Graphviz 并把HAVE_DOT设为YES同时打开CALL_GRAPH、CALLER_GRAPH。这套图对理清大型项目结构很有帮助但安装 Graphviz 这一步经常被忽略。后面我会专门讲这个坑。4. 实操从源码到 HTML 文档的完整流程这一节按完整流程走一遍从安装到看到文档成品你把每一步照抄即可。4.1 安装 Doxygen 和 Graphviz安装现在比较简单。Linux 上我用的是 aptsudo apt update sudo apt install doxygen graphvizmacOS 可以用 Homebrewbrew install doxygen graphvizWindows 用户去 Doxygen 官网下载安装包Graphviz 去 graphviz.org 下载安装时记住勾选添加到 PATH的选项否则后面 Doxygen 找不到 dot 命令。安装完验证一下doxygen --version dot -V两条命令都能正常输出版本号说明环境没问题。dot -V输出版本信息时是把版本写到 stderr 的所以终端里显示“错误”字样不要慌这是正常现象。4.2 配置示例可直接复制假设你有一个项目结构如下my_project/ ├── src/ │ ├── core.c │ └── utils.c ├── include/ │ ├── core.h │ └── utils.h └── Doxyfile在项目根目录生成 Doxyfile 后把以下几项修改成对应的值# 项目信息 PROJECT_NAME My Project PROJECT_BRIEF A brief description of the project. OUTPUT_DIRECTORY docs # 输入范围 INPUT src include RECURSIVE YES # 提取范围 EXTRACT_ALL YES EXTRACT_PRIVATE YES EXTRACT_STATIC YES # 输出格式 GENERATE_HTML YES GENERATE_LATEX NO # 图表 HAVE_DOT YES CALL_GRAPH YES CALLER_GRAPH YESGENERATE_LATEX我直接关掉了因为团队文档标准是 HTMLLaTeX 那套还要本地装 TeX 环境默认打开会拖慢生成速度还容易因为环境不合报错。想要 PDF 文档的话单独开一个配置文件来处理别让日常生成流程背着 PDF 的负担。4.3 生成文档与关键输出物配置好之后运行doxygen Doxyfile几条干净的信息输出之后打开docs/html/index.html就能看到文档首页。我第一次生成时用的就是上面这套配置效果立竿见影。源码里那些规范的注释全部进入了文档左侧是类列表、命名空间列表、文件列表中间是类关系图。比起手写手册多了几个明显好处第一同名的类和函数不需要人工维护索引。Doxygen 自动按字母排好找起来比翻 Word 文档快得多。第二函数签名直接跟着注释走文档里看到的参数类型和返回值就是代码里的真实类型不存在手误抄错的问题。第三类之间的继承关系图是自动画的略一浏览就能了解整体架构。我项目里的src和include分别对应实现和接口。团队内部约定接口头文件写完整注释给同事看源文件里的注释则以“为什么这么写”为主给后来接手的人看。Doxygen 默认只提取声明部分的注释但如果声明处没写它也会“智能”地尝试从定义处提取。这个特性在工具链里叫“来自注释块的跨对象合并”理解成“先找声明注释找不到就找定义注释”就行。这个行为省了我很多重复劳动——有时候老代码里只有定义处有注释生成出来的文档依然有余不会空白一片。5. 常见问题与排查我踩过的坑这部分记录我在实际使用中反复遇到过的问题按照“现象-原因-解决”的方式整理成速查表。5.1 中文注释变乱码Doxygen 项目里中文乱码是最常见的本质是编码设置不匹配。Doxygen 默认按 UTF-8 解析文件如果你的源文件是 GB2312 或 GBK就必然乱码。解决方法是统一源文件编码。新项目直接全用 UTF-8老项目量太多改不动时可以在 Doxyfile 里设置INPUT_ENCODING GBK但要注意一个项目里最好只出现一种编码。如果一部分文件 UTF-8、一部分 GBK那INPUT_ENCODING无论设成哪个都有部分文件会乱。从根上解决的办法是批量转码一次源文件然后彻底切换成 UTF-8。团队协作时也要约定好编辑器的默认保存编码。5.2 图形不显示类图生成失败文档里没有类图和调用图一般来说是HAVE_DOT设置了对但 Graphviz 没装好。检查方法在命令行输入dot -V。如果提示找不到命令说明 Graphviz 没装或者没加入 PATH。Windows 用户特别注意安装 Graphviz 时有个选项“Add Graphviz to the system PATH”默认可能不勾选必须手动勾上。还有一次我遇到的情况是dot -V正常类图依然没有。后来发现是 Doxyfile 里的DOT_PATH指错了位置。这个配置项默认是空Doxygen 会自动从 PATH 里找 dot。如果自己手动填了错误路径反而会覆盖自动搜索。解法就是把这个选项留空让系统按 PATH 找别画蛇添足。5.3 递归扫描目录后文档内容过多打开RECURSIVE YES之后文档可能会冒出许多开发工具自动生成的目录文件比如 UI 层自动生成的.moc文件、构建中间产物等。解决办法有两层一是用EXCLUDE配置项把不需要的目录排除掉二是养成好习惯把输入放在 src、include 这种明确的源码目录里。EXCLUDE_PATTERNS */build/* */moc/* */.*EXCLUDE_PATTERNS支持通配符可以排除特定模式。我用它过滤掉.git这类隐藏目录以及build中间目录。5.4 同名文件导致的文档覆盖项目里两个子目录下各有一个utils.h默认情况下 Doxygen 会把它们当成两个独立的文件处理不会互相覆盖。但一旦把文件列表关闭或源码浏览打开跳转时容易搞混。更好用的方式是在头文件里用file命令加上文件全路径标识/** * file core/utils.h * brief 核心工具函数集 */这样文档里就能根据路径区分同名文件不再迷路。5.5 修改注释后文档没变化Doxygen 默认会缓存上次扫描的结果部分情况下更新不及时。官方的说法是它根据时间戳判断是否重新解析但如果你连续生成间隔太短、或者文件时间戳被某些工具修改过结果可能不太新鲜。最简单的做法是先把输出目录清掉再生成rm -rf docs doxygen Doxyfile不要在小改动后直接看浏览器里的旧文件导致误以为配置没生效。6. 进阶玩法让生成的文档真正好用基础文档生成只是第一步。项目中后期你会发现默认生成的文档还是“偏平”的。此时有几个进阶技巧值得投入它们对可用性的提升远远大于配置里的其他花哨项。6.1 模块分组告别大杂烩目录文档一旦有几十个类左侧目录就会挤成一大片。这时应该按模块分组。在头文件里加上defgroup和ingroup命令/** * defgroup network 网络模块 * brief 封装 TCP/UDP 通信相关接口 */然后在各个类或函数注释里声明归属/** * ingroup network * class TcpClient * brief TCP 客户端封装 */ class TcpClient { ... };这样生成的文档左侧会多出一个“网络模块”分组该类归于其下。团队里的人看文档时先按功能模块找而不是从庞大的类列表里硬翻。这个技巧在大型 C 或 C 项目里特别值得推广。我带过的一个通信中间件项目四百多个类没分组时文档完全没法用分组之后检索效率提升了一个量级。它还顺便把模块边界在代码注释里显式声明了跟架构文档形成映射。6.2 自定义页头和页尾文档不那么“模板脸”默认生成的 HTML 模板一眼就能看出是 Doxygen 生成的。如果你希望文档看起来更像“自己人整理的手册”可以用HTML_HEADER、HTML_FOOTER、HTML_STYLESHEET三个配置项分别指定自定义页头、页脚、样式表。第一次做时用doxygen -w html header.html footer.html stylesheet.css导出默认模板作为起点在默认模板基础上改样式比自己从零写要省力得多。页头里可以加一个团队内部链接或文档首页说明页脚里可以写明责任人和版本信息。对团队协作来说“文档是哪个版本生成、谁负责更新”这些信息特别重要默认模板反而是没有的值得手动补上。6.3 版本号与宏过滤管好可见范围大型项目的文档通常不止一套比如对外 API 文档和内部实现文档。Doxygen 提供了配置项可以结合 C/C 预处理功能来控制哪些代码出现在文档里。PREDEFINED可以定义宏配合EXTRACT_ALL实现“双版本文档”。比如PREDEFINED INTERNAL_API然后在代码里#ifdef INTERNAL_API void debugPrint(const char* msg); #endif这样就形成了两类文档对外版不定义这个宏内部版定义它。对外文档清爽内部文档完整用同一套 Doxyfile 加不同的预设值就能实现。还有一个小技巧VERSION_NUMBER标注文档版本PACKAGE_NAME 修改包名。生成的 HTML 页面标题会以它为前缀是规范化的第一步。6.4 在 CI 中自动生成文档建议把文档生成集成到 CI让每次提交自动跑一遍doxygen并发布到内部文档站。即便团队只有两三个人这一步也值得做。我用的是 GitLab CI核心步骤如下pages: stage: deploy script: - doxygen Doxyfile - mv docs/html public artifacts: paths: - public这样每次提交后最新的文档会自动发布到 pages。在本地顺手跑一下当然也能用但 CI 化之后文档会和代码保持同步不会再出现“代码改了文档还停留在上个月”的尴尬。如果你用的是 GitHub对应机制也大同小异。核心思想就一个文档生成是构建过程的一环不是人工运维的额外任务。最后再分享一个实用小技巧我对成员的描述风格坚持“一句话简介 完整说明 必要示例”的模式。一句话简介保证索引页看起来干净利落完整说明承载使用细节示例则降低阅读者理解成本。写注释时不贪多不堆砌形容词用户看文档时能三秒钟找到关键信息这才是自动化文档该有的体验。Doxygen 这套体系运行稳定我已经在自己的多个项目里验证过你照此搭起来不会走弯路。
返回列表