QDoc 简介

QDoc 是 Qt 开发者用来生成软件项目文档的工具。它的工作原理是从项目源文件中提取 QDoc 注释,然后将这些注释格式化为 HTML 页面或 DocBook XML 文档。QDoc 在 .cpp 文件和 .qdoc 文件中查找 QDoc 注释。QDoc 不在 .h 文件中查找 QDoc 注释。QDoc 注释总是以感叹号开头。例如:

     / *!
         \class QObject
         \brief The QObject class is the base class of all Qt objects.

         \ingroup objectmodel

         \reentrant

         QObject is the heart of the Qt \l{Object Model}. The
         central feature in this model is a very powerful mechanism
         for seamless object communication called \l{signals and
         slots}. You can connect a signal to a slot with connect()
         and destroy the connection with disconnect(). To avoid
         never ending notification loops you can temporarily block
         signals with blockSignals(). The protected functions
         connectNotify() and disconnectNotify() make it possible to
         track connections.

         QObjects organize themselves in \l {Object Trees &
         Ownership} {object trees}. When you create a QObject with
         another object as parent, the object will automatically
         add itself to the parent's \c children() list. The parent
         takes ownership of the object. It will automatically
         delete its children in its destructor. You can look for an
         object by name and optionally type using findChild() or
         findChildren().

         Every object has an objectName() and its class name can be
         found via the corresponding metaObject() (see
         QMetaObject::className()). You can determine whether the
         object's class inherits another class in the QObject
         inheritance hierarchy by using the \c inherits() function.

     ....
 * /

从上面的 QDoc 注释中,QDoc 生成了 HTML QObject 类参考 页面。

本手册说明了如何使用 QDoc 注释中的 QDoc 命令将文档嵌入到源文件中。它还说明了如何编写 QDoc 配置文件,可以在命令行传参给 QDoc。

运行 QDoc

QDoc 程序的名称是 qdoc。要在命令行运行 QDoc,请为其指定配置文件的名称:

$ ../../bin/qdoc ./config.qdocconf

QDoc 将后缀为 .qdocconf 的文件识别为 QDoc 配置文件。配置文件指定 QDoc 在哪个位置可以找到项目源文件、头文件和 .qdoc 文件。还有 QDoc 生成什么样的输出(HTML、DocBook XML...)以及将生成的文档放在哪里。配置文件还包含 QDoc 的其他信息。

有关如何设置 QDoc 配置文件的说明,请参阅 QDoc 配置文件

在单次执行模式下运行 QDoc

从 Qt 5.5 开始,提供了一种运行 QDoc 的新方法,可将生成 Qt5 文档的时间减少 90% 之多。运行 QDoc 的新方法是 单次执行 模式。Qt5 构建系统目前不提供单次执行模式,仍然使用 标准 模式。单次执行模式只有在你自己运行 QDoc 时才可用,在你记录模块并将文档与其他 Qt 模块整合时,你会经常这样做。

要在单次执行模式下运行 QDoc,请在命令行中添加 -single-exec,并向 QDoc 传递一个 qdocconf 文件,该文件只是所有 Qt5 模块的 qdocconf 文件的文件路径列表。例如:

 /Users/me/qt5/qtbase/bin/qdoc -outputdir /Users/me/qt5/qtbase/doc -installdir /Users/me/qt5/qtbase/doc /Users/me/qt5/master.qdocconf -single-exec

