RFC(勿合入): 适配 HarmonyOS/OpenHarmony —— 可重定向的 clang + 平台 SDK 作 sysroot - #351
RFC(勿合入): 适配 HarmonyOS/OpenHarmony —— 可重定向的 clang + 平台 SDK 作 sysroot#351speak-agent wants to merge 2 commits into
Conversation
CI 运行说明本仓的
唯一跑不了的是 本机验证结果:e2e 全量 178 passed / 0 failed / 8 skipped(含新增的 103/104 两条鸿蒙用例),单测 56 个二进制全绿(新增 16 条),自举构建正常。 |
CI 结果:五个 workflow 全绿(跑的就是本 PR 的 commit)
最后一条是这次最想看到的:既有 cross 矩阵(aarch64-musl/qemu、mingw/wine、windows→linux)在 在 CI runner 上 一处补验(把 CI 与本机之间最后一点偏差抹掉)
所以 还差一个 job 没跑
|
…atform SDK
RFC / 探针 —— 不要合入。方案与实测数据:
.agents/docs/2026-08-04-harmonyos-target-design.md
鸿蒙 SDK 自带的编译器 mcpp 永远用不了:实测 SDK 6.1(API 23,2026-03)仍是
clang 15.0.4,比 C++20 模块所需的 -fmodule-output(clang 16)差一代,比
import std(libc++ 19+)差四代。而 GCC 根本没有 ohos target。
所以鸿蒙是第一个「只能走 config②、且只能用 clang」的目标 —— mcpp 自带编译器,
平台只提供 sysroot。这逼出了仓库里早就记着待做的那件事:
# cross-build-test.yml
# * llvm/clang cross : … Wire the clang cross path first, then add a row.
本 commit 就是 wire 那条缝(Toolchain::crossTarget:--target= + 外部 sysroot
+ 目标 libc++ + 链接侧 -resource-dir),鸿蒙只是第一个非它不可的消费者。
三个不显然的决定:
* `ohos` 是 env 不是 os(与上游 LLVM 一致)。内核确实是 Linux,已有包里的
cfg(os = "linux") / cfg(family = "unix") 必须继续匹配;拼成新 os 会让那些段
静默失配。反过来 is_musl() 返回 false —— OHOS libc 是 musl 的 fork,但 mcpp
里 "musl" 处处指上游 musl,产物不可互换 ⇒ ABI 上 libc = "ohos" 自成取值。
* -resource-dir 只在链接侧。它供给目标的 compiler-rt/crt*,但同一个 flag 也换掉
内建头,让新 clang 读 clang-15 的内建头是另一个安静得多的 bug。
* std.cppm 由 provider 给,不由 driver 探。driver 答的是它自己的 libc++,交叉时
那是宿主的 —— 拿去编 std BMI 不报错,只产出面向错误平台的 BMI。
两档,mcpp 自动识别并明说在哪一档:原版 SDK ⇒ 具名模块可用、import std 不可用
(有明确提示);再配一份为目标编译的 libc++($MCPP_OHOS_LIBCXX)⇒ import std
也可用。两档都有 e2e,都在 qemu-aarch64 下真跑。
验证:e2e 全量 178 passed / 0 failed / 8 skipped;单测 56 个二进制全绿(新增 16
条 host-independent 用例);自举构建正常 —— 宿主路径零行为变化,
OhosCross.HostToolchainIsUntouchedByTheseChanges 是这条的回归闸。
CI 证明不了:.hnp/.hap 打包、链接平台 NDK 库、任何需要真机或模拟器的行为。
qemu-user 跑的是指令集不是 HarmonyOS。
41ff2d9 to
e5abd27
Compare
…mlink chain
The import-std job failed on the line AFTER mcpp printed
Installed llvm@20.1.7 → …/xim-x-llvm/20.1.7/bin/clang++
because the probe was `find … -type f -name clang++`, and in the LLVM
payload that path is a symlink chain (clang++ -> clang -> clang-20). The
install had succeeded; only the check was wrong. Name the path instead, and
list bin/ on failure so the next wrong assumption is visible from the log.
✅ 全绿 —— 包括
|
| workflow | 结果 |
|---|---|
ci-linux / ci-linux-e2e |
✅ |
ci-macos / ci-macos-e2e |
✅ |
ci-windows / ci-windows-e2e |
✅ |
cross-build-test |
✅ 既有 cross 矩阵一条没坏 |
ci-harmonyos |
✅ 两个 job 都过 |
CI 里的实证(不是本机的)
tier 1(原版 SDK):
OHOS (dev) clang version 15.0.4 ← SDK 自带的,mcpp 不用它
aarch64-linux-ohos static, cross llvm 20.1.7 available ← host_can_serve 探到 SDK
… ELF 64-bit LSB executable, ARM aarch64 … statically linked
harmonyos cross named-module OK
__OHOS__ is defined: this really is a HarmonyOS target
OK: HarmonyOS cross artefact builds and executes
tier 2(用 llvmorg-20.1.7 源码为目标现编 libc++):
Resolved aarch64-linux-ohos → OpenHarmony SDK 6.1.0.31 (API 23) + external libc++
import std on HarmonyOS
total=20
OK: import std works on HarmonyOS
两条都在 qemu-aarch64 下真的执行过。
中间挂过一次,记一下
tier 2 第一轮红在我自己的探针上,不在改动上 —— mcpp 刚打印完
Installed llvm@20.1.7 → …/xim-x-llvm/20.1.7/bin/clang++
下一行就 FAIL: no clang++ in the llvm payload。因为探针写的是 find … -type f -name clang++,而 payload 里那是一条符号链(clang++ -> clang -> clang-20),-type f 看不见。已改成直接写路径,并在失败时把 bin/ 列出来,免得下一个错误假设又只能靠猜。
方案文档:
.agents/docs/2026-08-04-harmonyos-target-design.md(含全部实测数据)参考项目:TermonyHQ/Termony
TL;DR
鸿蒙可以成为 mcpp 的一等目标,而且是完整体验 —— 具名模块和
import std都跑通了。代价是必须先把 config② 里一直没实现的那半(clang 交叉通道)做出来。
结论 1:鸿蒙 SDK 自带的编译器,mcpp 永远用不了
这是整个设计的地基,所以先摆证据 —— 实测,不是从文档推断:
这是 SDK 6.1(API 23,2026-03 发布),不是什么老版本。而:
-fmodule-output(C++20 模块)import std(libc++ std 模块)std.cppm差一到四代,而且已经这样很多年。「等厂商升级」不是方案。
Termony 走的是「用 SDK 编译器」那条路,对它成立(编的基本是 C);对 mcpp 结构性
不成立 —— 模块图和
import std是 mcpp 的全部价值,那个编译器一行都编不了。Termony 真正给到的参考是验证路径:qemu-user 跑鸿蒙 aarch64 产物可行(已复现)。
结论 2:所以鸿蒙是第一个「只能 config②、且只能 clang」的目标
.agents/docs/2026-07-24-embedded-platform-support-design.md的决策 #2 定了config②(mcpp 自带编译器 + 消费外部 sysroot),决策 #8 定了「先 GCC,clang 路线存档」。
鸿蒙把这两条撞在一起:
不是「适合用 clang」,是「除了 clang 无路可走」。 决策 #8 存档的那条路线被现实
提前触发了。
而这个缺口仓库里早就写着:
本 PR 做的就是 "wire the clang cross path first"。鸿蒙只是第一个非它不可的消费者
——
aarch64-linux-gnu(决策 #3 的树莓派滩头)之后可以走同一条缝,不必再造一套。结论 3:
import std不是能力缺口,是 payload 缺口用 LLVM 源码给
aarch64-linux-ohos编一份 libc++(关键是-DLIBCXX_HAS_MUSL_LIBC=ON),std.cppm就有了:所以是两档,mcpp 自动识别并明说自己在哪一档:
import stdMCPP_OHOS_LIBCXX)两档都有 e2e,都在 qemu 下真跑。
机制(三个不显然的决定)
ohos是 env 不是 osaarch64-linux-ohos= archaarch64+ oslinux+ envohos,与上游 LLVM(
llvm::Triple::OpenHOS)一致。这有后果,而且后果是对的:内核确实是 Linux,所以已有可移植包里的
cfg(os = "linux")/cfg(family = "unix")必须继续匹配。若拼成一个新 os,那些段会静默失配 —— 一个包在鸿蒙上突然少编一半源码,零诊断。
反向那条同样重要:
is_musl()返回 false。OHOS libc 确实是 musl 的 fork,但mcpp 里 "musl" 处处指上游 musl(payload 选择、
abi:muslcapability),鸿蒙产物与之不可互换 ⇒ ABI 维度上
libc = "ohos"自成取值。而 loader 命名照旧走 musl 那条(实测 PT_INTERP =
/lib/ld-musl-aarch64.so.1)。同一个事实在两个问题上给出不同答案,这正是它们得是两个函数的原因。
-resource-dir只能出现在链接侧上游 clang 的 OHOS driver 会去自己的 resource dir 找
libclang_rt.builtins.a和clang_rt.crt{begin,end}.o—— 它当然没有aarch64-linux-ohos子目录,于是cannot open crtbeginT.o。指向 SDK 的那份就解决了。但同一个 flag 也换掉内建头(
stddef.h等),让 clang 20 去读 clang 15 的内建头是另一个安静得多的 bug。mcpp 的 compile/link flags 本来就是两条串,所以只在后者发。
std.cppm由 provider 给,不由 driver 探clang::enrich_toolchain靠-print-library-module-manifest-path找std.cppm——driver 答的是它自己的 libc++,交叉时那是宿主的。拿它去编
stdBMI 不会报错,只会产出一个面向错误平台的 BMI。所以交叉路径整段短路;供不出来就
hasImportStd = false+ 明确提示。同理
probe_payload_paths()在交叉时必须跳过 —— 它会找到编译器旁边宿主的 glibcxpkg,而
CLibMode::PayloadFirst会把宿主的crt1.o/libc.so/loader 喂给一个外国目标。验证
本机全部跑过(x86_64 Linux + OpenHarmony SDK 6.1.0.31 + qemu-aarch64):
mcpp build --target aarch64-linux-ohos→ELF … ARM aarch64 … statically linkedmcpp test56 个测试二进制全绿;自举构建正常 ⇒ 宿主路径零行为变化(
OhosCross.HostToolchainIsUntouchedByTheseChanges是这条的回归闸)CI(
.github/workflows/ci-harmonyos.yml,两个 job):tier 1 用openharmony-rs/setup-ohos-sdk装 SDK 后交叉编 + qemu 跑;tier 2 额外从 LLVM 源码编目标 libc++ 再验
import std。CI 证明不了什么(重要)
.hnp/.hap打包与hdc安装完全没做。libace_napi.z.so等)。全静态产物本来也链不了(只有.so);真做鸿蒙 App 要走动态链接,而实测动态产物在 qemu 下跑不起来 ⇒ 那一档必须真机/
模拟器验证,CI 给不了绿。
这是「利用 CI 的各种 OS 资源」这条里唯一没兑现的部分,原因是客观不可得。
verified档位的定义是「CI builds and executes」。本 PR 的aarch64-linux-ohos满足这个定义 —— 但满足的是与
aarch64-linux-musl同级的断言,不是「鸿蒙 App能上架」。
需要维护者定调的三件事
ohos-libcxx要不要 payload 化? 这是唯一横在「能用」与「好用」之间的东西。做了,
mcpp build --target aarch64-linux-ohos就是开箱完整体验。.hnp/.hap打包属不属于 mcpp 职责? 对照决策 feat: compile .c sources via a separate C rule #1「不做发行版构建器」,我倾向不做,但这是边界问题不是技术问题。
aarch64-linux-gnu(树莓派)? 如果要,feat/RFC: 支持 Buildroot/OpenWrt/Yocto 等嵌入式 Linux SDK 的工程化集成 #276 的P0 排序会变 —— 原方案里「自建低 glibc 的 GCC 16」是唯一真实未知,而这条缝让它
不再是前置条件。
改了什么
引擎 12 个文件(见方案文档 §4 的表),核心是新的
Toolchain::crossTarget+src/toolchain/ohos.cppm。新增单测 1 个文件 16 条、e2e 2 条、CI workflow 1 个、示例 1 个、文档若干。