@chengzhao-dev/dsh-cpp-workflow 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.en.md +10 -9
  2. package/README.md +10 -9
  3. package/lib/index.js +4 -4
  4. package/locale/en.json +1 -1
  5. package/locale/zh.json +1 -1
  6. package/package.json +2 -2
  7. package/skills/building-cpp/SKILL.md +24 -11
  8. package/skills/building-cpp/references/build-script.md +11 -0
  9. package/skills/building-cpp/references/linking.md +1 -1
  10. package/skills/building-cpp/references/standards/README.md +1 -1
  11. package/skills/building-cpp/steps/01-build.md +22 -35
  12. package/skills/configuring-cmake/SKILL.md +57 -0
  13. package/skills/configuring-cmake/assets/cmake/CompileSettings.cmake +126 -0
  14. package/skills/configuring-cmake/assets/cmake/LinkSettings.cmake +48 -0
  15. package/skills/{building-cpp → configuring-cmake}/examples/cmake.md +84 -10
  16. package/skills/{building-cpp → configuring-cmake}/references/cmake.md +49 -43
  17. package/skills/configuring-cmake/references/cxx-standard.md +67 -0
  18. package/skills/configuring-cmake/references/modules.md +105 -0
  19. package/skills/configuring-cmake/steps/01-targets.md +53 -0
  20. package/skills/configuring-cmake/steps/02-modules.md +37 -0
  21. package/skills/configuring-cmake/steps/03-check.md +39 -0
  22. package/skills/developing-cpp/SKILL.md +1 -1
  23. package/skills/developing-cpp/examples/comments.md +24 -0
  24. package/skills/developing-cpp/examples/exceptions.md +38 -19
  25. package/skills/developing-cpp/examples/structure.md +71 -0
  26. package/skills/developing-cpp/references/comments.md +49 -137
  27. package/skills/developing-cpp/references/exceptions.md +12 -13
  28. package/skills/developing-cpp/references/language.md +1 -0
  29. package/skills/developing-cpp/references/naming.md +2 -2
  30. package/skills/developing-cpp/references/standard-library.md +1 -0
  31. package/skills/developing-cpp/references/types.md +1 -2
  32. package/skills/developing-cpp/scripts/checking-comments.mjs +57 -2
  33. package/skills/developing-cpp/steps/03-declaration.md +3 -3
  34. package/skills/developing-cpp/steps/04-implementation.md +4 -3
  35. package/skills/building-cpp/assets/cmake/applying-warnings.cmake +0 -10
  36. package/skills/building-cpp/assets/cmake/fetching-googletest.cmake +0 -15
  37. package/skills/building-cpp/assets/cmake/setting-rpath.cmake +0 -8
@@ -0,0 +1,48 @@
1
+ # LinkSettings
2
+ #
3
+ # 设置构建期运行时路径,并为测试取回 GoogleTest。
4
+ #
5
+ # 用 include(LinkSettings) 载入。LinkSettings_FetchGoogleTest 是本文件
6
+ # 唯一会下载的函数,只在测试需要时调用。
7
+ include_guard(GLOBAL)
8
+
9
+ #-------------------------------------------------------------------------------
10
+ # 让 build/bin 里的程序找到同级 build/lib 下的库。
11
+ #
12
+ # 只改构建树,不改安装后的 RPATH。
13
+ #
14
+ # :param target: 已经存在的 CMake 目标。
15
+ function(LinkSettings_SetBuildRpath target)
16
+ if(NOT TARGET "${target}")
17
+ message(FATAL_ERROR
18
+ "LinkSettings_SetBuildRpath: target '${target}' does not exist")
19
+ endif()
20
+
21
+ if(APPLE)
22
+ set_target_properties("${target}" PROPERTIES
23
+ BUILD_RPATH "@loader_path/../lib")
24
+ else()
25
+ set_target_properties("${target}" PROPERTIES BUILD_RPATH "$ORIGIN/../lib")
26
+ endif()
27
+ endfunction()
28
+
29
+ #-------------------------------------------------------------------------------
30
+ # 取回 GoogleTest 并让其 target 可用。
31
+ #
32
+ # 固定 v1.17.0 与 SHA-256;只在 tests 目录调用,库与程序代码不调用。
33
+ function(LinkSettings_FetchGoogleTest)
34
+ include(FetchContent)
35
+
36
+ if(POLICY CMP0135)
37
+ cmake_policy(SET CMP0135 NEW)
38
+ endif()
39
+
40
+ FetchContent_Declare(
41
+ googletest
42
+ URL https://codeload.github.com/google/googletest/tar.gz/refs/tags/v1.17.0
43
+ URL_HASH
44
+ SHA256=65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c
45
+ )
46
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
47
+ FetchContent_MakeAvailable(googletest)
48
+ endfunction()
@@ -10,15 +10,17 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
10
10
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
11
11
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
12
12
 
13
+ # 先把 assets/cmake/ 里的 CompileSettings.cmake 复制到工程的 cmake/。
14
+ include(CompileSettings)
15
+
13
16
  add_library(greeting STATIC greeting/src/greeting.cpp)
14
17
  target_include_directories(greeting PUBLIC greeting/include)
15
- target_compile_features(greeting PUBLIC cxx_std_23)
16
- set_target_properties(greeting PROPERTIES CXX_EXTENSIONS OFF)
17
- target_compile_options(greeting PRIVATE -Wall -Wextra -Werror)
18
+ CompileSettings_SetCxxStandard(greeting PUBLIC c++23)
19
+ CompileSettings_SetWarnings(greeting PRIVATE LEVEL standard)
18
20
 
19
21
  add_executable(app src/main.cpp)
20
- target_compile_features(app PRIVATE cxx_std_23)
21
- set_target_properties(app PROPERTIES CXX_EXTENSIONS OFF)
22
+ CompileSettings_SetCxxStandard(app PRIVATE c++23)
23
+ CompileSettings_SetWarnings(app PRIVATE LEVEL standard)
22
24
  target_link_libraries(app PRIVATE greeting)
