Doxygen 10 分鐘入門教程

https://cedar-renjun.github.io/2014/03/21/learn-doxygen-in-10-minutes/

綜述

本文試圖在10分鐘內,幫助您瞭解文檔生成工具Doxygen的基本概念,並熟悉Doxygen的使用規則,同時給予深入學習的一些方向性建議。

因爲本文僅僅是幫助初學者入門,所以選例方面較爲簡單,缺乏廣度和深度。後期會編寫一些深度學習Doxygen的文章,敬請關注

【佔位符:文章結構整體描述】

Doxygen是什麼東西?

Doxygen是一款文檔生成工具,它可以從代碼中提取出相應的文檔,並組織,輸出成各種漂亮的文檔(如HTML,PDF,RTF等)

有了Doxygen工具,程序員便可以在寫代碼的時候,直接內嵌文檔,再也不需要爲某個功能代碼單獨寫文檔,從而最大程度的保持了文檔和代碼的統一性

另外,Doxygen 1.8.x版本中增加對markdown的支持,也支持內嵌部分HTML標籤,從而極大的簡化了文檔編寫難度,甚至,您可以用Doxygen生成一個靜態的網站。

目前Doxygen支持C/C++,Objective-C, C#,PHP等語言,支持多平臺(Mac OS, Linux, Windows),更多信息,請參考Doxygen官方介紹

Doxygen適合什麼人?

適合對代碼文檔有一定要求的程序員

PS:能看到這裏的程序員,一定是位有追求,有理想的工程獅,哈哈,就你了,go on please

開始學習Doxygen

注:在繼續閱讀之前,請確保您:

  • 知道如何在windows環境下調出命令窗口
    如果您初次接觸命令窗口,請參考附錄中關於CMD的內容
  • 具有簡單的編程基礎

下載和安裝

doxygen最新版爲1.8.6版本,下載鏈接如下

http://sourceforge.net/projects/doxygen/files/latest/download?source=files

下載完成後,雙擊安裝,採用默認設置就ok,這裏不做過多介紹

準備源文件

這裏準備了三個簡單的C語言源代碼

main.c  // 演示如何調用Dev中的設備接口
dev.c   // Dev設備的實現代碼
dev.h   // Dev設備的操作接口

具體代碼如下:

main.cview raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "dev.h"

#define CNT_MAX 10

void DEV_Example(void)
{
	int i = 0;

	Dev_Init();
	
	for (i = 0; i < CNT_MAX; ++i)
	{
		Dev_PrintInt(i);
	}

	Dev_Close();
}

int main(void)
{

	DEV_Example();

	return 0;
}

dev.cview raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void Dev_Init(void)
{
	printf("Dev Initialize OK!\r\n");
}

int Dev_PrintInt(int number)
{
	printf("Print IntType number: %d\r\n", number);
}

void Dev_Close(void)
{
	printf("Dev Close OK!\r\n");
}

dev.hview raw

1
2
3
4
5
#include <stdio.h>

extern void Dev_Init(void);
extern int Dev_PrintInt(int number);
extern void Dev_Close(void);

將這3個源文件放在某個文件夾內,這裏以GettingStart文件夾爲例,其目錄組織結構如下所示

GettingStart
   |-- dev.c
   |-- dev.h
   |-- main.c

第一次嘗試

接下來,我們看看,不編寫任何註釋的情況下,Doxygen會怎麼生成文檔

打開命令行(powershell)窗口,並CD到GettingStart目錄下,輸入下面命令

doxygen -g  

Powershell的返回信息如下,同時,我們的GettingStart目錄下增加了一個名叫Doxyfile的文件(注意第21行)

注:如果您初次接觸命令窗口,請參考附錄中關於CMD的內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
F:\Doxygen_Demo\GettingStart> doxygen -g


