在我看来它真的非常好用非常具有规范性。它可以根据我们的代码注释生成我们需要的文档,这个文档明显比自己整理的要好看的多有结构的多,另外它还能生成每个文件的调用关系图、每个类的继承关系图、每个函数的调用关系图、每个文件夹的包含关系图……
最近才接触到它确实是我之前的遗憾。
它叫doxygen,它生成的文档大概是什么样子?看上面的几张图。它的文档在doxygen doc,它也是开源的,doxygen github。我对它,相见恨晚。


接触

平时开发的时候经常会去看代码说明,比如opencv源码的说明文档,比如ros一些源代码的说明文档。我之前从没想过如何去生成这些,直到最近阅读PX4的代码,很无语它为什么没有像opencv和ros那样子的文档。


但是在PX4的Documentation目录下有一个叫Doxyfile.in的文件,折腾了一段时间我生成了属于PX4的类似文档,具体操作就是把/cmake/doxygen.cmake里更改一句话

option(BUILD_DOXYGEN "Build doxygen documentation" ON)

编译完成后,用浏览器打开build/px4_fmu-v3_default/Documentation/index.html就是我想要的文档。


安装

当我知道这个东西这么神奇之后地花了几天去它的官方说明去好好了解了一番doxygen doc
首先是安装
它支持linux、windows、 Mac OS
我是在linux里使用它,官网说的是按照源码安装的
我这里直接使用命令去安装的。

sudo apt install doxygen
sudo apt install doxygen-gui

可以通过doxywizard打开gui,但是一般不用这个GUI,我感觉比用这个GUI方便一些,也就没怎么去关注doxywizard怎么用。
关于doxywizard怎么用可以看这里

终端输入doxywizard看到上面的样子,说明安装的没问题啦。


创建配置文件和运行得到文档

doxygen -g “config-file”生成一个配置文件
“config-file”默认名称为
若在当前目录已经存在同名,则前一个会被转换为.bat文件
所以直接在终端输入

doxygen -g

就会生成一个叫Doxyfile的文件


运行doxygen “config-file”就可以生成文档


找到/html/index.html通过浏览器打开,就是生成的文档主界面啦


上面是一整个流程走下来,中间还缺最重要的一步,就是修改配置文件啦。当然,一份注释完整的代码也是需要的。


修改配置文件

配置文件里面的参数大部分为默认的就好
具体修改参考每个参数的注释部分和https://www.doxygen.nl/manual/config.html

这里我说一些我有过整理的配置参数。


PROJECT_NAME标签是项目名称,将作为于所生成的程序文档首页标题,需要使用双引号括住。

PROJECT_NAME           = "MySDK Doc"

PROJECT_NUMBER标签是文档版本号。

PROJECT_NUMBER = “1.0.0”

OUTPUT_LANGUAGE标签是程序文档语言环境,默认为 English,也可以设置为 Chinese。

OUTPUT_LANGUAGE        = English

CREATE_SUBDIRS标签设置为YES,会细分生成的文档
doxygen将在每个输出格式的输出目录下创建4096个子目录,并将生成的文件分发到这些目录上。
当向doxygen提供大量源文件时,启用此选项非常有用,如果将所有生成的文件放在同一目录中,则会导致文件系统出现性能问题。
说白了是优化生成界面的,防止某个页面太大难以加载。(默认是NO)

CREATE_SUBDIRS         = YES

BUILTIN_STL_SUPPORT标签设置为YES,doxygen会包含STL类(即std::string、std::vector等),设置为NO将不会把STL源文件作为输入。
这也使得涉及STL类的继承和协作关系图更加完整和准确。(默认是NO)

BUILTIN_STL_SUPPORT    = YES

CASE_SENSE_NAMES标签,表示文件系统里的大小写区分,如果文件系统区分大小写
即它支持同一目录中名称大小写不同的文件,则必须将该选项设置为“是”,以便在输入中出现此类文件时正确处理这些文件。
对于不区分大小写的文件系统,该选项应设置为“否”。(默认是NO)

CASE_SENSE_NAMES       = YES