23
25
  ```
24
26
 
@@ -84,6 +86,45 @@ target_link_libraries(app PRIVATE core)
84
86
 
85
87
  库不依赖 `main()`;测试链接库时不会碰到入口。
86
88
 
89
+ ## 标准只写一半
90
+
91
+ **差**:
92
+
93
+ ```cmake
94
+ target_compile_features(app PRIVATE cxx_std_20)
95
+ # 没写 CXX_STANDARD_REQUIRED;编译器不支持时可能静默降级
96
+ set_target_properties(app PROPERTIES CXX_STANDARD 20)
97
+ ```
98
+
99
+ 问题:meta-feature 只保证“至少”,`CXX_STANDARD` 又允许 decay,实际标准可能不是 20。
100
+
101
+ **好**:
102
+
103
+ ```cmake
104
+ CompileSettings_SetCxxStandard(app PRIVATE c++20)
105
+ ```
106
+
107
+ 两套声明同时写;`CXX_STANDARD_REQUIRED ON` 禁止降级,`CXX_EXTENSIONS OFF` 把 `gnu++20` 收成 `c++20`。
108
+
109
+ ## 警告等级
110
+
111
+ **差**:
112
+
113
+ ```cmake
114
+ target_compile_options(app PRIVATE -Wall -Wextra -Werror)
115
+ target_compile_options(core PRIVATE -Wall -Wextra -Werror)
116
+ # 每个 target 复制一遍;想加 -Wshadow 时要逐处改
117
+ ```
118
+
119
+ **好**:
120
+
121
+ ```cmake
122
+ CompileSettings_SetWarnings(app PRIVATE LEVEL standard)
123
+ CompileSettings_SetWarnings(core PRIVATE LEVEL strict)
124
+ ```
125
+
126
+ `basic` 只开 `-Wall -Wextra`;`standard` 加 `-Werror`、`-Wshadow`、`-Wold-style-cast`、`-Wpedantic`、`-Wnon-virtual-dtor`、`-Woverloaded-virtual`;`strict` 再加转换、格式化与可移植性告警。警告只作用于传入 target,不通过 `INTERFACE` 传播。
127
+
87
128
  ## 编译数据库
88
129
 
89
130
  **差**:
@@ -120,17 +161,16 @@ LD_LIBRARY_PATH=build/clang/lib ./build/clang/bin/app # 每次运行都要包
120
161
  **好**:
121
162
 
122
163
  ```cmake
164
+ # LinkSettings.cmake 提供 RPATH 与 GoogleTest 依赖函数。
165
+ include(LinkSettings)
166
+
123
167
  add_library(greeting SHARED greeting/src/greeting.cpp)
