Skip to content

Latest commit

 

History

History
224 lines (158 loc) · 17.8 KB

File metadata and controls

224 lines (158 loc) · 17.8 KB

嵌入式平台支持 — 方案设计 (Embedded Platform Support)

  • Date: 2026-07-24
  • Status: 设计定调(维护者决策已锁),待实施
  • 关联: issue #276 — feat/RFC: 支持 Buildroot/OpenWrt/Yocto 等嵌入式 Linux SDK 的工程化集成
  • 结构:背景与定位 → 决策点(为什么,前置)→ 工作清单 → 详情(现状核实/可行性,后置)

0. TL;DR

mcpp 进入嵌入式 Linux 生态的方案:mcpp 自带匹配-libc 的交叉编译器(最新 GCC),消费开发者提供的外部 sysroot;不做发行版构建器、不逐个适配厂商 SDK。

  • 核心只需开一个 sysroot 缝(机制已在),其余(pkg-config、qemu test-run)是 xlings provisioning + 薄接线。
  • 首发滩头 = 树莓派 aarch64
  • 这个架构把 #276 最难的「厂商旧编译器 vs C++23」问题用**「根本不用旧编译器」**直接溶掉——mcpp 永远用自己的现代编译器,设备只提供库。

1. 背景与现状(为什么需要这个方案)

1.1 issue #276 要什么

一份 RFC,讨论 mcpp 对 hosted 嵌入式 Linux(Buildroot / OpenWrt / Yocto / uClibc·musl·glibc + 厂商定制工具链)的定位。三个核心问题:

  1. 厂商 SDK 锁死旧编译器(如 GCC 8.3、uClibc 绑定)与 mcpp 要 C++23 的矛盾;
  2. 与 Buildroot/Yocto/OpenWrt SDK 的集成方式(外部 TARGET_CC、staging sysroot、作为 build backend);
  3. 第三方库管理与动态链接(如 ALSA:必须用设备那份、带插件/配置/ABI 耦合)。

诉求一句话:mcpp 能否从「自成体系、自管依赖、hermetic 静态倾向」转向能「消费外部 SDK 的交叉工具链 + 外部 sysroot + 外部第三方库」。

1.2 mcpp 今天的模型 vs 嵌入式的需要

mcpp 今天 嵌入式需要
工具链 自带、download-managed 现代交叉编译器(可自带)
sysroot 仅内部(自家 payload) 消费外部设备 rootfs
第三方库 索引下载、源码构建 消费设备已有(动态、pkg-config)
链接 hermetic(拒绝宿主库) 需对着外部 sysroot 链

gap = 缺入口(通用缝),不是缺能力:CLibMode::Sysroot 已能发 --sysroot、hermetic 白名单已含 tc.sysroot——只差把外部路径喂进去的配置通道。


2. 定位与职责边界

mcpp 做发行版构建器(≠ Yocto/Buildroot),逐个适配厂商 SDK。它是「构建现代 C++23-modules 应用的最佳工具」。职责四分:

mcpp 拥有:      编译器 + C++ 构建/包模型 + 通用交叉缝(target / sysroot / pkg-config)
库作者/社区拥有: 把每个库从源码打包给 mcpp(compat.*),一次,通吃所有目标
平台/厂商拥有:   设备 rootfs/sysroot(含专有 + 运行期耦合库)
mcpp 永不拥有:   逐 SDK 适配、变成发行版构建器

3. 决策点 + 理由(核心,前置)

维护者(sunrisepeak)在设计评审中锁定的定调。每条都附「为什么」。