QUIET标签可用于打开/关闭由doxygen生成为标准输出的消息。如果QUIET设置为YES,则表示消息已关闭。(默认是NO)
这个设置为YES后就只会打印一些warning,其它的不会打印,个人觉得设置为 NO 好一些。

QUIET                  = NO

REFERENCED_BY_RELATION标签设置为YES,则对于每个记录的实体,将列出引用它的所有记录的功能。(默认是NO)

REFERENCED_BY_RELATION = YES

REFERENCES_RELATION标签设置为YES,则对于每个记录的功能,将列出该功能调用/使用的所有记录实体。(默认是NO)

REFERENCES_RELATION    = YES

OPTIMIZE_OUTPUT_FOR_C标签,如果是制作 C 程序文档,该选项必须设为YES,否则默认生成 C++ 文档格式(默认是NO)

OPTIMIZE_OUTPUT_FOR_C  = NO

TYPEDEF_HIDES_STRUCT标签对于使用 typedef 定义的struct, union, enum按照 typedef 定义的类型名进行文档化(默认是NO)

TYPEDEF_HIDES_STRUCT = YES

INPUT_ENCODING标签输入文件的编码格式,需要自己根据INPUT指定的文件选择,否则会乱码(默认是UTF-8)

INPUT_ENCODING         = UTF-8

INPUT标签是输入的源码文件的目录

INPUT                  = firmware/src \
                         build/include

FILE_PATTERNS标签是要针对的文件后缀

FILE_PATTERNS          = *.h \
                         *.hpp \
                         *.hh \
                         *.c \
                         *.cc \
                         *.cpp \
                         *.md

RECURSIVE标签设置为YES表示递归检测源码文件的目录(默认是NO)
RECURSIVE = YES

EXCLUDE标签可用于指定应从输入源文件中排除文件或目录

EXCLUDE                = firmware/src/modules/uavcan/libuavcan \
                         firmware/src/modules/micrortps_bridge/micro-CDR \
                         firmware/src/examples \
                         firmware/src/templates

EXCLUDE_PATTERNS标签可以理解为EXCLUDE的通配符版本

