Skip to content

Commit b4306eb

Browse files
committed
feat(aarch64): cross-build to aarch64-linux-musl + Android-capable CI
Enables `mcpp build --target aarch64-linux-musl` to cross-compile from x86_64 (and natively on ARM) to a fully static aarch64 ELF — runnable on Android/Termux with no bionic dependency. Validated end-to-end: a tiny `import std` program AND mcpp itself cross-build and run under qemu-aarch64. Toolchain selection (host-aware native vs cross): - platform/common.cppm: add host_arch. - build/prepare.cppm: *-musl convention picks native (musl-gcc, target==host) vs cross (<triple>-gcc) by host_arch. - toolchain/registry.cppm: recognize target-named musl specs (aarch64-linux-musl-gcc) -> package name == triple, frontend <triple>-g++; derive musl frontend/ar from the target triple instead of hardcoded x86_64. - mcpp.toml: [target.aarch64-linux-musl] (host-aware, no pinned toolchain). Cross-correctness fixes (mcpp wrongly applied the glibc-gcc model to the self-contained musl-cross-make toolchain): - toolchain/gcc.cppm: std-module build skips the external binutils -B for musl (else host x86 `as` chokes: `unrecognized option '-EL'`). - toolchain/stdmod.cppm + build/flags.cppm: skip the external linux-headers -isystem for musl (sysroot ships its own; host headers are the wrong arch). CI: - .github/workflows/ci-aarch64.yml: cross-build + qemu-run (uses the toolchain delivered via xim:aarch64-linux-musl-gcc -> xlings-res). Docs: .agents/docs/ aarch64/Android MVP design + ecosystem analysis.
1 parent b8241a8 commit b4306eb

10 files changed

Lines changed: 773 additions & 17 deletions

