@chengzhao-dev/dsh-cpp-workflow 0.0.0-stage → 0.1.1
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.
- package/LICENSE +21 -0
- package/README.en.md +106 -0
- package/README.md +105 -2
- package/cordis.patch.yml +11 -0
- package/icon.svg +7 -0
- package/lib/index.js +203 -0
- package/lib/workspace.js +168 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +80 -4
- package/skills/building-cpp/SKILL.md +44 -0
- package/skills/building-cpp/assets/cmake/applying-warnings.cmake +10 -0
- package/skills/building-cpp/assets/cmake/fetching-googletest.cmake +15 -0
- package/skills/building-cpp/assets/cmake/setting-rpath.cmake +8 -0
- package/skills/building-cpp/assets/config/.clang-format +40 -0
- package/skills/building-cpp/assets/config/.clang-tidy +92 -0
- package/skills/building-cpp/assets/config/.clangd +12 -0
- package/skills/building-cpp/assets/config/.vscode/extensions.json +6 -0
- package/skills/building-cpp/assets/config/.vscode/settings.json +34 -0
- package/skills/building-cpp/assets/templates/projects/common/running-build.sh +152 -0
- package/skills/building-cpp/assets/templates/projects/multi/CMakeLists.txt +18 -0
- package/skills/building-cpp/assets/templates/projects/multi/include/greeting.h +10 -0
- package/skills/building-cpp/assets/templates/projects/multi/src/greeting.cpp +13 -0
- package/skills/building-cpp/assets/templates/projects/multi/src/main.cpp +8 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/CMakeLists.txt +24 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/CMakeLists.txt +5 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/include/greeting.h +10 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/greeting/src/greeting.cpp +13 -0
- package/skills/building-cpp/assets/templates/projects/shared-library/src/main.cpp +8 -0
- package/skills/building-cpp/assets/templates/projects/single/CMakeLists.txt +14 -0
- package/skills/building-cpp/assets/templates/projects/single/src/main.cpp +9 -0
- package/skills/building-cpp/assets/templates/projects/static-library/CMakeLists.txt +18 -0
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/CMakeLists.txt +5 -0
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/include/greeting.h +10 -0
- package/skills/building-cpp/assets/templates/projects/static-library/greeting/src/greeting.cpp +13 -0
- package/skills/building-cpp/assets/templates/projects/static-library/src/main.cpp +8 -0
- package/skills/building-cpp/examples/cmake.md +150 -0
- package/skills/building-cpp/examples/environment.md +125 -0
- package/skills/building-cpp/examples/tooling.md +155 -0
- package/skills/building-cpp/references/build-script.md +63 -0
- package/skills/building-cpp/references/cmake.md +168 -0
- package/skills/building-cpp/references/environment.md +104 -0
- package/skills/building-cpp/references/linking.md +101 -0
- package/skills/building-cpp/references/static-analysis.md +136 -0
- package/skills/building-cpp/scripts/checking-environment.mjs +498 -0
- package/skills/building-cpp/steps/01-build.md +76 -0
- package/skills/building-cpp/steps/02-tooling.md +41 -0
- package/skills/building-cpp/steps/03-check.md +60 -0
- package/skills/developing-cpp/SKILL.md +42 -0
- package/skills/developing-cpp/examples/comments.md +231 -0
- package/skills/developing-cpp/examples/exceptions.md +134 -0
- package/skills/developing-cpp/examples/naming.md +157 -0
- package/skills/developing-cpp/examples/structure.md +79 -0
- package/skills/developing-cpp/references/comments.md +123 -0
- package/skills/developing-cpp/references/declaration-order.md +97 -0
- package/skills/developing-cpp/references/exceptions.md +79 -0
- package/skills/developing-cpp/references/glossary.md +88 -0
- package/skills/developing-cpp/references/google-cpp-style.md +109 -0
- package/skills/developing-cpp/references/language.md +119 -0
- package/skills/developing-cpp/references/layout.md +73 -0
- package/skills/developing-cpp/references/naming.md +62 -0
- package/skills/developing-cpp/references/performance.md +82 -0
- package/skills/developing-cpp/references/project-design.md +79 -0
- package/skills/developing-cpp/references/standard-library.md +100 -0
- package/skills/developing-cpp/references/types.md +147 -0
- package/skills/developing-cpp/steps/01-behavior.md +36 -0
- package/skills/developing-cpp/steps/02-boundary.md +40 -0
- package/skills/developing-cpp/steps/03-declaration.md +32 -0
- package/skills/developing-cpp/steps/04-implementation.md +33 -0
- package/templates/cpp-workflow-project/SKILL.md +49 -0
- package/templates/cpp-workflow-project/environment.json +5 -0
- package/templates/cpp-workflow-project/stages/stage-table.md +4 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# CMake
|
|
2
|
+
|
|
3
|
+
目标:一个编译器、Ninja、target-based 规则、可用的编译数据库。下面是最小形状和常见边界;模板在 `assets/templates/projects/`。
|
|
4
|
+
|
|
5
|
+
## 最小可用形状
|
|
6
|
+
|
|
7
|
+
### 单文件
|
|
8
|
+
|
|
9
|
+
```cmake
|
|
10
|
+
cmake_minimum_required(VERSION 3.20)
|
|
11
|
+
project({{PROJECT_NAME}} VERSION 0.1.0 LANGUAGES CXX)
|
|
12
|
+
|
|
13
|
+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
14
|
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
|
15
|
+
|
|
16
|
+
add_executable(app src/main.cpp)
|
|
17
|
+
target_compile_features(app PRIVATE cxx_std_20)
|
|
18
|
+
set_target_properties(app PROPERTIES CXX_EXTENSIONS OFF)
|
|
19
|
+
target_compile_options(app PRIVATE -Wall -Wextra -Werror)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 库 + 程序
|
|
23
|
+
|
|
24
|
+
```cmake
|
|
25
|
+
cmake_minimum_required(VERSION 3.20)
|
|
26
|
+
project({{PROJECT_NAME}} VERSION 0.1.0 LANGUAGES CXX)
|
|
27
|
+
|
|
28
|
+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
29
|
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
|
30
|
+
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
|
31
|
+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
|
32
|
+
|
|
33
|
+
add_subdirectory(greeting)
|
|
34
|
+
|
|
35
|
+
add_executable(app src/main.cpp)
|
|
36
|
+
target_compile_features(app PRIVATE cxx_std_20)
|
|
37
|
+
set_target_properties(app PROPERTIES CXX_EXTENSIONS OFF)
|
|
38
|
+
target_link_libraries(app PRIVATE greeting)
|
|
39
|
+
target_compile_options(app PRIVATE -Wall -Wextra -Werror)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```cmake
|
|
43
|
+
# greeting/CMakeLists.txt
|
|
44
|
+
add_library(greeting STATIC src/greeting.cpp)
|
|
45
|
+
target_include_directories(greeting PUBLIC include)
|
|
46
|
+
target_compile_features(greeting PUBLIC cxx_std_20)
|
|
47
|
+
set_target_properties(greeting PROPERTIES CXX_EXTENSIONS OFF)
|
|
48
|
+
target_compile_options(greeting PRIVATE -Wall -Wextra -Werror -Wold-style-cast -Wshadow)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- 库的 `PUBLIC include` 让程序能 `#include "greeting.h"`,不需要程序再写 include 路径。
|
|
52
|
+
- `cxx_std_20` 用 `PUBLIC` 从库传播;程序自己再加一次也无害。
|
|
53
|
+
- `CXX_EXTENSIONS OFF` 把 `-std=gnu++20` 收成 `-std=c++20`,与 Google 的「不用非标准扩展」一致;每个 target 都要设。
|
|
54
|
+
- 程序 target 固定 `app`;库 target 用组件名。
|
|
55
|
+
|
|
56
|
+
## target-based 规则
|
|
57
|
+
|
|
58
|
+
- 每个 target 自己声明 include、标准、编译选项、链接库;不要用全局 `include_directories()`、`add_definitions()`、`CMAKE_CXX_FLAGS` 给所有 target 加料。
|
|
59
|
+
- `target_include_directories(<target> PUBLIC|PRIVATE <dir>)`:公共头给 `PUBLIC`,私有实现目录给 `PRIVATE`。
|
|
60
|
+
- `target_compile_features(<target> PUBLIC cxx_std_20)`:标准是 target 属性,不是全局开关。
|
|
61
|
+
- `target_link_libraries(<target> PRIVATE|PUBLIC <lib>)`:实现依赖用 `PRIVATE`,接口依赖用 `PUBLIC`。
|
|
62
|
+
- 不要 `file(GLOB ...)`;显式列源文件,新增文件时改 CMake,构建系统才知道。`CONFIGURE_DEPENDS` 依赖构建系统每次检查目录,跨生成器行为也不完全一致,不作为工程规则。
|
|
63
|
+
- 不把 `main()` 编进库;程序只做装配和 I/O。
|
|
64
|
+
- 头文件不参与编译时,不需要 `target_sources` 列出;需要 IDE 展示时再列。
|
|
65
|
+
- 组件多起来时,一个组件一个 `CMakeLists.txt`:库放 `<component>/include` 与 `<component>/src`,程序放 `apps/include` 与 `apps/src`;`apps/include` 只给程序内部用,不要被库 `PUBLIC` 导出。
|
|
66
|
+
- 测试 target 等出现第二个消费者时再加,不要为单个 `main()` 提前引入测试框架;测试只链接库的公共接口。
|
|
67
|
+
|
|
68
|
+
## 工程名
|
|
69
|
+
|
|
70
|
+
`project()` 的名字用 PascalCase,目录名用 kebab-case:
|
|
71
|
+
|
|
72
|
+
```cmake
|
|
73
|
+
project(TaskList VERSION 0.1.0 LANGUAGES CXX)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
目录 `task-list/` 对应 `project(TaskList ...)`。派生规则是「按 `-` 切分、每段首字母大写、其余保留」,不要直接照抄目录名,也不要把 `{{PROJECT_NAME}}` 留在模板里。库 target 用组件名的小写形式(`task_list` 或 `tasks`),程序 target 固定 `app`。
|
|
77
|
+
|
|
78
|
+
## 编译数据库
|
|
79
|
+
|
|
80
|
+
```cmake
|
|
81
|
+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- 只对 Makefile 和 Ninja 生成器生效;本项目固定 Ninja。
|
|
85
|
+
- 生成位置是构建目录根部:`build/<compiler>/compile_commands.json`。
|
|
86
|
+
- 构建脚本把它链接到 `build/compile_commands.json`;clangd 默认读这个路径,clang-tidy 用 `-p build/<compiler>`。
|
|
87
|
+
- 换编译器时,旧目录的数据库不能复用;清理或换目录。
|
|
88
|
+
|
|
89
|
+
## 输出目录
|
|
90
|
+
|
|
91
|
+
```cmake
|
|
92
|
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
|
93
|
+
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
|
94
|
+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- 可执行文件统一到 `build/<compiler>/bin`;库到 `build/<compiler>/lib`。
|
|
98
|
+
- 不要输出到源码目录;`git status` 不应出现构建产物。
|
|
99
|
+
- 运行程序时从 `build/<compiler>/bin` 启动,或由脚本设置工作目录。
|
|
100
|
+
|
|
101
|
+
## 共享库运行时路径
|
|
102
|
+
|
|
103
|
+
```cmake
|
|
104
|
+
if(APPLE)
|
|
105
|
+
set_target_properties(app PROPERTIES BUILD_RPATH "@loader_path/../lib")
|
|
106
|
+
else()
|
|
107
|
+
set_target_properties(app PROPERTIES BUILD_RPATH "$ORIGIN/../lib")
|
|
108
|
+
endif()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- 只对需要直接运行构建产物的程序设置;安装规则另算。
|
|
112
|
+
- 不要用全局 `LD_LIBRARY_PATH` 包装所有命令;共享库的运行时路径由 target 的 RPATH 设置,不在脚本里全局改寻库路径。
|
|
113
|
+
- `$ORIGIN` 在 CMake 字符串里要转义成 `\$ORIGIN` 吗?在双引号里 `$ORIGIN` 会被 CMake 当变量;写成 `"$ORIGIN/../lib"` 在 CMake 中 `$ORIGIN` 不是 `${...}`,不会被展开,保持字面量。若遇到展开问题,用 `"\$ORIGIN/../lib"`。
|
|
114
|
+
|
|
115
|
+
## 标准库与编译器一致
|
|
116
|
+
|
|
117
|
+
- 一个构建树只用一个 C++ 标准库实现。Clang 可以配 libstdc++(Linux 默认)或 libc++,但 `clang++`、`clang-tidy`、`clangd` 和链接进同一个程序的库必须一致。
|
|
118
|
+
- `-stdlib=libc++` 是编译和链接都要的选项,不要只加在编译选项上;也不要全局 `add_compile_options`,按 target 传,或由工具链文件统一设置。
|
|
119
|
+
- 混用 libstdc++ 与 libc++ 的典型症状是链接期大量未定义符号、`std::string` ABI 不匹配,或运行期崩溃;先查 `clang++ -v` 与 `ldd`,不要靠加 `-lstdc++` 掩盖。
|
|
120
|
+
- 项目想固定 libc++ 时,在 CMake 工具链文件或项目自己的编译选项里写清,不要只写在某个人的脚本里;`environment.json` 只保留 `host`、`build_host`、`compiler` 三个轴,不放 libc++。换机器后必须能复现同一组合。
|
|
121
|
+
- 本插件默认使用目标平台默认标准库;只有项目明确要求 libc++ 时才切换,并保证整套工具一致。
|
|
122
|
+
|
|
123
|
+
## 警告与静态检查
|
|
124
|
+
|
|
125
|
+
- 警告选项统一:`-Wall -Wextra -Werror`;库额外 `-Wold-style-cast -Wshadow`。
|
|
126
|
+
- 不要把警告选项加到全局 `CMAKE_CXX_FLAGS`;每个 target 显式加,或用一个函数统一施加。
|
|
127
|
+
- `clang-tidy` 在构建后单独跑,不挂进 CMake 的编译命令;这样编译数据库稳定,诊断可复现。
|
|
128
|
+
- 需要 `clang-tidy` 集成时用 `CMAKE_CXX_CLANG_TIDY`,但本项目默认不启用,避免构建与检查耦合。
|
|
129
|
+
|
|
130
|
+
## GoogleTest
|
|
131
|
+
|
|
132
|
+
需要测试框架时,用固定版本的 `FetchContent`:
|
|
133
|
+
|
|
134
|
+
```cmake
|
|
135
|
+
include(FetchContent)
|
|
136
|
+
FetchContent_Declare(
|
|
137
|
+
googletest
|
|
138
|
+
URL https://codeload.github.com/google/googletest/tar.gz/refs/tags/v1.17.0
|
|
139
|
+
URL_HASH SHA256=65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c
|
|
140
|
+
)
|
|
141
|
+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
142
|
+
FetchContent_MakeAvailable(googletest)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- 版本固定,不用 `master`;URL 和哈希写在一起,避免供应链漂移。
|
|
146
|
+
- 只在 `tests/` 需要时拉取;库和程序不依赖测试框架。
|
|
147
|
+
- 没有网络时,项目可以自带 vendored 副本;不要构建时静默跳过测试。
|
|
148
|
+
|
|
149
|
+
## 模板选择
|
|
150
|
+
|
|
151
|
+
| 模板 | 何时用 | 关键文件 |
|
|
152
|
+
| --- | --- | --- |
|
|
153
|
+
| `single` | 一个 `main.cpp`,辅助函数很少 | `CMakeLists.txt`, `src/main.cpp` |
|
|
154
|
+
| `multi` | 多个源文件,一个程序 | `CMakeLists.txt`, `include/`, `src/` |
|
|
155
|
+
| `static-library` | 库需要被测试或第二个程序链接 | 根 + `greeting/` 子目录 |
|
|
156
|
+
| `shared-library` | 需要运行时加载/替换 | 同上,`SHARED` + RPATH |
|
|
157
|
+
|
|
158
|
+
模板里的 `{{PROJECT_NAME}}` 必须替换;不要留下占位符。
|
|
159
|
+
|
|
160
|
+
## 反模式
|
|
161
|
+
|
|
162
|
+
- 全局 `include_directories(include)`:所有 target 共享,边界消失。
|
|
163
|
+
- `file(GLOB sources "src/*.cpp")`:新增文件不触发重新配置。
|
|
164
|
+
- 把库和 `main()` 编进同一个 target:测试无法只链接库。
|
|
165
|
+
- 在源码目录生成 `compile_commands.json` 或可执行文件。
|
|
166
|
+
- 同时设 `CMAKE_CXX_COMPILER` 和 `CXX` 环境变量,互相覆盖。
|
|
167
|
+
- 在 `CMakeLists.txt` 里 `execute_process` 下载依赖或改源码。
|
|
168
|
+
- 用 `CMAKE_CXX_FLAGS` 加 `-std=c++23` 之类与声明不符的标准。
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 环境组合
|
|
2
|
+
|
|
3
|
+
环境是**声明**,不是探测结果。项目技能 `cpp-workflow-project` 的 `environment.json` 写清楚三个轴;构建规则、工具配置和检查都按这个声明执行。声明为空或组合非法时,先修声明,不要靠猜。
|
|
4
|
+
|
|
5
|
+
## 三个轴
|
|
6
|
+
|
|
7
|
+
| 轴 | 取值 | 说明 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `host` | `windows` / `linux` / `macos` | 编辑器与代理壳所在的系统 |
|
|
10
|
+
| `build_host` | `native` / `wsl` | 编译在哪执行;`wsl` 只对 `host=windows` 合法 |
|
|
11
|
+
| `compiler` | `clang` / `gcc` | 一次只配一个;不支持 MSVC、clang-cl,也不把 C 编译器单独作为一个轴 |
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"host": "windows",
|
|
16
|
+
"build_host": "wsl",
|
|
17
|
+
"compiler": "clang"
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 合法组合
|
|
22
|
+
|
|
23
|
+
| host | build_host | compiler | 声明 | 本版本能构建 |
|
|
24
|
+
| --- | --- | --- | --- | --- |
|
|
25
|
+
| windows | wsl | clang | 合法 | 是(WSL 内跑 POSIX 脚本) |
|
|
26
|
+
| windows | wsl | gcc | 合法 | 是 |
|
|
27
|
+
| windows | native | clang | 合法 | 否:缺 POSIX 壳(本版无 PowerShell 脚本) |
|
|
28
|
+
| windows | native | gcc | 合法 | 否:缺 POSIX 壳(本版无 PowerShell 脚本) |
|
|
29
|
+
| linux | native | clang | 合法 | 是 |
|
|
30
|
+
| linux | native | gcc | 合法 | 是 |
|
|
31
|
+
| macos | native | clang | 合法 | 是 |
|
|
32
|
+
| macos | native | gcc | 合法 | 是(要求真正的 GCC,不是 Apple 的 `g++` 别名) |
|
|
33
|
+
|
|
34
|
+
非法组合:
|
|
35
|
+
|
|
36
|
+
- `host` 不是三个值之一,或 `build_host` / `compiler` 不是两个值之一。
|
|
37
|
+
- 三个轴任意为空、缺失、写成数组或同时写两个值。
|
|
38
|
+
- `host=linux` / `host=macos` + `build_host=wsl`。
|
|
39
|
+
- `compiler=msvc`、`compiler=clang-cl`、`compiler=c`。
|
|
40
|
+
|
|
41
|
+
`windows` + `native` 是合法声明,但本版本**没有** PowerShell 构建脚本。`--live` 会明确报告「缺的是 shell,不是编译器」;不要为了让它通过把 `build_host` 改成 `wsl`,也不要临时写一个脚本伪装成已支持。
|
|
42
|
+
|
|
43
|
+
## 工具角色
|
|
44
|
+
|
|
45
|
+
| 角色 | clang 模式 | gcc 模式 |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| 编译器 | `clang++` | `g++`(macOS 上必须是真正的 GCC) |
|
|
48
|
+
| 格式化 | `clang-format` | `clang-format` |
|
|
49
|
+
| 静态检查 | `clang-tidy` | `clang-tidy` |
|
|
50
|
+
| 语言服务 | `clangd` | `clangd` |
|
|
51
|
+
| 构建 | `cmake` + `ninja` | `cmake` + `ninja` |
|
|
52
|
+
| 测试 | `ctest` | `ctest` |
|
|
53
|
+
| 程序 | `app` | `app` |
|
|
54
|
+
|
|
55
|
+
工具角色与编译器解耦:GCC 模式下仍用 Clang 的格式化、静态检查和语言服务。不要因为编译器是 GCC 就换成 `gcc-format` 之类不存在的工具。工程确有 C 源时,C 编译器用同一套的 `clang` 或 `gcc`,不能与 C++ 编译器分属两套;`--live` 的 compiler 角色核对的是 C++ 驱动,C 驱动缺失时由 configure 报错。
|
|
56
|
+
|
|
57
|
+
## 版本与能力
|
|
58
|
+
|
|
59
|
+
- **C++20**:编译器必须支持 `-std=c++20`;CMake ≥ 3.20(C++20 标准属性从 3.12 起可用)。
|
|
60
|
+
- **clang-format / clang-tidy**:≥ 16。`BreakAfterAttributes` 在 16 才支持,15 及以下没有该选项。
|
|
61
|
+
- **clang 模式同版本**:`clang++`、`clang-format`、`clang-tidy`、`clangd` 必须是同一 LLVM 主版本。混装 17 的 clangd 与 18 的 clang-format 会出现诊断与格式化不一致。Apple Clang 也按这条核对;若工具版本对不上,报告的是具体角色和版本,不要为了通过而改声明。
|
|
62
|
+
- **gcc 模式**:检查器不拿 GCC 的主版本去比 Clang 工具;`clang-format` / `clang-tidy` 仍要 ≥ 16,`clangd` 只要求可用。`g++` 必须是真的 GCC,不能是 Apple clang 的别名。GCC 需要自带 C++20 `<format>` 的标准库;版本过旧时先升级工具链,不要改检查器绕过。
|
|
63
|
+
- **Apple Clang**:`/usr/bin/clang++` 通常是 Apple Clang;clangd 需能经 `xcrun` 找到 SDK。不要混用 Homebrew LLVM 与 Xcode 资源目录,也不要把 Homebrew 路径写死。
|
|
64
|
+
|
|
65
|
+
## 构建目录
|
|
66
|
+
|
|
67
|
+
| compiler | 构建目录 | 编译数据库 |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| clang | `build/clang/` | `build/clang/compile_commands.json` |
|
|
70
|
+
| gcc | `build/gcc/` | `build/gcc/compile_commands.json` |
|
|
71
|
+
| 当前 | `build/` | `build/compile_commands.json` → 上面之一的符号链接 |
|
|
72
|
+
|
|
73
|
+
一次 configure 只用一个编译器;切换编译器时用另一个目录,不要在同一目录里改 `CMAKE_CXX_COMPILER` 后复用缓存。
|
|
74
|
+
|
|
75
|
+
## 活动校验
|
|
76
|
+
|
|
77
|
+
`<skill-dir>/scripts/checking-environment.mjs` 做两件事:
|
|
78
|
+
|
|
79
|
+
1. **静态**(默认):读 `environment.json`,检查三个轴和组合。轴为空或组合非法时以退出码 2 结束,不查找任何可执行文件。
|
|
80
|
+
2. **活动**(`--live`):先做声明校验,再核对当前进程是否在声明的 build host 上,然后查 PATH、角色和版本。声明本身不合法时仍以退出码 2 结束;声明合法但活动核对失败时以退出码 3 结束。
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node "<skill-dir>/scripts/checking-environment.mjs" --project . --json
|
|
84
|
+
node "<skill-dir>/scripts/checking-environment.mjs" --project . --live
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`<skill-dir>` 是安装后的 `building-cpp` 目录;脚本不复制到目标工程,也不改目标工程的 PATH。
|
|
88
|
+
|
|
89
|
+
约束:
|
|
90
|
+
|
|
91
|
+
- 只读:不安装、不下载、不改 PATH、不写项目文件。
|
|
92
|
+
- `--live` 在 `host=windows` + `build_host=native` 下直接报告「缺 POSIX 壳;本版没有 PowerShell 构建脚本」,不继续找编译器。
|
|
93
|
+
- WSL 模式要求当前进程在 WSL 内(`WSL_DISTRO_NAME` / `WSL_INTEROP` 或 `/proc/version` 含 Microsoft);只在 Linux 上但不在 WSL 时失败。
|
|
94
|
+
- macOS 上声明 `compiler=gcc` 时,`g++ --version` 若显示 Apple clang,按「不是真正的 GCC」失败。
|
|
95
|
+
- 没有 `--live` 时不做任何 PATH 探测,CI 和 Windows 开发机都能做声明校验。
|
|
96
|
+
|
|
97
|
+
## 退出码
|
|
98
|
+
|
|
99
|
+
| 码 | 含义 |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| 0 | 通过 |
|
|
102
|
+
| 2 | 声明缺失、轴为空、组合非法 |
|
|
103
|
+
| 3 | 活动校验失败:不在声明的 build host、角色缺失、版本不符 |
|
|
104
|
+
| 1 | 脚本自身错误:参数错误或内部异常 |
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# 从源文件到可执行文件
|
|
2
|
+
|
|
3
|
+
链接错误和编译错误是两类问题。先判断失败发生在哪个阶段,再改对应的文件。
|
|
4
|
+
|
|
5
|
+
## 四个阶段
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
预处理 -> 编译 -> 汇编 -> 链接 -> 加载运行
|
|
9
|
+
.i .s / .o 可执行文件 / 库
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
- 预处理:展开 `#include`、宏和条件编译,得到翻译单元。头文件找不到在这一步失败。
|
|
13
|
+
- 编译:把翻译单元变成目标文件,做语法、类型和模板检查。函数体错误在这一步失败。
|
|
14
|
+
- 汇编:把汇编文本变成目标文件;通常由编译器驱动。
|
|
15
|
+
- 链接:把多个目标文件和库合并,解析符号引用。`undefined reference` 在这一步失败。
|
|
16
|
+
- 加载:程序启动时由动态加载器找到共享库;`error while loading shared libraries` 在这一步失败。
|
|
17
|
+
|
|
18
|
+
`g++ -c file.cpp` 只编译不链接;`g++ file.o -o app` 只链接。`-E` 只看预处理,`-S` 只看汇编。
|
|
19
|
+
|
|
20
|
+
## 头文件、声明与定义
|
|
21
|
+
|
|
22
|
+
- `#include` 是文本替换,不是模块导入;头文件只放声明、`inline` 定义、模板和 `constexpr` 定义。
|
|
23
|
+
- 普通函数的定义放 `.cpp`;在头文件里写非 `inline` 函数定义,多个翻译单元包含时会重复定义。
|
|
24
|
+
- 类定义放头文件;成员函数在类外定义时写 `ClassName::`,定义放 `.cpp`。
|
|
25
|
+
- 只用到声明时不要包含完整定义;前置声明可以减少编译依赖,但不能替代需要完整类型的场景(继承、成员对象、按值返回)。
|
|
26
|
+
- 每个头文件自包含:单独编译一个只包含该头文件的 `.cpp` 应通过。用 `#pragma once` 防止重复包含。
|
|
27
|
+
|
|
28
|
+
## `undefined reference` 分诊
|
|
29
|
+
|
|
30
|
+
| 现象 | 常见原因 | 修法 |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| 链接器说某个成员函数未定义 | 声明了但没写定义,或 `.cpp` 没加入 target | 补定义,或把源文件加入 `target_sources` |
|
|
33
|
+
| 只有某个平台失败 | 条件编译把定义排除了 | 检查宏和平台分支,保证每个平台都有定义 |
|
|
34
|
+
| 第三方库符号缺失 | 没链接库,或库顺序不对 | `target_link_libraries` 加库;CMake 会处理顺序 |
|
|
35
|
+
| C++ 符号被当成 C 符号 | 头文件缺少 `extern \"C\"` | C 接口头加 `extern \"C\"`,C++ 调用方也可见 |
|
|
36
|
+
| 模板成员未定义 | 模板定义在 `.cpp`,实例化点看不到 | 模板定义移到头文件,或显式实例化 |
|
|
37
|
+
| 静态库中的目标文件没被拉入 | 链接器只取被引用符号所在的目标文件 | 确认符号确实被引用,或改用对象库/共享库 |
|
|
38
|
+
|
|
39
|
+
先看链接器报的完整符号名和来源文件,不要只看第一行。
|
|
40
|
+
|
|
41
|
+
## 静态库与共享库
|
|
42
|
+
|
|
43
|
+
| 形式 | 产物 | 链接时机 | 运行期依赖 | 适用 |
|
|
44
|
+
| --- | --- | --- | --- | --- |
|
|
45
|
+
| 静态库 | `libname.a` | 链接时复制所需目标文件 | 无 | 分发简单、启动快、库小 |
|
|
46
|
+
| 共享库 | `libname.so` / `.dylib` / `.dll` | 链接时只记录依赖 | 运行时要能找到库 | 多程序共享、插件、独立升级 |
|
|
47
|
+
|
|
48
|
+
- 静态库不是“把源码打包”;它仍是多个目标文件的归档,链接器按需取用。
|
|
49
|
+
- 共享库的符号默认可能被导出;需要隐藏内部符号时设置可见性(`-fvisibility=hidden` + 显式导出)。
|
|
50
|
+
- 共享库的 ABI 包括类布局、虚表、标准库类型和编译器选项;不同编译器或 `-stdlib` 混用可能链接成功但运行崩溃。
|
|
51
|
+
- 一个项目里不要同时链接两套标准库(libstdc++ 与 libc++);编译器、clangd、库和可执行文件必须一致。
|
|
52
|
+
|
|
53
|
+
## 输出目录与 RPATH
|
|
54
|
+
|
|
55
|
+
推荐布局:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
build/<compiler>/
|
|
59
|
+
bin/app
|
|
60
|
+
lib/libgame.so
|
|
61
|
+
compile_commands.json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- 可执行文件在 `bin/`,共享库在 `lib/`;不要把库和可执行文件混在同一层。
|
|
65
|
+
- 运行时动态加载器默认不搜索可执行文件旁边的 `lib/`;需要 RPATH:
|
|
66
|
+
- Linux:`-Wl,-rpath,'$ORIGIN/../lib'`
|
|
67
|
+
- macOS:`-Wl,-rpath,@loader_path/../lib`
|
|
68
|
+
- CMake 用 `BUILD_RPATH` / `INSTALL_RPATH` 设置,不要手工拼链接命令;见 `assets/cmake/setting-rpath.cmake`。
|
|
69
|
+
- RPATH 里的 `$ORIGIN` 要防止被 shell 展开:CMake 字符串里写 `\$ORIGIN` 或使用 `BUILD_RPATH_USE_ORIGIN`。
|
|
70
|
+
|
|
71
|
+
## 验收:`file` 与 `ldd`
|
|
72
|
+
|
|
73
|
+
链接成功不等于能运行。构建后检查产物:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
file build/clang/bin/app
|
|
77
|
+
ldd build/clang/bin/app
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- `file` 确认架构、位数、是否动态链接。
|
|
81
|
+
- `ldd` 确认每个共享库都能解析到实际路径;出现 `not found` 就是加载期会失败。
|
|
82
|
+
- 运行 `app` 做一次最小启动;动态库缺失会在启动时暴露,而不是链接时。
|
|
83
|
+
- macOS 用 `otool -L` 代替 `ldd`;Windows 用 `dumpbin /dependents`。
|
|
84
|
+
|
|
85
|
+
## 构建脚本里的顺序
|
|
86
|
+
|
|
87
|
+
1. 配置:CMake 生成构建系统,导出编译数据库。
|
|
88
|
+
2. 编译 + 链接:构建所有 target。
|
|
89
|
+
3. 产物检查:确认 `bin/app`、`lib/`、编译数据库存在。
|
|
90
|
+
4. 运行:用项目入口命令启动,检查退出码和输出。
|
|
91
|
+
5. 测试:`ctest` 跑注册的测试。
|
|
92
|
+
|
|
93
|
+
不要用“链接成功”代替“运行成功”;也不要用“测试通过”代替“产物存在”。
|
|
94
|
+
|
|
95
|
+
## 检查清单
|
|
96
|
+
|
|
97
|
+
- [ ] 头文件自包含,普通函数定义不在头文件。
|
|
98
|
+
- [ ] 每个声明都有定义,且定义所在源文件加入 target。
|
|
99
|
+
- [ ] 静态库/共享库选择有理由,没有混用两套标准库。
|
|
100
|
+
- [ ] 产物在 `bin/` 与 `lib/`,RPATH 指向相对位置。
|
|
101
|
+
- [ ] `file` 与 `ldd` 检查过,程序能最小启动。
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# 静态分析与语言服务
|
|
2
|
+
|
|
3
|
+
三件工具各管一段:`clang-format` 排版、`clang-tidy` 诊断、`clangd` 提供补全/跳转/诊断。它们都读同一份编译数据库,但职责不重叠。
|
|
4
|
+
|
|
5
|
+
## clang-format
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
clang-format --dry-run --Werror src/*.cpp include/*.h tests/*.cpp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- 检查用 `--dry-run --Werror`;需要自动修复时单独跑 `clang-format -i`,不要在构建脚本里悄悄改源码。
|
|
12
|
+
- 工程必须有根 `.clang-format`;缺失时先复制配置,不要回退到内置风格,回退会掩盖配置缺失。
|
|
13
|
+
- 只扫描项目源码:`src/`、`include/`、`apps/`、`tests/` 等实际存在的目录;不扫描 `build/`、第三方、生成文件。
|
|
14
|
+
- 配置文件在工程根 `.clang-format`;不要依赖用户主目录或编辑器内置配置。
|
|
15
|
+
- `ColumnLimit: 100`、`Standard: c++20`、`BasedOnStyle: Google`;`IncludeCategories` 维持包含顺序。
|
|
16
|
+
- 格式化失败就是失败,不要用 `// clang-format off` 绕过;确需关闭时只包住无法排版的宏或表格,并写一句理由。
|
|
17
|
+
|
|
18
|
+
## clang-tidy
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
clang-tidy -p build/clang \
|
|
22
|
+
--header-filter="^$(pwd -P)/(include|src|apps|tests)/" \
|
|
23
|
+
--warnings-as-errors='*' \
|
|
24
|
+
src/*.cpp apps/*.cpp tests/*.cpp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `-p` 指向当前编译器的构建目录,读取 `compile_commands.json`。
|
|
28
|
+
- `--header-filter` 控制哪些头文件的诊断被显示,不改变 clang-tidy 实际解析哪些头;模板实例化可能让一条根因产生多条警告,警告条数不等于缺陷条数。按根因修,不要按条数修。
|
|
29
|
+
- `--header-filter` 用项目头目录的白名单,不要用 `.*` 把系统头和第三方头一并纳入。clang-tidy 按绝对路径匹配,所以上面用 `$(pwd -P)` 前缀;上面是常见目录,工程用别的顶层目录时按实际替换。`--exclude-header-filter` 到 clang-tidy 19 才加入,16/17/18 都没有,不要依赖它。
|
|
30
|
+
- 系统头默认被排除,不要加 `--system-headers`。
|
|
31
|
+
- 只扫描项目源文件;不要把 `build/` 或第三方源码传给 clang-tidy。
|
|
32
|
+
- `--warnings-as-errors='*'` 把警告当失败;修代码,不要新增 `NOLINT`。确需抑制时写 `// NOLINT(检查名): 理由`,理由必须具体。
|
|
33
|
+
- 配置在工程根 `.clang-tidy`;关闭项逐条写理由,不整族关闭。`cppcoreguidelines-pro-bounds-constant-array-index` 不能整族关闭;优先把下标换成 `at()` 或带边界检查的辅助函数,并补负向越界测试。
|
|
34
|
+
- 没有编译数据库时先回到 `building-cpp` 的定构建生成;不要用 `--` 手工拼编译参数绕过。
|
|
35
|
+
- `clang-tidy` 的诊断可能指向头文件;修头文件里的声明或实现,不要只改调用点。
|
|
36
|
+
|
|
37
|
+
## clangd
|
|
38
|
+
|
|
39
|
+
`.clangd`:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
CompileFlags:
|
|
43
|
+
CompilationDatabase: build/clang
|
|
44
|
+
Add:
|
|
45
|
+
- -std=c++20
|
|
46
|
+
- -Wall
|
|
47
|
+
- -Wextra
|
|
48
|
+
Diagnostics:
|
|
49
|
+
Suppress:
|
|
50
|
+
- pp_file_not_found
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `CompilationDatabase` 指向当前编译器目录;GCC 模式改 `build/gcc`。
|
|
54
|
+
- `Add` 只补编译数据库里没有的公共选项;不要重复 `-I` 项目 include(数据库里已有)。
|
|
55
|
+
- 静态检查规则只在 `.clang-tidy` 里维护;clangd 读取同一份配置,不要在这里复制检查列表。
|
|
56
|
+
- `Suppress` 只放确实无法避免的解析噪声(如 `pp_file_not_found`),并写理由。
|
|
57
|
+
|
|
58
|
+
`--query-driver`:
|
|
59
|
+
|
|
60
|
+
- 它是 clangd 的命令行参数,不是 `.clangd` 的 YAML 键。
|
|
61
|
+
- 作用:允许 clangd 执行编译器驱动,读取系统头路径;只对编译数据库里出现的驱动生效。
|
|
62
|
+
- 写在 `.vscode/settings.json`:
|
|
63
|
+
|
|
64
|
+
```jsonc
|
|
65
|
+
{
|
|
66
|
+
"clangd.arguments": [
|
|
67
|
+
"--query-driver=/usr/bin/clang++",
|
|
68
|
+
"--header-insertion=never",
|
|
69
|
+
"--background-index"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- 路径用绝对路径;多个驱动用逗号分隔。GCC 模式指向 `/usr/bin/g++`。
|
|
75
|
+
- 不要写 `--query-driver=*` 或宽泛的 glob;这等于允许 clangd 执行任意匹配的驱动。
|
|
76
|
+
- 如果编译数据库里的编译器是相对路径,clangd 可能拒绝执行;用绝对路径重新 configure。
|
|
77
|
+
- `--query-driver` 只影响 clangd,不影响 clang-tidy;tidy 用编译数据库里的命令。
|
|
78
|
+
|
|
79
|
+
## 编辑器
|
|
80
|
+
|
|
81
|
+
`.vscode/extensions.json` 推荐:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"recommendations": ["llvm-vs-code-extensions.vscode-clangd", "ms-vscode.cmake-tools"]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`.vscode/settings.json` 固定:
|
|
90
|
+
|
|
91
|
+
```jsonc
|
|
92
|
+
{
|
|
93
|
+
"clangd.arguments": [
|
|
94
|
+
"--query-driver=/usr/bin/clang++",
|
|
95
|
+
"--header-insertion=never",
|
|
96
|
+
"--background-index"
|
|
97
|
+
],
|
|
98
|
+
"cmake.buildDirectory": "${workspaceFolder}/build/clang",
|
|
99
|
+
"cmake.generator": "Ninja"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- 不推荐与 clangd 冲突的 C++ 智能提示插件;只保留一个语言服务。
|
|
104
|
+
- 不把 `compile_commands.json` 提交进仓库;它是构建产物。
|
|
105
|
+
- 切换编译器时同时改 `.clangd`、`settings.json` 和构建目录;三处不一致会让诊断指向旧代码。
|
|
106
|
+
|
|
107
|
+
## 门禁分级
|
|
108
|
+
|
|
109
|
+
`--warnings-as-errors='*'` 是默认门禁。若项目必须把一组诊断先设为观察项,按检查名逐条写进项目脚本,不按 `*` 整族降级:
|
|
110
|
+
|
|
111
|
+
| 级别 | 典型检查 | 处理 |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| 失败 | `bugprone-*`、`readability-math-missing-parentheses`、`readability-identifier-naming`、项目声明的致命集 | 修代码;不允许用 NOLINT 掩盖 |
|
|
114
|
+
| 失败 | 编译诊断、链接诊断、格式检查 | 直接失败 |
|
|
115
|
+
| 观察 | 迁移期暂时无法修完的 `modernize-*` | 在项目技能里列清单、写负责人和退出条件;不默认全仓关闭 |
|
|
116
|
+
|
|
117
|
+
- 致命集宁可小、可解释,也不要把所有检查一刀切。
|
|
118
|
+
- 观察项不是免死金牌:新代码不应新增观察项,已有项只能减少。
|
|
119
|
+
- 关闭某条检查时,在 `.clang-tidy` 里写一行理由,并在项目技能里记录退出条件。
|
|
120
|
+
|
|
121
|
+
## 版本与一致性
|
|
122
|
+
|
|
123
|
+
- `clang-format` / `clang-tidy` ≥ 16;clang 模式下与 `clang++`、`clangd` 同主版本。
|
|
124
|
+
- 混版本常见症状:格式化结果与 CI 不同、clangd 诊断与 tidy 不一致、选项被静默忽略。
|
|
125
|
+
- 检查器在 `--live` 时核对版本;不要用「能跑就行」绕过。
|
|
126
|
+
|
|
127
|
+
## 失败分诊
|
|
128
|
+
|
|
129
|
+
| 现象 | 可能原因 | 处理 |
|
|
130
|
+
| --- | --- | --- |
|
|
131
|
+
| `clang-tidy` 报找不到编译数据库 | 没 configure 或路径错 | 回到 `building-cpp` 的定构建生成 `build/<compiler>/compile_commands.json` |
|
|
132
|
+
| clangd 报找不到系统头 | 编译数据库里的编译器是相对路径 | 用绝对路径重新 configure,或配 `--query-driver` |
|
|
133
|
+
| 格式化结果与 CI 不同 | 版本不同或配置不是工程根那份 | 统一版本,确认 `.clang-format` 在工程根 |
|
|
134
|
+
| tidy 报系统头 | 加了 `--system-headers` 或 header-filter 过宽 | 去掉 `--system-headers`,用项目头目录白名单 |
|
|
135
|
+
| 大量 `readability-*` | 代码风格未统一 | 回到 `developing-cpp` 的定声明 / 定实现修声明与实现,不要批量 NOLINT |
|
|
136
|
+
| clangd 与 tidy 诊断不一致 | 两者读的数据库/版本不同 | 统一 `CompilationDatabase` 与工具版本 |
|