# 决策 为什么
1 mcpp = C++ app 构建工具,不做发行版构建器/不逐 SDK 适配 造整个平台是 Yocto/Buildroot 的活(几千 recipe + BSP + 十年生命周期),且平台 99% 是 C 而非 C++23-modules,mcpp 的模块图/import std 优势完全用不上;战略应聚焦本职
2 架构 = config②:mcpp 带(匹配 libc 的)交叉编译器 + 消费外部 sysroot ABI 沿缝解耦——--sysroot 管设备 libc、静态 libstdc++ 自包含 C++ 运行期、C-ABI 管第三方;让 mcpp 用自己的现代编译器,设备只提供库;把 C++23 门槛搬到 mcpp 掌控的编译器一侧
3 滩头 = 树莓派 aarch64(后续 Jetson…) 三轴全占最优(mainline ISA + hosted + 浅锁 + glibc);出货最大、社区最广、mcpp 已有 aarch64 交叉基建;打通几乎白送 Jetson(同为 Ubuntu/aarch64/glibc)
4 核心单点特性 = 开 sysroot 缝(L2) 90/10 的一刀,解锁标准「对着 rootfs 交叉」工作流(所有嵌入式 Linux C++ 开发的通用姿势);机制已在,最小改动
5 C++ 只源码;二进制只 C-ABI + 封装层 C++ 二进制 ABI 跨编译器/版本天生不安全(名字修饰、libstdc++ ABI、vtable、异常);源码编 = ABI 一致;C-ABI 是唯一稳定的二进制互操作契约。拒绝预编译 C++ .so = 正确性,非限制
6 绑定质量/类型安全 = 封装作者的领域知识,非 mcpp 机制 schema-ownership 原则(mcpp 定机制、领域专家定语义);保持核心小,绑定可经索引一次写、人人用
7 运行期耦合/专有库 = dlopen 或有 sysroot 时直接链 dlopen 让链接期只碰 libc/libdl(完全 hermetic、无需 sysroot),自包含二进制 + 运行期用设备库;有 sysroot 时直接链更类型安全。绑定(源码)随索引、二进制留设备。[runtime] dlopen_libs 钩子已存在
8 编译器:先 GCC GCC 的 import std(bits/std.cc)比 clang 实验实现成熟,且 mcpp 已以 GCC 为默认。clang 路线(像 rustc,一个编译器 --target 切 libc,少维护 payload)存档待议
9 厂商预编译 C++-only 库 = 用户用厂商 ABI 建 C 桥 .so,mcpp 经 C ABI 消费 把 ABI 匹配负担搬进桥(用厂商工具链编、纯 C 边界、catch 住 C++ 异常),mcpp 只见 C ABI,政策 #5 不破——所以是支持,非 non-goal
10 glibc 基线 = 造 payload 时 pin 低 glibc + 一次实证 GCC 版本 × glibc 版本解耦,可「最新 GCC + 低 glibc」;工具链自带的静态 libstdc++ 的 glibc 基线 --sysroot 压不下去,但 payload 是 mcpp 自己造的 → 造时 pin 低即可。payload 用最新 GCC(当前 GCC 16.1 = mcpp 默认),GCC 15 只是 import-std 能力 floor,不是 payload 版本
11 .pc:开发者自解决 生产 rootfs 常剥掉 -dev(头 + .pc);mcpp 有 .pc 就用 pkg-config、无则退回手写 ldflags(永远可用);mcpp 不造 .pc
12 sysroot 获取/管理/pinning = 开发者的事 产品专属 sysroot 是某个产品的指纹,只有「造了产品的东西」能产出;mcpp 消费不制造。可复现 pinning 仅量产需要(而量产后端不做,见 #14)
13 sysroot 布局:支持 3 主流覆盖大部分 ① Debian/Ubuntu multiarch /usr/lib/<triple>/(滩头)② 纯 /usr/lib+/lib(musl/Buildroot/OpenWrt)③ /usr/lib64(RPM 系/部分 Yocto);靠工具链自带搜索 + 少量 -L 兜底
14 L4 后端模式(被 Yocto/Buildroot 驱动):不做 与「自管闭环」价值观张力最大;只在量产为 Yocto/Buildroot recipe 时才需;开发者交叉工作流不需要
15 裸机 / freestanding-modules / ESP32:出范围 / 推迟 需 freestanding modules(无 hosted import std)核心特性;ESP32 三难合一(定制 ISA Xtensa + 裸机 + 专有 IDF),且 512KB RAM MCU 上 C++23-modules 优势无关
16 qemu 交叉 test-run + pkg-config = xlings provisioning 免费 + 薄接线 xlings 能装 qemu/pkgconf(同装工具链机制),不是新子系统;qemu 覆盖逻辑测试,碰硬件的测试仍需真机(模拟固有边界,非缺陷)
17 「编译器不支持 modules/C++23」明确诊断取消 config② 下 target 编译器永远是 mcpp 自带的最新 GCC(16+),该场景架构上构造不出;唯一残留(显式 [toolchain]="system" 指向旧编译器)已被现有 hasImportStd 门 + import-std 诊断(prepare.cppm:3419)兜住

元结论:config②(mcpp 带编译器)把 #276 核心问题 1(厂商旧编译器 vs C++23)整个溶掉——靠「根本不用旧编译器」,而非「诊断旧编译器」。对 #276 那句「目标编译器不支持 C++23 时给诊断」的正确答复 = 「不需要,设备的编译器从不被调用」。用架构消解问题,比诊断问题更干净。


4. 净工作清单

优先级 性质
P0 sysroot 缝(L2):manifest/CLI 字段 → Toolchain::sysrootCLibMode::Sysroot(已能发 --sysroot)+ hermetic 白名单(已含 tc.sysroot);含 3 种布局解析(#13) 核心,小
P0 匹配 libc 的最新 GCC(GCC 16) payload、pin 低 glibc(aarch64-linux-gnu 首要,补 armv7/riscv64;glibc+musl 变体)+ 一次 glibc 地板实证 分发/数据,非引擎
P1 pkg-config(pkgconf via xlings + PKG_CONFIG_SYSROOT_DIR/LIBDIR sysroot-aware) 工效,薄
P1 qemu 交叉 test-run(qemu-user via xlings + QEMU_LD_PREFIX=<sysroot>) 工作流,薄
去掉 L4 后端模式、sysroot pinning/管理、造 .pc、modules-不支持诊断
推迟 freestanding modules(裸机)、clang 路线

glibc 地板实证(P0 的唯一真实未知):自建 or 采纳 GCC 16 × 低 glibc(如 2.28/2.31)的 aarch64 交叉链,编一个 import std; 最小静态程序,在 qemu-user / 真机不同 glibc 版本下验证「import-std 静态产物的 glibc 地板能压多低」。注:Bootlin 2025.08 bleeding-edge 仍是 GCC 15.1,GCC 16 大概率要自建(mcpp 已有 musl-cross/mingw-cross 自建先例)或等 Bootlin 2026.x。该实证决定工具链选型(自建低-glibc GCC16 / 采纳 Bootlin 某档 / musl-static 兜底)。


5. 关键机制(怎么做)

5.1 config②:带编译器 + 消费 sysroot

mcpp 交叉 GCC 16(匹配 libc)+ 外部设备 sysroot(--sysroot)。运行期这样拆:

组件 来自 方式
机器码 mcpp 的 GCC 16(ISA 代码生成)
libstdc++ / import stdstd.o mcpp 的 GCC 16 静态嵌入(-static-libstdc++ 默认)→ 设备老 libstdc++ 不参与
glibc / CRT / 动态链接器 设备 sysroot --sysroot,匹配设备实际 loader
第三方 C-ABI 库(ALSA 等) 设备 sysroot 动态链接,ABI 与 rootfs 一致

5.2 sysroot 缝(L2)= 最小插入点

  • 已有:CLibMode::Sysroot(linkmodel.cppm)link_flags()--sysroot=<root>;hermetic 白名单(hermetic.cppm:117-130)已含 tc.sysroot
  • 只需:一个 manifest/CLI 字段把外部路径填进 Toolchain::sysrootresolve_link_model 自动走 Sysroot 模式 → 链接对着设备 libc + 库,类型安全、hermetic(不用 allow_host_libs)。

5.3 三条溶解尾巴的政策

  1. C++ 只源码;二进制只 C-ABI + 封装。 拒绝预编译 C++ .so = 正确性。
  2. 绑定质量 = 封装作者的活。 mcpp 定机制、作者定语义。
  3. 运行期耦合/专有库 = dlopen 或有 sysroot 直接链。 绑定随索引、二进制留设备。

5.4 C++ 封装的两种形态

  • 有源码/C-ABI 库:全局模块片段 #include C 头 → export module xxx re-export(generated_files/scan_overrides 已支持)。
  • 厂商 C++-only 二进制:用厂商 ABI 编译器建一个纯 C 边界的桥 .so(内部调 C++、catch 住异常、只导出 extern "C"),mcpp 经 C ABI 消费。

5.5 dlopen vs 直接链(何时用哪个)

直接链(需 sysroot) dlopen
编译期类型检查 ✅(有头) ✗(手动 dlsym,封装层内隐藏)
链接期依赖 需 sysroot 里的头/桩 只需 libc/libdl,完全 hermetic
二进制形态 动态链设备库 链接期自包含(需动态可执行文件才能 dlopen)
适合 sysroot 齐全、要类型安全 版本韧性、避免构建期依赖设备库

注:全静态可执行文件不能 dlopen(musl 是失败桩、glibc 不可靠)→ 走 dlopen 需动态可执行文件 + libc 匹配设备 + 静态 libstdc++。


6. 详情:现状核实与可行性(后置)

6.1 硬边界:C++23-modules floor(不可协商,但交叉模式绕开)

mcpp 产物 = C++23 modules + import std + 链接 std.o,要求编译器 GCC ≥ 15 / Clang ≥ 18–19。hasImportStd能力门控非版本门控(gcc.cppm:66-136:只看有没有 bits/std.cc)。std.o 必须由 target 工具链的 bits/std.cc 编出(stdmod.cppmgcc.cppm:155-192)。

交叉模式下 target 编译器是 mcpp 自带的(满足),设备只需兼容运行期、不需现代编译器 → 「厂商 GCC 8.3」被绕开。真正出范围:裸机无 import std(需 freestanding-modules)。

6.2 现状核实(四条线,均代码验证)

  • 工具链身份:封闭 Family{Gcc,Llvm,Msvc}(registry.cppm:30);GCC/LLVM 一律走 to_xim_package → xim 下载(registry.cppm:200-255);manifest toolchain 值只接受 family@version 不接受路径。唯一 detection-first「系统工具链」是 msvc@system(is_system_toolchain 硬编码只认 MSVC,registry.cppm:298-300);explicit_compiler 路径通道已存在(probe.cppm:234-242)。
  • sysroot/链接模型:CLibMode{None,PayloadFirst,Sysroot}(linkmodel.cppm);Sysroot 模式已能发 --sysroot,但来源写死(probe_sysroot:-print-sysroot/xlings remap/macOS SDK),无外部输入通道;hermetic 白名单已含 tc.sysroot(hermetic.cppm:117-130)。
  • pkg-config:全仓库(src/docs/README/CHANGELOG)grep 零命中——完全不存在。
  • 环境注入:只 fallback 读 CXX(probe.cppm:246),不读 CC/CFLAGS/LDFLAGS/AR/SYSROOT/PKG_CONFIG_*
  • C++23 门控:默认 c++23;import std 路径有明确诊断(prepare.cppm:3419);modules-无-import-std 透传原始编译器错误。

6.3 限制清单(config② 下)

  1. libc 家族必须匹配(glibc vs musl):--sysroot 换路径不换 libc 目标(GCC 编译期烙死);需同时发 glibc/musl 两种交叉工具链。clang 可用 --target 切(更像 rustc)。
  2. 设备 glibc ≥ 工具链 glibc 基线:预编译静态 libstdc++/libgcc 的基线 --sysroot 压不下去 → 造 payload 时 pin 低(决策 #10)。
  3. 保持静态 libstdc++:动态链设备旧 libstdc++.so 给不了 C++23 符号 → 崩;用默认(静态)。
  4. sysroot 里 C++-ABI 第三方:名字修饰 + 两份 libstdc++ 隐患 → 需 C 桥(决策 #9);C-ABI 第三方无碍。
  5. 运行期库闭包完整性(libatomic 那课):静态兜底或确保设备有需要的运行期库。

6.4 版本图景(哪些真实工具链跨过 import-std floor)

工具链 / SDK GCC libc 过 floor
mcpp 默认 16.1 glibc/musl ✅ payload 用这个
Bootlin 2025.08 bleeding-edge 15.1 glibc 2.41 / uClibc-ng / musl ✅(但仍 15,GCC16 待自建)
ARM GNU 15.2.rel1 15.x newlib/glibc
Zephyr SDK 14.3 picolibc
Yocto 5.0 Scarthgap(LTS) 13.2 glibc 2.39
OpenWrt / 厂商老 SDK 12 / 8.x musl / uClibc

结论:现代 import-std-capable 交叉工具链已存在(Bootlin GCC 15 覆盖三大 libc、ARM GNU 15),但厂商锁死的旧 SDK 仍在硬边界外——所以 config②「自带编译器」而非「消费厂商工具链」是对的。

6.5 平台可攻性三轴(为什么树莓派是滩头,ESP32 是远角)

可攻性 = ISA 是否 mainline × hosted-or-裸机 × 厂商锁深度

  • 树莓派:mainline aarch64 + hosted + 浅锁 = 三轴最优 → 滩头。
  • Jetson:同形状(Ubuntu/aarch64/glibc)→ 几乎白送。
  • ESP32:定制 ISA(Xtensa,被迫用厂商工具链)+ 裸机(需 freestanding modules)+ 深度专有 IDF(拥有构建/烧录/WiFi blob)= 三轴最差 → 队尾,且 C++23 优势无关。

6.6 开放风险 / 需实证的点

  • A(头号,需实证):glibc 基线选型 —— 见决策 #10 + P0 实证。musl-static 可完全绕开(可移植性务实默认)。
  • B:生产 rootfs 常剥 -dev(无头/.pc)→ 需 SDK staging sysroot;归开发者(决策 #11/#12)。
  • C:aarch64-linux-gnu payload 还没有(只有 musl)→ 工作项非风险(决策 C)。
  • D:clang vs gcc 战略分叉 → 先 GCC(决策 #8),clang 存档。
  • E:厂商 C++-only 库 → C 桥支持(决策 #9)。
  • F:sysroot 可复现/pinning → 归开发者(决策 #12)。
  • G:multiarch 布局差异 → 支持 3 主流(决策 #13)。

7. 关键文件索引

  • 工具链身份 / xim 映射 / is_system_toolchain:src/toolchain/registry.cppm(30, 41-62, 200-255, 298-300)
  • explicit_compiler 路径通道(路径式编译器的种子):src/toolchain/probe.cppm:234-250detect.cppm:16-37
  • 链接模型 + Sysroot 模式(最小切入点 L2):src/toolchain/linkmodel.cppm(26-31, 80-95, 226-279)
  • hermetic 断言(白名单已含 tc.sysroot):src/build/hermetic.cppm:103-212(117-130)
  • sysroot 探测(无外部入口 = 待补):src/toolchain/probe.cppm:271-380src/fallback/probe_sysroot.cppm
  • C++23 能力门控 / std.o:src/toolchain/gcc.cppm:66-192stdmod.cppmbuild/prepare.cppm:3419-3464
  • manifest schema(无 sysroot/pkg-config 字段 = 待补)、per-target cfg 条件:src/manifest/types.cppmtoml.cppmbuild/prepare.cppm:77-191
  • [runtime] dlopen_libs 钩子:src/manifest/types.cppm(RuntimeConfig)、build/plan.cppm
  • detection-first 先例(msvc@system):src/toolchain/msvc.cppm.agents/docs/2026-07-13-msvc-system-toolchain-detection-design.md
  • hermetic link model 底座:.agents/docs/2026-07-07-hermetic-toolchain-link-model-design.md
  • 交叉工具链先例:.agents/docs/2026-07-15-mingw-linux-cross-windows-design.md2026-06-23-aarch64-musl-gcc-canadian-cross-rebuild.md

附录 A:Rust/Cargo 如何解决同类问题(对照)

Rust 靠三个结构性属性,mcpp 有其二、缺其一:

  1. 一个通用 LLVM 编译器 + rustup 管理器(编译器与目标解耦,rustup target add 只下预编译 std)→ mcpp 已选「自管工具链」,但 GCC 非天生多目标(须每 arch 一套;clang 路线更接近)。
  2. no_std 分层 std(裸机目标侧运行期需求为零)→ mcpp 的等价物 = freestanding C++23 modules,尚未实现(决策 #15 推迟)。
  3. 静态优先 + -sys crate + pkg-config(动态库尾巴走 PKG_CONFIG_SYSROOT_DIR)→ mcpp 静态优先已有,pkg-config 待补(P1)。

关键洞见:Rust 没有消灭 sysroot/外部库问题——hosted Linux + 动态系统库场景 Rust 一样有,解法也一样(委托 C 交叉工具链 + pkg-config + 目标 sysroot,或走静态)。Rust 消灭的是「旧厂商编译器」问题(单一通用上游 + no_std)。 mcpp 的 config② + 自管工具链正是对标这一点。