124
168
  set_target_properties(greeting PROPERTIES
125
169
  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
126
170
 
127
171
  add_executable(app src/main.cpp)
128
172
  target_link_libraries(app PRIVATE greeting)
129
- if(APPLE)
130
- set_target_properties(app PROPERTIES BUILD_RPATH "@loader_path/../lib")
131
- else()
132
- set_target_properties(app PROPERTIES BUILD_RPATH "$ORIGIN/../lib")
133
- endif()
173
+ LinkSettings_SetBuildRpath(app)
134
174
  ```
135
175
 
136
176
  从 `build/<compiler>/bin/app` 直接启动即可找到同级 `../lib` 下的共享库;安装场景另配 RPATH。
@@ -148,3 +188,37 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
148
188
  ```
149
189
 
150
190
  构建产物全在 `build/<compiler>/` 下。
191
+
192
+ ## 注释
193
+
194
+ **差**:
195
+
196
+ ```cmake
197
+ #[[.rst:
198
+ CompileSettings_SetWarnings
199
+ ---------------------------
200
+
201
+ .. command:: CompileSettings_SetWarnings(target visibility)
202
+
203
+ :param target: 目标。
204
+ @param visibility: 可见性。
205
+ @return: 无。
206
+ ]]
207
+ ```
208
+
209
+ `#[[.rst:` 与 `.. command::` 是给要进 CMake 手册的模块用的抽取标记,本仓模块不进手册;`@param`、`@return` 是 C++ 的 Doxygen 标签,CMake 里不用。CMake 函数也没有返回值,`:return:` 同样不写。
210
+
211
+ **好**:
212
+
213
+ ```cmake
214
+ # 给一个目标加上编译警告。
215
+ #
216
+ # 只改传入的目标。PRIVATE 不向使用方传播。
217
+ #
218
+ # :param target: 已经存在的 CMake 目标。
219
+ # :param visibility: 取 PRIVATE 或 INTERFACE。
220
+ # :keyword LEVEL: 可省略。取 basic、standard 或 strict;省略时按 standard。
221
+ function(CompileSettings_SetWarnings target visibility)
222
+ ```
223
+
224
+ 普通 `#` 行,签名留在 `function()` 那一行;位置参数用 `:param`,`cmake_parse_arguments` 的关键字用 `:keyword`。
@@ -1,10 +1,12 @@
1
1
  # CMake
2
2
 
3
- 目标:一个编译器、Ninja、target-based 规则、可用的编译数据库。下面是最小形状和常见边界;模板在 `assets/templates/projects/`,可复用的 CMake 片段在 `assets/cmake/`:`applying-warnings.cmake` 管警告选项,`fetching-googletest.cmake` 取测试框架,`setting-rpath.cmake` 设运行时库路径。
3
+ 目标:一个编译器、Ninja、target-based 规则、可用的编译数据库。下面是最小形状和常见边界;工程模板仍由 `building-cpp` 提供,在 `../../building-cpp/assets/templates/projects/`,可复用的 CMake 函数在本技能的 `assets/cmake/`:`CompileSettings.cmake` 收标准与警告,`LinkSettings.cmake` 收构建期 RPATH 与 GoogleTest 依赖。
4
4
 
5
5
  ## 排版
6
6
 
7
- CMake 没有官方风格指南。这里跟随 Google 自家项目的 `cmake-format` 配置:缩进 2 个空格、行宽 80、不用 tab。来源是 [or-tools 的 .cmake-format.py](https://github.com/google/or-tools/blob/stable/.cmake-format.py) 与 [draco 的 .cmake-format.py](https://github.com/google/draco/blob/master/.cmake-format.py),两处都设 `tab_size=2`、`line_width=80`。命令行、列表和注释都按这个宽度换行;`CMakeLists.txt` 与 `*.cmake` 一视同仁。
7
+ CMake 没有单独的官方风格指南文档,但官方仓库的 [.editorconfig](https://raw.githubusercontent.com/Kitware/CMake/master/.editorconfig) 明确规定:`CMakeLists.txt`、`*.cmake`、`*.rst` 使用 `indent_size = 2`、`indent_style = space`。这是官方代码风格的直接依据。行宽 80 列来自社区 `cmake-format` 约定([or-tools](https://github.com/google/or-tools/blob/stable/.cmake-format.py)、[draco](https://github.com/google/draco/blob/master/.cmake-format.py) 都设 `tab_size=2`、`line_width=80`),不是 CMake 官方强制值。命令行、列表和注释都按项目约定的宽度换行;`CMakeLists.txt` 与 `*.cmake` 一视同仁。
8
+
9
+ 注释不用 `#[[.rst:`。那层标记只属于要进 CMake 手册的模块:Kitware 在 [documentation.rst](https://github.com/Kitware/CMake/blob/master/Help/dev/documentation.rst) 里用 `.. command::` 与参数段,供 Sphinx 从源码抽进手册;本仓的模块不进手册,读者也读不懂这层标记。公开函数上方写中文 `#` 契约:第一句说功能,空一行写边界,再用 `:param 名字:` 标位置参数、`:keyword 名字:` 标 `cmake_parse_arguments` 的关键字;CMake 函数没有返回值,不写 `:return:`,也不把 C++ 的 `@param`、`@return` 搬进来。字段取自 [CMinx](https://cmakepp.github.io/CMinx/documenting/function.html);本仓不跑 CMinx,注释保持普通 `#` 行。签名留在 `function()` 那一行,同一文件的函数组之间用一条 `#---` 短横线分隔;行注释只写原因,不画 `#===...===#` 方框。官方 [GoogleTest.cmake](https://github.com/Kitware/CMake/blob/master/Modules/GoogleTest.cmake) 在一个文件里放多个测试函数,可作“文件按职责聚合”的参照;社区 [CompilerWarnings.cmake](https://github.com/cpp-best-practices/cmake_template/blob/main/cmake/CompilerWarnings.cmake) 在每个警告 flag 后写它抓什么问题,可作参数注释的参照。
8
10
 
9
11
  Shell 脚本用 2 个空格缩进、不用 tab,来源是 [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html#indentation)。
10
12
 
@@ -19,14 +21,12 @@ project({{PROJECT_NAME}} VERSION 0.1.0 LANGUAGES CXX)
19
21
  set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
20
22
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
21
23
 
24
+ # 先把 assets/cmake/ 里的 CompileSettings.cmake 复制到工程的 cmake/。
25
+ include(CompileSettings)
26
+
22
27
  add_executable(app src/main.cpp)
23
- target_compile_features(app PRIVATE cxx_std_23)
24
- set_target_properties(app PROPERTIES
25
- CXX_STANDARD 23
26
- CXX_STANDARD_REQUIRED ON
27
- CXX_EXTENSIONS OFF
28
- )
29
- target_compile_options(app PRIVATE -Wall -Wextra -Werror)
28
+ CompileSettings_SetCxxStandard(app PRIVATE c++23)
29
+ CompileSettings_SetWarnings(app PRIVATE LEVEL standard)
30
30
  ```
31
31
 
32
32
  ### 库 + 程序
@@ -40,42 +40,56 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
40
40
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
41
41
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
42
42
 
43
+ # 先把 assets/cmake/ 里的 CompileSettings.cmake 复制到工程的 cmake/。
44
+ include(CompileSettings)
45
+
43
46
  add_subdirectory(greeting)
44
47
 
45
48
  add_executable(app src/main.cpp)
46
- target_compile_features(app PRIVATE cxx_std_23)
47
- set_target_properties(app PROPERTIES
48
- CXX_STANDARD 23
49
- CXX_STANDARD_REQUIRED ON
50
- CXX_EXTENSIONS OFF
51
- )
49
+ CompileSettings_SetCxxStandard(app PRIVATE c++23)
50
+ CompileSettings_SetWarnings(app PRIVATE LEVEL standard)
52
51
  target_link_libraries(app PRIVATE greeting)
53
- target_compile_options(app PRIVATE -Wall -Wextra -Werror)
54
52
  ```
55
53
 
56
54
  ```cmake
57
55
  # greeting/CMakeLists.txt
58
56
  add_library(greeting STATIC src/greeting.cpp)
59
57
  target_include_directories(greeting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
60
- target_compile_features(greeting PUBLIC cxx_std_23)
61
- set_target_properties(greeting PROPERTIES
62
- CXX_STANDARD 23
63
- CXX_STANDARD_REQUIRED ON
64
- CXX_EXTENSIONS OFF
65
- )
66
- target_compile_options(greeting PRIVATE -Wall -Wextra -Werror -Wold-style-cast -Wshadow)
58
+ CompileSettings_SetCxxStandard(greeting PUBLIC c++23)
59
+ CompileSettings_SetWarnings(greeting PRIVATE LEVEL standard)
67
60
  ```
68
61
 
69
62
  - 库的 `PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..` 让程序能从仓库根写 `#include "greeting/include/greeting.h"`,不需要程序再写 include 路径。
70
- - `cxx_std_23` 用 `PUBLIC` 从库传播;程序自己再加一次也无害。`CXX_STANDARD_REQUIRED ON` 防止 CMake 在编译器不支持时静默 decay;显式 `CXX_STANDARD` 与 `standard` 保持一致。
71
- - `CXX_EXTENSIONS OFF` 把 `-std=gnu++23` 收成 `-std=c++23`,与 Google 的「不用非标准扩展」一致;每个 target 都要设。configure 后检查 `compile_commands.json` 的最后一条 `-std=`,不能是 `gnu++NN`。
63
+ - 标准用 `CompileSettings_SetCxxStandard`:库用 `PUBLIC` 传播,程序用 `PRIVATE`;函数内部同时写 `target_compile_features` 与 `CXX_STANDARD` 三属性。
64
+ - `CXX_STANDARD_REQUIRED ON` 防止 CMake 在编译器不支持时静默 decay;`CXX_EXTENSIONS OFF` 把 `-std=gnu++NN` 收成 `-std=c++NN`。configure 后检查 `compile_commands.json` 的最后一条 `-std=`。
72
65
  - 程序 target 固定 `app`;库 target 用组件名。
73
66
 
67
+ ## 标准与复用
68
+
69
+ C++11–C++23 的两层声明、可见性、常见错误与验证见 `references/cxx-standard.md`。通用函数 `CompileSettings_SetCxxStandard(target visibility c++NN)` 在 `assets/cmake/CompileSettings.cmake`:
70
+
71
+ ```cmake
72
+ CompileSettings_SetCxxStandard(app PRIVATE c++23)
73
+ CompileSettings_SetCxxStandard(greeting PUBLIC c++20)
74
+ ```
75
+
76
+ 函数名不绑定单一标准;新增档位只改函数里的支持列表。模块何时抽、文件怎么放、怎么导入与测试见 `references/modules.md`。
77
+
78
+ ## 警告等级
79
+
80
+ `cmake/CompileSettings.cmake` 的 `CompileSettings_SetWarnings(<target> <visibility> LEVEL basic|standard|strict)` 把常用 flag 收成三档;`visibility` 用 `PRIVATE` 或 `INTERFACE`,`PRIVATE` 不向使用方传播。警告与标准写在同一份编译设置模块里。
81
+
82
+ - `basic`:`-Wall -Wextra`;适合刚开始接 CI 的旧代码。
83
+ - `standard`:`basic` 加 `-Werror`、`-Wshadow`、`-Wold-style-cast`、`-Wpedantic`、`-Wnon-virtual-dtor`、`-Woverloaded-virtual`;默认档。
84
+ - `strict`:`standard` 加 `-Wconversion`、`-Wsign-conversion`、`-Wcast-align`、`-Wcast-qual`、`-Wformat=2`、`-Wundef`、`-Wdouble-promotion`、`-Wimplicit-fallthrough`、`-Wnull-dereference`;适合新代码或质量闸门。
85
+
86
+ MSVC 分支用 `/W4`、`/permissive-`、`/WX`;其他编译器直接报错,避免静默地没有警告。警告是 target 的 `PRIVATE` 选项,库不把自己的策略强加给消费者。
87
+
74
88
  ## target-based 规则
75
89
 
76
90
  - 每个 target 自己声明 include、标准、编译选项、链接库;不要用全局 `include_directories()`、`add_definitions()`、`CMAKE_CXX_FLAGS` 给所有 target 加料。
77
91
  - `target_include_directories(<target> PUBLIC|PRIVATE <dir>)`:公共头给 `PUBLIC`,私有实现目录给 `PRIVATE`。
78
- - `target_compile_features(<target> PUBLIC cxx_std_23)`:标准是 target 属性,不是全局开关。
92
+ - `target_compile_features(<target> PUBLIC cxx_std_NN)`:标准是 target 属性,不是全局开关;优先调用项目 `cmake/` 里的标准函数。
79
93
  - `target_link_libraries(<target> PRIVATE|PUBLIC <lib>)`:实现依赖用 `PRIVATE`,接口依赖用 `PUBLIC`。
80
94
  - 不要 `file(GLOB ...)`;显式列源文件,新增文件时改 CMake,构建系统才知道。`CONFIGURE_DEPENDS` 依赖构建系统每次检查目录,跨生成器行为也不完全一致,不作为工程规则。
81
95
  - 不把 `main()` 编进库;程序只做装配和 I/O。
@@ -119,16 +133,14 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
119
133
  ## 共享库运行时路径
120
134
 
121
135
  ```cmake
122
- if(APPLE)
123
- set_target_properties(app PROPERTIES BUILD_RPATH "@loader_path/../lib")
124
- else()
125
- set_target_properties(app PROPERTIES BUILD_RPATH "$ORIGIN/../lib")
126
- endif()
136
+ include(LinkSettings)
137
+ LinkSettings_SetBuildRpath(app)
127
138
  ```
128
139
 
140
+ - 函数按平台写 `BUILD_RPATH`:Linux 用 `$ORIGIN/../lib`,Apple 用 `@loader_path/../lib`。
129
141
  - 只对需要直接运行构建产物的程序设置;安装规则另算。
130
142
  - 不要用全局 `LD_LIBRARY_PATH` 包装所有命令;共享库的运行时路径由 target 的 RPATH 设置,不在脚本里全局改寻库路径。
131
- - `$ORIGIN` 在 CMake 字符串里要转义成 `\$ORIGIN` 吗?在双引号里 `$ORIGIN` 会被 CMake 当变量;写成 `"$ORIGIN/../lib"` 在 CMake 中 `$ORIGIN` 不是 `${...}`,不会被展开,保持字面量。若遇到展开问题,用 `"\$ORIGIN/../lib"`。
143
+ - `$ORIGIN` 在 CMake 双引号里不是 `${...}`,不会被展开,保持字面量;若遇到展开问题,用 `"\$ORIGIN/../lib"`。
132
144
 
133
145
  ## 标准库与编译器一致
134
146
 
@@ -147,21 +159,15 @@ endif()
147
159
 
148
160
  ## GoogleTest
149
161
 
150
- 需要测试框架时,用固定版本的 `FetchContent`:
162
+ 需要测试框架时,用固定版本的 `LinkSettings_FetchGoogleTest`:
151
163
 
152
164
  ```cmake
153
- include(FetchContent)
154
- FetchContent_Declare(
155
- googletest
156
- URL https://codeload.github.com/google/googletest/tar.gz/refs/tags/v1.17.0
157
- URL_HASH SHA256=65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c
158
- )
159
- set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
160
- FetchContent_MakeAvailable(googletest)
165
+ include(LinkSettings)
166
+ LinkSettings_FetchGoogleTest()
161
167
  ```
162
168
 
163
- - 版本固定,不用 `master`;URL 和哈希写在一起,避免供应链漂移。
164
- - 只在 `tests/` 需要时拉取;库和程序不依赖测试框架。
169
+ - 函数内部固定 GoogleTest v1.17.0 的 URL 与 SHA-256,避免供应链漂移;版本升级只改一处。
170
+ - 只在 `tests/` 需要时调用;库和程序不依赖测试框架。
165
171
  - 没有网络时,项目可以自带 vendored 副本;不要构建时静默跳过测试。
166
172
 
167
173
  ## 模板选择
@@ -0,0 +1,67 @@
1
+ # C++ 标准与 CMake
2
+
3
+ 目标:一个 target 只声明一个标准,声明在 target 上,编译数据库里的实际参数与声明一致。
4
+
5
+ ## 两层声明
6
+
7
+ CMake 有两套相关机制,必须同时写:
8
+
9
+ - `target_compile_features(target visibility cxx_std_NN)`:声明目标至少需要该标准。它是 meta-feature,编译器默认标准更高时可能不产生 `-std=`;只写这一项,不能保证实际使用哪个标准。
10
+ - `CXX_STANDARD NN` + `CXX_STANDARD_REQUIRED ON` + `CXX_EXTENSIONS OFF`:把请求标准写死,禁止不可用时静默降级,并关闭 `gnu++NN` 扩展。
11
+
12
+ | `standard` | meta-feature | `CXX_STANDARD` | `__cplusplus` | CMake 最低版本 |
13
+ | --- | --- | --- | --- | --- |
14
+ | `c++11` | `cxx_std_11` | `11` | `201103L` | 3.1 |
15
+ | `c++14` | `cxx_std_14` | `14` | `201402L` | 3.1 |
16
+ | `c++17` | `cxx_std_17` | `17` | `201703L` | 3.8 |
17
+ | `c++20` | `cxx_std_20` | `20` | `202002L` | 3.12 |
18
+ | `c++23` | `cxx_std_23` | `23` | `202302L` | 3.20 |
19
+
20
+ `cxx_std_23` 与 `CXX_STANDARD 23` 都要求 CMake 3.20;工程 `cmake_minimum_required` 低于该版本时先升最低版本,不要悄悄降到 C++20。
21
+
22
+ ## 通用函数
23
+
24
+ `assets/cmake/CompileSettings.cmake` 提供:
25
+
26
+ ```cmake
27
+ CompileSettings_SetCxxStandard(app PRIVATE c++23)
28
+ CompileSettings_SetCxxStandard(greeting PUBLIC c++20)
29
+ ```
30
+
31
+ 第一个参数是已由 `add_library` 或 `add_executable` 创建的目标,第二个是 `PUBLIC` / `PRIVATE` / `INTERFACE`,第三个是 `c++11`–`c++23`。函数内部做三步:校验目标与参数,调用 `target_compile_features`,再设置 `CXX_STANDARD` 三属性。
32
+
33
+ 扩展新标准时改函数里的 `supported_standards` 与错误提示;不要在目标目录复制第二份三属性。
34
+
35
+ ## 可见性
36
+
37
+ - 库的公共接口需要该标准时用 `PUBLIC`,链接者自动继承。
38
+ - 程序、测试、实现文件用 `PRIVATE`。
39
+ - `INTERFACE` 只用于纯头文件 target;不要用它绕过「标准应写在有源文件的目标上」。
40
+ - `target_compile_features` 重复调用会追加特性;同一目标不要同时写两个不同标准。
41
+
42
+ ## 常见错误
43
+
44
+ | 写法 | 问题 |
45
+ | --- | --- |
46
+ | 只写 `target_compile_features(... cxx_std_23)` | 编译器默认更高时可能不加 `-std`,实际标准不可见 |
47
+ | 只写 `CXX_STANDARD 23`,不写 `CXX_STANDARD_REQUIRED` | 编译器不支持时静默降级 |
48
+ | 不写 `CXX_EXTENSIONS OFF` | 得到 `-std=gnu++23`,用了非标准扩展 |
49
+ | 用 `CMAKE_CXX_STANDARD` 全局设一次 | 所有 target 被同一个值绑住,无法按组件降档 |
50
+ | 在 `CMAKE_CXX_FLAGS` 里手写 `-std=c++23` | 与 target 属性冲突,数据库不可信 |
51
+ | 把 `cxx_std_23` 写成 `cxx_std_2023` | meta-feature 名字错误,configure 失败 |
52
+
53
+ ## 验证
54
+
55
+ configure 后读 `build/<compiler>/compile_commands.json`,取最后一条 `-std=`:
56
+
57
+ ```bash
58
+ grep -o -- '-std=[^ ]*' build/clang/compile_commands.json | sort -u
59
+ ```
60
+
61
+ 输出必须只有一条,且等于 `-std=c++NN`;出现 `gnu++NN`、两条不同值或没有 `-std=` 都回第 1 步。`CXX_STANDARD_REQUIRED ON` 与 `CXX_EXTENSIONS OFF` 同时存在时,主流编译器会给出精确的 `-std=c++NN`;仍以数据库为准,不靠猜。
62
+
63
+ ## 官方依据
64
+
65
+ - [`target_compile_features`](https://cmake.org/cmake/help/latest/command/target_compile_features.html):meta-feature、scope 与追加语义。
66
+ - [`CXX_STANDARD`](https://cmake.org/cmake/help/latest/prop_tgt/CXX_STANDARD.html):支持值、3.20 起支持 23、`CXX_STANDARD_REQUIRED` 与 `CXX_EXTENSIONS`。
67
+ - [`cmake-compile-features(7)`](https://cmake.org/cmake/help/latest/manual/cmake-compile-features.7.html):meta-feature 的「至少」语义与编译器默认标准。
@@ -0,0 +1,105 @@
1
+ # CMake 模块与函数
2
+
3
+ 目标:同一段构建逻辑只有一份权威,函数可导入、可校验、可测试,目标目录只表达差异。
4
+
5
+ ## 何时抽
6
+
7
+ 抽函数的条件是「已经真实复用」或「需要统一校验」,不是「看起来可以复用」:
8
+
9
+ - 至少两个 target 或两个子目录重复同一段语义,并且这段语义已经稳定。
10
+ - 标准、警告、RPATH 这类横切规则需要一处升级、全仓生效。
11
+ - 逻辑有独立失败路径,需要 `message(FATAL_ERROR)` 把输入错误挡在 configure 阶段。
12
+
13
+ 一次性三行属性、仍在频繁变化的实验代码、只服务一个 target 的私有设置不抽;等第二次复用出现再抽。
14
+
15
+ ## 文件布局
16
+
17
+ ```text
18
+ project/
19
+ CMakeLists.txt
20
+ cmake/
21
+ CompileSettings.cmake
22
+ LinkSettings.cmake
23
+ component/
24
+ CMakeLists.txt
25
+ apps/
26
+ CMakeLists.txt
27
+ ```
28
+
29
+ - 一个文件放一组语义相近的函数,不为每个函数建单独文件;文件过大或出现无关职责时再拆。
30
+ - 文件名用 UpperCamelCase,与 CMake 官方 `Modules/` 一致;按职责命名,例如 `CompileSettings.cmake`、`LinkSettings.cmake`。不用 kebab-case,也不用现在分词。
31
+ - 函数名用 `<ModuleName>_` 加 PascalCase:`CompileSettings_SetCxxStandard`、`LinkSettings_FetchGoogleTest`。前缀取模块文件名的原样大小写,复制到工程后不改名。
32
+ - 工程自己新增、不准备回流的函数才用项目前缀加 snake_case,例如 `my_project_do_something`。
33
+ - 只有项目真的需要时才建 `cmake/`;一个函数都没有时不建空目录。
34
+
35
+ ## 导入
36
+
37
+ 根 `CMakeLists.txt` 在 `project()` 之后、`add_subdirectory()` 之前:
38
+
39
+ ```cmake
40
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
41
+ include(CompileSettings)
42
+ ```
43
+
44
+ - `CMAKE_MODULE_PATH` 只在根追加一次。
45
+ - 子目录只调用函数,不重复 `include()`;重复导入靠模块里的 `include_guard(GLOBAL)` 兜底。
46
+ - 模块名与文件名一致;改名时全仓搜索旧 `include()` 与旧函数名。
47
+ - 不使用 `include()` 执行有副作用的脚本;模块只定义函数。
48
+
49
+ ## 函数契约
50
+
51
+ ```cmake
52
+ include_guard(GLOBAL)
53
+
54
+ function(CompileSettings_SetCxxStandard target visibility standard)
55
+ if(NOT TARGET ${target})
56
+ message(FATAL_ERROR "目标 ${target} 尚未创建")
57
+ endif()
58
+ # 校验 visibility 与 standard,再设置 target 属性。
59
+ endfunction()
60
+
61
+ # 同一文件的第二组函数前再放一条 #--- 分隔线。
62
+ function(CompileSettings_SetWarnings target visibility level)
63
+ # 校验 LEVEL,再调用 target_compile_options。
64
+ endfunction()
65
+ ```
66
+
67
+ 规则:
68
+
69
+ - 参数顺序稳定,调用处不靠位置猜;参数多时在注释里列出类型与取值范围。
70
+ - 先校验目标存在、可见性合法、标准在支持列表内,再调用 `target_*`。
71
+ - 失败用 `FATAL_ERROR`,消息带函数名与非法值;不要 `WARNING` 后继续。
72
+ - 只改传入 target 的属性;不写全局变量,不读环境变量,不下载依赖。`LinkSettings_FetchGoogleTest` 是唯一例外,调用点限制在测试需要它的时候。
73
+ - 函数体保持幂等:同一目标重复调用不产生两份冲突属性。
74
+
75
+ ## 契约注释
76
+
77
+ 公开函数上方写中文 `#` 契约:一句功能、空行、边界,再用 `:param` 标位置参数、`:keyword` 标关键字参数;不写 `#[[.rst:` 与 `.. command::`,那是进 CMake 手册的模块才用的标记。同一文件的函数组之间用一条 `#---` 分隔,行注释只写原因,不画 `#===...===#` 方框。完整写法见 `references/cmake.md`。
78
+
79
+ ## 测试
80
+
81
+ 模块不能只在真实工程里碰运气。建一个临时最小工程,复制模块并覆盖:
82
+
83
+ 1. 合法目标 + 合法可见性 + 每个支持的标准,configure 与构建成功。
84
+ 2. 未创建的目标,configure 失败且消息可读。
85
+ 3. 非法可见性、非法标准,configure 失败。
86
+ 4. 重复 `include()` 不报函数重定义。
87
+ 5. `compile_commands.json` 出现对应 `-std=`。
88
+
89
+ 测试完删除临时工程;不把测试用 `CMakeLists.txt` 留在仓库。真实工程里的验证由 `steps/03-check.md` 完成。
90
+
91
+ ## 迁移旧模块
92
+
93
+ 1. 先在新位置建立函数与文件,保留旧入口一个提交周期(如果项目要求兼容)。
94
+ 2. 全仓搜索旧函数名与旧 `include()`,逐个替换。
95
+ 3. 删除旧文件,更新 `docs/architecture.md` 的职责表与结构检查。
96
+ 4. 重新 configure;缓存里残留的旧模块不会自动消失,必要时删构建目录。
97
+
98
+ ## 官方依据
99
+
100
+ - [`function()`](https://cmake.org/cmake/help/latest/command/function.html):函数在调用时执行,拥有自己的作用域。
101
+ - [`include_guard()`](https://cmake.org/cmake/help/latest/command/include_guard.html):按目录或全局防止重复导入。
102
+ - [`CMAKE_MODULE_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_MODULE_PATH.html):`include()` 的模块搜索路径。
103
+ - [CMake Source Code Guide](https://github.com/Kitware/CMake/blob/master/Help/dev/source.rst):只规定 C++ 风格,没有 CMake 语言风格章节。
104
+ - [Modules/GoogleTest.cmake](https://github.com/Kitware/CMake/blob/master/Modules/GoogleTest.cmake):官方模块的括号文档块、行注释与 `#---` 函数分隔实例。
105
+ - [cpp-best-practices CompilerWarnings.cmake](https://github.com/cpp-best-practices/cmake_template/blob/main/cmake/CompilerWarnings.cmake):社区警告模块的注释与命名参数写法。
@@ -0,0 +1,53 @@
1
+ # 第 1 步:定 target 与标准
2
+
3
+ ## 产出
4
+
5
+ 一张明确的 target 图、一组 target 属性、一个与 `environment.json` 一致的语言标准。构建结果只取决于声明的组合,不取决于当前 shell 的默认值。
6
+
7
+ ## 通用情况
8
+
9
+ 先读 `environment.json` 的 `standard`,再把它映射成两层:
10
+
11
+ | `standard` | `target_compile_features` | `CXX_STANDARD` | CMake 最低版本 |
12
+ | --- | --- | --- | --- |
13
+ | `c++11` | `cxx_std_11` | `11` | 3.1 |
14
+ | `c++14` | `cxx_std_14` | `14` | 3.1 |
15
+ | `c++17` | `cxx_std_17` | `17` | 3.8 |
16
+ | `c++20` | `cxx_std_20` | `20` | 3.12 |
17
+ | `c++23` | `cxx_std_23` | `23` | 3.20 |
18
+
19
+ target 规则:
20
+
21
+ - 程序 target 固定叫 `app`;库 target 用组件名,程序链接库;不把 `main()` 编进库。
22
+ - 每个 target 自己声明 include、标准、编译选项和链接库;公共头用 `PUBLIC`,实现目录用 `PRIVATE`。
23
+ - 用 `target_include_directories`、`target_compile_features`、`target_link_libraries`;禁止全局 `include_directories()`、`add_definitions()`、`CMAKE_CXX_FLAGS`。
24
+ - 源文件显式列出;不写 `file(GLOB ...)`,新增文件时改 CMake。
25
+ - `set(CMAKE_EXPORT_COMPILE_COMMANDS ON)`;可执行文件输出到 `build/<compiler>/bin`,库到 `build/<compiler>/lib`。
26
+ - 需要直接运行构建产物的共享库程序,设 `BUILD_RPATH "$ORIGIN/../lib"`;Apple 用 `@loader_path/../lib`。
27
+ - 标准用项目 `cmake/` 里的 `CompileSettings_SetCxxStandard(target visibility c++NN)`;没有这个函数时先从 `assets/cmake/CompileSettings.cmake` 复制并导入,不要在每个 target 重复三属性。
28
+ - 警告用同一份 `CompileSettings.cmake` 里的 `CompileSettings_SetWarnings(target visibility LEVEL basic|standard|strict)`;`visibility` 用 `PRIVATE` 或 `INTERFACE`,不要在每个 target 重复 `-Wall` 列表。
29
+
30
+ ## 命令形状
31
+
32
+ ```bash
33
+ cmake -S . -B build/clang -G Ninja \
34
+ -DCMAKE_BUILD_TYPE=Debug \
35
+ -DCMAKE_CXX_COMPILER=clang++ \
36
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
37
+ ```
38
+
39
+ GCC 模式把目录和编译器换成 `build/gcc`、`g++`。一个 configure 只用一个编译器;CMake 最低 3.20,低于目标标准所需版本时先升 `cmake_minimum_required`,不要静默降标准。
40
+
41
+ ## 情况
42
+
43
+ **新工程**:工程模板仍由 `building-cpp` 提供,从 `../../building-cpp/assets/templates/projects/` 选形态,只复制目标工程缺的文件;模板里的 `{{PROJECT_NAME}}` 换成真实工程名,再按本节改 target 与标准。
44
+
45
+ **已有工程**:先读现有 `CMakeLists.txt`,找出全局设置、GLOB、输出目录和 target 命名;只改与当前任务相关的一处。大改前先让现有构建跑通,保留可回退点。
46
+
47
+ **库 + 程序**:库的公共 include 用 `PUBLIC` 传播;程序不直接包含库的 `src/`。静态库与动态库的 target 写法相同,动态库额外设运行时路径。
48
+
49
+ **测试**:确有第二个消费者时再加 `enable_testing()` 与 `add_subdirectory(tests)`;测试 target 只链接被测库的公共接口,没有测试时不加空测试。
50
+
51
+ **预设**:使用 preset 时名称固定 `clang` / `gcc`,与构建目录一致;没有 preset 时直接用上面的命令,不要两套并存。
52
+
53
+ 下一步:target 图稳定后,进 `steps/02-modules.md` 决定哪些片段抽成函数。
@@ -0,0 +1,37 @@
1
+ # 第 2 步:定模块与函数
2
+
3
+ ## 产出
4
+
5
+ 一个可以重复导入的 `cmake/` 模块,或一份明确不抽函数的理由。模块只定义函数,目标目录不再复制同一段配置。
6
+
7
+ ## 什么时候抽
8
+
9
+ - 同一段 CMake 在至少两个 target 或两个子目录重复出现,且语义已经稳定。
10
+ - 标准、警告、RPATH 这类横切规则需要一处升级、全仓生效。
11
+ - 逻辑有独立失败路径,需要 `message(FATAL_ERROR)` 把非法输入挡在 configure 阶段。
12
+
13
+ 一次性三行属性、仍在频繁变化的实验片段、只服务一个 target 的私有设置不抽;等第二次真实复用再抽。
14
+
15
+ ## 文件与命名
16
+
17
+ - 一个文件放一组语义相近的函数,不为每个函数建单独文件;文件过大或出现无关职责时再拆。
18
+ - 文件名用 UpperCamelCase,按职责命名,例如 `CompileSettings.cmake`、`LinkSettings.cmake`。
19
+ - 函数名用 `<ModuleName>_` 加 PascalCase,例如 `CompileSettings_SetCxxStandard`;前缀取模块文件名的原样大小写,复制到工程后不改名。
20
+ - 工程自己新增、不准备回流的函数才用项目前缀加 snake_case。
21
+ - 模块开头写 `include_guard(GLOBAL)`,避免重复导入时重定义函数。
22
+
23
+ ## 导入
24
+
25
+ 根 `CMakeLists.txt` 在 `project()` 之后、`add_subdirectory()` 之前追加一次 `CMAKE_MODULE_PATH`,再 `include()` 需要的模块。子目录只调用函数,不再次导入。
26
+
27
+ ## 契约
28
+
29
+ - 公开函数上方写中文 `#` 契约:一句功能、空行、边界,再用 `:param` / `:keyword` 标参数;不写 `#[[.rst:` 与 `.. command::`。
30
+ - 完整写法见 `references/cmake.md`;签名留在 `function()` 那一行。
31
+ - 签名、警告等级与错误消息以 `assets/cmake/` 和 `references/modules.md` 为准;本步不复制第二份。
32
+ - 普通函数只改传入 target 的属性,不写全局变量、不读环境变量、不下载依赖;唯一例外是 `LinkSettings_FetchGoogleTest`,只在测试需要时调用。
33
+ - 先校验 target、可见性与标准,再调用 `target_*` 或 `set_target_properties`;失败用 `FATAL_ERROR`,消息带函数名与非法值。
34
+
35
+ ## 退出
36
+
37
+ 模块可被根文件导入且函数签名稳定后,进 `steps/03-check.md` 跑 configure、构建与数据库核对。排版、2 空格与注释形态按 `references/cmake.md`。
@@ -0,0 +1,39 @@
1
+ # 第 3 步:通过检查
2
+
3
+ ## 产出
4
+
5
+ 一次干净的 configure、一次构建、一份与 `environment.json` 一致的编译数据库。检查只回答「target 契约是否成立」,完整的格式化、静态检查、CTest 与运行由 `building-cpp` 的通过检查接手。
6
+
7
+ ## 命令
8
+
9
+ ```bash
10
+ cmake -S . -B build/clang -G Ninja \
11
+ -DCMAKE_BUILD_TYPE=Debug \
12
+ -DCMAKE_CXX_COMPILER=clang++ \
13
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
14
+ cmake --build build/clang
15
+ ```
16
+
17
+ GCC 模式把目录和编译器换成 `build/gcc`、`g++`。每次检查都重新 configure,不依赖上一次缓存;同一构建目录只放同一编译器的配置。
18
+
19
+ ## 核对项
20
+
21
+ - **target 图**:`app` 链接库,库的公共 include 用 `PUBLIC`;没有把 `main()` 编进库。
22
+ - **标准**:读 `build/clang/compile_commands.json` 的最后一个 `-std=`,必须与 `environment.json` 的 `standard` 一致,不能是 `gnu++NN` 或更高默认标准。
23
+ - **没有全局污染**:全仓搜索 `include_directories`、`add_definitions`、`CMAKE_CXX_FLAGS`、`file(GLOB`,命中就回第 1 步。
24
+ - **模块导入**:根 `CMakeLists.txt` 只导入一次;函数名与文件名可追溯;删除旧模块后没有残留 `include()`。
25
+ - **排版**:`CMakeLists.txt` 与 `*.cmake` 无 tab、2 空格缩进、UTF-8 无 BOM、LF;抽查 80 列以上的行是否有必要。
26
+ - **产物位置**:可执行文件在 `build/<compiler>/bin`,库在 `build/<compiler>/lib`;源码目录没有新增构建产物。
27
+
28
+ ## 失败分流
29
+
30
+ | 现象 | 回哪一步 |
31
+ | --- | --- |
32
+ | 找不到 target、链接失败、include 路径不对 | 第 1 步 |
33
+ | 目标标准没有生效、`-std` 与声明不一致 | 第 1 步,必要时重读 `references/cxx-standard.md` |
34
+ | 函数参数没校验、模块重复导入、旧模块残留 | 第 2 步 |
35
+ | 只是 clang-format、clang-tidy、CTest 或运行失败 | 转 `building-cpp` 的通过检查 |
36
+
37
+ ## 边界
38
+
39
+ 本步不替代 `building-cpp`:不安装工具链,不改 `running-build.sh`,不跑 `clang-tidy` 或 CTest。检查通过的判据是 configure 与构建成功、数据库标准正确、结构规则无命中;完整的工具链检查仍由 `building-cpp` 的通过检查给出。
@@ -9,7 +9,7 @@ metadata:
9
9
 
10
10
  ## 适用场景
11
11
 
12
- 管 `.h` / `.hpp` / `.cpp` / `.cc` 里换一台机器也不会改的内容:可观察行为、库与程序的边界、声明、命名、异常和源码注释。文件槽位与守卫以 `references/file-template.md` 为准;排版、命名、头文件与包含顺序以 `references/google-cpp-style.md` 的 Google 基线为准。异常条文采用 C++ Core Guidelines 的 Error handling,作为开发阶段的源码指导;它不进入插件运行时。`CMakeLists.txt`、`cmake/`、`running-build.sh`、`.clang-*`、`.vscode/` 和 `environment.json` 只开 `building-cpp`。只改函数体、命名、异常或注释且入口命令可运行时,定实现后只开 `building-cpp` 的「通过检查」;新增或移动源文件、改库与程序的边界、补测试 target 时,声明和实现可以先写,但完成前必须开 `building-cpp` 的定构建,把文件写进 CMake,再决定是否定工具配置,最后通过检查。`.qmd`、仓库 README、Python 和非 C++ 文档不打开本技能。
12
+ 管 `.h` / `.hpp` / `.cpp` / `.cc` 里换一台机器也不会改的内容:可观察行为、库与程序的边界、声明、命名、异常和源码注释。文件槽位与守卫以 `references/file-template.md` 为准;排版、命名、头文件与包含顺序以 `references/google-cpp-style.md` 的 Google 基线为准。异常条文采用 C++ Core Guidelines 的 Error handling,作为开发阶段的源码指导;它不进入插件运行时。`CMakeLists.txt` 与 `cmake/` 开 `configuring-cmake`;`running-build.sh`、`.clang-*`、`.vscode/` 和 `environment.json` 开 `building-cpp`。只改函数体、命名、异常或注释且入口命令可运行时,定实现后只开 `building-cpp` 的「通过检查」;新增或移动源文件、改库与程序的边界、补测试 target 时,声明和实现可以先写,但完成前必须开 `configuring-cmake` 把文件写进 CMake,再开 `building-cpp` 定工具配置并通过检查。`.qmd`、仓库 README、Python 和非 C++ 文档不打开本技能。
13
13
 
14
14
  存在项目技能 `cpp-workflow-project` 时先读它的 `SKILL.md`:工程名、库与程序的边界、阶段表、术语增补、入口命令。没有项目技能时门禁不会放出本技能,不要猜项目约定。
15
15