Repository navigation
feat/RFC: 支持 Buildroot/OpenWrt/Yocto 等嵌入式 Linux SDK 的工程化集成 #276
Description
背景
对于实际嵌入式开发来说,交叉编译出一个目标架构的 ELF 通常不是最困难的部分。更关键的问题在于工程化集成:目标系统的 libc、编译器版本、sysroot、第三方库、动态链接、SDK 构建体系,以及最终如何被 Buildroot/OpenWrt/Yocto 这类系统编译和打包。
单片机裸机工程、或者 Ubuntu ARM / Debian ARM 这类主流发行版目标相对简单,暂时不作为这个 issue 的重点。这里主要讨论更常见的嵌入式 Linux 场景,例如:
- Buildroot
- OpenWrt
- Yocto
- 使用 uClibc/musl/glibc + 厂商定制交叉工具链的系统
核心问题 1:旧版/厂商定制编译器与 C++23 的矛盾
很多嵌入式系统的根文件系统本身也是由指定交叉工具链构建出来的。
例如某个厂商 SDK 只提供 GCC 8.3,并且整个 rootfs、libc、系统库、第三方库都使用这个 GCC 8.3 构建。实际项目中甚至可能遇到:
- uClibc 依赖特定版本工具链
- RISC-V 工具链是厂商定制版本
- libc / libstdc++ / ABI 与厂商工具链强绑定
- 升级系统编译器版本等于重做整个 SDK,成本很高
- 有些 SDK 根本没有可行的升级路径
这种情况下,mcpp 如果需要 C++23 编译器,是否还能编译出运行在该系统上的 C++23 程序?
我目前没有想到一个清晰的实现方式。因为问题不只是 mcpp 自身能否运行,而是目标程序最终要链接到该系统的 libc、libstdc++、动态链接器和 sysroot。即使 mcpp 自身可以在 host 上运行,target 侧如果只能使用 GCC 8.3,也很难真正编译 C++23 代码。
所以我想明确 mcpp 对这类场景的定位:
- mcpp 是否只支持目标工具链本身具备 C++23 能力的嵌入式系统?
- 是否可能支持“host 使用现代 mcpp,但 target 使用 SDK 内旧编译器”的模式?
- 对于厂商锁死工具链版本的 SDK,mcpp 有没有推荐路线?
核心问题 2:与 Buildroot/OpenWrt/Yocto SDK 的集成方式
在真实嵌入式系统中,应用通常不是单独手写一条交叉编译命令完成的,而是要接入系统构建体系。
以 Buildroot 为例,通常需要:
- 用 Makefile / Kconfig 管理包
- 使用 Buildroot 提供的
TARGET_CC/TARGET_CXX - 使用 Buildroot 生成的 staging sysroot
- 依赖 Buildroot 的 package 机制准备第三方库
- 最终由 Buildroot 负责安装到 target rootfs 或生成固件镜像
Rust 在 Buildroot 中就是类似模式:Buildroot 内部维护 rustc/cargo 的构建和打包逻辑,由 Buildroot 控制版本、target、sysroot 和安装过程。但 Rust 也会受到 rustc 版本限制,这一点在 C++23 场景下可能更明显。
所以问题是:mcpp 如果要用于这类嵌入式系统,是否需要提供一种“外部 SDK/sysroot 集成模式”,而不是只管理自己的工具链?
期望讨论的方向包括:
- mcpp 是否支持使用外部 SDK 提供的交叉编译器?
- 是否支持显式指定 sysroot?
- 是否支持继承 Buildroot/OpenWrt/Yocto 注入的编译、链接、pkg-config 环境?
- 是否支持作为 Buildroot/OpenWrt/Yocto package 的 build backend 被调用?
核心问题 3:第三方库管理与动态链接
嵌入式 C++ 项目还有一个关键问题是第三方库链接。
Rust 程序很多时候倾向于全静态链接,集成到嵌入式系统里相对直接。但 C++ 项目不一定能这样处理。很多实际场景强依赖系统动态库,例如音频开发常见的 ALSA:
libasound.so- 插件机制
- 配置文件
- 运行时动态加载
- 与 rootfs 中其他库的 ABI 兼容
即使某些库理论上可以静态链接,实际系统中也经常必须使用动态链接。
因此,如果 mcpp 集成到 Buildroot/OpenWrt/Yocto 构建体系中,它需要能适配这些系统的交叉动态链接模型:
- 第三方库由 Buildroot/OpenWrt/Yocto 的 package/Kconfig 体系编译进 sysroot
- C++ 应用从该 sysroot 中查找头文件和库
- 链接时使用 target 动态链接器、rpath/runpath 或系统默认搜索路径
- 不破坏目标 rootfs 的 ABI 和 libc 约束
- 能够正确处理
pkg-config、-I、-L、-l、动态库依赖等信息
也就是说,问题不只是“mcpp 自己能不能下载依赖”,而是 mcpp 能否很好地消费嵌入式 SDK 已经管理好的 sysroot 和第三方库。
期望能力
我希望 mcpp 未来能支持一种更贴近嵌入式 Linux SDK 的工作流。
期望至少能支持:
- 外部交叉工具链
- 外部 sysroot
- SDK 提供的 target 编译/链接 flags
- target
pkg-config - 动态链接到 sysroot 中的第三方库
- 与 Buildroot/OpenWrt/Yocto package 构建流程集成
- 当目标编译器不支持 C++23 时给出明确诊断
希望讨论的问题
- mcpp 对 Buildroot/OpenWrt/Yocto 这类嵌入式 Linux SDK 的定位是什么?
- mcpp 是否计划支持外部 SDK/sysroot,而不是只使用 mcpp 自己管理的工具链?
- 如果厂商 SDK 只能使用 GCC 8.3 这类旧编译器,mcpp 是否有可行方案编译 C++23 项目?
- mcpp 是否可以作为 Buildroot/OpenWrt/Yocto 的 package build backend?
- 第三方动态库链接应该由 mcpp 管理,还是主要消费 SDK/sysroot/pkg-config 提供的信息?
Activity
docs: embedded platform support design (#276) (#277)
Sunrisepeak commentedon Jul 24, 2026
目前mcpp支持交叉编译, 如果是纯app应用 或基于内核syscall进行开发, mcpp 已经可以通过静态链接实现
mcpp后续可能支持的方向
详细的决策点
fix: #278 package identity convergence — index side + dependency side…
speak-agent commentedon Aug 8, 2026
#177 与 #144 已合并到本 issue
本轮全量核验(#397)确认这三条是同一个缺口的三个面:
共同根因:mcpp 只有「自己下载并完全掌控」这一种资源获取模式,凡是「外部已经存在的东西」都缺少一个申报入口 —— 而通道之所以缺,是因为工具链模型只承认一个权威:编译器二进制自己。
detect()(src/toolchain/detect.cppm:37-125)把 triple(:81)、sysroot(:112)、std 模块源(:105-109)全部从同一个二进制推导。这在「mcpp 自带整套工具链」的世界里正确且优雅,但外部 SDK 要求解耦「用什么编译器生成机器码」和「产物将运行在哪个世界里」。好消息:地基已经全在,缺的只是入口
src/toolchain/linkmodel.cppm:29-30—CLibMode { None, PayloadFirst, Sysroot, … }:81-83编译侧发--sysroot=;:106-109链接侧发--sysroot=+-Wl,--dynamic-linker=+-L/-rpath:405-408—else if (!tc.sysroot.empty()) sysroot_mode(tc.sysroot);src/build/hermetic.cppm:130—if (!tc.sysroot.empty()) allowed.push_back(tc.sysroot);外部 sysroot 自动放行,不需要allow_host_libs:112-116— 编译器不在xpkgs下时 hermetic 检查整体早退(「A host/system compiler is the user's explicit opt-in to the host world」)只要
tc.sysroot有值,整条 Sysroot 链路自动就绪。今天就能试(#144 / #276 都没提到的既有能力)
路径:
prepare.cppm:1556-1558(tcSpec == "system"⇒ explicit_compiler 留空)→probe.cppm:249-253(读$CXX)→probe.cppm:330-336(「foreign but usable sysroot beats no sysroot」采纳 SDK 自己的-print-sysroot)→linkmodel.cppm:405-408→hermetic.cppm:112-116。前提:厂商 SDK 的 gcc ≥ 15(带
bits/std.cc)。 这条路tests/e2e/14_toolchain_fallback.sh:35-42钉住了它存在,但明文不断言构建成功,所以请当成「可以试」而不是「受支持」。建议的最小切入点(P0「sysroot 缝」)
一个字段贯穿三个文件,其余全是既有机制自动生效:
TargetSection加std::filesystem::path sysroot,src/manifest/toml.cppm:1155-1160的[target.<triple>]循环加一行解析,并在:1010的合法键白名单登记(否则未知键会让整份 manifest 报错)--sysroot <path>写进BuildOverrides,供 CI / Buildroot 的.mk直接注入,不必改 manifestdetect之后(src/build/prepare.cppm:1729与:1841两次 detect 之后)——detect.cppm:112会无条件覆盖它src/toolchain/fingerprint.cppm必须同时加轴,不加就是回归:外部工具链的身份(路径 +--version摘要)不进指纹,换一台机器复用target/会拿到别人 ABI 的对象文件顺带把
"system"逃生阀产品化(文档化 +mcpp toolchain default system放行 —— 今天被src/toolchain/compat.cppm:110-151的normalize_spec闭词表拒,只能手写 mcpp.toml)。两个文档缺口(从 #177 转来)
docs/下没有「接入非 mcpp 的 C/C++ 库」这一章。三条现有路线([build] include_dirs+ldflags/build.mcpp/ compat 描述符 + 本地[indices])的选择判据无处可查:grep -n "compat\.\|Form B\|xpm" docs/10-publishing-a-library.md命中 0,[indices]在 docs 里只有 1 处顺带提及。docs/05-mcpp-toml.md§2.3 应明写「链接期没有library_dirs/link_libs,用ldflags」,避免下一个用户按 CMake 直觉找键。/cc @tz12323 @haoyn231 @SekaiArendelle @yizhinailong @SuchaharCan