From e846880200d6a04731559be7b998caf69117f1b5 Mon Sep 17 00:00:00 2001 From: silencebadwolf <132798190+silencebadwolf@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:37:19 +0800 Subject: [PATCH] feat: add rtthread-bsp-builder and rtconfig-kconfig-sync skills - rtthread-bsp-builder: find official RT-Thread BSP and build (GCC/MDK/IAR/RT-Studio) - rtconfig-kconfig-sync: .config / rtconfig.h / Kconfig sync check - Based on official BSP README, not custom project templates - Multi-target build support, Windows Env compatible --- README.md | 27 ++- skills/rtconfig-kconfig-sync/SKILL.md | 152 ++++++++++++++++ skills/rtthread-bsp-builder/README.md | 14 ++ skills/rtthread-bsp-builder/SKILL.md | 166 ++++++++++++++++++ skills/rtthread-bsp-builder/capability.json | 9 + .../references/common-build-flow.md | 136 ++++++++++++++ .../references/common-failures.md | 95 ++++++++++ .../rtthread-bsp-builder/scripts/build_bsp.sh | 126 +++++++++++++ .../scripts/ensure_repo.sh | 106 +++++++++++ .../rtthread-bsp-builder/scripts/find_bsp.py | 136 ++++++++++++++ .../scripts/inspect_bsp.py | 110 ++++++++++++ .../rtthread-bsp-builder/scripts/list_refs.sh | 49 ++++++ 12 files changed, 1124 insertions(+), 2 deletions(-) create mode 100644 skills/rtconfig-kconfig-sync/SKILL.md create mode 100644 skills/rtthread-bsp-builder/README.md create mode 100644 skills/rtthread-bsp-builder/SKILL.md create mode 100644 skills/rtthread-bsp-builder/capability.json create mode 100644 skills/rtthread-bsp-builder/references/common-build-flow.md create mode 100644 skills/rtthread-bsp-builder/references/common-failures.md create mode 100644 skills/rtthread-bsp-builder/scripts/build_bsp.sh create mode 100644 skills/rtthread-bsp-builder/scripts/ensure_repo.sh create mode 100644 skills/rtthread-bsp-builder/scripts/find_bsp.py create mode 100644 skills/rtthread-bsp-builder/scripts/inspect_bsp.py create mode 100644 skills/rtthread-bsp-builder/scripts/list_refs.sh diff --git a/README.md b/README.md index fd97a09..bc504de 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# rtthread-skills -SKILLs for RT-Thread & Embedded System Developement +# rtthread-skills + +SKILLs for RT-Thread & Embedded System Development + +## 技能列表 + +| 技能 | 说明 | 适用范围 | +|------|------|----------| +| `rtthread-bsp-builder` | 查找官方 RT-Thread BSP 并按 BSP README 推动构建 | 官方 BSP + GCC/MDK/IAR/RT-Studio | +| `rtconfig-kconfig-sync` | `.config` / `rtconfig.h` / `Kconfig` 同步检查 | RT-Thread 配置系统 | + +## 贡献规则 + +- 一个 PR 只新增或修改一个 skill 主题 +- 通用 RT-Thread skill 不应把产品专属模板或编号项目布局作为默认行为 +- skill 内容应以官方 RT-Thread BSP 布局和 BSP README 为主要依据 +- 提交前请验证:frontmatter 校验、Python 语法检查、helper 脚本 help 输出 + +## 目录结构 + +``` +skills/ +├── rtthread-bsp-builder/ # RT-Thread 官方 BSP 构建助手 +└── rtconfig-kconfig-sync/ # rtconfig 与 Kconfig 配置同步 +``` diff --git a/skills/rtconfig-kconfig-sync/SKILL.md b/skills/rtconfig-kconfig-sync/SKILL.md new file mode 100644 index 0000000..ca14c7e --- /dev/null +++ b/skills/rtconfig-kconfig-sync/SKILL.md @@ -0,0 +1,152 @@ +--- +name: rtconfig-kconfig-sync +description: 当修改 RT-Thread 项目的 rtconfig.h/.config/Kconfig 或执行 scons --menuconfig/defconfig/genconfig 时自动触发,确保配置同步。 +version: 1.2.0 +author: Hermes Agent +license: MIT +metadata: + hermes: + tags: ['embedded', 'mcu', 'rt-thread', 'kconfig', 'scons', 'config-sync'] + related_skills: ['embedded-mcu', 'embedded-environment-config', 'gcc', 'keil'] +--- + +# RT-Thread rtconfig.h 与 .config 同步检查 + +## 触发条件 + +当执行以下操作时自动触发: +1. 修改 `rtconfig.h`(手动添加宏) +2. 修改 `.config`(手动添加选项) +3. 执行 `scons --menuconfig` / `--defconfig` / `--genconfig` +4. 编译时出现 `RT_USING_XXX` 未定义错误 + +## 核心概念 + +RT-Thread 配置系统基于 Kconfig,核心链路: + +1. `Kconfig` 声明配置选项(`config BSP_USING_XXX`) +2. `scons --menuconfig` 交互式选择,写入 `.config` +3. `scons --defconfig` 从 `.config` 生成 `rtconfig.h` +4. `scons --genconfig` 从 `rtconfig.h` 反向同步 `.config` + +**关键点**: +- `.config` 是配置源头(由 menuconfig 或手动编辑产生) +- `rtconfig.h` 是 C 头文件(由 defconfig 生成,供编译使用) +- 两者之间的宏映射不是简单的一一对应,涉及前缀转换(`CONFIG_XXX` → `RT_USING_XXX`) + +## 检查步骤 + +### 1. 检查同步状态 + +```bash +# 对比 .config 和 rtconfig.h 中的宏 +diff <(grep -E "^CONFIG_" .config | sort) \ + <(grep -E "^#define RT_USING" rtconfig.h | sed 's/#define //' | sort) +``` + +### 2. 如果手动改了 rtconfig.h + +**必须执行**: +```bash +scons --genconfig # 反向同步 .config +``` + +**验证**: +```bash +grep "CONFIG_BSP_USING_XXX" .config # 应该存在 +``` + +### 3. 如果手动改了 .config + +**必须执行**: +```bash +scons --defconfig # 重新生成 rtconfig.h +``` + +**验证**: +```bash +grep "RT_USING_XXX" rtconfig.h # 应该存在 +``` + +### 4. 如果新增 BSP 选项 + +**检查 Kconfig**: +```bash +# 在 BSP 目录下的 Kconfig 中查找 +find . -name "Kconfig" -exec grep -l "BSP_USING_XXX" {} \; +``` + +**如果缺失,添加**: +```kconfig +config BSP_USING_XXX + bool "Enable XXX" + default n + select RT_USING_YYY # 如果有依赖 +``` + +## 常见错误 + +### 错误 1:手动改 rtconfig.h 没同步 .config + +**症状**:用 `scons --menuconfig` 改选项无效,`.config` 是 `not set` + +**修复**: +```bash +scons --genconfig # 同步 +``` + +### 错误 2:新选项没在 Kconfig 定义 + +**症状**:`scons --menuconfig` 看不到新选项 + +**修复**:在 BSP 或相关模块的 `Kconfig` 里添加 `config BSP_USING_XXX` + +### 错误 3:编译报宏未定义 + +**症状**:`error: 'RT_USING_XXX' undeclared` + +**修复**: +1. 检查 `.config` 里是否有 `CONFIG_XXX=y` +2. 如果没有,用 `scons --menuconfig` 启用 +3. 或手动加到 `.config`,再 `scons --defconfig` + +### 错误 4:package 宏消失 + +**症状**:`scons --defconfig` 后 `PKG_USING_*` 宏在 rtconfig.h 中消失 + +**修复**: +1. 确认 package 的 `Kconfig` 中有对应的 `config PKG_USING_XXX` +2. 确认 `.config` 中有 `CONFIG_PKG_USING_XXX=y` +3. 执行 `pkgs --update` 确保 package 已下载 +4. 重新 `scons --defconfig` + +## 最佳实践 + +1. **优先用 `scons --menuconfig`**(自动同步,最安全) +2. **如果 TUI 不可用**: + - 编辑 `.config`(源头) + - 执行 `scons --defconfig`(重新生成 `rtconfig.h`) +3. **如果必须手动改 `rtconfig.h`**: + - 改完后执行 `scons --genconfig`(反向同步 `.config`) +4. **新增选项前先查上游/原生开关**: + - 先在 RT-Thread、BSP、厂商 SDK、已有 `Kconfig` 中搜索是否已有控制项 + - 例如 FinSH/MSH 已有 `RT_USING_MSH`、`RT_USING_FINSH`、`FINSH_USING_MSH` + - 若已有原生开关能满足需求,直接复用,不要自建重复 wrapper +5. **新增选项**: + - 先在 `Kconfig` 里定义 + - 再用 `scons --menuconfig` 启用 +6. **导出工程文件**: + - `CMakeLists.txt`、MDK/IAR/VS Code 工程文件是 SCons 导出物 + - 新增/移动源码时先改 `SConscript`,再重新导出 + - 不要直接手改生成的工程文件 + +## 注意事项 + +- 不同 RT-Thread 版本的 Kconfig 结构可能不同,操作前先确认版本 +- BSP 的 Kconfig 路径因芯片厂商而异,用 `find` 命令定位 +- `scons --defconfig` 和 `scons --genconfig` 的行为可能因版本有差异 +- 某些旧版 BSP 可能不支持 `scons --genconfig` + +## 参考 + +- RT-Thread 文档:https://www.rt-thread.io/document/site/programming-manual/basic/basic/#内核配置 diff --git a/skills/rtthread-bsp-builder/README.md b/skills/rtthread-bsp-builder/README.md new file mode 100644 index 0000000..2e49547 --- /dev/null +++ b/skills/rtthread-bsp-builder/README.md @@ -0,0 +1,14 @@ +# rtthread-bsp-builder RT-Thread 官方 BSP 构建 + +## 作用 +用于拉取或更新官方 RT-Thread 仓库,定位官方 BSP,检查 BSP 构建条件,并按官方 GCC 流程完成构建。 + +## 何时使用 +当需求涉及确认 RT-Thread 是否官方支持某个芯片或板卡、选择官方 BSP,或让官方 BSP 按文档流程成功编译时使用。 + +## 主要文件 +- `SKILL.md`:skill 定义、触发条件和完整工作流 + +## 备注 +这个 README 用来快速说明该 skill 是做什么的,便于在技能工作区里浏览和维护。 + diff --git a/skills/rtthread-bsp-builder/SKILL.md b/skills/rtthread-bsp-builder/SKILL.md new file mode 100644 index 0000000..5fad2f8 --- /dev/null +++ b/skills/rtthread-bsp-builder/SKILL.md @@ -0,0 +1,166 @@ +--- +name: rtthread-bsp-builder +description: 克隆或更新官方 RT-Thread 仓库,可按需让用户选择分支、标签或提交,查找指定芯片或板卡的官方 BSP,检查 BSP 的 README 与 rtconfig,并按官方工作流推动其完成构建(GCC/MDK/IAR/RT-Studio)。当处理 RT-Thread MCU 或嵌入式项目,且需要回答"官方是否支持这个芯片或板卡"、选择版本、设置官方 BSP、运行 menuconfig/pkgs/scons 时使用。如果没有可信的官方 BSP,就明确停止,不要自行虚构或移植。 +version: 1.1.0 +author: Hermes Agent +license: MIT +metadata: + hermes: + tags: ['rt-thread', 'bsp', 'mcu', 'gcc', 'keil', 'iar', 'scons', 'embedded'] + related_skills: ['embedded-mcu', 'embedded-environment-config', 'gcc', 'keil'] +--- + +# RT-Thread 官方 BSP 构建助手 + +这个 skill 只适用于 **官方 `RT-Thread/rt-thread` 仓库中已经存在的官方 BSP**。 + +不要新建 BSP、不要从零移植芯片、不要克隆第三方 fork,也不要把“看起来差不多”的 BSP 当成可替代方案。如果不存在可信的官方 BSP,就明确说明并停止。 + +## 范围 + +本技能覆盖: + +- 官方 `RT-Thread/rt-thread` +- 已存在于 `bsp/` 下的官方 BSP +- 根据 BSP README 支持的构建目标:GCC、MDK、IAR、RT-Studio +- 关键选择点上与用户交互确认 + +当前不在范围内的包括: + +- 新 BSP 创建或新芯片移植 +- 厂商 fork 支持 +- 静默替换 BSP +- 自动烧录 +- 自动安装系统级软件包 +- 自动修改全局环境 + +## 构建目标选择 + +**不要假设 GCC 是唯一或优先目标。** 根据 BSP README 和用户需求决定: + +| 目标 | scons 命令 | 典型产物 | +|------|-----------|----------| +| GCC | `scons` | `rtthread.bin`, `rtthread.elf` | +| MDK | `scons --target=mdk5` | `project.uvprojx` | +| IAR | `scons --target=iar` | `project.eww` | +| RT-Studio | `scons --target=eclipse` | `.cproject` | + +如果 BSP README 明确推荐 MDK 或 IAR,不要强行走 GCC。 + +## 必须交互的节点 + +不要跳过会影响可复现性的关键决策。 + +必要时向用户确认: + +1. **仓库版本** + - 如果用户没有指定版本,要询问是使用: + - 默认分支最新版本 + - 某个分支 + - 某个标签 + - 某个具体提交 + - 需要时可用 `scripts/list_refs.sh` 展示候选分支或标签。 + - 用 `scripts/ensure_repo.sh --dest --ref ` 检出指定版本。 + +2. **BSP 选择存在歧义时** + - 如果存在多个较强候选 BSP,需要展示出来并让用户选择。 + +3. **构建目标** + - 如果 BSP 支持多个构建目标,让用户选择。 + +4. **可能影响宿主环境的操作** + - 安装 `scons`、`pkgs`、交叉编译工具链或系统软件包前,必须得到用户确认。 + +## 工作流程 + +1. **先明确目标** + - 如果请求比较模糊,先问清楚具体芯片或板卡。 + - 如果没有指定 RT-Thread 版本,先问清楚要用哪个版本。 + +2. **确保官方仓库存在** + - 使用 `scripts/ensure_repo.sh --dest `。 + - 如需固定版本,增加 `--ref `。 + - 如果仓库已存在且有未提交改动,不要强行覆盖。 + +3. **查找 BSP** + - 使用 `scripts/find_bsp.py --repo --query `。 + - 只有在结果是以下情况时才继续: + - `exact` + - 或单个 `strong` 官方匹配 + - 如果结果是 `ambiguous`,让用户选择。 + - 如果结果是 `none`,明确告诉用户没有官方 BSP,并停止。 + +4. **构建前先检查** + - 使用 `scripts/inspect_bsp.py --repo --bsp `。 + - 然后自行阅读 BSP README。 + - 特别关注: + - 需要的工具链和前缀 + - 是否强制要求 `pkgs --update` + - 是否必须走 `menuconfig` + - 该 BSP 支持哪些构建目标(GCC/MDK/IAR/RT-Studio) + +5. **先跑预检查** + - 使用 `scripts/build_bsp.sh --repo --bsp --preflight`。 + - 这一步会检查 BSP 路径、README 提示、`scons`、可选 `pkgs` 以及编译器前缀。 + - 如果宿主环境缺前置条件,就停止并明确告知缺什么。 + +7. **按官方顺序构建** + - 先阅读 README,再按 README 执行。 + - 常见顺序是: + 1. `pkgs --update`,如果 README 或 BSP 要求 + 2. 仅在有要求或用户明确希望改配置时,再执行 `scons --menuconfig` 或 `menuconfig` + 3. 根据目标执行构建: + - GCC: `scons -j$(nproc)` + - MDK: `scons --target=mdk5` + - IAR: `scons --target=iar` + - **Windows 用户**:RT-Thread Env 环境下 `pkgs` 可能是 `pkgs.exe`,不要只依赖 `command -v pkgs`。 + +8. **只有真实编译成功后才能宣称成功** + - “预检查通过” 不等于 “编译通过”。 + - 汇报时要包含: + - 仓库路径与检出版本 + - BSP 路径 + - 使用了哪个 README + - 使用了哪个构建目标 + - 实际执行了哪些命令 + - 产物路径,例如 `rtthread.bin`、`.elf`、`.hex`、`project.uvprojx` + - 仍然缺少的工具链或软件包条件 + +## 安全规则 + +- 如果 **没有官方 BSP**,必须明确说明并停止。 +- 不要静默替换为别的 BSP。 +- 除非用户明确要求,不要安装系统软件包、烧录硬件或修改全局 shell 启动文件。 +- 更新或检出 RT-Thread 仓库时,不要覆盖脏工作区。 +- 优先使用默认配置,不要随意引入不必要的 `menuconfig` 变更。 + +## 脚本说明 + +- `scripts/list_refs.sh` + - 列出官方 RT-Thread 分支和标签,便于用户选版本。 +- `scripts/ensure_repo.sh` + - 克隆官方 RT-Thread 仓库,或安全地报告/更新已存在仓库。 + - 支持 `--ref `。 +- `scripts/find_bsp.py` + - 按芯片或板卡关键词搜索官方 BSP,并区分 exact / strong / ambiguous / none。 +- `scripts/inspect_bsp.py` + - 解析 BSP 并汇总 README、工具链和构建提示。 +- `scripts/build_bsp.sh` + - 执行预检查和默认 GCC 构建流程。 + +## 参考资料 + +- `references/common-build-flow.md` + - 对澄清目标、选择版本、定位 BSP、预检查、构建和汇报的决策规则说明。 +- `references/common-failures.md` + - 常见 RT-Thread BSP 构建失败场景,以及下一步该检查什么。 + +## 推荐默认做法 + +- 优先使用官方仓库,而不是镜像或 fork。 +- 优先以 BSP README 为准,而不是泛泛的 RT-Thread 通用建议。 +- 根据 BSP README 推荐的构建目标来选择,不要默认假设 GCC。 +- 如果 README 要求 `pkgs --update`,不要静默跳过。 +- 如果因为缺少工具链而无法继续,要明确告诉用户缺的编译器前缀或环境变量。 +- `pkgs` 检测应支持 Env 下的显式路径(如 `pkgs.exe`),不能只依赖 `command -v pkgs`。 + diff --git a/skills/rtthread-bsp-builder/capability.json b/skills/rtthread-bsp-builder/capability.json new file mode 100644 index 0000000..676908c --- /dev/null +++ b/skills/rtthread-bsp-builder/capability.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "type": "skill", + "id": "rtthread-bsp-builder", + "displayName": "rtthread-bsp-builder", + "summary": "克隆或更新官方 RT-Thread 仓库,可按需让用户选择分支、标签或提交,查找指定芯片或板卡的官方 BSP,检查 BSP 的 README 与 rtconfig,并按官方工作流推动其完成 GCC 构建。当处理 RT-Thread MCU 或嵌入式项目,且需要回答“官方是否支持这个芯片或板卡”、选择版本、设置官方 BSP、运行 menuconfig/pkgs/scons,或让官方 BSP 编译通过时使用。如果没有可信的官方 BSP,就明确停止,不要自行虚构或移植。", + "legacyAliases": [], + "status": "migrated" +} diff --git a/skills/rtthread-bsp-builder/references/common-build-flow.md b/skills/rtthread-bsp-builder/references/common-build-flow.md new file mode 100644 index 0000000..ef48ea0 --- /dev/null +++ b/skills/rtthread-bsp-builder/references/common-build-flow.md @@ -0,0 +1,136 @@ +# Common build flow + +## Goal + +Take a user request like “帮我把 RT-Thread 的某个芯片/开发板工程拉起来并编译过” and turn it into a safe, repeatable workflow that only uses **official BSPs** and keeps the user involved in key choices. + +## Decision tree + +### 1. Clarify the target + +Input may be any of these: + +- exact BSP name: `stm32f103-blue-pill` +- chip family: `stm32f103` +- board name: `blue pill` +- vendor board: `ESP32_C3`, `artpi`, `n32g452xx-mini-system` + +If the request is vague, ask before guessing. + +### 2. Clarify the repository version + +For reproducibility, prefer asking the user which official repo version to use: + +- latest default branch +- specific branch +- specific tag +- specific commit + +Use `scripts/list_refs.sh` when the user wants choices. +Use `scripts/ensure_repo.sh --dest --ref ` to pin the checkout. + +### 3. Confirm official support + +A target counts as supported only when it exists under `bsp/` in the official RT-Thread repo. + +Good: +- exact directory exists +- README exists for the BSP or BSP family +- `SConstruct`/`rtconfig.py` exists in the BSP + +Bad: +- only a blog/tutorial exists +- only a fork exists +- only a similar chip exists +- only Studio-generated artifacts exist with no official BSP path + +If unsupported, stop with a clear message such as: + +> 没在官方 `RT-Thread/rt-thread` 的 `bsp/` 里找到这个芯片/开发板的官方 BSP,所以这个 skill 按约定不会替你硬造工程。 + +### 4. Resolve BSP selection strictly + +Use `find_bsp.py` results this way: + +- `exact` → can proceed +- `strong` with exactly one candidate → can proceed +- `ambiguous` → ask the user to choose +- `none` → report unsupported and stop + +Do not auto-pick from weak fuzzy matches. + +### 5. Inspect the BSP + +Check these files first: + +- `README.md` / `README_zh.md` / `readme.md` +- `rtconfig.py` +- `SConstruct` +- `.config` if present + +Extract: +- compiler family (`gcc`, `keil`, `iar`, etc.) +- compiler prefix from `rtconfig.py` +- whether `RTT_EXEC_PATH` / `RTT_CC_PREFIX` / `RTT_CC` are used +- whether `pkgs --update` is required +- whether `menuconfig` is required before first build + +### 6. Choose the build stance + +#### Proceed + +Proceed only if there is a credible GCC path: +- README says GCC is supported, or +- `rtconfig.py` clearly supports `CROSS_TOOL='gcc'` + +#### Stop and report + +Stop if: +- the BSP is clearly MDK/IAR-only, +- GCC support looks absent or broken by design, +- required compiler prefix is unknown and cannot be inferred, +- mandatory prerequisites are missing. + +### 7. Preflight + +Before the real build, verify: + +- repo exists +- BSP exists +- `scons` exists +- compiler binary exists or the exact expected prefix is known +- package manager command requirements are understood + +Do not call it “success” yet. + +### 8. Build + +Typical order: + +1. enter BSP directory +2. `pkgs --update` if the BSP/README requires packages +3. optional `scons --menuconfig` when needed +4. `scons -j$(nproc)` + +Keep environment changes local to the process when possible, for example: + +```sh +RTT_EXEC_PATH=/opt/arm-none-eabi/bin scons -j$(nproc) +``` + +instead of editing shell startup files. + +### 9. Report + +A good completion report contains: + +- repo path +- checked-out branch/tag/commit +- BSP path +- official README used +- whether official support exists +- GCC toolchain prefix expected +- commands run +- whether build succeeded +- artifact files found +- exact blockers if it failed diff --git a/skills/rtthread-bsp-builder/references/common-failures.md b/skills/rtthread-bsp-builder/references/common-failures.md new file mode 100644 index 0000000..6e9e040 --- /dev/null +++ b/skills/rtthread-bsp-builder/references/common-failures.md @@ -0,0 +1,95 @@ +# Common failures + +## 1. `scons: command not found` + +Meaning: +- SCons is not installed or not in PATH. + +What to report: +- The BSP uses SCons for build orchestration. +- Ask the user whether to install/fix SCons. + +## 2. `pkgs: command not found` + +Meaning: +- RT-Thread Env / package tool is not installed or not exposed in PATH. + +What to report: +- Some BSPs require `pkgs --update` before they can compile because HAL/CMSIS/packages are pulled on demand. +- Do not fake success if this step is mandatory. +- Ask before installing anything. + +## 3. Missing cross compiler + +Typical examples: +- `arm-none-eabi-gcc: not found` +- `riscv64-unknown-elf-gcc: not found` +- `riscv32-esp-elf-gcc: not found` + +What to inspect: +- `rtconfig.py` for `PREFIX` +- env vars `RTT_EXEC_PATH`, `RTT_CC_PREFIX`, `RTT_CC` +- README setup steps + +What to report: +- Exact compiler prefix expected +- Exact env var/path that would satisfy the BSP +- Ask before installing toolchains + +## 4. README says GCC is supported, but only IDE projects are obvious + +Meaning: +- GCC may still be supported through `SConstruct` + `rtconfig.py`, even if README examples focus on MDK/IAR. + +What to do: +- Check `rtconfig.py` before rejecting. +- If `CROSS_TOOL='gcc'` and GCC tools are defined, a GCC path likely exists. + +## 5. Packages not pulled yet + +Typical symptom: +- missing HAL/CMSIS/package headers or sources + +What to do: +- Search README for `pkgs --update` +- If required, run that before blaming the BSP + +## 6. `menuconfig` required before first build + +Typical symptom: +- config-dependent headers/files missing +- README explicitly says to refresh config first + +What to do: +- Use the official step order +- Prefer `scons --menuconfig` if the BSP documents it that way +- If config choices materially affect the build, interact with the user instead of changing options silently + +## 7. Dirty repo blocks update or ref switch + +Meaning: +- local modifications exist in the RT-Thread repo clone + +What to do: +- Do not hard reset or stash silently +- Ask the user before destructive cleanup + +## 8. No official BSP found + +Meaning: +- the target is unsupported in official `RT-Thread/rt-thread` + +What to report: +- Say there is no official BSP in `bsp/` +- Optionally give nearby candidates +- Stop instead of generating a fake board port + +## 9. Ambiguous official matches + +Meaning: +- multiple official BSPs look plausible for the user's chip/board query + +What to do: +- show the top strong candidates +- ask the user to choose +- do not auto-pick silently when reproducibility matters diff --git a/skills/rtthread-bsp-builder/scripts/build_bsp.sh b/skills/rtthread-bsp-builder/scripts/build_bsp.sh new file mode 100644 index 0000000..49b8e6d --- /dev/null +++ b/skills/rtthread-bsp-builder/scripts/build_bsp.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO="" +BSP="" +PREFLIGHT=0 +JOBS="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)" + +usage() { + cat <<'EOF' +Usage: build_bsp.sh --repo --bsp [--preflight] + +Run preflight or the default GCC build flow for an official RT-Thread BSP. +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --repo) + REPO="$2"; shift 2 ;; + --bsp) + BSP="$2"; shift 2 ;; + --preflight) + PREFLIGHT=1; shift ;; + -h|--help) + usage; exit 0 ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 2 ;; + esac +done + +[ -n "$REPO" ] || { echo "--repo is required" >&2; exit 2; } +[ -n "$BSP" ] || { echo "--bsp is required" >&2; exit 2; } + +resolve_bsp() { + if [ -d "$REPO/$BSP" ]; then + printf '%s\n' "$REPO/$BSP" + return 0 + fi + if [ -d "$REPO/bsp/$BSP" ]; then + printf '%s\n' "$REPO/bsp/$BSP" + return 0 + fi + local found + found="$(find "$REPO/bsp" -type f -name SConstruct -path "*/$BSP/SConstruct" | head -n1 || true)" + if [ -n "$found" ]; then + dirname "$found" + return 0 + fi + return 1 +} + +pick_readme() { + local dir="$1" + for name in README.md README_zh.md README_ZH.md readme.md readme_en.md; do + if [ -f "$dir/$name" ]; then + printf '%s\n' "$dir/$name" + return 0 + fi + done + return 1 +} + +parse_prefix() { + local rtconfig="$1" + python - "$rtconfig" <<'PY' +import re, sys, pathlib +p = pathlib.Path(sys.argv[1]) +text = p.read_text(encoding='utf-8', errors='ignore') if p.exists() else '' +m = re.search(r"PREFIX\s*=\s*'([^']*)'", text) +print(m.group(1) if m else '') +PY +} + +BSP_DIR="$(resolve_bsp)" || { echo "error=bsp_not_found"; exit 1; } +README="$(pick_readme "$BSP_DIR" || true)" +RTCONFIG="$BSP_DIR/rtconfig.py" +PREFIX="$(parse_prefix "$RTCONFIG")" +CC_BIN="${PREFIX}gcc" + +printf 'repo=%s\n' "$REPO" +printf 'bsp_dir=%s\n' "$BSP_DIR" +printf 'readme=%s\n' "${README:-}" +printf 'rtconfig=%s\n' "$RTCONFIG" +printf 'compiler_prefix=%s\n' "$PREFIX" + +if ! command -v scons >/dev/null 2>&1; then + echo "missing=scons" + exit 3 +fi + +if [ -n "$PREFIX" ] && ! command -v "$CC_BIN" >/dev/null 2>&1; then + echo "missing_compiler=$CC_BIN" + if [ -n "${RTT_EXEC_PATH:-}" ]; then + echo "RTT_EXEC_PATH=${RTT_EXEC_PATH}" + fi + exit 4 +fi + +NEEDS_PKGS=0 +if [ -n "$README" ] && grep -qi 'pkgs --update' "$README"; then + NEEDS_PKGS=1 + if ! command -v pkgs >/dev/null 2>&1; then + echo "missing=pkgs" + exit 5 + fi +fi + +echo "preflight=ok" +if [ "$PREFLIGHT" -eq 1 ]; then + exit 0 +fi + +cd "$BSP_DIR" +if [ "$NEEDS_PKGS" -eq 1 ]; then + echo "+ pkgs --update" + pkgs --update +fi + +echo "+ scons -j$JOBS" +scons -j"$JOBS" + +echo "artifacts:" +find "$BSP_DIR" -maxdepth 2 -type f \( -name 'rtthread.bin' -o -name 'rt-thread.hex' -o -name '*.elf' -o -name '*.axf' -o -name '*.out' -o -name 'rtthread.map' -o -name 'rt-thread.map' \) | sort diff --git a/skills/rtthread-bsp-builder/scripts/ensure_repo.sh b/skills/rtthread-bsp-builder/scripts/ensure_repo.sh new file mode 100644 index 0000000..ec3d36c --- /dev/null +++ b/skills/rtthread-bsp-builder/scripts/ensure_repo.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_URL="https://github.com/RT-Thread/rt-thread.git" +DEST="" +UPDATE=0 +REF="" + +usage() { + cat <<'EOF' +Usage: ensure_repo.sh --dest [--update] [--ref ] + +Clone the official RT-Thread repository if missing. +If the destination already contains a git repo, verify the origin and optionally fast-forward it. +If --ref is provided, checkout that branch/tag/commit safely (refuses on dirty repo). +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --dest) + DEST="$2"; shift 2 ;; + --update) + UPDATE=1; shift ;; + --ref) + REF="$2"; shift 2 ;; + -h|--help) + usage; exit 0 ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 2 ;; + esac +done + +[ -n "$DEST" ] || { echo "--dest is required" >&2; exit 2; } + +checkout_ref() { + local repo="$1" + local ref="$2" + [ -n "$ref" ] || return 0 + git -C "$repo" fetch --depth 1 origin "$ref" >/dev/null 2>&1 || git -C "$repo" fetch --tags origin >/dev/null 2>&1 || true + if git -C "$repo" rev-parse --verify --quiet "origin/$ref" >/dev/null; then + git -C "$repo" checkout -B "$ref" "origin/$ref" + echo "checked_out_ref=origin/$ref" + return 0 + fi + if git -C "$repo" rev-parse --verify --quiet "$ref" >/dev/null; then + git -C "$repo" checkout "$ref" + echo "checked_out_ref=$ref" + return 0 + fi + echo "error=ref_not_found" + echo "ref=$ref" + exit 6 +} + +if [ -d "$DEST/.git" ]; then + origin="$(git -C "$DEST" remote get-url origin 2>/dev/null || true)" + branch="$(git -C "$DEST" branch --show-current 2>/dev/null || true)" + dirty="$(git -C "$DEST" status --porcelain 2>/dev/null || true)" + head="$(git -C "$DEST" rev-parse HEAD 2>/dev/null || true)" + echo "repo_exists=1" + echo "repo_path=$DEST" + echo "origin=$origin" + echo "branch=${branch:-detached}" + echo "head=$head" + if [ -n "$dirty" ]; then + echo "dirty=1" + else + echo "dirty=0" + fi + if [ "$UPDATE" -eq 1 ]; then + if [ -n "$dirty" ]; then + echo "update_skipped=dirty_repo" + exit 0 + fi + git -C "$DEST" fetch --depth 1 origin + if [ -n "$branch" ]; then + git -C "$DEST" pull --ff-only origin "$branch" + else + echo "update_skipped=detached_head" + fi + echo "updated=1" + fi + if [ -n "$REF" ]; then + if [ -n "$dirty" ]; then + echo "checkout_skipped=dirty_repo" + exit 0 + fi + checkout_ref "$DEST" "$REF" + fi + exit 0 +fi + +mkdir -p "$(dirname "$DEST")" +git clone --depth 1 "$REPO_URL" "$DEST" +if [ -n "$REF" ]; then + checkout_ref "$DEST" "$REF" +fi +echo "repo_exists=1" +echo "repo_path=$DEST" +echo "origin=$REPO_URL" +echo "dirty=0" +echo "updated=0" +echo "head=$(git -C "$DEST" rev-parse HEAD 2>/dev/null || true)" diff --git a/skills/rtthread-bsp-builder/scripts/find_bsp.py b/skills/rtthread-bsp-builder/scripts/find_bsp.py new file mode 100644 index 0000000..a8cb61f --- /dev/null +++ b/skills/rtthread-bsp-builder/scripts/find_bsp.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +import argparse +import difflib +import json +import re +import sys +from pathlib import Path + + +README_NAMES = ["README.md", "README_zh.md", "README_ZH.md", "readme.md", "readme_en.md"] + + +def norm(text: str) -> str: + text = text.lower().strip() + text = re.sub(r"[^a-z0-9]+", " ", text) + return re.sub(r"\s+", " ", text).strip() + + +def token_set(text: str) -> set[str]: + n = norm(text) + return set(n.split()) if n else set() + + +def score(query: str, rel: str) -> float: + q = norm(query) + r = norm(rel) + base = difflib.SequenceMatcher(None, q, r).ratio() + if q == r or q == norm(Path(rel).name): + base += 1.5 + elif q and q in r: + base += 0.7 + q_tokens = set(q.split()) + r_tokens = set(r.split()) + if q_tokens: + overlap = len(q_tokens & r_tokens) / len(q_tokens) + base += overlap + return round(base, 4) + + +def find_readme(path: Path): + for name in README_NAMES: + p = path / name + if p.exists(): + return str(p) + return None + + +def is_strong_match(query: str, rel: str, name: str, match_score: float) -> bool: + q = norm(query) + if not q: + return False + + norm_name = norm(name) + norm_rel = norm(rel) + q_tokens = token_set(query) + name_tokens = token_set(name) + rel_tokens = token_set(rel) + + exact = q == norm_name or q == norm_rel + contains = q in norm_name or q in norm_rel + token_subset = bool(q_tokens) and (q_tokens <= name_tokens or q_tokens <= rel_tokens) + + return exact or token_subset or (contains and match_score >= 1.0) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--repo", required=True) + ap.add_argument("--query", required=True) + ap.add_argument("--limit", type=int, default=10) + args = ap.parse_args() + + repo = Path(args.repo) + bsp_root = repo / "bsp" + if not bsp_root.is_dir(): + print(json.dumps({"error": "bsp directory not found", "repo": str(repo)}, ensure_ascii=False, indent=2)) + return 1 + + rows = [] + for sconstruct in bsp_root.rglob("SConstruct"): + bsp_dir = sconstruct.parent + rel = bsp_dir.relative_to(repo).as_posix() + match_score = score(args.query, rel) + rows.append( + { + "path": rel, + "name": bsp_dir.name, + "score": match_score, + "readme": find_readme(bsp_dir), + "rtconfig": str((bsp_dir / "rtconfig.py")) if (bsp_dir / "rtconfig.py").exists() else None, + "strong_match": is_strong_match(args.query, rel, bsp_dir.name, match_score), + } + ) + + rows.sort(key=lambda x: (-x["score"], x["path"])) + strong = [row for row in rows if row["strong_match"]] + + exact = [ + row + for row in strong + if norm(args.query) in {norm(row["name"]), norm(row["path"])} + ] + + if len(exact) == 1: + resolution = "exact" + supported = True + chosen = exact[0] + elif len(strong) == 1: + resolution = "strong" + supported = True + chosen = strong[0] + elif len(strong) > 1: + resolution = "ambiguous" + supported = False + chosen = None + else: + resolution = "none" + supported = False + chosen = None + + out = { + "query": args.query, + "repo": str(repo), + "total": len(rows), + "supported": supported, + "resolution": resolution, + "chosen": chosen, + "strong_matches": strong[: args.limit], + "matches": rows[: args.limit], + } + print(json.dumps(out, ensure_ascii=False, indent=2)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/skills/rtthread-bsp-builder/scripts/inspect_bsp.py b/skills/rtthread-bsp-builder/scripts/inspect_bsp.py new file mode 100644 index 0000000..0b087e0 --- /dev/null +++ b/skills/rtthread-bsp-builder/scripts/inspect_bsp.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +import re +import sys +from pathlib import Path + +README_NAMES = ["README.md", "README_zh.md", "README_ZH.md", "readme.md", "readme_en.md"] + + +def find_bsp_dir(repo: Path, bsp_arg: str) -> Path | None: + p = repo / bsp_arg + if p.is_dir(): + return p + p = repo / 'bsp' / bsp_arg + if p.is_dir(): + return p + for sconstruct in (repo / 'bsp').rglob('SConstruct'): + if sconstruct.parent.name == bsp_arg: + return sconstruct.parent + return None + + +def readme_path(bsp_dir: Path): + for name in README_NAMES: + p = bsp_dir / name + if p.exists(): + return p + return None + + +def parse_rtconfig(rtconfig: Path | None): + info = { + "cross_tool_default": None, + "prefix": None, + "uses_rtt_exec_path": False, + "uses_rtt_cc_prefix": False, + "uses_rtt_cc": False, + } + if not rtconfig or not rtconfig.exists(): + return info + text = rtconfig.read_text(encoding='utf-8', errors='ignore') + m = re.search(r"CROSS_TOOL\s*=\s*'([^']+)'", text) + if m: + info["cross_tool_default"] = m.group(1) + m = re.search(r"PREFIX\s*=\s*'([^']*)'", text) + if m: + info["prefix"] = m.group(1) + info["uses_rtt_exec_path"] = 'RTT_EXEC_PATH' in text + info["uses_rtt_cc_prefix"] = 'RTT_CC_PREFIX' in text + info["uses_rtt_cc"] = 'RTT_CC' in text + return info + + +def parse_readme(readme: Path | None): + info = { + "pkgs_update_required": False, + "menuconfig_mentions": [], + "gcc_mentions": [], + "mdk_mentions": [], + "iar_mentions": [], + } + if not readme or not readme.exists(): + return info + lines = readme.read_text(encoding='utf-8', errors='ignore').splitlines() + for i, line in enumerate(lines, 1): + low = line.lower() + if 'pkgs --update' in low: + info["pkgs_update_required"] = True + if 'menuconfig' in low: + info["menuconfig_mentions"].append({"line": i, "text": line.strip()[:220]}) + if 'gcc' in low: + info["gcc_mentions"].append({"line": i, "text": line.strip()[:220]}) + if 'mdk' in low or 'keil' in low: + info["mdk_mentions"].append({"line": i, "text": line.strip()[:220]}) + if 'iar' in low: + info["iar_mentions"].append({"line": i, "text": line.strip()[:220]}) + return info + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('--repo', required=True) + ap.add_argument('--bsp', required=True) + args = ap.parse_args() + + repo = Path(args.repo) + bsp_dir = find_bsp_dir(repo, args.bsp) + if not bsp_dir: + print(json.dumps({"error": "bsp not found", "bsp": args.bsp, "repo": str(repo)})) + return 1 + + readme = readme_path(bsp_dir) + rtconfig = bsp_dir / 'rtconfig.py' + out = { + "repo": str(repo), + "bsp": bsp_dir.relative_to(repo).as_posix(), + "readme": str(readme) if readme else None, + "rtconfig": str(rtconfig) if rtconfig.exists() else None, + "sconstruct": str(bsp_dir / 'SConstruct') if (bsp_dir / 'SConstruct').exists() else None, + "rtconfig_info": parse_rtconfig(rtconfig if rtconfig.exists() else None), + "readme_info": parse_readme(readme), + } + print(json.dumps(out, ensure_ascii=False, indent=2)) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/skills/rtthread-bsp-builder/scripts/list_refs.sh b/skills/rtthread-bsp-builder/scripts/list_refs.sh new file mode 100644 index 0000000..a0e9337 --- /dev/null +++ b/skills/rtthread-bsp-builder/scripts/list_refs.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_URL="https://github.com/RT-Thread/rt-thread.git" +TYPE="all" +LIMIT=30 + +usage() { + cat <<'EOF' +Usage: list_refs.sh [--type branches|tags|all] [--limit N] + +List recent remote branches/tags from the official RT-Thread repository. +Use this when the user wants to choose a repository version before cloning/building. +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --type) + TYPE="$2"; shift 2 ;; + --limit) + LIMIT="$2"; shift 2 ;; + -h|--help) + usage; exit 0 ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 2 ;; + esac +done + +case "$TYPE" in + branches) + git ls-remote --heads "$REPO_URL" | sed 's#.*refs/heads/##' | sort | tail -n "$LIMIT" + ;; + tags) + git ls-remote --tags "$REPO_URL" | sed 's#.*refs/tags/##' | sed 's/\^{}$//' | sort -Vu | tail -n "$LIMIT" + ;; + all) + echo "[branches]" + git ls-remote --heads "$REPO_URL" | sed 's#.*refs/heads/##' | sort | tail -n "$LIMIT" + echo + echo "[tags]" + git ls-remote --tags "$REPO_URL" | sed 's#.*refs/tags/##' | sed 's/\^{}$//' | sort -Vu | tail -n "$LIMIT" + ;; + *) + echo "Invalid --type: $TYPE" >&2 + exit 2 ;; +esac