- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
本指南面向 CMake 开发者与文档贡献者,系统讲解 CMake 帮助文档的源码组织、本地构建方法、--help-*命令行帮助处理器(cmRST)所支持的标记子集、cmakeSphinx Domain 的对象模型与指令、交叉引用语法以及文档风格规范,并延伸介绍 Modules 目录中.cmake模块文档的编写流程。读完本文,你将能够从Help/与Modules/源码出发,独立编写、校验并构建出 HTML、man 页与命令行帮助三种输出形态的 CMake 文档。
Help 目录:CMake 手册的唯一事实来源
CMake 的帮助手册源码全部位于仓库顶层的Help目录中,包含命令(Help/command/)、模块(Help/module/)、策略(Help/policy/)、变量(Help/variable/)、属性(Help/prop_*系列目录)、生成器(Help/generator/)、环境变量(Help/envvar/)、手册页(Help/manual/)等分类文档。
这些源码文件使用 reStructuredText(RST)标记语法编写,并由 Sphinx 处理生成 CMake 帮助手册。也就是说,Help目录是 HTML 在线手册、man 手册、以及cmake --help-*命令行输出的共同事实来源。开发者想要了解文档开发的其他约定,可进一步阅读 Help/dev/README.rst(即本文所述的 "CMake Development" 文档)。
本地构建 HTML 与 man 手册
文档构建入口位于Utilities/Sphinx,这是一个独立的 CMake 项目。在 CMake 仓库内本地生成 HTML 与 man 手册到build/html与build/man目录,只需两条命令:
$ cmake -S Utilities/Sphinx -B build -DSPHINX_HTML=ON -DSPHINX_MAN=ON $ cmake --build build如果系统中 Sphinx 的安装位置不在默认搜索路径,可以显式指定:
$ cmake -S Utilities/Sphinx -B build -DSPHINX_HTML=ON -DSPHINX_MAN=ON \ -DSPHINX_EXECUTABLE=/path/to/sphinx-build从构建脚本 Utilities/Sphinx/CMakeLists.txt 可以看到,SPHINX_EXECUTABLE通过find_program查找名为sphinx-build的可执行文件;除 HTML 与 man 之外,该脚本还提供了一系列可选的文档输出格式开关:
| CMake 选项 | 输出内容 |
|---|---|
SPHINX_HTML | HTML 手册(安装到doc目录,sphinx-html组件) |
SPHINX_MAN | man 手册(按Help/manual/*.[1-9].rst逐一安装到 man 分区) |
SPHINX_SINGLEHTML | 单页 HTML 手册 |
SPHINX_LINKCHECK | 检查文档中的外部链接 |
SPHINX_QTHELP | Qt 帮助(需要qhelpgenerator) |
SPHINX_LATEXPDF | 基于 LaTeX 的 PDF |
SPHINX_TEXT | 纯文本帮助(不安装) |
SPHINX_INFO | texinfo / info 手册(需要makeinfo) |
SPHINX_HTML_COPYBUTTON | 启用 sphinx-copybutton 扩展 |
其中 man 手册的安装会跳过未构建的ccmake(BUILD_CursesDialog关闭时)与cmake-gui(BUILD_QtDialog关闭时)对应页面。构建完成后,HTML 输出路径会以file://.../html/index.html的形式打印到控制台,方便直接打开。
可选的 Sphinx 第三方扩展
构建 HTML 帮助时,CMake 使用第三方扩展 [sphinx-copybutton]——它会在code-block指令渲染出的代码块角落添加一个可交互的"复制"按钮。要在本地生成带该扩展的文档,按上文方式配置时额外加上:
$ cmake -S Utilities/Sphinx -B build -DSPHINX_HTML=ON -DSPHINX_HTML_COPYBUTTON=ON需要注意:该扩展必须安装在与SPHINX_EXECUTABLE相同的环境(或系统)中,否则构建会因导入失败而报错。
命令行帮助处理器:cmRST 与受支持的标记构造
除了用 Sphinx 生成 HTML/man 手册外,CMake 还内置了一个用 C++ 实现的文档处理器,用于为cmake --help-*系列命令行帮助选项输出文本。该处理器的实现位于 Source/cmRST.cxx,由 Source/cmDocumentation.cxx 驱动,例如cmake --help-module <name>会调用它解析对应模块源码中的.rst:注释。
它只支持 reStructuredText 标记的一个子集。因此,在编写或修改文档时,除了关注 Sphinx 生成的 HTML 与 man 页效果,还必须验证命令行帮助的输出效果。以下是 cmRST 支持的构造清单(该清单必须与 cmRST 实现保持一致,见 Source/cmRST.cxx 顶部构造的正则):
| 构造 | 命令行帮助处理方式 |
|---|---|
CMake Domain 指令(command/envvar/genex/signature/variable/diagnostic) | 按普通段落文本输出并解释 |
| CMake Domain 解释文本角色(cross-reference roles) | 替换为其链接文本;其他角色原样输出,不处理 |
code-block指令 | 去掉指令行,缩进统一替换为一个空格后原样输出代码块 |
include指令 | 将所引用的文档内联输出 |
以::结尾的段落后的字面块 | 原样输出::,块内容公共缩进替换为一个空格 |
note指令 | 按普通段落文本输出并解释 |
parsed-literal指令 | 去掉指令行,按普通文本输出块内容(保留解释) |
productionlist指令 | 按普通段落文本输出并解释 |
replace指令 | 定义\|substitution\|替换;必须先定义后引用 |
\|substitution\|引用 | 执行替换,替换文本中的换行全部转换为空格 |
toctree指令 | 将被引用的文档内联到引用文档中 |
versionadded/versionchanged指令 | 按普通段落文本输出并解释 |
需要注意两个边界行为:
- 未在上表中列出的行内标记构造(inline markup)在命令行帮助输出中原样打印。文档作者应优先使用在源码形态下看起来正确的行内标记,避免使用
\转义,尽量改用行内字面量(inline literal)。 - 未匹配上述任何指令的显式标记块(explicit markup block)会从命令行输出中移除。除非是
..纯注释(Sphinx 同样会移除它们),否则不要使用这类块。
缩进的限制:避免嵌套块
cmRST 不识别块的嵌套缩进。具体后果是:
- 显式标记块只有不缩进在其他块内部时才会被识别;
- 以
::结尾的段落之后的字面块,如果不在顶层缩进级别,可能吞掉其后所有缩进的行。
实践中应尽量避免这两种情况,保证命令行帮助与 HTML/man 输出的一致性。
CMake Domain:对象模型
CMake 为 Sphinx 增加了一个名为cmake的 Sphinx Domain("CMake Domain"),其 Python 实现位于 Utilities/Sphinx/cmake.py(CMakeDomain类),定义了如下文档对象类型:
| 对象类型 | 含义 | 关联参考 |
|---|---|---|
command | CMake 语言命令 | cmake(1)、cmake_policy() |
cpack_gen | CPack 打包生成器 | cpack(1)的-G选项 |
envvar | 环境变量 | cmake-env-variables(7)手册、set()命令 |
generator | CMake 原生构建系统生成器 | cmake(1)的-G选项 |
genex | CMake 生成器表达式 | cmake-generator-expressions(7)手册 |
manual | CMake 手册页 | 如cmake(1) |
module | CMake 模块 | cmake-modules(7)手册、include()命令 |
policy | CMake 策略 | cmake-policies(7)手册、cmake_policy()命令 |
prop_cache/prop_dir/prop_gbl/prop_sf/prop_inst/prop_test/prop_tgt | 缓存/目录/全局/源文件/安装文件/测试/目标属性 | cmake-properties(7)手册、set_property()命令 |
variable | CMake 语言变量 | cmake-variables(7)手册、set()命令 |
在cmake.py中,这些对象类型在CMakeDomain.object_types中注册,并为每种类型提供了同名的交叉引用角色(roles)。
对象的两大来源
CMake Domain 文档对象来自两个途径:
1. 文档自动变换(document transform)
Sphinx 的 CMake 扩展(CMakeTransform,见cmake.py)会把每个命名为Help/<type>/<file-name>.rst形式的文档自动变换为一个类型为<type>的 domain 对象。对象名从文档标题提取,标题须采用如下形式,并出现在.rst文件顶部附近、早于任何以字母、数字、<或$开头的其他行:
<object-name> -------------如果.rst文件中没有这种字面标题,则对象名取<file-name>;如果存在标题,则要求<file-name>等于去掉所有<与>字符后的<object-name>;对于$<genex-name>或$<genex-name:...>形式,则要求<file-name>等于genex-name。例如 Help/module/AddFileDependencies.rst 只有一行.. cmake-module::指令,真正的对象名来自模块文件内部的标题。
2. CMake Domain 指令
文档中也可以使用显式指令来定义部分对象类型,可用指令包括command指令、envvar指令、genex指令、variable指令(详见下文)。没有对应指令的对象类型(如module、policy、各类prop_*)必须通过上述文档自动变换来定义。
CMake Domain 指令详解
CMake Domain 提供以下指令,均需在 Sphinx 与 cmRST 两侧保持一致支持(cmake.py中注册于CMakeDomain.directives,cmRST.cxx中也有对应正则)。
command指令
文档化一个 "command" 对象,要求一个参数(命令名):
.. command:: <command-name> This indented block documents <command-name>.envvar指令
文档化一个 "envvar" 对象,要求一个参数(环境变量名):
.. envvar:: <envvar-name> This indented block documents <envvar-name>.genex指令
文档化一个 "genex" 对象,要求一个参数(生成器表达式名):
.. genex:: <genex-name> This indented block documents <genex-name>.该指令还支持可选的:target:选项,用于指定自定义目标名。但由于这会影响用:genex:角色引用该对象的能力,该选项应极少使用。
signature指令
用于在Help/command/<command-name>.rst文档中记载 CMake 命令签名:
.. signature:: <command-name>(<signature>) This indented block documents one or more signatures of a CMake command.该指令要求一个参数(签名摘要),并遵循如下规则:
::之后必须紧跟一个或多个签名;第一个签名可以选择放在同一行。若指令后紧跟空行,会产生文档生成错误:1 argument(s) required, 0 supplied。- 签名可以跨多行,但每个签名的最后一个
)必须是该行的最后一个字符。 - 签名之间不允许空行(空行之后的内容被视为描述文字)。
- 签名中的空白不会被保留。要记载复杂签名时,在
signature指令参数中写缩写形式,并在描述中用code-block写出完整签名。
目标名称(target)生成规则:
- 默认目标名自动从签名中开头的 "keyword" 参数提取——keyword 指任何以字母开头、不含空格的序列。例如签名
string(REGEX REPLACE <match-regex> ...)生成目标REGEX REPLACE,等价于.. _\REGEX REPLACE`:`。 - 也可以用
:target:选项指定自定义目标名,每个签名一行,例如:
.. signature:: cmake_path(GET <path-var> ROOT_NAME <out-var>) cmake_path(GET <path-var> ROOT_PATH <out-var>) :target: GET ROOT_NAME GET ROOT_PATH第一个目标可以放在:target:同一行。
- 如果目标名已在文档更早位置使用,则不再生成超链接目标。
- 目标可在同一文档内用
REF`_或TEXT <REF_>`_语法引用;与 RST 章节标题一样,这些目标不适用于 Sphinx:ref:语法,但可以用例如:command:string(APPEND)``` 的形式全局引用。
换行控制(:break:选项):
虽然签名中的空白不被保留,但默认情况下方括号或尖括号内部的换行会被抑制。该行为可通过:break:选项控制,取值如下:
| 取值 | 行为 |
|---|---|
all | 允许在任何空白处换行 |
smart(默认) | 允许在空白处换行,但配对的方/尖括号之间除外。例如\<input\>... [OUTPUT_VARIABLE \<out-var\>]中,允许在<input>...之后换行,但不允许在OUTPUT_VARIABLE与<out-var>之间换行 |
verbatim | 仅在源码文档存在换行处换行 |
注意:没有任何方式可以强制换行。指令内容即签名文档,需相应缩进。
variable指令
文档化一个 "variable" 对象,要求一个参数(变量名):
.. variable:: <variable-name> This indented block documents <variable-name>.交叉引用机制
Sphinx 使用 reStructuredText 解释文本角色提供交叉引用语法。CMake Domain 为每种对象类型提供了同名角色,形式为:
:type:`name` :type:`text <name>`其中type是 domain 对象类型,name是对象名。第一种形式链接文本为name(若类型为command则为name());第二种形式链接文本为显式的text。例如:
* The :command:`list` command. * The :command:`list(APPEND)` sub-command. * The :command:`list() command <list>`. * The :command:`list(APPEND) sub-command <list>`. * The :variable:`CMAKE_VERSION` variable. * The :prop_tgt:`OUTPUT_NAME_<CONFIG>` target property.尖括号的语义差异
CMake Domain 角色与 Sphinx/reStructuredText 惯例有一个重要区别:不带空格的a<b>形式被解释为"名字"而非"链接文本 + 显式目标"。这是必要的,因为对象名中频繁使用<占位符>,如OUTPUT_NAME_<CONFIG>。而带空格的a <b>形式仍解释为"链接文本 + 显式目标"。
此外,cref角色可用于创建指向本地目标、并带有字面量样式的引用,特别适合在命令文档中引用其子命令。实现上,CMakeCRefRole(cmake.py)直接使用nodes.reference加nodes.literal渲染,而CMakeXRefRole则处理:command:、:genex:、:guide:等角色的语法展开与索引登记。
文档风格规范
为保证全部文档形态的一致观感,CMake 文档有明确的风格约定。
章节标题
- 标题装饰线长度与标题文本等长,只画在标题下方,不画在上方:
Title Text ----------- 标题中每个非次要单词的首字母大写。
- 标题下划线字符层级自上而下为:
| 字符 | 用途 |
|---|---|
# | 总文档中的手册分组(part) |
* | 手册(chapter)标题 |
= | 手册内的章节(section) |
- | 子章节或 CMake Domain 对象文档标题 |
^ | 子子章节或 CMake Domain 对象文档的小节 |
" | 段落或 CMake Domain 对象文档的子小节 |
~ | CMake Domain 对象文档的子子小节 |
空白与行宽
- 缩进使用两个空格。
- 散文(prose)中句子之间使用两个空格。
- 行宽尽量限制在 75–80 列;这不是硬性限制,但新段落按 75 列换行能为后续小幅增补留出空间,避免大幅重排。
行内字面量
对签名中的关键字、文件名及其他技术术语,使用inline-literal语法标记。例如:
If ``WIN32`` is used with :command:`add_executable`, the :prop_tgt:`WIN32_EXECUTABLE` target property is enabled. That command creates the file ``<name>.exe`` on Windows.命令签名写法约定
在Help/command/<command-name>.rst文档中,使用 CMake Domain 的signature指令为每个签名单独建档;用章节标题把签名与前置内容分隔开,例如:
... preceding paragraph. Normal Libraries ^^^^^^^^^^^^^^^^ .. signature:: add_library(<lib> ...) This signature is used for ...签名文档的约定:
- 用尖括号
<placeholder>表示由调用方指定的参数,正文中用行内字面量语法引用它们; - 可选部分用方括号包裹;
- 可重复部分以省略号(
...)结尾; - 同一命令的不同签名可多次使用
signature指令。
布尔常量
- 用户可修改的布尔值(如
POSITION_INDEPENDENT_CODE)使用OFF与ON,这些属性可以说 "enabled"/"disabled"。 - 一经设置便不可修改的固有值(如构建目标的
IMPORTED属性)使用True与False。
交叉引用与概念引用
- 所有可链接的引用都标记为链接,包括重复出现的引用(与 Wikipedia "一篇文章只链接一次" 的风格不同,CMake 文档不采用后者)。
- 当某个概念对应一个属性、且该概念在高层级手册中有描述时,优先链接到手册章节而非属性。例如:
This command creates an :ref:`Imported Target <Imported Targets>`.而不是:
This command creates an :prop_tgt:`IMPORTED` target.后者仅当专门指该属性本身时才使用。手册章节不会因创建章节而自动生成引用目标,需要显式锚点:
.. _`Imported Targets`:锚点名应与对应章节名一致,并用带指定文本的交叉引用指向它。注意IMPORTED这个术语可能指命令关键字、目标属性或概念,标记时需特别小心。
另外,属性、命令或变量若与其他概念相关(例如与构建系统描述、生成器表达式或 Qt 相关),每个相关对象都应链接到提供高层信息的主手册;只有与该命令相关的特定信息才放在该命令的文档中。
引用 CMake Domain 对象
当引用属性、变量、命令等 CMake Domain 对象时,优先链接到目标对象并紧跟其对象类型。例如:
Set the :prop_tgt:`AUTOMOC` target property to ``ON``.而不是:
Set the target property :prop_tgt:`AUTOMOC` to ``ON``.policy指令是例外,类型通常放在链接之前:
If policy :policy:`CMP0022` is set to ``NEW`` the behavior is ...另外,文档中的自我引用使用inline-literal语法。例如在add_executable命令文档内部使用 add_executable,而不是:command:add_executable``(后者用于其他位置的引用)。
模块文档编写
Modules目录存放 CMake 语言的.cmake模块文件,其文档同样由Help侧生成。
注册流程
要为Modules/<module-name>.cmake建档,需要两步:
- 编辑 Help/manual/cmake-modules.7.rst,在
toctree指令中按排序顺序加入:
/module/<module-name>- 新增模块文档文件
Help/module/<module-name>.rst,其中只包含一行:
.. cmake-module:: ../../Modules/<module-name>.cmakecmake-module指令会扫描模块文件,从以.rst:开头的注释块中提取 reStructuredText 标记(该指令的解析逻辑见cmake.py中的CMakeModule类,它同时支持括号注释与#.rst:行注释两种形态)。
模块文件内的文档注释
在Modules/<module-name>.cmake顶部,首先放置如下许可证声明:
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file LICENSE.rst or https://cmake.org/licensing for details.声明之后加一个空行,然后使用 Bracket Comment 形式书写文档:
#[=======================================================================[.rst: <module-name> ------------- <reStructuredText documentation of module> #]=======================================================================]规则要点:
- 开闭括号中可使用任意数量的
=,只要两边匹配即可。 - 如果闭合括号所在行以
#开头,则该行内容被排除(不入文档)。 - 额外的
.rst:注释可以出现在模块文件的任意位置,但所有此类注释必须以#开头且位于第一列。
一个完整的示例,FindXxx.cmake模块:
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file LICENSE.rst or https://cmake.org/licensing for details. #[=======================================================================[.rst: FindXxx ------- This is a cool module. This module does really cool stuff. It can do even more than you think. It even needs two paragraphs to tell you about it. And it defines the following variables: ``VAR_COOL`` this is great isn't it? ``VAR_REALLY_COOL`` cool right? #]=======================================================================] <code> #[=======================================================================[.rst: .. command:: Xxx_do_something This command does something for Xxx:: Xxx_do_something(some arguments) #]=======================================================================] macro(Xxx_do_something) <code> endmacro()仓库中的真实范例可对照 Modules/AddFileDependencies.cmake:文件顶部是 BSD 许可证注释,随后是一个.rst:Bracket Comment,内含AddFileDependencies标题、deprecated提示、include(AddFileDependencies)用法示例以及.. command:: add_file_dependencies指令。
校验与隐藏
- 运行
cmake --help-module <module-name>测试命令行帮助的排版效果;同时开启SPHINX_HTML与SPHINX_MAN选项构建文档,反复调整注释直到各形态输出都令人满意。 - 如果希望某个
.cmake文件不出现在模块文档中,只需不添加Help/module/<module-name>.rst文件、也不在Help/manual/cmake-modules.7.rst的toctree中登记它即可。
模块内函数与宏的命名规范
模块可以提供由function()与macro()命令定义的 CMake 函数和宏。为避免跨模块冲突,命名约定为:使用<ModuleName>_前缀(<ModuleName>为模块名的精确大小写拼写)加上其余名称;前缀之后的部分没有统一约定。
出于历史原因,CMake 自带的部分模块并未遵循此前缀约定。为这些模块新增函数时,在评审讨论中可决定是遵循其既有约定,还是改用模块名前缀。
公开函数与宏的文档应写在模块中,通常放在顶部的主文档区域。例如MyModule模块可这样文档化一个函数:
#[=======================================================================[.rst: MyModule -------- This is my module. It provides some functions. .. command:: MyModule_Some_Function This is some function: .. code-block:: cmake MyModule_Some_Function(...) #]=======================================================================]文档也可以放在每个定义之前。例如另一个函数:
#[=======================================================================[.rst: .. command:: MyModule_Other_Function This is another function: .. code-block:: cmake MyModule_Other_Function(...) #]=======================================================================] function(MyModule_Other_Function ...) # ... endfunction()文档管线全景:三种输出的源码印证
综合全文,CMake 文档从源码到三种输出的完整管线如下:
- HTML / man / 其他 Sphinx 格式:由 Utilities/Sphinx/CMakeLists.txt 生成构建规则,
configure_file生成conf.py,再调用sphinx-build以Help目录为源进行构建。cmake.py中的setup()注册了cmake-module指令、CMakeTransform变换、CMakeXRefTransform变换与CMakeDomain。 - 命令行帮助:由 C++ 实现的 Source/cmRST.cxx 解析 RST 子集,Source/cmDocumentation.cxx 负责
cmake --help-*的输出;其指令与角色正则(CMakeDirective、CMakeRole、CodeBlockDirective、VersionDirective、ModuleRST等)与文档中列出的构造清单一一对应。 - 模块对象索引:
CMakeTransform依据Help/<type>/<file-name>.rst的路径前缀自动登记 domain 对象并建立索引条目,cmake.py中的_cmake_index_objs表则定义了各对象类型在 Sphinx 索引中的呈现名称(如command、cache property、target property等)。
此外,CMakeSignatureObject在cmake.py中实现了:break:选项的all/smart/verbatim三种换行策略(_break_signature_all/_break_signature_smart/_break_signature_verbatim),并利用 pygments 的 CMakeLexer 对签名做语法高亮,这与文档中"签名空白不保留、括号内抑制换行"的描述完全对应。理解了这条双处理器(Sphinx + cmRST)管线,即可在新增或修改任何 CMake 文档时,一次性保证 HTML、man 与命令行帮助三种形态的行为一致。
- 构建工具
- 开发工具
- CLI
【免费下载链接】CMake
Mirror of CMake upstream repository
相关推荐
Infer `help` 子命令完全指南:从命令行手册到网站文档生成
Infer help 子命令完全指南:从命令行手册到网站文档生成 导读 infer help 是 Infer 静态分析器内置的文档子命令,它既承担着"命令行手册
静态分析代码质量开发工具asdf 命令全景手册:从 `asdf help` 到源码级解析的完整命令指南
asdf 命令全景手册:从 asdf help 到源码级解析的完整命令指南 asdf 是一个可扩展的多运行时版本管理器,通过统一的命令接口管理 Ruby、Nod
CLI开发工具Sphinx reStructuredText 完全指南:从语法基础到高级指令的实战手册
Sphinx reStructuredText 完全指南:从语法基础到高级指令的实战手册 reStructuredText(reST)是 Sphinx 文档生成
文档开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考