Configuration file `Doxyfile' created.

Now edit the configuration file and enter

  doxygen Doxyfile

to generate the documentation for your project

PS F:\Doxygen_Demo\GettingStart> ls


    dir: F:\Doxygen_Demo\GettingStart

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         2014/3/22     15:44        212 dev.c
-a---         2014/3/22     16:14        119 dev.h
-a---         2014/3/22     16:46     103431 Doxyfile
-a---         2014/3/22     15:56        236 main.c

注:上面使用的是win7的powershell,它支持很多傳統CMD不支持的命令,比如ls,如果您使用的是傳統的CMD窗口,這裏請用dir來代替ls

這個Doxyfile就是Doxygen工程的配置文件,裏面含有一些配置信息

接下來我們做一些修改

在默認情況下,Doxygen會輸出HTML和LATEX形式文檔,LATEX主要用於生成PDF,這裏暫時不需要,所以我們禁用LATEX輸出

在Doxyfile中將下面一行

GENERATE_LATEX         = YES

修改爲

GENERATE_LATEX         = NO

接下來,我們修改HTML的顯示方式,將下面兩行代碼

DISABLE_INDEX          = NO  
GENERATE_TREEVIEW      = NO  

修改爲

DISABLE_INDEX          = YES  
GENERATE_TREEVIEW      = YES  

至於爲什麼修改,暫時不用深究,我們後期再討論

現在我們就可以輸出文檔了,在命令行(powershell)下輸入

doxygen .\Doxyfile

注:這個命令稱爲編譯,下文直接用編譯來代替表示這個指令

這時,Doxygen會從我們的代碼中提取相應的文檔,並生成HTML文件,進入GettingStart
文件夾內,雙擊打開HTML文件夾下的index.html文件

點擊這裏查看

可以看到,當Doxygen對沒有任何註釋的代碼,也可以生成對應的文檔框架,不過,僅僅是框架而已,沒有太大作用。Doxygen針對這種情況,專門設置了一個選項EXTRACT_ALL,默認情況下爲NO狀態,手工設置爲YES後,Doxygen會盡可以的從代碼中提取信息,這裏我們將Doxyfile中的

EXTRACT_ALL            = NO

改爲

EXTRACT_ALL            = YES

然後再次編譯,前後輸出結果,對比如下:

 

注:請點擊這裏查看EXTRACT_ALL = YES的輸出結果

最終效果展示

接下來,我們爲代碼中增加對應的描述信息

Doxygen制定了一套註釋規範,在保證正確輸出文檔的同時,也兼顧了良好的可讀性。對編程人員來說,只需在編寫註釋的時候,稍微注意格式,即可生成非常優秀的文檔,額外增加的工作量可忽略不計。

下面就是某個函數的Doxygen註釋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//*****************************************************************************
//
//! \brief Write one byte to special register
//!
//! This function is to write one byte to LIS302DL register,one byte will be
//! writen in appointed address.
//!
//! \param RegAddr specifies the target register address.
//! \param Data is the data written to target register.
//!
//! \return Indicate the status of operation which can be one of the following
//! value \b SUCCESS or  \b FAILURE .
//!
//! \note This function is used by internal, user MUST NOT call it in your 
//!  Application.
//
//*****************************************************************************
static Result _I2CRegWriteByte(uint8_t RegAddr, uint8_t Data)
{
    Result retv = SUCCESS;

    // Begin to I2C Transfer
    // first send START signal to control I2C bus
    // then send 7-bit address and R/W bit to I2C salve
    // at last send target register address
    retv = xI2CMasterWriteS1(LIS302DL_PIN_I2C_PORT, LIS302DL_I2C_ADDR,
            RegAddr, I2C_TRAN_NOT_END);
    if(retv != SUCCESS)
    {
        return (FAILURE);
    }

    // Send the wanted data to I2C bus
    // then Send STOP signal to release I2C bus
    retv = xI2CMasterWriteS2(LIS302DL_PIN_I2C_PORT, Data, I2C_TRAN_END);
    if(retv != SUCCESS)
    {
        return (FAILURE);
    }

    return(SUCCESS);
}

我們可以看到,程序代碼可讀性非常好,即便沒有生成單獨的文檔,任何具有一定英文基礎的同學都可以
輕鬆的瞭解到函數的用法和入口參數,注意事項等信息。

接下來,我們採用Doxygen語法爲main.c dev.c dev.h添加註釋信息,完成後的效果如下所示:

main.cview raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//***************************************************************************************
//
//! \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.cview raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//***************************************************************************************
//
//! \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.hview raw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//***************************************************************************************
//
//! \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.
//
//***************************************************************************************

用Doxygen編譯後,生成的HTML文檔如下所示:

 

注:請點擊這裏查看詳細的輸出文檔。

Doxygen把代碼中的文件,例子,函數,宏等信息提取出來,一目瞭然

這裏要說一下,如果沒有Doxygen,我們該怎麼做呢?

很多人會用SourceInsight這個代碼閱讀工具來輔助閱讀,功能非常強大,但你電腦上必須裝SI,否則沒法閱讀,這就限制了使用範圍,從這裏可以看到Doxygen的優勢所在:提取代碼文檔,並將所有部分鏈接起來,形成統一整體

也許還有人會說,SI也可以生成繼承圖,調用圖等圖標,Doxygen可以嗎?完全可以的,藉助於Dot工具,Doxygen可以生成各種關係圖表

注:Doxygen配合Dot工具生成圖表以及Dot工具的用法,後期專門寫一篇文章來介紹

一步一步跟我學Doxygen

現在讓我們開始從0開始,一步步爲代碼添加註釋信息,最終生成上面所看到的效果

首先爲函數添加註釋信息,這是必須要做的。這裏有個選擇性問題,添加到哪裏呢?.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,你怎麼識別哪個是普通文本,
哪個是特殊標記?

也許你會說了,可以採用$FUNCTION$這種形式啊,恩,這麼做是可行的,可以確保識別出來特殊標記

接下來,還有一個問題,我們上面的註釋中,有很多*號,僅僅起到美觀和格式化的作用,當然不希望在
輸出文檔中顯示這些東西,問題是你怎麼識別這些符號,並不顯示呢?也許你會說,可以強制規定註釋的
格式,不讓用戶在代碼中寫很多*,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就可以正確區分普通註釋和特殊註釋了,^_^

更詳細的信息,請參考Doxygen註釋規範

區分特殊標記符和普通文本

ok,現在可以識別出了普通註釋和特殊註釋,接下來,Doxygen是怎麼從特殊註釋裏面提取信息的呢

比如

 

注意左邊導航欄,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認識哪些符號呢,參考Doxygen自帶手冊啊,Special Commands章節

授之於魚,不如授之於漁,該說的都介紹完了,至於具體的指令含義和用法,自己慢慢看手冊唄

注:點擊這裏下載GettingStart的壓縮包,包含源碼,HTML輸出文檔,配置腳本
如果在看腳本的時候遇到問題,請參考這裏

深入研究

不好意思,文章有點長,估計過了10分鐘,so bad,o(╯□╰)o

根據20/80原則,一個軟件,常用的僅僅是20%的功能,其餘80%功能是極少用到的

上面僅僅介紹了Doxygen的設計思想和學習範例,抓住了綱領,相信您通過研究代碼和幫助手冊,很快就能學會使用Doxygen

如果您想深入研究Doxygen,比如markdown和代碼無縫交互,插入HTML代碼,定製HTML網頁,CSS等功能,
請參考Doxygen幫助手冊,裏面有詳細說明,如果學習過程中,遇到問題,歡迎和我交流,互相學習


Do one thing and do it well

CedarQQ:819280802


參考資料

  1. Doxygen官網

  2. Doxygen支持的命令

  3. IBM - 學習用doxygen生成源碼文檔

 

https://blog.csdn.net/Candy1232009/article/details/80786179

   Doxygen配置 界面

Doxygen產生文檔可以分爲三個步驟,一是在程序代碼中加上符合Doxygen所定義註釋格式;二是使用Doxywizard進行配置;三是使用Doxygen來產生註釋文檔。現在我們假定電腦中已經安裝了Doxygen並且代碼中的註釋已經符合Doxygen規範,下面我們來通過設置配置來生成註釋文檔。
————————————————
版權聲明:本文爲CSDN博主「開心笑」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/Candy1232009/article/details/80786179

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章