@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.
- package/README.en.md +10 -9
- package/README.md +10 -9
- package/lib/index.js +4 -4
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +2 -2
- package/skills/building-cpp/SKILL.md +24 -11
- package/skills/building-cpp/references/build-script.md +11 -0
- package/skills/building-cpp/references/linking.md +1 -1
- package/skills/building-cpp/references/standards/README.md +1 -1
- package/skills/building-cpp/steps/01-build.md +22 -35
- package/skills/configuring-cmake/SKILL.md +57 -0
- package/skills/configuring-cmake/assets/cmake/CompileSettings.cmake +126 -0
- package/skills/configuring-cmake/assets/cmake/LinkSettings.cmake +48 -0
- package/skills/{building-cpp → configuring-cmake}/examples/cmake.md +84 -10
- package/skills/{building-cpp → configuring-cmake}/references/cmake.md +49 -43
- package/skills/configuring-cmake/references/cxx-standard.md +67 -0
- package/skills/configuring-cmake/references/modules.md +105 -0
- package/skills/configuring-cmake/steps/01-targets.md +53 -0
- package/skills/configuring-cmake/steps/02-modules.md +37 -0
- package/skills/configuring-cmake/steps/03-check.md +39 -0
- package/skills/developing-cpp/SKILL.md +1 -1
- package/skills/developing-cpp/examples/comments.md +24 -0
- package/skills/developing-cpp/examples/exceptions.md +38 -19
- package/skills/developing-cpp/examples/structure.md +71 -0
- package/skills/developing-cpp/references/comments.md +49 -137
- package/skills/developing-cpp/references/exceptions.md +12 -13
- package/skills/developing-cpp/references/language.md +1 -0
- package/skills/developing-cpp/references/naming.md +2 -2
- package/skills/developing-cpp/references/standard-library.md +1 -0
- package/skills/developing-cpp/references/types.md +1 -2
- package/skills/developing-cpp/scripts/checking-comments.mjs +57 -2
- package/skills/developing-cpp/steps/03-declaration.md +3 -3
- package/skills/developing-cpp/steps/04-implementation.md +4 -3
- package/skills/building-cpp/assets/cmake/applying-warnings.cmake +0 -10
- package/skills/building-cpp/assets/cmake/fetching-googletest.cmake +0 -15
- package/skills/building-cpp/assets/cmake/setting-rpath.cmake +0 -8
|
@@ -21,7 +21,7 @@ const SOURCE_EXT = new Set([
|
|
|
21
21
|
])
|
|
22
22
|
const HEADER_EXT = new Set(['.h', '.hh', '.hpp', '.hxx', '.h++', '.inl', '.ipp', '.tpp'])
|
|
23
23
|
const SKIP_DIR = new Set([
|
|
24
|
-
'.git', '.hg', '.svn', 'build', 'out', 'node_modules', '_book', '.quarto', '.cache',
|
|
24
|
+
'.git', '.hg', '.svn', 'build', 'build-release', 'out', 'node_modules', '_book', '.quarto', '.cache',
|
|
25
25
|
'.tmp', 'temp', 'third_party', 'third-party', 'external', 'vendor', 'deps', 'cmake-build-debug',
|
|
26
26
|
'cmake-build-release', '.vs', '.idea',
|
|
27
27
|
])
|
|
@@ -40,7 +40,7 @@ function usage(message) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function parseArgs(argv) {
|
|
43
|
-
const options = { root: undefined, width: 80, bannerWidth: 80, requireHeader: true, json: false, quiet: false }
|
|
43
|
+
const options = { root: undefined, width: 80, bannerWidth: 80, widthExplicit: false, bannerWidthExplicit: false, requireHeader: true, json: false, quiet: false }
|
|
44
44
|
for (let index = 0; index < argv.length; index += 1) {
|
|
45
45
|
const arg = argv[index]
|
|
46
46
|
if (arg === '--root') {
|
|
@@ -48,9 +48,11 @@ function parseArgs(argv) {
|
|
|
48
48
|
index += 1
|
|
49
49
|
} else if (arg === '--width') {
|
|
50
50
|
options.width = Number(argv[index + 1])
|
|
51
|
+
options.widthExplicit = true
|
|
51
52
|
index += 1
|
|
52
53
|
} else if (arg === '--banner-width') {
|
|
53
54
|
options.bannerWidth = Number(argv[index + 1])
|
|
55
|
+
options.bannerWidthExplicit = true
|
|
54
56
|
index += 1
|
|
55
57
|
} else if (arg === '--no-header') {
|
|
56
58
|
options.requireHeader = false
|
|
@@ -72,6 +74,18 @@ function parseArgs(argv) {
|
|
|
72
74
|
return options
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
/** 从工程 .clang-format 读取 ColumnLimit;没有配置时返回 undefined。 */
|
|
78
|
+
function readColumnLimit(root) {
|
|
79
|
+
const dir = statSync(root).isDirectory() ? root : path.dirname(root)
|
|
80
|
+
try {
|
|
81
|
+
const text = readFileSync(path.join(dir, '.clang-format'), 'utf8')
|
|
82
|
+
const match = text.match(/^\s*ColumnLimit:\s*(\d+)\s*$/m)
|
|
83
|
+
return match ? Number(match[1]) : undefined
|
|
84
|
+
} catch {
|
|
85
|
+
return undefined
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
/** 中文按两列计宽,用于判断渲染后的行宽。 */
|
|
76
90
|
export function displayWidth(text) {
|
|
77
91
|
let width = 0
|
|
@@ -225,6 +239,18 @@ export function checkText(text, options = {}) {
|
|
|
225
239
|
}
|
|
226
240
|
}
|
|
227
241
|
|
|
242
|
+
for (let index = 0; index + 1 < lines.length; index += 1) {
|
|
243
|
+
const trimmed = lines[index].trim()
|
|
244
|
+
if (!trimmed.startsWith('//') || trimmed.startsWith('///') || trimmed.startsWith('//!')) continue
|
|
245
|
+
const next = lines[index + 1].trim()
|
|
246
|
+
if (next === '' || next.startsWith('//')) continue
|
|
247
|
+
if (!/(下标|存储|对应坐标|行优先)/.test(trimmed)) continue
|
|
248
|
+
const identifiers = next.match(/[A-Za-z_]\w*/g) ?? []
|
|
249
|
+
if (identifiers.some((identifier) => trimmed.includes(identifier))) {
|
|
250
|
+
add(index + 1, 'restates-next', '实现注释在复述下一行声明或存储细节:删掉这句,让声明自己表达')
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
228
254
|
for (let index = 0; index < lines.length; index += 1) {
|
|
229
255
|
const trimmed = lines[index].trim()
|
|
230
256
|
if (!/^(template\s*<[^>]*>\s*)?enum\b/.test(trimmed)) continue
|
|
@@ -249,6 +275,30 @@ export function checkText(text, options = {}) {
|
|
|
249
275
|
index = body
|
|
250
276
|
}
|
|
251
277
|
|
|
278
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
279
|
+
const trimmed = lines[index].trim()
|
|
280
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('///') || !/\bnoexcept\b/.test(trimmed)) continue
|
|
281
|
+
let bodyStart = index
|
|
282
|
+
if (!trimmed.includes('{')) {
|
|
283
|
+
let next = index + 1
|
|
284
|
+
while (next < lines.length && lines[next].trim() === '') next += 1
|
|
285
|
+
if (next >= lines.length || !lines[next].trim().startsWith('{')) continue
|
|
286
|
+
bodyStart = next
|
|
287
|
+
}
|
|
288
|
+
const afterBrace = trimmed.slice(trimmed.indexOf('{') + 1)
|
|
289
|
+
if (afterBrace.includes('.at(')) {
|
|
290
|
+
add(index + 1, 'noexcept-throws', 'noexcept 函数体内调用 at():越界会抛 std::out_of_range,去掉 noexcept 或改用 [] 与显式越界检查')
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
for (let cursor = bodyStart + 1; cursor < lines.length; cursor += 1) {
|
|
294
|
+
if (/^\}/.test(lines[cursor])) break
|
|
295
|
+
if (lines[cursor].includes('.at(')) {
|
|
296
|
+
add(cursor + 1, 'noexcept-throws', 'noexcept 函数体内调用 at():越界会抛 std::out_of_range,去掉 noexcept 或改用 [] 与显式越界检查')
|
|
297
|
+
break
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
252
302
|
return issues.sort((left, right) => left.line - right.line)
|
|
253
303
|
}
|
|
254
304
|
|
|
@@ -280,6 +330,11 @@ function main(argv) {
|
|
|
280
330
|
}
|
|
281
331
|
if (options.help) return usage()
|
|
282
332
|
const root = options.root ? path.resolve(options.root) : process.cwd()
|
|
333
|
+
const columnLimit = readColumnLimit(root)
|
|
334
|
+
if (columnLimit !== undefined) {
|
|
335
|
+
if (!options.widthExplicit) options.width = columnLimit
|
|
336
|
+
if (!options.bannerWidthExplicit) options.bannerWidth = columnLimit
|
|
337
|
+
}
|
|
283
338
|
let files
|
|
284
339
|
try {
|
|
285
340
|
files = collectFiles(root)
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
- 公共头文件自包含:自身需要的头都直接包含,不依赖调用方的包含顺序。
|
|
13
13
|
- 单参数构造函数写 `explicit`;可查询的成员函数加 `[[nodiscard]]`;默认成员初始化写在声明处,不在构造函数里重复。
|
|
14
14
|
- 头文件只就地定义大约 10 行以内的短函数;更长或需要完整定义的普通函数只在 `.cpp` 定义,模板和 `constexpr` 例外。
|
|
15
|
-
- 失败是调用方的正常结果时,用结果类型、`bool` + 输出参数或 `std::optional` 表达;函数无法完成自己的任务、或构造函数无法建立 invariant
|
|
16
|
-
- 声明注释按 `references/comments.md`
|
|
15
|
+
- 失败是调用方的正常结果时,用结果类型、`bool` + 输出参数或 `std::optional` 表达;函数无法完成自己的任务、或构造函数无法建立 invariant 时,才在声明注释里写清抛出的异常类型;越界用 `std::out_of_range`,前置条件被破坏用 `std::logic_error`,只有调用方必须区分时才新增派生类型;Debug/Release 的抛出条件相同。判据见 `references/exceptions.md`。
|
|
16
|
+
- 声明注释按 `references/comments.md` 分四层写:文件头的范围与边界、类型注释的结果/限制与约定、函数注释的第一句结果或失败方式与契约、数据成员的行尾说明。类型注释不罗列成员功能、不写方案对比与设计论证;全项目的风格理由写进项目文档,不写进注释。
|
|
17
17
|
- `std::format` 用于组合消息;声明里不要承诺 `std::print`(C++23)。
|
|
18
18
|
|
|
19
19
|
## 情况
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
**改公共接口**:先找所有调用方;改签名、改返回类型、改异常声明都算破坏性变更。能加默认参数或重载时优先加,不要静默改变旧调用的含义。
|
|
24
24
|
|
|
25
|
-
**异常声明**:构造函数、`Get` 类查询、解析函数最容易抛;在声明注释里写「当……时抛出 `
|
|
25
|
+
**异常声明**:构造函数、`Get` 类查询、解析函数最容易抛;在声明注释里写「当……时抛出 `std::out_of_range`」这样的具体类型,不要只写「可能抛异常」,也不要给每个参数错误都新建类型。类型选择见 `references/exceptions.md`。
|
|
26
26
|
|
|
27
27
|
**注释**:声明注释用 `///` 写中文散文,只写调用方需要知道的;实现细节留给 `.cpp`。`@param`、`@return`、`@throws` 要么全写要么全不写,标识符和标签保持英文。写完运行 `node "<skill-dir>/scripts/checking-comments.mjs" --root <项目根>`,横幅、标签、参数完整性、成员清单与设计论证由脚本兜底。文件头、章节横幅和 TODO 的格式见 `references/comments.md`。
|
|
28
28
|
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
- 函数保持短小;一个函数只做一件事。超过约 40 行时先找可以命名的中间步骤,不要用注释把长函数切成几段。
|
|
10
10
|
- 按「准备 → 操作 → 收尾」排列语句,用空行分组;组与组之间的空行表达阶段,不用大段叙述。
|
|
11
11
|
- `.cpp` 的第一个 include 是自己的配对头,然后直接包含本文件用到的标准库头;不要依赖公共头替本文件转发声明。包含顺序见 `references/google-cpp-style.md`;文件槽位见 `references/file-template.md`。
|
|
12
|
-
-
|
|
13
|
-
- 错误处理:失败是调用方正常结果时返回结果类型、`bool` 或 `std::optional
|
|
12
|
+
- `.cpp` 的函数体默认不写注释:契约在头文件的 `///` 里。只有删掉会让下一次修改得到错误结果、而代码本身看不出的地方才写一句(次序依赖、类型收窄、解析失败整行丢弃、共享方式),写在被解释语句上方,不复述代码,记录变更史的内容留给提交说明。
|
|
13
|
+
- 错误处理:失败是调用方正常结果时返回结果类型、`bool` 或 `std::optional`;函数无法完成自己的任务时抛异常,不拿异常做正常分支。Debug 与 Release 用同一套返回值与抛出条件,只有诊断详略不同。两种模式都要守的 invariant 用 `Check`(项目保留时),只在开发期做的自检用 `DebugCheck`;没有这两个宏就分别用 `assert` 与 `std::terminate`。捕获时按 `const` 引用,只处理能处理的类型,其余继续抛。
|
|
14
|
+
- 声明 `noexcept` 前核对函数体:出现 `at()` 或其他可能抛出的调用就去掉。越界用 `at()` 抛 `std::out_of_range`,这个函数就不能标 `noexcept`。
|
|
14
15
|
- 组合输出用 `std::format`,再写入 `std::cout` / `std::cerr`;不要在库实现里直接打印。C++23 且库支持时程序边界可用 `std::print`,C++20 及更早没有。
|
|
15
16
|
- 不写 C 风格强制转换;用 `static_cast` / `std::bit_cast` 等具名转换。
|
|
16
17
|
- 所有权用 `std::unique_ptr`;没有共享所有权的理由时不用 `std::shared_ptr`。
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
|
|
25
26
|
**重构**:一次只动一种东西(改名、抽函数、换容器)。每步都让测试保持绿色;行为句不变。
|
|
26
27
|
|
|
27
|
-
**异常边界**:库内部让异常向上传播;程序入口捕获 `std::exception
|
|
28
|
+
**异常边界**:库内部让异常向上传播;程序入口捕获 `std::exception`(或项目自己的异常基类),用 `std::format("错误: {}", error.what())` 打印,返回项目约定的非零退出码。不要吞掉异常后继续用无效状态。
|
|
28
29
|
|
|
29
30
|
## 交回构建
|
|
30
31
|
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# 固定版本拉取 GoogleTest;只在 tests/ 需要时调用。
|
|
2
|
-
include(FetchContent)
|
|
3
|
-
|
|
4
|
-
function(cpp_workflow_fetch_googletest)
|
|
5
|
-
if(POLICY CMP0135)
|
|
6
|
-
cmake_policy(SET CMP0135 NEW)
|
|
7
|
-
endif()
|
|
8
|
-
FetchContent_Declare(
|
|
9
|
-
googletest
|
|
10
|
-
URL https://codeload.github.com/google/googletest/tar.gz/refs/tags/v1.17.0
|
|
11
|
-
URL_HASH SHA256=65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c
|
|
12
|
-
)
|
|
13
|
-
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
14
|
-
FetchContent_MakeAvailable(googletest)
|
|
15
|
-
endfunction()
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# 让构建目录里的程序直接找到同目录的共享库;安装规则另配。
|
|
2
|
-
function(cpp_workflow_set_build_rpath target)
|
|
3
|
-
if(APPLE)
|
|
4
|
-
set_target_properties(${target} PROPERTIES BUILD_RPATH "@loader_path/../lib")
|
|
5
|
-
else()
|
|
6
|
-
set_target_properties(${target} PROPERTIES BUILD_RPATH "$ORIGIN/../lib")
|
|
7
|
-
endif()
|
|
8
|
-
endfunction()
|