EXCLUDE_PATTERNS       = */*_params.c

EXTRACT_ALL标签。如果EXTRACT_ALL标记设置为YES,则doxygen将假定文档中的所有实体都有文档,即使没有可用的文档。说白了会为所有的对象文件什么的生成一个说明,不管他们有没有对应的说明。并且只要EXTRACT_ALL设置为YES,就不会生成有关未记录成员的警告。(默认是NO)

EXTRACT_ALL            = YES

EXTRACT_PRIVATE标签。如果设置为YES,一个文件的所有静态成员都将包含在文档中。(默认是NO)

EXTRACT_STATIC         = YES

EXTRACT_PRIVATE标签。如果设置为YES,一个类的所有私有成员都将包含在文档中。(默认是NO)

EXTRACT_PRIVATE        = YES

SOURCE_BROWSER标签。如果设置为YES,为所有对象生成的文档都可以被交叉引用,相互点击跳转。(默认是NO)

SOURCE_BROWSER         = YES

INLINE_SOURCES标签。如果设置为YES,可以将源代码直接包含到文档中。(默认是NO)
一般设置为NO,在这个时候不会包含在文档里面,但是会告诉Definition at line 49 of file ping.cpp.就够了

INLINE_SOURCES         = NO

OUTPUT_DIRECTORY标签设置输出文档的目录
同样可以设置HTML_OUTPUT, RTF_OUTPUT, LATEX_OUTPUT, XML_OUTPUT, MAN_OUTPUT, DOCBOOK_OUTPUT.
但是只用配置HTML_OUTPUT就好,生成网页形式的文档,这个doxygen支持的也好一些。

HTML_OUTPUT标签,HTML文档输出的目录

OUTPUT_DIRECTORY和HTML_OUTPUT的设置方法有两种。
一种类似如下,生成的文档文件放在/home/john/arc/src/Documentation里

OUTPUT_DIRECTORY       =
...
...
HTML_OUTPUT            = /home/john/arc/src/Documentation

一种类似如下,生成的文档文件放在/home/john/arc/src/Documentation/html里

OUTPUT_DIRECTORY       = /home/john/arc/src/Documentation
...
...
HTML_OUTPUT            = html

GENERATE_HTML使能HTML输出,(默认是YES)

GENERATE_HTML          = YES

HTML_COLORSTYLE_HUE标签设置生成的HTML页面的背景颜色。
Minimum value: 0, maximum value: 359, default value: 220.

HTML_COLORSTYLE_HUE    = 220

HTML_COLORSTYLE_SAT标签设置生成的HTML页面背景颜色的饱和度,对于值0输出将仅使用灰度,值255将生成最鲜艳的颜色。
Minimum value: 0, maximum value: 255, default value: 100.

HTML_COLORSTYLE_SAT    = 100

HTML_COLORSTYLE_GAMMA标签设置生成的HTML页面背景颜色的亮度。
于100的值会逐渐使输出变亮,而高于100的值会使输出变暗。
值除以100即为实际应用的伽马值,因此80表示伽马值为0.8,220表示伽马值为2.2,100不会改变伽马值。
Minimum value: 40, maximum value: 240, default value: 80.

HTML_COLORSTYLE_GAMMA  = 80

HTML_TIMESTAMP标签设置为YES,则每个生成的HTML页面的页脚将包含生成页面的日期和时间
将此设置为“是”有助于显示上次运行doxygen的时间,从而显示文档是否最新。
生成在页脚很小一块儿不影响美观,我觉得这个还是有帮助的。(默认是NO)

HTML_TIMESTAMP         = YES

GENERATE_TREEVIEW标签用于指定是否应生成树状索引结构以显示分层信息。(默认是NO)
如果将标记值设置为YES,将生成一个包含树状索引结构的侧面板(就像为HTML帮助生成的索引结构一样)。

GENERATE_TREEVIEW      = YES

USE_MDFILE_AS_MAINPAGE标签可以把.md文件设置为主页,但是注意要把.md文件路径放到 INPUT 标签里

USE_MDFILE_AS_MAINPAGE = /home/john/arc/DynamixelSDK/README.md

注意INPUT里面要包含这个.md文件,比如

INPUT                  =/home/john/arc/DynamixelSDK/c++ \
                        /home/john/arc/DynamixelSDK/README.md

UML_LOOK标签设置为YES,doxygen将以类似于OMG的统一建模语言的方式生成继承和协作图。(默认是NO)

UML_LOOK               = NO

CALL_GRAPH标签设置为YES,那么doxygen将为每个全局函数或类方法生成一个调用依赖关系图。(默认是NO)
这个强调的是调用了谁。

CALL_GRAPH             = YES

CALLER_GRAPH标记设置为YES,那么doxygen将为每个全局函数或类方法生成一个调用方依赖关系图。(默认是NO)
这个强调的是被谁调用。

CALLER_GRAPH           = YES

DOT_IMAGE_FORMAT标签,设置生成图片的格式,默认是 png 格式,但是 svg 格式更好。

DOT_IMAGE_FORMAT       = svg

INTERACTIVE_SVG标签,可以让 svg 图片在页面中缩放移动。

INTERACTIVE_SVG        = YES

UML_LOOK、CALL_GRAPH、CALLER_GRAPH、DOT_IMAGE_FORMAT、INTERACTIVE_SVG、都有一个共同要求
This tag requires that the tag HAVE_DOT is set to YES.
HAVE_DOT标签默认是YES,不改它就好




综上,我着重关注修改的配置参数有

PROJECT_NAME
PROJECT_NUMBER
OUTPUT_LANGUAGE
CREATE_SUBDIRS
BUILTIN_STL_SUPPORT
CASE_SENSE_NAMES
QUIET
REFERENCED_BY_RELATION
REFERENCES_RELATION
OPTIMIZE_OUTPUT_FOR_C
TYPEDEF_HIDES_STRUCT
INPUT_ENCODING
INPUT
FILE_PATTERNS
RECURSIVE
EXCLUDE
EXCLUDE_PATTERNS
EXTENSION_MAPPING
EXTRACT_ALL
EXTRACT_PRIVATE
EXTRACT_PRIVATE
SOURCE_BROWSER
INLINE_SOURCES
OUTPUT_DIRECTORY
HTML_OUTPUT
GENERATE_HTML
HTML_COLORSTYLE_HUE
HTML_COLORSTYLE_SAT
HTML_COLORSTYLE_GAMMA
HTML_TIMESTAMP
GENERATE_TREEVIEW
USE_MDFILE_AS_MAINPAGE
UML_LOOK
CALL_GRAPH
CALLER_GRAPH
DOT_IMAGE_FORMAT
INTERACTIVE_SVG
HAVE_DOT

为DynamixelSDK生成文档

刚好最近在折腾DynamixelSDK这个东西
GitHub地址
它的代码里面有一个Doxygen文件,但是这个文件我看了下需要自己去修改的,巧了。

然后看了看代码,我主要需要用到C++部分,看了一下它也只有C++里面有标准注释,巧了。
修改的Doxygen文件在我的码云地址里。
因为其它协议还在调试,这里先把修改好的Doxygen文件放上去了。

注意里面的 USE_MDFILE_AS_MAINPAGE、INPUT 参数是我自己的绝对路径


然后打开终端
运行doxygen Doxyfile

用浏览器打开 DynamixelSDK/doc/html/index.html 就可以看到


代码注释规范

并不是所有的代码都可以生成好的文档,我们需要规范自己的注释习惯。
doxygen的注释规范可以参考这里
对于它说的那些注释块儿,我自己认为不用在意,不用它给的这些格式,自己这么喜欢怎么来也行。
如果连这些注释框架都要去记住,我是很难接受的。
下面说一下我自己的总结。


对于注释和说明
或者在前面加上以如下注释格式

与代码同行的注释类型我喜欢:

/// ... txt ... 

//! ... txt ... 

/*! ... txt ... */

