fix(skill): 修正 @build 退出码口径不符(环境错误 1→2、用法 2→3)
`AGENTS.md` 与 `keil-build` / `new-machine-setup` 的 SKILL.md 一直写着
"2=UV4 未找到/无效(环境错误)、3=用法",但实现是反的:
- 环境错误走 `raise SystemExit("...")` → **实际退出码 1**(与"构建失败"
撞号,调用方分不清"编译不过"和"UV4 没配好");
- 用法错误由 argparse 抛 `SystemExit(2)` → 恰好占用了文档里"环境错误"
的位置,于是 `new-machine-setup` 里"报 exit 2 先分清真没设还是没继承"
这条排障指引**从未生效**。
改法(让实现服从既有文档口径,而非改文档):
- 新增 `BuildEnvError`;`find_uv4` / `find_project` / `load_config` 失败
一律抛它,由 `main()` 捕获 → 打印 + 退出码 **2**;
- 覆盖 argparse 的 `error()`(默认 `SystemExit(2)`)→ 用法错误退出码 **3**;
- `run_build` 的档案解析失败原为 `return 3`(用法),实属环境错误 → 并入 2。
调用方安全性:`test/run_all_suites.py` 与 `task-worktree` 的 `run_script`
都只判 `rc == 0` / `rc != 0`,不区分具体码,故本次改动不影响它们。
验证(全部实测,用假 UV4 避免与在途构建争 UV4 与 build 锁):
| 场景 | 期望 | 实测 |
|---|---|---|
| `--help` | 0 | 0 |
| 成功构建(假 UV4 写 0E0W) | 0 | 0(BUILD_OK) |
| 构建失败(假 UV4 写 `error:`) | 1 | 1(BUILD_FAIL) |
| `--project` 不存在 | 2 | 2 |
| 档案 `uvprojx` 不存在 | 2 | 2 |
| 档案 JSON 解析失败 | 2 | 2 |
| 未知参数 `--bogus` | 3 | 3 |
| 参数缺值 `--timeout` | 3 | 3 |
| build 锁被占用 | 4 | 4(B9 在构建时实测) |
同步更新 `AGENTS.md` 的退出码行(补 2 的完整含义与 4)与 `keil-build`
SKILL.md(记录本次修正的来龙去脉)。pre-commit 全绿。
This commit is contained in:
@@ -18,8 +18,13 @@ whenToUse: 需要编译 Keil MDK 工程、验证代码改动是否 0 错误 0
|
||||
解析顺序(命令行 > 项目档案 `.serialtest/config.json` > 环境 > 探测):
|
||||
工程 `uvprojx`(档案缺省时自动搜 cwd 下唯一 `*.uvprojx`);UV4 `uv4` >
|
||||
`$DSH_KEIL_UV4` > `$DTU_KEIL_UV4` > PATH(**显式设了但路径无效 = 立即报错,不静默回退**)。
|
||||
退出码:`0`=PASS(0错0警) / `1`=构建失败(打印报错行)/ `2`=UV4 未找到/无效 / `3`=用法 /
|
||||
`4`=build 锁被占用(并发 `-b` 不可行)。
|
||||
退出码:`0`=PASS(0错0警) / `1`=构建失败(打印报错行)/ **`2`=环境错误**(UV4 找不到或
|
||||
路径无效、工程找不到、档案解析失败)/ `3`=用法错误 / `4`=build 锁被占用(并发 `-b` 不可行)。
|
||||
|
||||
> 2026-10-12 修正:旧实现把环境错误写成 `raise SystemExit("...")` → 实际退出码是 **1**,
|
||||
> 而用法错误由 argparse 抛 `SystemExit(2)` → 恰好占了"环境错误"的位置。故 `AGENTS.md` /
|
||||
> `new-machine-setup` 里写的"报 exit 2 = 环境问题"当时**不成立**。现按文档口径改正:
|
||||
> 环境错误 → 2(`BuildEnvError`),用法错误 → 3(覆盖 argparse 的 `error()`)。
|
||||
|
||||
## build 锁:脚本自持自释(2026-10-12)
|
||||
|
||||
|
||||
@@ -33,8 +33,17 @@
|
||||
- 调用方**已持有** build 锁时(如 `task-worktree done` 的门禁),设
|
||||
`DTU_LOCK_HELD=build` 让本脚本跳过自持自释,避免自我死锁
|
||||
|
||||
退出码: 0=PASS(0错0警) 1=构建失败(看打印的报错行/日志) 2=UV4 未找到/无效
|
||||
3=用法错误 4=build 锁被占用(并发 -b 不可行)
|
||||
退出码(与 task-worktree 同一约定;2026-10-12 修正——此前实现与文档不符):
|
||||
0 = PASS(0 错 0 警)
|
||||
1 = 构建失败(看打印的报错行 / 日志)
|
||||
2 = **环境/前置错误**(UV4 找不到或路径无效、工程找不到、档案解析失败)
|
||||
3 = 用法错误
|
||||
4 = build 锁被占用(并发 -b 不可行)
|
||||
|
||||
⚠ 修正说明:旧实现把环境错误写成 `raise SystemExit("...")` → **实际退出码是 1**,
|
||||
而用法错误由 argparse 抛 SystemExit(2) → 恰好占用了文档里"环境错误"的位置。
|
||||
故 AGENTS.md / `new-machine-setup` SKILL.md 里写的"报 exit 2 = 环境问题"当时**不成立**。
|
||||
现已按文档口径改正:环境错误 `BuildEnvError` → 2,argparse 用法错误 → 3。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -48,6 +57,21 @@ from pathlib import Path
|
||||
LOCK_NAME = "build"
|
||||
|
||||
|
||||
class BuildEnvError(Exception):
|
||||
"""环境/前置错误(UV4 或工程找不到、档案解析失败)→ 退出码 2。"""
|
||||
|
||||
|
||||
class _ArgumentParser(argparse.ArgumentParser):
|
||||
"""用法错误 → 退出码 3。
|
||||
|
||||
argparse 默认 `error()` 走 `SystemExit(2)`,与"环境错误=2"冲突,故覆盖之。
|
||||
"""
|
||||
|
||||
def error(self, message):
|
||||
self.print_usage(sys.stderr)
|
||||
self.exit(3, "{}: error: {}\n".format(self.prog, message))
|
||||
|
||||
|
||||
def find_config_file():
|
||||
env = os.environ.get("DSH_SERIAL_CONFIG")
|
||||
if env:
|
||||
@@ -84,26 +108,26 @@ def find_uv4(cli_arg, cfg):
|
||||
if val:
|
||||
if Path(val).is_file():
|
||||
return Path(val), label
|
||||
raise SystemExit(("BUILD_ENV_ERROR: {} is set but not a file: {} "
|
||||
"(fix the value; no silent fallback)").format(label, val))
|
||||
raise BuildEnvError(("BUILD_ENV_ERROR: {} is set but not a file: {} "
|
||||
"(fix the value; no silent fallback)").format(label, val))
|
||||
w = shutil.which("UV4.exe") or shutil.which("UV4")
|
||||
if w:
|
||||
return Path(w), "PATH"
|
||||
raise SystemExit("BUILD_ENV_ERROR: UV4.exe not found "
|
||||
"(--uv4 / config 'uv4' / $DSH_KEIL_UV4 / $DTU_KEIL_UV4 / PATH)")
|
||||
raise BuildEnvError("BUILD_ENV_ERROR: UV4.exe not found "
|
||||
"(--uv4 / config 'uv4' / $DSH_KEIL_UV4 / $DTU_KEIL_UV4 / PATH)")
|
||||
|
||||
|
||||
def find_project(cli_arg, cfg):
|
||||
if cli_arg:
|
||||
p = Path(cli_arg)
|
||||
if not p.is_file():
|
||||
raise SystemExit("BUILD_ENV_ERROR: --project not found: {}".format(p))
|
||||
raise BuildEnvError("BUILD_ENV_ERROR: --project not found: {}".format(p))
|
||||
return p
|
||||
v = cfg.get("uvprojx")
|
||||
if v:
|
||||
p = Path(v)
|
||||
if not p.is_file():
|
||||
raise SystemExit("BUILD_ENV_ERROR: config 'uvprojx' not found: {}".format(p))
|
||||
raise BuildEnvError("BUILD_ENV_ERROR: config 'uvprojx' not found: {}".format(p))
|
||||
return p
|
||||
hits = sorted(p for p in Path.cwd().rglob("*.uvprojx")
|
||||
if not any(part.startswith(".") for part in p.parts))
|
||||
@@ -111,11 +135,11 @@ def find_project(cli_arg, cfg):
|
||||
print("PROJ: auto-detected {}".format(hits[0]))
|
||||
return hits[0]
|
||||
if not hits:
|
||||
raise SystemExit("BUILD_ENV_ERROR: no *.uvprojx under cwd "
|
||||
"(--project or config 'uvprojx')")
|
||||
raise SystemExit("BUILD_ENV_ERROR: multiple .uvprojx found, pick one "
|
||||
"(--project or config 'uvprojx'):\n " +
|
||||
"\n ".join(str(h) for h in hits))
|
||||
raise BuildEnvError("BUILD_ENV_ERROR: no *.uvprojx under cwd "
|
||||
"(--project or config 'uvprojx')")
|
||||
raise BuildEnvError("BUILD_ENV_ERROR: multiple .uvprojx found, pick one "
|
||||
"(--project or config 'uvprojx'):\n " +
|
||||
"\n ".join(str(h) for h in hits))
|
||||
|
||||
|
||||
# --------------------------------------------------------------- build 锁(自持自释)
|
||||
@@ -188,8 +212,7 @@ def run_build(args):
|
||||
"""真正的构建主体(锁由 main 负责)。返回退出码。"""
|
||||
cfg, err = load_config()
|
||||
if err:
|
||||
print("BUILD_ENV_ERROR: {}".format(err))
|
||||
return 3
|
||||
raise BuildEnvError("BUILD_ENV_ERROR: {}".format(err))
|
||||
|
||||
proj = find_project(args.project, cfg)
|
||||
uv4, src = find_uv4(args.uv4, cfg)
|
||||
@@ -261,7 +284,7 @@ def main(argv=None):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ap = argparse.ArgumentParser(description="Keil UV4 CLI build + verdict (pure stdlib)")
|
||||
ap = _ArgumentParser(description="Keil UV4 CLI build + verdict (pure stdlib)")
|
||||
ap.add_argument("--project", default=None, help="*.uvprojx (default: config/auto-detect)")
|
||||
ap.add_argument("--uv4", default=None, help="UV4.exe path")
|
||||
ap.add_argument("--target", default=None, help="build target name (-t; default: project default)")
|
||||
@@ -292,6 +315,9 @@ def main(argv=None):
|
||||
|
||||
try:
|
||||
return run_build(args)
|
||||
except BuildEnvError as e:
|
||||
print(str(e))
|
||||
return 2
|
||||
finally:
|
||||
if state == "ok":
|
||||
ok, out = lock_cli(tool, "release", task)
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
## 构建与验证
|
||||
|
||||
- `@build` = `& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py`(仓库根执行)。退出码 **0=PASS / 1=构建失败 / 2=UV4 未找到 / 3=用法**;日志 `MDK-ARM/build_log.txt`;换机必设 `DTU_KEIL_UV4` → 细节 **`keil-build` skill** + `docs/环境与部署/本地工具路径环境变量方案.md`
|
||||
- `@build` = `& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py`(仓库根执行)。退出码 **0=PASS / 1=构建失败 / 2=环境错误(UV4/工程/档案)/ 3=用法 / 4=build 锁被占用**;日志 `MDK-ARM/build_log.txt`;换机必设 `DTU_KEIL_UV4` → 细节 **`keil-build` skill** + `docs/环境与部署/本地工具路径环境变量方案.md`
|
||||
- `@test` = `& '.venv\Scripts\python.exe' .dsh\skills\serial-boot-test\scripts\run_boot_test.py`:开串口+清日志 → 烧录(**固件身份戳**保证烧进去的就是这份构建、**回读校验**)→ wait-idle → 采日志 → 标记校验(含启动锚点计数)。退出码 **0=PASS / 1=FAIL / 2=采集或烧录错误 / 3=用法**;戳提取规则、锚点计数判据与按模块定制标记见 `docs/测试说明/自动化测试流程.md`
|
||||
- Forbidden 标记含 `[ERR]`:**四个 Fault 的崩溃日志都以 `[ERR]` 开头 → 出现 Fault 即本次不通过**(刻意)
|
||||
- **烧录必须 `-Rst -Run`**(`stlink-flash` skill 的 `flash`):裸 `-P` 会报 `Programming Complete`,但目标核处于死循环/异常时写入并不落地("反复重下载、芯片一直是旧镜像、二分法失效"的根因)。**烧后必须回读校验**(`stlink_flash.py cmp` = `-CmpFile`,`No difference found` 才算真正写入);⚠ `-CmpFile` 连接时会**复位目标** → 必须排在**采日志之后** → `docs/测试说明/ST-LINK_CLI_复位使用说明.md`
|
||||
|
||||
Reference in New Issue
Block a user