Doxygen是API文档生成工具,可以根据代码注释生成文档的工具。支持HTML、CHM、PDF等格式。主要支持C语言、Python语言,其它C语系语言也支持(如C++、Java、C#等)。

Doxygen效果演示

本教程的测试环境

  • 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,你怎么识别哪个是普通文本,哪个是特殊标记?

也许你会说了,可以采用 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就可以正确区分普通注释和特殊注释了。

**注:**提到特殊标记,其实吧,编程语言非常常用,比如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. 生成文档

$ doxygen <config-file>

Win 图形界面使用

第1章 安装

在Linux下可以通过apt install doxygen安装命令行工具,然后用apt install doxygen-gui安装图形界面。对Linux用户来说,命令行工具可以通过doxygen命令运行,而图形界面可以通过doxywizard命令运行。

而Windows用户可以在这里下载,安装完毕后,直接双击就能运行图形界面。

1.1 基本使用

图形工具的基本使用如下图所示,有非常多的配置选项,这里我们只填入必要的配置,其它配置都用默认值。

doxywizard使用步骤

doxywizard使用步骤

我们的工作目录如下:

.
├── 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所示。

HTML界面

1.2 保存配置

在1.1节中我们配置了一些选项,也成功生成了HTML文档。我们希望下次代码改动后能够继续沿用上次配置,那么我们可以把这些配置保存成Doxyfile文件,见图1-4。

保存Doxyfile配置文件

1.3 命令行运行Doxygen

有了配置文件后我们完全可以通过命令行来生成API文档,假设配置文件名为Doxyfile,那么我们只需要执行doxygen /path/to/Doxyfile即可生成API文档。

通过命令行生成文档有许多好处,其中最主要的好处就是:能够集成到持续集成之类的自动化系统中。

第2章 为代码编写注释

2.1 什么样的注释会被Doxygen识别?

Doxygen能识别这几种风格的注释:

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

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

///
/// ... text ...
///

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

文件的开头必须有文件注释,否则该文件不会被识别:

/*! \file math.h */

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等等。

img

如果我们的扩展名并不在FILE_PATTERNS内,那么可以加上去。例如我们项目下的所有.ccc文件,其实是C语言代码(这很奇葩,举个例子而已)。那我们可以编辑Doxyfile配置文件满足这一需求,需要2个步骤。

(1) 在FILE_PATTERNS中添加*.ccc,如图3-2

img

(2) 在EXTENSION_MAPPING中添加映射规则ccc=C,如图3-3。语法是ext=language,其中language可以取的值有:IDL、Java、Javascript、C#、C、C++、D、PHP、Objective-C、Python、Fortran、VHDL。

img

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。

img

如果嫌生成的HTML不好看,可以自定义HTML页面头部、尾部以及页面整体CSS样式表。
(1) 生成默认的风格的配置文件,敲这个命令:doxygen -w html header.html footer.html customdoxygen.css,可以生成header.htmlfooter.htmlcustomdoxygen.css
(2) 根据自己的需求修改这三个文件。
(3) 配置HTML_HEADERHTML_FOOTERHTML_STYLESHEET指向修改后的文件,如图4-2。

img

Doxygen默认的页面主色调大约是天蓝色的,可以通过HTML_COLORSTYLE_HUEHTML_COLORSTYLE_SATHTML_COLORSTYLE_GAMMA修改主色调,这3个配置分别对应色相、饱和度、Gamma校正,见图4-3。如果不太懂色相、饱和度是啥意思,请自行百度「色彩模式」或参考Photoshop相关教程。

img

经过图4-3的修改,页面的主色调变为图4-4的样子。

img

4.2 导航栏

Doxygen中「导航栏」有两种展示方式:Treeview和Index,分别是竖向和横向的,如图4-5。

img

可以配置DISABLE_INDEXGENERATE_TREEVIEW来控制是否显示它们,如图4-6。

img

4.3 自定义「导航栏」的目录结构

我们已经知道Doxygen中「导航栏」有Treeview和Index两种了。这节介绍如何定制导航栏的目录结构。这需要三个步骤。
(1) 执行doxygen -l,生成DoxygenLayout.xml文件
(2) 编辑DoxygenLayout.xml文件,修改其中的布局
(3) 修改LAYOUT_FILE配置,使其指向DoxygenLayout.xml文件,如图4-7
(4) 运行Doxygen

img

那么如何修改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>

img

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使用案例

Gitlab Doxygen运行效果如下

参考资源

系列教程

全部文章RSS订阅

tools系列

Tools 分类 RSS 订阅

图书、音乐、视频多媒体锦集

Nas RSS分类订阅

Nas系列

Nas 分类 RSS 订阅

Docker系列

Docker 分类 RSS 订阅


发布于
2022年5月28日
许可协议。转载请注明来源
评论
数据加载中 ...
 上一篇

阅读全文

常用Linux Bash命令教程
常用Linux Bash命令教程 常用Linux Bash命令教程
常用linux命令介绍。入门Linux必学的命令行,熟练掌握后,效率远超Windows GUI操作。重要的是,可以方便的做自动化处理!
2022-05-28
下一篇 

阅读全文

自建全套开源Devops开发系统
自建全套开源Devops开发系统 自建全套开源Devops开发系统
国内的Devops云平台动辄封号,删库,屏蔽,还多次发生数据泄密,项目被云平台克隆事件,国内云平台devops已死!数据安全没法保证,自建私有Devops平台才靠谱。这里采用开源项目建立一个完善的低成本Devops系统,基本运行良好!
2022-05-09