/** ... txt ... */

在代码上方的注释类型我喜欢:

/*!
 * ... text ...
 */

/**
 * ... txt ...
 */

其它不想被包含到文档里的注释我一般用:

// ... text ...
/*  ... text ... */

如果针对函数、结构体的多行注释,我个人更加喜欢用

/**
 * ... txt ...
 */

比如

/**
 * @brief  ... txt ...
 * @param  ... txt ...
 * @return ... txt ...
 */
 viod test (void){

 }

如果是针对某个变量或者结构体成员或者类属性的单行注释,我比较习惯下面这种

int var; /*! ... txt ... */

但是要小心使用下面这个形式

这是错误的
int var1; /** ... txt1 ... */
int var2; /** ... txt2 ... */
这是错误的

这个时候 txt1 会被看做是对 var2 的注释。


容易注意到,很多官方程序的注释都非常标准,而且带有像@brief@param这样子的特殊标记命令符号,我叫标签吧。
关于每种标签的说明可以看这里

一般放在文件最开头的
@file 表示文件的描述说明
@author 表示对作者的说明
注意 @file 后面要加上这个文件的名字
例如:

/**
 * @file ekf2_main.cpp
 * Implementation of the attitude and position estimator.
 *
 * @author Roman Bapst
 */

一般放在函数、对象、结构体上的
@brief 表示具体描述说明
@param 表示参数说明,有@param@param[in]和@param[out]三种写法,字面意思
@return 表示返回参数
@see 表示另见See also,多用在存在继承关系上,比如A继承了B,但是A没有重写继承来的方法C,在C上就可以注释另见B
如果想取多行,就多用一个标签

@brief The function that transmits and receives the packet which might be come from the Dynamixel
@brief The function that transmits and receives the packet which might be come from the Dynamixel11111
@return COMM_RX_FAIL
@return   when there is no packet recieved
@return COMM_SUCCESS
@return   when there is packet recieved
@return or the other communication results which come from GroupBulkRead::txPacket or GroupBulkRead::rxPacket

效果就是


对于python
注释格式是这样子的:

## @package pyexample
#  Documentation for this module.
#
#  More details.

## Documentation for a function.
#
#  More details.
def func():
    pass

## Documentation for a class.
#
#  More details.
class PyClass:

    ## The constructor.
    def __init__(self):
        self._memVar = 0;

    ## Documentation for a method.
    #  @param self The object pointer.
    def PyMethod(self):
        pass

    ## A class variable.
    classVar = 0;

    ## @var _memVar
    #  a member variable

具体参见这里

over~