master.qdocconf 只是列出所有要处理的 Qt5 模块的 qdocconf 文件:

 /Users/me/qt5/qtbase/src/corelib/doc/qtcore.qdocconf
 /Users/me/qt5/qtbase/src/network/doc/qtnetwork.qdocconf
 /Users/me/qt5/qtbase/src/sql/doc/qtsql.qdocconf
 /Users/me/qt5/qtbase/src/xml/doc/qtxml.qdocconf
 /Users/me/qt5/qtbase/src/testlib/doc/qttestlib.qdocconf
 /Users/me/qt5/qtbase/src/concurrent/doc/qtconcurrent.qdocconf
 /Users/me/qt5/qtbase/src/gui/doc/qtgui.qdocconf
 /Users/me/qt5/qtbase/src/platformheaders/doc/qtplatformheaders.qdocconf
 /Users/me/qt5/qtbase/src/widgets/doc/qtwidgets.qdocconf
 /Users/me/qt5/qtbase/src/opengl/doc/qtopengl.qdocconf
 /Users/me/qt5/qtbase/src/printsupport/doc/qtprintsupport.qdocconf
 /Users/me/qt5/qtbase/src/tools/qdoc/doc/config/qdoc.qdocconf
 /Users/me/qt5/qtbase/qmake/doc/qmake.qdocconf
 /Users/me/qt5/qtsvg/src/svg/doc/qtsvg.qdocconf
 /Users/me/qt5/qtxmlpatterns/src/xmlpatterns/doc/qtxmlpatterns.qdocconf
 /Users/me/qt5/qtdeclarative/src/qml/doc/qtqml.qdocconf
 /Users/me/qt5/qtdeclarative/src/quick/doc/qtquick.qdocconf
 /Users/me/qt5/qtquickcontrols/src/controls/doc/qtquickcontrols.qdocconf
 /Users/me/qt5/qtquickcontrols/src/layouts/doc/qtquicklayouts.qdocconf
 /Users/me/qt5/qtquickcontrols/src/dialogs/doc/qtquickdialogs.qdocconf
 /Users/me/qt5/qtmultimedia/src/multimedia/doc/qtmultimedia.qdocconf
 /Users/me/qt5/qtmultimedia/src/multimediawidgets/doc/qtmultimediawidgets.qdocconf
 /Users/me/qt5/qtactiveqt/src/activeqt/doc/activeqt.qdocconf
 /Users/me/qt5/qtsensors/src/sensors/doc/qtsensors.qdocconf
 /Users/me/qt5/qtwebkit/Source/qtwebkit.qdocconf
 /Users/me/qt5/qttools/src/assistant/help/doc/qthelp.qdocconf
 /Users/me/qt5/qttools/src/assistant/assistant/doc/qtassistant.qdocconf
 /Users/me/qt5/qttools/src/designer/src/uitools/doc/qtuitools.qdocconf
 /Users/me/qt5/qttools/src/designer/src/designer/doc/qtdesigner.qdocconf
 /Users/me/qt5/qttools/src/linguist/linguist/doc/qtlinguist.qdocconf
 /Users/me/qt5/qtwebkit-examples/doc/qtwebkitexamples.qdocconf
 /Users/me/qt5/qtgraphicaleffects/src/effects/doc/qtgraphicaleffects.qdocconf
 /Users/me/qt5/qtscript/src/script/doc/qtscript.qdocconf
 /Users/me/qt5/qtscript/src/scripttools/doc/qtscripttools.qdocconf
 /Users/me/qt5/qtserialport/src/serialport/doc/qtserialport.qdocconf
 /Users/me/qt5/qtdoc/doc/config/qtdoc.qdocconf

为什么标准模式很慢

目前,Qt5 构建系统不使用 QDoc 的 单次执行 模式来生成 Qt5 文档。它在 标准 模式下运行 QDoc。标准模式的出现是因为它是转换 Qt4 QDoc 以处理 Qt5 中 Qt 模块化的最简单方法。在 Qt4 中,QDoc 在所有 Qt4 源代码上运行一次,以生成 Qt 的 HTML 文档。在生成 Qt 文档的同时,Qt4 QDoc 还为 Qt 生成了一个 索引文件。该索引文件旨在作为后续 QDoc 运行的输入,为其他基于 Qt 的软件库或产品生成 HTML 文档。Qt 索引文件允许 QDoc 将其他库或产品的文档链接到 Qt4 文档中。

Qt5 出现时,Qt 模块被划分。从那时起,许多新模块被添加到 Qt 中。截至 5.5 版本,Qt5 中有超过 40 个独立的模块,每个模块都有自己的文档,并链接到(依赖于)其他 Qt 模块的文档。

标准模式 下,QDoc 为每个模块运行两次。第一次 QDoc 为特定 Qt 模块运行,解析所有模块的源文件,然后使用这些信息生成模块的索引文件。它被称为 准备阶段,因为它为模块的索引文件做 准备。第二次 QDoc 也是解析所有模块的源文件,然后生成模块的文档页面。这称为 生成阶段,因为它生成了模块的文档。

该模块的文档可能包含指向一个或多个其他 Qt 模块文档的 HTML 链接。例如,大多数 Qt5 模块都包含指向 QtCore 文档的链接。当一个 Qt 模块包含指向其他 Qt 模块文档的链接时,该模块被称为依赖于其他 Qt 模块。因此,当 QDoc 为该模块运行 生成阶段 时,它还必须加载这些模块的索引文件,以便它可以创建这些链接。