File tree

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# aarch64 / Android 支持 —— 跨仓库 MVP 顶层设计方案
2+
3+
> 北极星目标:**让 mcpp 在 Android 手机(Termux)上原生运行,并能在手机上构建出 C++ 程序**,全程不依赖 Android 的 bionic libc。
4+
> 本文是**顶层执行方案**(phases / 跨仓库协调 / 验收门)。逐点 file:line 适配清单见 [`todos/2026-06-22-aarch64-linux-ecosystem-support-analysis.md`](todos/2026-06-22-aarch64-linux-ecosystem-support-analysis.md)
5+
> 日期:2026-06-22。起因:[mcpp#143](https://github.com/mcpp-community/mcpp/issues/143)
6+
7+
---
8+
9+
## 1. 战略决策(已在分析中定型)
10+
11+
| 决策 | 选择 | 理由 |
12+
|---|---|---|
13+
| **路线** | **musl-static 全静态** | 全静态 = 无 PT_INTERP、无 glibc payload、无 sysroot patch、不碰 bionic;同时绕开 "glibc 能否在 Android 内核跑" 这个变量 |
14+
| **arch token** | `arm64`(非 `aarch64`) | 引擎 `detect_arch_()` 强制;XLINGS_RES 资产必须叫 `…-linux-arm64.tar.gz` |
15+
| **分发** | 复用 `XLINGS_RES` 哨兵 | 引擎自动按 `detect_arch_()` 拼 arch,`.lua``linux={}`**不需改**,只上传资产 |
16+
| **arch 系统** | **暂不做完整三元组** | 现在靠 XLINGS_RES 自动 arch 足够;`abi` 轴(glibc/musl/bionic)比 arch 轴更关键,留到 Android/多 abi 阶段再上 `(os,arch,abi)` target 概念 |
17+
| **toolchain** | `aarch64-linux-musl` gcc-15 | `import std` 需要 gcc≥15;`musl-cross-make.lua` 已支持 15.1.0 + loader 派生已 arch-generic |
18+
19+
### 关键技术事实(支撑可行性)
20+
- `mcpp build` / `pack` **不执行产物**(执行只在 `run`/`test`)→ **交叉编译可行**;代价:x86_64 上不能 `test/run` aarch64 产物。
21+
- musl-static 链接路径 **arch-干净**:`-static` 无 INTERP,glibc loader 硬编码(`flags.cppm:336``pack.cppm:648`)都不在此路径上。
22+
- 显式 `--target aarch64-linux-musl` 绕开 `pipeline.cppm:37,61` 的 x86_64 默认 fallback。
23+
- `targetTriple` 来自编译器 `-dumpmachine` → 指向真 `aarch64-linux-musl-g++` 即自动产 aarch64。
24+
- **自举无捷径**:必须先有"第 0 个 aarch64 mcpp"(连 Nix 也需 per-platform bootstrap seed)。
25+
26+
---
27+
28+
## 1b. 工具链包模型(2026-06-22 细化,已部分落地)
29+
30+
把"原生"和"交叉"切成**两个独立的 xim 包**,mcpp 按 host 感知二选一:
31+
32+
|| 语义 | host / target | XLINGS_RES 资产 | 谁用 |
33+
|---|---|---|---|---|
34+
| `musl-gcc`(已存在,需泛化) | **原生** target=host | host=target | `musl-gcc-<ver>-linux-<hostarch>.tar.gz`(host 自动) | 设备端(aarch64→aarch64 在 ARM 上) |
35+
| `aarch64-linux-musl-gcc`(**新增**) | **交叉** target≠host | host=x86_64 / target=aarch64 | `aarch64-linux-musl-gcc-<ver>-linux-x86_64.tar.gz` | x86 上交叉构建 aarch64 |
36+
37+
**mcpp 的 host 感知选择**(`src/build/prepare.cppm` `*-musl` 约定分支,已实现):
38+
- target arch == `mcpp::platform::host_arch` → 原生 → `gcc@15.1.0-musl``xim:musl-gcc`
39+
- target arch != host_arch → 交叉 → `<triple>-gcc@15.1.0``xim:<triple>-gcc`
40+
41+
配套已实现的 mcpp 改动:
42+
- `src/platform/common.cppm`:新增 `host_arch` 常量(GNU 拼写 `aarch64`,非 xlings 资产用的 `arm64`)。
43+
- `src/toolchain/registry.cppm`:识别目标命名工具链 spec(`*-linux-musl-gcc`)→ 包名即 triple,前端 `<triple>-g++`;`frontend_candidates_for`/`archive_tool` 按 triple 派生。
44+
- `mcpp.toml`:`[target.aarch64-linux-musl]` 不写死 toolchain,交给 host 感知约定。
45+
46+
**Canadian-cross 两步产出序列**(都入 xlings 生态):
47+
```
48+
A. build=x86 host=x86 target=aarch64 → 交叉编译器 → 包 aarch64-linux-musl-gcc
49+
(musl-cross-make,gcc 15.1.0)
50+
B. 基于 A,build=x86 host=aarch64 target=aarch64 → 原生 → musl-gcc 的 arm64 资产
51+
(Canadian-cross,或在 ARM 上原生构建)
52+
```
53+
- A 解决 x86 CI 交叉构建(也用于引导第 0 个 aarch64 mcpp)。
54+
- B 解决设备端原生构建(#143 端用户),产出 `musl-gcc-<ver>-linux-arm64.tar.gz`,经 `musl-gcc.lua` 泛化后由 XLINGS_RES 在 aarch64 host 自动选中。
55+
56+
> 命名要点:交叉包 **必须** 把 target 写进包名(host 维度交给 XLINGS_RES);原生包沿用 `musl-gcc`(host=target,XLINGS_RES 按 host arch 自动区分)。
57+
58+
## 1c. 进展记录(2026-06-22)
59+
60+
**✅ G0 达成 + mcpp 自身交叉编译成功** —— mcpp 在 x86_64 上交叉构建出可运行的 aarch64 静态 ELF,**并交叉编译出 mcpp 本身**(`mcpp-0.0.58-linux-aarch64`,15M 全静态;`qemu-aarch64 mcpp --version``mcpp 0.0.58`)。这就是 #143 的可交付二进制(musl 全静态 → 可在 Android/Termux 原生运行,不依赖 bionic)。
61+
62+
验证链:
63+
- 交叉工具链 `aarch64-linux-musl` gcc-15.1.0 经 musl-cross-make 构建成功;直接编译 + `import std` 均产出 `ELF aarch64, statically linked`
64+
- `mcpp build --target aarch64-linux-musl` 端到端:解析 cross 工具链 → 跨编 std BMI → 静态链接 → `target/aarch64-linux-musl/.../aatest` 为 aarch64 ELF。
65+
- **qemu-aarch64 实际运行通过**;`x86_64-linux-musl` 原生路径回归正常(未破坏)。
66+
67+
落地的代码改动(mcpp):
68+
- `src/platform/common.cppm`:`host_arch` 常量。
69+
- `src/build/prepare.cppm`:host 感知 native/cross 选择。
70+
- `src/toolchain/registry.cppm`:目标命名 spec(`*-linux-musl-gcc`)识别 + triple 派生前端/ar。
71+
- `mcpp.toml`:`[target.aarch64-linux-musl]`(host 感知,无写死 toolchain)。
72+
73+
发现并修复的**交叉正确性 bug**(mcpp 此前把 glibc-gcc 模型套用到 musl 自包含工具链):
74+
- `src/toolchain/gcc.cppm`:std 构建对 musl 跳过外部 binutils `-B`(否则 x86 `as``unrecognized option '-EL'`)。
75+
- `src/toolchain/stdmod.cppm` + `src/build/flags.cppm`:对 musl 跳过外部 linux-headers `-isystem`(self-contained sysroot 自带,且交叉时 host 头是错 arch)。
76+
77+
xlings 生态侧:
78+
- `xim-pkgindex/pkgs/a/aarch64-linux-musl-gcc.lua`:cross 包(长命令 + gcc-flavor `15.1.0-aarch64-musl` 版本标识)。
79+
- `xim-pkgindex/pkgs/m/musl-cross-make.lua`:target_list`aarch64-linux-musl`
80+
81+
**已知限制 / 待办**:
82+
- 本次端到端测试用"预装到 xpkgs(`.mcpp_ok`)"绕过下载,因为 **xlings 下载器仅支持 HTTPS,不支持 `file://`**。生产路径 = 把 `aarch64-linux-musl-gcc-15.1.0-linux-x86_64.tar.gz`(411MB,已打包)发到 `https://xlings-res`(GLOBAL github + CN gitcode)。
83+
- 可选改进:给 xlings downloader 加 `file://` 本地资源支持,利于离线/本地包开发。
84+
- 步骤 B(Canadian-cross 产 host=aarch64 原生 `musl-gcc` arm64 资产)尚未做。
85+
- libcc1 在 install 阶段失败(可选的 GDB 插件,非编译必需);打包时建议 `--disable-libcc1` 去噪。
86+
87+
## 2. 三个根本难题与解法
88+
89+
```mermaid
90+
graph TD
91+
P1["难题1: 鸡生蛋<br/>(mcpp 自己编自己,但没有 aarch64 引导二进制)"]
92+
P2["难题2: 设备上要原生 aarch64 编译器<br/>(交叉 gcc 只解决构建主机,不解决设备)"]
93+
P3["难题3: Android 不是标准 Linux<br/>(bionic / W^X / SELinux / $PREFIX 布局)"]
94+
95+
P1 --> S1["交叉编译第0个 mcpp<br/>(x86_64 → aarch64-linux-musl static)"]
96+
P2 --> S2["Canadian-cross 造原生 aarch64 gcc<br/>(build=x86_64, host=aarch64, target=aarch64)<br/>或 ARM runner 原生构建"]
97+
P3 --> S3["全静态绕开 bionic ABI<br/>+ 适配 Termux $PREFIX/exec 限制"]
98+
```
99+
100+
---
101+
102+
## 3. 分阶段执行方案(MVP → Android)
103+
104+
### 阶段总览
105+
106+
```mermaid
107+
flowchart LR
108+
A["P0<br/>交叉出 aarch64 mcpp"] --> B["P1<br/>aarch64-Linux 原生跑通"]
109+
B --> C["P2<br/>xlings aarch64 + 分发闭环"]
110+
C --> D["P3<br/>Android/Termux 原生构建"]
111+
A -.最快验证.-> V["给 #143 试用<br/>(proot 或真机)"]
112+
```
113+
114+
---
115+
116+
### P0 — 交叉构建第 0 个 aarch64 mcpp(打破鸡生蛋)
117+
118+
**目标**:在 x86_64 主机上产出一个 `aarch64-linux-musl` 全静态 mcpp 二进制。
119+
120+
| # | 仓库 | 交付物 |
121+
|---|---|---|
122+
| P0-1 | `xim-pkgindex` | `musl-cross-make.lua``target_list``aarch64-linux-musl`(上游原生支持,loader 派生已 arch-generic) |
123+
| P0-2 | (本地) |`xlings run musl-cross-make --target aarch64-linux-musl --gcc 15.1.0` 造出交叉 gcc-15 |
124+
| P0-3 | `mcpp` | 摸清/打通"消费本地交叉工具链"的接法(见 §5 风险①);`mcpp.toml``[target.aarch64-linux-musl] linkage=static` |
125+
| P0-4 | `mcpp` | `mcpp build --target aarch64-linux-musl` + `mcpp pack --target aarch64-linux-musl` |
126+
127+
**验收门 G0**:`file <out>``ELF 64-bit LSB, ARM aarch64, statically linked`;QEMU-user 或 aarch64 box 上能 `--version`
128+
129+
> 🚀 **此处即可给 #143 报告人最快验证**:把 G0 产物丢进 Termux(原生或 proot)跑 `--version`,证明 self-contained 在 ARM 成立。
130+
131+
---
132+
133+
### P1 — aarch64-Linux 标准发行版原生跑通
134+
135+
**目标**:在真 aarch64 Linux(ARM 服务器 / RPi64 / QEMU / Termux-proot-Ubuntu)上,mcpp 能 `build` 出 hello-world。
136+
137+
| # | 仓库 | 交付物 |
138+
|---|---|---|
139+
| P1-1 | (构建) | **原生 aarch64 gcc-15**:Canadian-cross(build=x86_64,host=aarch64,target=aarch64)或 ARM runner 原生构建 —— 因为编译器要在设备上跑 |
140+
| P1-2 | `mcpp` | 修阻塞运行时的 arch 硬编码:`pipeline.cppm:37,61` 默认 target 按 host 派生;`install.sh:28-38``Linux-aarch64→linux-arm64` |
141+
| P1-3 | `mcpp` | (建议)`src/platform/common.cppm``host_arch` 抽象 + `arch→loader` 映射,统一收编散落硬编码 |
142+
| P1-4 | `mcpp` | 顺手修 `doctor.cppm:106`(只认 `std.gcm`,Clang 产 `std.pcm` 误报) |
143+
144+
**验收门 G1**:aarch64 Linux 上 `mcpp build` + 运行一个 `import std` 的 hello-world 成功。
145+
146+
---
147+
148+
### P2 — xlings aarch64 + 分发闭环
149+
150+
**目标**:`xlings install mcpp` 在 aarch64-Linux 上一键拉通完整工具链。
151+
152+
| # | 仓库 | 交付物 |
153+
|---|---|---|
154+
| P2-1 | `xlings` | CI 产出 `xlings-<ver>-linux-arm64.tar.gz`(`ubuntu-24.04-arm` 原生或交叉);`linux_release.sh` 参数化 `ARCH`/`MCPP_TARGET`;`setup_musl_runtime.sh` musl loader 参数化;`installer.cppm:779` os.arch stub 修正 |
155+
| P2-2 | `xlings-res` | 上架 arm64 资产:`mcpp-<ver>-linux-arm64` + 原生 aarch64 musl 工具链(GLOBAL=github + CN=gitcode 双端) |
156+
| P2-3 | `xim-pkgindex` | 各包 `archs={}``arm64`;走 XLINGS_RES 的包 **块不改**(macOS 已是活证据);仅 `musl-gcc.lua` 需把 `x86_64-linux-musl-*` 程序名泛化 |
157+
| P2-4 | `libxpkg` | `elfpatch.lua` + `xpkg-lua-stdlib.cppm:1181` 加 aarch64 loader 路径(`ld-musl-aarch64.so.1`)—— 供非全静态包用;纯静态可后置 |
158+
| P2-5 | `mcpp` | `release.yml` 加 aarch64 job;`.xlings.json` pin 无需改 arch;README 状态 🔄→✅ |
159+
| P2-6 | (引导) | 把 P0 的引导产物注册成 xim-x-mcpp 的 arm64 资产,完成自举闭环 |
160+
161+
**验收门 G2**:干净 aarch64 Linux 上 `curl install.sh | bash``mcpp build` 全程自动,无手工干预。
162+
163+
---
164+
165+
### P3 — Android / Termux 原生构建(北极星)
166+
167+
**目标**:真 Android 手机的 Termux 里,**不进 proot**,mcpp 原生 build + run 出程序。
168+
169+
| # | 仓库 | 交付物 / 攻关点 |
170+
|---|---|---|
171+
| P3-1 | (构建) | **设备上的编译器**:原生 aarch64 musl gcc 要能在 Android 内核跑。优先 **musl-static gcc**(无 loader);退路 musl-dynamic + bundle 极简 `ld-musl-aarch64.so.1` + patch INTERP |
172+
| P3-2 | `mcpp`/`xlings` | Termux 环境适配:`$PREFIX` 路径、`PATH`、Android 10+ 的 **W^X / SELinux exec 限制**(Termux home 可执行,验证下载二进制的 exec 权限链) |
173+
| P3-3 | `mcpp` | std module BMI 在 musl-libstdc++ 上的设备内生成验证(编译期,不需执行) |
174+
| P3-4 | (验证) | 真机:`mcpp new``mcpp build``mcpp run` 一个 `import std` 程序 |
175+
176+
**验收门 G3**:Android 手机 Termux 原生(无 proot)跑通 `mcpp build && mcpp run`
177+
178+
> Android **bionic 原生 target**(`android` 平台维度 + NDK 工具链)**不在本方案** —— 那是再大一个量级的独立工程。本方案用 musl-static **在 Android 上但不依赖 bionic** 达成目标。
179+
180+
---
181+
182+
## 4. 跨仓库职责矩阵
183+
184+
| 仓库 | P0 | P1 | P2 | P3 |
185+
|---|---|---|---|---|
186+
| `mcpp` | target 配置 + 工具链消费 | arch 硬编码 + install.sh | release CI | Termux 环境适配 |
187+
| `xim-pkgindex` | musl-cross-make target || archs 元数据 + musl-gcc 泛化 ||
188+
| `xlings` ||| CI 产 arm64 + os.arch 修正 ||
189+
| `xlings-res` ||| 上架 arm64 资产 | (设备工具链资产) |
190+
| `libxpkg` ||| elfpatch aarch64 loader | (musl loader 验证) |
191+
| (构建基建) | 交叉 gcc | 原生 gcc(canadian-cross) || 设备可跑的静态 gcc |
192+
193+
---
194+
195+
## 5. 风险与待确认(决定 P0/P3 成败)
196+
197+
1. **🔴 mcpp 消费"非索引本地工具链"的接法**(P0 阻塞项)
198+
`[target].toolchain``to_xim_package` 解析成 xim 包名。本地自建交叉 gcc 怎么喂给 mcpp?——选项:`$CXX`/PATH 让 probe 探到(probe 支持 `$CXX`)/ 包一层薄 xpkg / 临时注册。**P0 第一步先确认这条。**
199+
200+
2. **🟡 import std 跨/原生 BMI**
201+
gcc-15 musl 必须带 libstdc++ 的 `bits/std.cc` + `include/c++/15/aarch64-linux-musl/` 目标头(musl-cross-make 会编 libstdc++,应有,需实测)。
202+
203+
3. **🟡 设备上 gcc 可跑性**(P3 核心)
204+
musl-static gcc 最稳;若只能 musl-dynamic,需 bundle loader + 验证 Android exec 限制。这是 P3 唯一的真不确定点。
205+
206+
4. **🟢 abi 校验**
207+
mcpp 唯一依赖 `mcpplibs.cmdline``abi:glibc`,musl-static 不冲突。
208+
209+
5. **🟢 ARM runner 可用性**
210+
GitHub `ubuntu-24.04-arm` 是否对本 org 可用,决定 P1/P2 走原生还是交叉。
211+
212+
---
213+
214+
## 6. 最小关键路径(MVP 主线)
215+
216+
```
217+
P0(交叉 mcpp)→ G0 给 #143 验证
218+
└─ 阻塞先解:风险①(工具链消费接法)
219+
P1(原生 aarch64-Linux 跑通)→ G1
220+
P2(xlings + 分发闭环)→ G2 ← 标准 aarch64-Linux 用户可用
221+
P3(Android 原生)→ G3 ← 北极星达成
222+
```
223+
224+
**建议节奏**:先做 P0 的 P0-1 + 风险①探路(轻量),确认接法后再启动耗时的 gcc 构建;G0 一旦达成立即给 #143 闭环反馈,再推 P1→P3。
225+
226+
---
227+
228+
## 7. 关联文档
229+
- 逐点 file:line 适配清单 + 拓扑图:[`todos/2026-06-22-aarch64-linux-ecosystem-support-analysis.md`](todos/2026-06-22-aarch64-linux-ecosystem-support-analysis.md)
230+
- 既有平台支持参考:`2026-05-16-macos-support-design.md``platform-abstraction-plan.md`
231+
- 发布闭环:记忆 [[release-publish-pipeline]]
232+
</content>

0 commit comments

Comments
 (0)