Doxygen是API文档生成工具,可以根据代码注释生成文档的工具。支持HTML、CHM、PDF等格式。主要支持C语言、Python语言,其它C语系语言也支持(如C++、Java、C#等)。
本教程的测试环境
Ubuntu 18.04 LTS Doxygen 1.8.13 C++ Gitlab CI/CD windows 什么是 Doxygen? Doxygen 是一个将文件的特定注释转化为文档的工具
如何安装 Doxygen? $ sudo apt install graphviz
$ sudo apt install doxygen
如何使用 Doxygen? 1. 查看你使用的语言 Doxygen 是否默认支持?
Doxygen 默认支持的语言有:C,C++,C#,Objective-C,IDL,Java,VHDL,PHP,Python,Tcl,Fortran 和 D
2. 生成配置文件
$ ## 生成配置文件,默认配置文件名为:Doxyfile
$ doxygen -g <config-file>
$
$ ## 生成配置文件(不含注释)
$ doxygen -s -g <config-file>
$
3. 修改配置文件
## 常见配置选项
## 设置项目编码,默认为 UTF-8
DOXYFILE_ENCODING = UTF-8
## 设置项目名称
PROJECT_NAME = "project-name"
## 设置项目版本号
PROJECT_NUMBER = "1.0.0"
## 设置项目的描述
PROJECT_BRIEF = "这是项目描述"
## 设置项目的 logo
PROJECT_LOGO = ""
## 设置输入目录,如果未设置,则在当前目录查找
INPUT = src
## 设置要匹配的输入文件
FILE_PATTERNS = *.cc *.h
## 设置不需要处理的输入目录
EXCLUDE =
## 设置不需要匹配的输入文件
EXCLUDE_PATTERNS =
## 设置输入编码,默认为 UTF-8
INPUT_ENCODING = UTF-8
## 设置是否递归搜索输入目录,默认为 NO
RECURSIVE = NO
## 设置是否提取所有类,函数等(不包括类的私有成员和静态成员),默认为 NO
EXTRACT_ALL = NO
## 设置是否提取类的私有成员,默认为 NO
EXTRACT_PRIVATE = NO
## 设置是否提取类的静态成员,默认为 NO
EXTRACT_STATIC = NO
## 设置文档是否包含源文件,默认为 NO
SOURCE_BROWSER = NO
## 设置是否对每个类都链接到其所在的头文件中,默认值为 YES
VERBATIM_HEADERS = YES
## 设置文档的输出目录
OUTPUT_DIRECTORY = doc
## 设置是否支持 Markdown,默认值为 YES
MARKDOWN_SUPPORT = YES
## 设置文档的主界面
USE_MDFILE_AS_MAINPAGE =
## 设置文档的语言,默认为 English
OUTPUT_LANGUAGE = Chinese
4. 给代码添加注释 并不是所有的注释都会被收入文档,Doxygen 支持的常用的注释风格有:
/** 注释的内容 */
/*! 注释的内容 */
## 在变量后 注释文件,类,结构体,共同体,枚举成员 或 函数参数
int a; /**< 注释的内容 */
int a; /*!< 注释的内容 */
注意: 这里并不是所有的注释风格,更多注释风格见 官网
Doxygen 常用的注释标记(标记以 / 或 @ 开头表示):
## 添加作者
@author 作者1 作者2
## 添加日期
@date 日期
## 添加文件名
@file 文件名
## 添加简单描述
@brief 简要描述
## 添加详细描述
@details 详细描述
## 添加类信息
@class 类名 类所在的文件 类所在的文件(可包括路径)
## 添加结构体信息
@class 结构体名 结构体所在的文件 结构体所在的文件(可包括路径)
## 添加宏信息
@enum 宏名
## 添加函数信息
@fn 函数信息
## 添加参数说明
@param [in] 输入参数名 说明
@param [out] 输出参数名 说明
## 添加返回说明
@return 返回说明
## 添加返回特定值说明
@retval 特定值 特定返回值说明
## 添加异常说明
@exception 异常类型 异常说明
## 添加代码
@code
...代码...
@encode
## 添加文件名说明
@headfile 文件名 文件名(可包括路径)
## 添加版本号
@version 版本号
## 添加计划做的事儿
@todo 计划做的事
## 添加参考
@see 参加其它
## 添加过时说明
@deprecated 过时说明
## 添加 bug 说明
@bug "bug 说明"
## 添加例子
@example 例子文件名
## 添加警告信息
@warning 警告信息
## 添加开始使用的版本
@since 版本
## 添加测试信息
@test 测试
## 添加主界面信息
@mainpage 标题
## 添加注意事项
@note 注意事项
## 添加协议信息
@copyright 协议信息
为C/C++添加注释 首先为函数添加注释信息,这是必须要做的。这里有个选择性问题,添加到哪里呢?.c文件?.h文件?
一般来说:
.h文件代表模块对外的接口最小信息,面向模块使用者 .c文件代表模块的实现代码,面向的是开发者 在实际编程中,事先约定各个模块间的接口,然后将不同的模块分配给不同的开发者,与此同时,测试人员根据接口要求,编写测试代码,这就完全保证了并发编程和白盒测试要求。
这里我们可以看到,文档主要是用来描述接口信息的,所以,我对代码的注释规定如下:
模块对外接口,仅在.h中提供注释信息 模块内部辅助函数,全部用static设为私有函数,同时仅在.c中保留注释信息 当然,您也可以同时为.c .h的接口函数编写两份完全一样的注释信息,但这么做,您会同时维护两份信息,出错的概率会更大些。
确定了注释位置,下一步考虑一个函数需要哪些信息
一般来说,需要函数功能,入口参数,返回值,注意事项,某些时候还需要说明上下文环境,从而保证函数能正确执行
比如这个函数
extern int Dev_PrintInt(int number);
它的功能就是打印一个整形数据,传入参数为整数,返回的是成功打印的数据长度(字节为单位),同时呢,我们在调用这个函数之前,必须要先初始化Dev设备
ok,这就是所有接口信息,稍微规范一下,就变成了下面的样子
// 函数功能:打印整数
// 入口参数:number为一个整数类型
// 返回结构:返回的是成功打印的数据长度(字节为单位)
// 注意事项:
// 1:在调用本函数前,请确保已经调用Dev_Init初始化设备
// 2:请注意函数返回值,如果该值为0,则说明函数执行失败
extern int Dev_PrintInt(int number);
用英文来书写呢,则变成下面的样子
//***************************************************************************************
//
// brief : Print Int number to terimal device.
//
// param : number is the data you want to print.
// retval : the number of print information, in bytes. return zero indicate print error !
//
// Note:
// * Be sure you have called \ref Dev_Init function before call this fuction.
// * Remember to check return value.
//
//***************************************************************************************
extern int Dev_PrintInt(int number);
注释信息写完了,一般来说,函数能达到这种信息程度就ok了,但既然要生成文档,就不得不考虑一个问题
如果你是Doxygen作者,怎么从上面的注释里面提取信息呢,信息那么多,有*
号,有各种文字信息。
你可以将所有的注释信息都输出出来,但这么做,等于没有分类整理,同时也包含了杂乱信息,比如一排*
另外一个解决方法是:设置某些特殊字符,比如function
表示,一旦检测到这个特殊标记,则认为是接下来 的一行是函数功能描述。但这么做,万一用户的注释里面出现很多个function,你怎么识别哪个是普通文本, 哪个是特殊标记?
也许你会说了,可以采用F U N C T I O N 这种形式啊,恩,这么做是可行的,可以确保识别出来特殊标记
接下来,还有一个问题,我们上面的注释中,有很多*
号,仅仅起到美观和格式化的作用,当然不希望在 输出文档中显示这些东西,问题是你怎么识别这些符号,并不显示呢?也许你会说,可以强制规定注释的 格式,不让用户在代码中写很多*
,ok,假设用户同意这么做。那接下来呢,如果我希望在代码中写某些话 ,但是不希望输出到文档中,比如“XX是2B”等等,你又该怎么做呢?
正向思考遇到问题时,不妨反向考虑,这是谁的问题:是我设计思路的问题还是用户用法的问题?
困难重重,肯定是设计思路的问题
如果设计一个标记符,将普通注释和要生成的文档注释区分开来,就能解决问题了。
Doxygen的用法,说白了,就是为了解决上面提到的两个问题:
怎么区分普通注释和输出注释
怎么在输出注释里面,识别特殊标记和普通文本
ok,讲到这里,基本把Doxygen的机制给解释清楚了,如果您还不理解,最简单的方法就是把你假设为Doxygen 作者,重新推演一遍。
下面咱们看看Doxygen怎么解决这两个问题的
区分普通注释和特殊注释
对于C/C++语言来说,注释形式有两种
Doxygen通过在这里增加*
,/
,!
来作为特殊标记,比如
对于/* */
这种注释来说,正常注释为
Doxygen在注释第一个*
后,设置*
或!
作为标志,如果检测到有这些, 就将接下来的注释作为导出文档来解释
/**
* 要输出成文档的注释
*/
或者
/*!
* 要输出成文档的注释
*/
同时,中间的*
号可以省略,像这样
/**
要输出成文档的注释
*/
或者
/*!
要输出成文档的注释
*/
对于//
这种类型的注释,Doxygen在第二个/
后,增加!
或/
作为区分标志,如果检测到有这些, 就将接下来的注释作为导出文档来解释
/// 要输出成文档的注释
或者
//! 要输出成文档的注释
对于这种呢,有一个潜在的问题,很多时候,我们需要在把注释放到后面,比如下面这种
#define DEV_ON ((int)(1)) //! Simple device is power on.
#define DEV_OFF ((int)(0)) //! Simple device is power off.
如果真要这么写的话,Doxygen会把//! Simple device is power on.
当做DEV_OFF
的注释,这 当然不是我们所希望的! 怎么办呢,只好再加一个特殊标记了,Doxygen针对这种情况,需要在!
后 再增加一个<
标志符,如果检测到这个,则认为这个注释是为前面代码准备的,所以,上面的注释应该 这么写
#define DEV_ON ((int)(1)) //!< Simple device is power on.
#define DEV_OFF ((int)(0)) //!< Simple device is power off.
做到这里,Doxygen就可以正确区分普通注释和特殊注释了。
**注:**提到特殊标记,其实吧,编程语言非常常用,比如HTML就是典型的markup语言,一堆一堆的括号,看着就头疼
Doxygen采用\
和@
作为特殊标记符,当在特殊注释里面检测到了特殊标记符,则接下来检测紧跟单词是不是Doxygen 事先规定好的,如果是,则将按照特定的规则来解释紧跟着的注释;如果不是呢,则将\
和@
解释为普通文本,聪明吧
可能有点拗口,下面给你个例子
//***************************************************************************************
//
//! \brief Print Int number to terimal device.
//!
//! \param [in] number is the data you want to print.
//! \retval the number of print information, in bytes. return zero indicate print error !.
//!
//! \note
//! * Be sure you have called \ref Dev_Init function before call this fuction.
//! * Remember to check return value.
//
//***************************************************************************************
extern int Dev_PrintInt(int number);
看到了吧,这里的\brief
和\param
都是特殊符号,表示简要描述和参数。万一你小手一抖,把\param
写成了\parame
,那就悲剧了,因为Doxygen不认识parame
,所以它会把这句话当做是普通文本来处理
其实,上面的\
换成@
也是ok的,如下所示
//***************************************************************************************
//
//! @brief Print Int number to terimal device.
//!
//! @param [in] number is the data you want to print.
//! @retval the number of print information, in bytes. return zero indicate print error !.
//!
//! @note
//! * Be sure you have called \ref Dev_Init function before call this fuction.
//! * Remember to check return value.
//
//***************************************************************************************
extern int Dev_PrintInt(int number);
相信某些玩过ARM芯片的,对这类注释非常熟悉,官方库都是采用Doxygen语法规则注释的
示例 采用Doxygen语法为main.c dev.c dev.h添加注释信息,完成后的效果如下所示:
main.c
//***************************************************************************************
//
//! \file main.c
//! This is an simple example show developer how to use dev api to print int number.
//!
//! \author Cedar
//! \version V1.0
//! \date 2014-03-23
//! \copyright GNU Public License V3.0
//
//***************************************************************************************
#include "dev.h"
#define CNT_MAX 10 //!< The maxium number of print
//! Simple device example.
void DEV_Example(void)
{
int i = 0;
Dev_Init();
for (i = 0; i < CNT_MAX; ++i)
{
Dev_PrintInt(i);
}
Dev_Close();
}
//! Application Entry
int main(void)
{
DEV_Example();
return 0;
}
dev.c
//***************************************************************************************
//
//! \file dev.c
//! the implement of simple device.
//!
//! \author Cedar
//! \version V1.0
//! \date 2014-03-23
//! \copyright GNU Public License V3.0
//
//***************************************************************************************
//! Simple device status.
//!
//! \warning This variable is designed for internal, user \b MUST \b NOT call it.
static int __DevStatus = 0
void Dev_Init(void)
{
// Print debug information
printf("Dev Initialize OK!\r\n");
}
int Dev_PrintInt(int number)
{
printf("Print IntType number: %d\r\n", number);
}
int Dev_StatusCheck(void)
{
return (__DevStatus);
}
void Dev_Close(void)
{
printf("Dev Close OK!\r\n");
}
dev.h
//***************************************************************************************
//
//! \file dev.h
//! Simple device user API.
//!
//! \author Cedar
//! \version V1.0
//! \date 2014-03-23
//! \copyright GNU Public License V3.0
//
//***************************************************************************************
#include <stdio.h>
//***************************************************************************************
//
//! \addtogroup Dev_Status Simple device status information.
//! @{
//
//***************************************************************************************
#define DEV_ON ((int)(1)) //!< Simple device is power on.
#define DEV_OFF ((int)(0)) //!< Simple device is power off.
//***************************************************************************************
//
//! @}
//
//***************************************************************************************
//***************************************************************************************
//
//! \addtogroup Dev_API Simple device APIs list.
//! @{
//
//***************************************************************************************
//***************************************************************************************
//
//! \brief Initialize simple device.
//!
//! \param none.
//! \retval none.
//!
//! \note This function \b MUST be called first before others function.
//
//***************************************************************************************
extern void Dev_Init(void);
//***************************************************************************************
//
//! \brief Print Int number to terimal device.
//!
//! \param [in] number is the data you want to print.
//! \retval the number of print information, in bytes. return zero indicate print error !.
//!
//! \note
//! * Be sure you have called \ref Dev_Init function before call this fuction.
//! * Remember to check return value.
//
//***************************************************************************************
extern int Dev_PrintInt(int number);
//***************************************************************************************
//
//! \brief Check simple device status information.
//!
//! \param none.
//! \retval status information of simple device, which can be one of the following value:\n
//! - \ref DEV_ON
//! - \ref DEV_OFF
//! \n More information, please reference \ref Dev_Status.
//
//***************************************************************************************
extern int Dev_StatusCheck(void);
//***************************************************************************************
//
//! \brief Close simple device.
//!
//! \param none.
//! \retval none.
//
//***************************************************************************************
extern void Dev_Close(void);
//***************************************************************************************
//
//! @}
//
//***************************************************************************************
//***************************************************************************************
//
//! \example main.c
//! Show how to use simple device to print int number.
//
//***************************************************************************************
4. 生成文档
Win 图形界面使用 第1章 安装 在Linux下可以通过apt install doxygen
安装命令行工具,然后用apt install doxygen-gui
安装图形界面。对Linux用户来说,命令行工具可以通过doxygen
命令运行,而图形界面可以通过doxywizard
命令运行。
而Windows用户可以在这里 下载,安装完毕后,直接双击就能运行图形界面。
1.1 基本使用 图形工具的基本使用如下图所示,有非常多的配置选项,这里我们只填入必要的配置,其它配置都用默认值。
我们的工作目录如下:
.
├── out
└── src
└── math.h
其中math.h
代码如下:
/*! \file math.h */
/*!
用于求一个角度的sin值,输入是字符串以便同时支持弧度制和角度制表示
\li 弧度制用pi表示,例如:2pi表示一圈、0.5pi表示直角
\li 角度制用d结尾,例如:360d表示一圈、90d表示直角
\li 输入也可以是数值,例如:输入3.14159大约表示180度
\param a 用弧度制或角度制表示都行,字符串必须用'\0'表示结束
\param[out] res 是输出参数,用于保存sin运算的结果
\return 错误码,0表示成功,其它表示失败
\todo 在xxx的情况下存在BUG,预计下一版本修复
*/
int sin(char *a, double *res);
Doxygen生成的HTML会放到out
目录下,生成的HTML如图1-3所示。
1.2 保存配置 在1.1节中我们配置了一些选项,也成功生成了HTML文档。我们希望下次代码改动后能够继续沿用上次配置,那么我们可以把这些配置保存成Doxyfile
文件,见图1-4。
1.3 命令行运行Doxygen 有了配置文件后我们完全可以通过命令行来生成API文档,假设配置文件名为Doxyfile,那么我们只需要执行doxygen /path/to/Doxyfile
即可生成API文档。
通过命令行生成文档有许多好处,其中最主要的好处就是:能够集成到持续集成之类的自动化系统中。
第2章 为代码编写注释 2.1 什么样的注释会被Doxygen识别? Doxygen能识别这几种风格的注释:
/**
* ... text ...
*/
/*!
* ... text ...
*/
///
/// ... text ...
///
//!
//!... text ...
//!
文件的开头必须有文件注释,否则该文件不会被识别:
2.2 注释怎么写 这个自己看官网例子 体会吧。
第3章 为其它编程语言生成注释 Doxygen主要支持C语言,其它语法跟C差不多的语言(如:C++/C#/PHP/Java)也能够支持,我们称这类语言为「C语系语言」。而哪些跟C语法差异较大的语言叫做「非C语系语言」。
对于大多非C语系语言,Doxygen都是支持的,Doxygen原生支持这些语言:IDL、Java、Javascript、C#、C、C++、D、PHP、Objective-C、Python、Fortran、VHDL。
万一项目需要的语言(例如:Lua)Doxygen官方并不支持,那么只能自行编写「第三方语言扩展」来支持了。
3.1 Doxygen官方支持的语言 见图3-1,文件名符合FILE_PATTERNS
都会被处理。其中包括了.c
、.h
、.py
等等。
如果我们的扩展名并不在FILE_PATTERNS
内,那么可以加上去。例如我们项目下的所有.ccc
文件,其实是C语言代码(这很奇葩,举个例子而已)。那我们可以编辑Doxyfile配置文件满足这一需求,需要2个步骤。
(1) 在FILE_PATTERNS
中添加*.ccc
,如图3-2
(2) 在EXTENSION_MAPPING
中添加映射规则ccc=C
,如图3-3。语法是ext=language
,其中language可以取的值有:IDL、Java、Javascript、C#、C、C++、D、PHP、Objective-C、Python、Fortran、VHDL。
3.2 Doxygen官方不支持的语言 以Lua语言为例,它的代码是长这样的:
-- \file lmath.h
--[[
用于求一个角度的sin值,输入是字符串以便同时支持弧度制和角度制表示
\li 弧度制用pi表示,例如:2pi表示一圈、0.5pi表示直角
\li 角度制用d结尾,例如:360d表示一圈、90d表示直角
\li 输入也可以是数值,例如:输入3.14159大约表示180度
\param a 字符串类型,表示角度,用弧度制或角度制表示都行
\return 返回sin运算的结果
\todo 在xxx的情况下存在BUG,预计下一版本修复
--]]
function sin(a)
return 1.123
end
可以看到Lua的语法既不像C也不像Python。本节以Lua为例,介绍如何为Doxygen编写Lua语言扩展。 好吧,大多数人没有这种需求,这里就不介绍了。
第4章 定制Doxygen的输出 4.1 定制页面样式 Doxygen输出的默认HTML比较难看,如图4-1。
如果嫌生成的HTML不好看,可以自定义HTML页面头部、尾部以及页面整体CSS样式表。 (1) 生成默认的风格的配置文件,敲这个命令:doxygen -w html header.html footer.html customdoxygen.css
,可以生成header.html
、footer.html
、customdoxygen.css
。 (2) 根据自己的需求修改这三个文件。 (3) 配置HTML_HEADER
、HTML_FOOTER
、HTML_STYLESHEET
指向修改后的文件,如图4-2。
Doxygen默认的页面主色调大约是天蓝色的,可以通过HTML_COLORSTYLE_HUE
、HTML_COLORSTYLE_SAT
、HTML_COLORSTYLE_GAMMA
修改主色调,这3个配置分别对应色相、饱和度、Gamma校正,见图4-3。如果不太懂色相、饱和度是啥意思,请自行百度「色彩模式」或参考Photoshop相关教程。
经过图4-3的修改,页面的主色调变为图4-4的样子。
4.2 导航栏 Doxygen中「导航栏」有两种展示方式:Treeview和Index,分别是竖向和横向的,如图4-5。
可以配置DISABLE_INDEX
和GENERATE_TREEVIEW
来控制是否显示它们,如图4-6。
4.3 自定义「导航栏」的目录结构 我们已经知道Doxygen中「导航栏」有Treeview和Index两种了。这节介绍如何定制导航栏的目录结构。这需要三个步骤。 (1) 执行doxygen -l
,生成DoxygenLayout.xml
文件 (2) 编辑DoxygenLayout.xml
文件,修改其中的布局 (3) 修改LAYOUT_FILE
配置,使其指向DoxygenLayout.xml
文件,如图4-7 (4) 运行Doxygen
那么如何修改XML文件呢?默认的DoxygenLayout.xml
代码如下:
<doxygenlayout version="1.0">
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<tab type="modules" visible="yes" title="" intro=""/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="yes" title="">
<tab type="classlist" visible="yes" title="" intro=""/>
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
<tab type="hierarchy" visible="yes" title="" intro=""/>
<tab type="classmembers" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="yes" title="">
<tab type="filelist" visible="yes" title="" intro=""/>
<tab type="globals" visible="yes" title="" intro=""/>
</tab>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
</doxygenlayout>
XML对应了导航栏的目录树结构,我们通过该文件改变布局。标签的type
属性取值除了上面列出的这些预定义值以外,还可以是type="user"
或type="usergroup"
,我们只能通过这两个type
自定义布局,例如下面这段代码,生成的效果如图4-8:
<doxygenlayout version="1.0">
<navindex>
<tab type="usergroup" visible="yes" title="友情链接(演示如何外链)">
<tab type="user" visible="yes" title="百度" url="http://www.baidu.com" />
<tab type="user" visible="yes" title="163" url="http://www.163.com" />
</tab>
<tab type="usergroup" visible="yes" title="数学库(演示如何链接文件)">
<tab type="user" visible="yes" url="@ref math.h" title="math" />
<tab type="user" visible="yes" url="@ref math2.h" title="math2" />
</tab>
<tab type="usergroup" visible="yes" title="三角函数(演示链接函数、结构体)">
<tab type="user" visible="yes" url="@ref sin" title="sin" />
<tab type="user" visible="yes" url="@ref sin2" title="sin2" />
</tab>
</navindex>
</doxygenlayout>
4.4 完全自定义 如果Doxygen输出的界面实在不入你的法眼,4.1~4.3介绍的定制化功能也不能彻底满足你的需求。那么你需要根据Doxygen输出的XML数据自行生成界面了。 (1) 将GENERATE_XML
配置为YES
(2) 去输出目录寻找生成的XML文件,XML文件包括了函数信息、注释信息等 (3) 自己写程序读取XML文件,并生成漂亮的文档
第5章 Markdown支持 待补充完善
Markdown在工业界是非常流行的文档格式,文件名以.md结尾,其简洁直观的语法深受广大程序员喜爱。对Markdown本身的介绍超出了本文范围,本章介绍Doxygen对Markdown的支持。
5.1 为.md文件生成文档 5.2 在代码注释中使用Markdown语法 第6章 搜索功能 Gitlab CI/CD使用 https://gitlab.com/pages/doxygen
结合Docker和Gitlab CI/CD使用案例
参考资源