因此,当 Qt 构建系统生成 Qt 文档时,它首先为每个模块运行一次 QDoc,执行 准备阶段,生成所有索引文件。然后,它为每个模块运行一次 QDoc,执行 生成阶段,使用依赖的索引文件来生成模块的文档,包括它发现的任何跨模块链接。每次执行 QDoc,无论是 准备阶段 还是 生成阶段,都会解析所有包含在模块中的源文件,在 生成阶段 还会解析依赖模块的索引文件。在 QDoc 运行之间没有任何被保留或可保留的内容。

为什么单次执行模式要快得多

顾名思义,单次执行模式使用单个 QDoc 进程来生成所有 Qt5 的文档。单个 QDoc 进程仍然为每个模块执行 准备阶段,然后为每个模块执行 生成阶段,但有一些区别。首先,它读取 qdocconf 文件。然后它读取主列表中的每个 qdocconf 文件,并为每个模块执行 准备阶段。在 准备阶段,解析模块的所有源文件,构建模块的语法树。QDoc 在 生成阶段 不会重新读取索引文件,依旧生成模块的索引文件。这里的重要区别是,在生成索引文件后,模块的语法树会被保留下来,因此在为所有模块运行 准备阶段 后,QDoc 仍然拥有它构建的所有语法树。

然后,QDoc 再次处理每个模块,进行 生成阶段。但现在 QDoc 不需要重新解析每个模块的源文件,因为模块的语法树还在内存中。QDoc 也不需要重新读取依赖模块的索引文件,因为在内存中它仍然拥有这些模块的语法树。它只需要遍历每个模块的语法树来生成文档页。

因此,QDoc 对每个源文件只解析一次,并且不需要读取索引文件。这就是单次执行模式比标准模式快得多的原因。预计 Qt 构建系统最终将以单次执行模式运行 QDoc。但是,可能需要对 qdocconf 主文件进行修改,所以上面描述的在单次执行模式下运行 QDoc 的方法可能要改变,请注意这块儿的更新。

QDoc 的工作原理

QDoc 首先读取你在命令行上指定的配置文件。它存储配置文件中的所有变量,以供以后使用。它使用的第一个变量是 outputformats。这个变量告诉 QDoc 它将运行哪些输出生成器。默认值是 HTML,所以如果你没有在配置文件中设置 outputformats,QDoc 将生成 HTML 输出。这通常是你想要的,但你也可以指定 DocBook 来获取 DocBook 输出。

接下来,QDoc 使用 headerdirs 变量和 headers 变量的值来查找和解析项目的所有头文件。 QDoc 不会 扫描头文件中的 QDoc 注释。它解析头文件是为了构建包含所有应该被记录的项目的主树,换句话说,就是 QDoc 应该找到哪些 QDoc 的注释项。

在解析所有头文件并构建要记录的项目的主树之后,QDoc 使用 sourcedirs 变量的值和 sources 变量的值来查找和解析项目的所有 .cpp.qdoc 文件。这些文件是 QDoc 扫描的 QDoc 注释 文件。记住,QDoc 注释以感叹号开头:/*!

对于它找到的每个 QDoc 注释,它都会在主树中搜索该文档所属的项目。然后它解释注释中的 QDoc 命令,并将解释后的命令和注释文本存储在该项目的树节点中。

最后,QDoc 遍历主树。对于每个节点,如果该节点已经存储了文档,QDoc 调用 outputformats 变量指定的输出生成器,将文档格式化并写入配置文件中 outputdir 变量指定的目录中。

命令类型

QDoc 可以解释三种类型的命令:

主题命令标识你正在记录的元素,例如 C++ 类、函数、类型,或未映射到底层 C++ 元素的额外文本页。

Context 命令告诉 QDoc 被记录的元素与其他被记录的元素的关系,例如,下一页和上一页的链接,包含在页面组中,或库模块。上下文命令还可以提供 QDoc 无法从源文件中获得的关于被记录元素的信息,例如,该元素是否是线程安全的,它是否是一个重载或重新实现的函数,或者它是否已经被废弃。

标记命令告诉 QDoc 应该如何呈现文档中的文本和图像元素,或者关于文档的大纲结构。