diff --git a/.dsh/skills/keil-build/SKILL.md b/.dsh/skills/keil-build/SKILL.md index ec2f8846..c8b40f70 100644 --- a/.dsh/skills/keil-build/SKILL.md +++ b/.dsh/skills/keil-build/SKILL.md @@ -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) diff --git a/.dsh/skills/keil-build/scripts/keil_build.py b/.dsh/skills/keil-build/scripts/keil_build.py index b9c23b8b..8657ea13 100644 --- a/.dsh/skills/keil-build/scripts/keil_build.py +++ b/.dsh/skills/keil-build/scripts/keil_build.py @@ -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) diff --git a/AGENTS.md b/AGENTS.md index 86da624d..5d264845 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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`