Skip to content

Commit 6de2070

Browse files
committed
docs(plan): aarch64 Linux ecosystem closure — what is missing, and in which repository
mcpp publishes a linux-aarch64 binary, and once it is there the only things that work are `musl-gcc` and `ninja`. Every other host-code package — llvm, glibc, linux-headers, zlib, libxml2, gcc-runtime, mingw-cross-gcc — ships x86_64 only. Surveyed and tabulated across four repositories, with the distinction the whole plan turns on: host code needs a per-host-arch build, target code does not. `picolibc-riscv` declares both arches and needs neither, because one archive of target code serves every host; `glibc` and `linux-headers` look like host packages and are the target's C library, which is a different piece of work. ⚠️ Upstream LLVM stopped publishing linux-aarch64 after 19.x, and mcpp pins 20.1.7 / 22.1.8 with `import std` — so this has to be built rather than repackaged. Two shapes are set out; static-musl is recommended because it takes the dependency chain from five packages to none, and because `musl-gcc` — the one toolchain that works on that machine today — is already that shape. Its first criterion is whether libc++'s std module survives it. ⚠️ And a warning against the obvious mcpp-side shortcut: do not copy the index's architecture coverage into mcpp. That table becomes wrong on the day the llvm build lands, and nothing would say so.
1 parent 5b82b17 commit 6de2070

1 file changed

Lines changed: 265 additions & 0 deletions

File tree

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
# aarch64 Linux:让整个生态可用,并且可测
2+
3+
> 2026-08-26。起因:把 `linux-aarch64` 加进 `ci-target-matrix.yml` 的构建机轴,
4+
> 第一次运行就连挖出三层缺陷,而它们都不在 mcpp 里。
5+
6+
---
7+
8+
## 0. 一句话
9+
10+
**mcpp 为 aarch64 Linux 发布二进制,而它到那里之后能用的东西只有 `musl-gcc`
11+
`ninja`** 其余每一个宿主代码包 —— llvm、glibc、linux-headers、zlib、
12+
libxml2、gcc-runtime、mingw-cross-gcc —— 都只发 x86_64。
13+
14+
⚠️ **这不是「aarch64 没做」,是「aarch64 做了一半而没有任何东西在看」**:
15+
`ci-aarch64-fresh-install.yml` 一直是绿的,因为它验的是
16+
`xlings install mcpp` 装得上、能编一个 hello world,而不是那台机器上的目标矩阵。
17+
18+
---
19+
20+
## 1. 判据:什么叫「aarch64 全生态可用」
21+
22+
不用形容词。四条,每条都能被一条命令回答:
23+
24+
| # | 判据 | 怎么测 |
25+
|---|---|---|
26+
| **J1** | `mcpp toolchain list --format json` 在 aarch64 上列出的每一行,`status != planned` 的都真的装得上 | `tests/matrix/scan.sh``unsupported/other``mismatch` 格数为 0 |
27+
| **J2** | aarch64 的 `expected.tsv` 行存在,且 `coverage` job 通过 | 发布集 == 矩阵集 |
28+
| **J3** | 七个 openkal 仓库在 aarch64 runner 上 CI 绿 | 各仓 `ci.yml` 加 aarch64 leg |
29+
| **J4** | `mcpp-index` 的每个包在 aarch64 上能被 `mcpp add` + 构建 | 索引 smoke 加 aarch64 leg |
30+
31+
**J1 不要求「全部 ok」。** 一个诚实的 `unsupported` 带具名 `reason` 就是通过。
32+
不可接受的是 `mismatch`(解析说行、构建炸了)与 `other`(拒绝了而没有理由)。
33+
34+
---
35+
36+
## 2. 现状:四个仓库各缺什么(实测 2026-08-26)
37+
38+
### 2.1 `xim-pkgindex` —— 主要缺口在这里
39+
40+
|| `archs` 声明 | 实际有 linux-aarch64 资产 | 性质 |
41+
|---|---|---|---|
42+
| `llvm` | `{x86_64, arm64}` | **** | ⚠️ 声明了却没有;`arm64` 只对 macosx 成立 |
43+
| `gcc` | `{x86_64}` || 正确 —— 其它架构由 `musl-gcc` 覆盖 |
44+
| `musl-gcc` | `{x86_64, aarch64}` | ****| |
45+
| `ninja` | `{x86_64, aarch64}` | ****| |
46+
| `glibc` | `{x86_64}` || 目标代码 |
47+
| `linux-headers` | `{x86_64}` || 目标代码 |
48+
| `zlib` | `{x86_64}` || llvm 的宿主侧依赖 |
49+
| `libxml2` | `{x86_64}` || llvm 的宿主侧依赖 |
50+
| `gcc-runtime` | `{x86_64}` || llvm 的宿主侧依赖 |
51+
| `mingw-cross-gcc` | `{x86_64}` || 宿主代码 |
52+
| `picolibc-riscv` | `{x86_64, aarch64}` | 不需要 ✅ | ⭐ 目标代码,一份归档服务所有宿主 |
53+
54+
⚠️ **`llvm.lua` 第 178 行**:`local llvmdir = "llvm-" .. version .. "-linux-x86_64"`
55+
—— 架构写死在解包路径里,与 `archs` 的声明矛盾。
56+
57+
### 2.2 `xlings` —— 发了 aarch64,但形状不同
58+
59+
`xlings-2026.8.17.2-linux-aarch64.tar.gz` 存在。⚠️ 但两个包的**内部布局不一样**:
60+
61+
```
62+
linux-x86_64 subos/default/bin/xlings 513 条目
63+
linux-aarch64 bin/xlings 494 条目
64+
```
65+
66+
(x86_64 的 `subos/default/bin/xlings` 是指向 `bin/xlings` 的符号链接,同一文件。)
67+
68+
### 2.3 `mcpp-index` —— 结构上无缺口
69+
70+
包是**源码分发**(`add_urls` + `add_versions` + sha256),没有 `archs` 字段,
71+
也不需要。缺的是**验证**:没有任何一条 CI 在 aarch64 上构建过它们。
72+
73+
### 2.4 `mcpp` 自己 —— 发布了,但表在说谎
74+
75+
`release.yml` 产出 `mcpp-<v>-linux-aarch64.tar.gz`。而
76+
`available_toolchain_indexes()` 是一张按 OS 分支、**不问架构**的静态表:
77+
78+
```cpp
79+
{ "gcc", ... }, { "musl-gcc", ... }, { llvm::package_name(), ... }
80+
… else if constexpr (is_linux) out.push_back({ "mingw-cross-gcc", ... });
81+
```
82+
83+
⇒ aarch64 上 `toolchain list` 会说 llvm 可装、四个裸机行 `available`,
84+
而 `mcpp toolchain install llvm 22.1.8` 会 404。**声明 ≠ 安装**,又一次。
85+
86+
---
87+
88+
## 3. 分类:宿主代码与目标代码,决定了每一项的工作量
89+
90+
⭐⭐ **这是整份计划里唯一真正的结构判断。** 一个包属于哪一类,决定它需不需要
91+
per-host-arch 的构建:
92+
93+
| 类 | 含义 | aarch64 需要什么 | 本仓涉及的包 |
94+
|---|---|---|---|
95+
| **宿主代码** | 在构建机上运行 | **一份 aarch64 构建** | llvm, musl-gcc, ninja, zlib, libxml2, gcc-runtime, mingw-cross-gcc |
96+
| **目标代码** | 在目标机上运行 | 什么都不用 —— 一份归档服务所有宿主 | picolibc-*, openkal-*(源码) |
97+
| **目标侧 sysroot** | 目标机的 C 库 | 一份 **per-target** 构建,与宿主无关 | glibc, linux-headers |
98+
99+
⚠️ **`glibc` 与 `linux-headers` 在这张表里最容易被归错。** 它们是
100+
`x86_64-linux-gnu` 这个**目标**的 C 库,不是宿主的。aarch64 宿主要它们,是因为
101+
`aarch64-linux-gnu` 这个**目标**需要 —— 那是另一件事,见 §5.4。
102+
103+
---
104+
105+
## 4. 关键决策:aarch64 的 LLVM 怎么建
106+
107+
### 4.1 上游没有
108+
109+
| 来源 | linux-aarch64 |
110+
|---|---|
111+
| `llvm/llvm-project` 19.1.7 | ✅ `clang+llvm-19.1.7-aarch64-linux-gnu.tar.xz` |
112+
| `llvm/llvm-project` 20.1.7 / 21.1.0 | **无** |
113+
| `xlings-res/llvm` 20.1.7 / 22.1.8 | **无** |
114+
115+
⚠️ 上游从 20.x 起停发 linux-aarch64,而 mcpp 钉 20.1.7 / 22.1.8,且需要
116+
C++23 modules + `import std` —— 19.x 顶不上。**只能自己建。**
117+
118+
### 4.2 两个方案
119+
120+
**方案 A:照搬 x86_64 的形状(glibc 动态链接 + 五个依赖包)**
121+
122+
x86_64 的载荷实测:896 MB(bin 756 MB / lib 123 MB / include 16 MB),
123+
`deps = {glibc>=2.39, linux-headers, zlib, libxml2, gcc-runtime}`。
124+
125+
照搬意味着 aarch64 上要先补齐 **zlib / libxml2 / gcc-runtime** 三个宿主包,
126+
外加 aarch64 的 `glibc` / `linux-headers`。⇒ **6 个包的链**。
127+
128+
**方案 B:静态链接 musl(⭐ 推荐)**
129+
130+
用索引里已有的 `aarch64-linux-musl-gcc` 自举,把 clang/lld 静态链到 musl:
131+
132+
- 依赖数 **5 → 0**。没有 glibc、没有 gcc-runtime、没有 zlib/libxml2 的
133+
运行期问题(编进去)。
134+
- 与 `musl-gcc` 在 aarch64 上已经成立的形状一致 —— 那个包正是这样做的,
135+
而它是这台机器上**唯一现在就能用的工具链**。
136+
- ⚠️ 代价:体积更大,且要关掉 `LLVM_ENABLE_LIBXML2` 等可选特性。
137+
- ⚠️ 未验:libc++ 的 `std` 模块在 musl-static 的 clang 下是否完整可用。
138+
**这是方案 B 的第一个判据,必须先于其余工作验证。**
139+
140+
### 4.3 建在哪
141+
142+
`ubuntu-24.04-arm` 是原生 aarch64 runner,GitHub 单 job 上限 6 小时。
143+
LLVM 完整构建约 1.5–3 小时 —— **放得下,不需要交叉编译**。
144+
145+
⚠️ `xlings-res/llvm` 目前是纯资产仓(只有 README + releases),构建脚本
146+
`build-llvm-subpkg.sh` 不在其中。⇒ 需要在该仓新建一个 workflow,
147+
并把 carve 配方(哪些留、哪些删)显式写进去,而不是继续留在某个人的工作目录里。
148+
149+
---
150+
151+
## 5. 分阶段计划与依赖
152+
153+
```
154+
P0 ──┬── P1(llvm 构建)──┬── P3(索引接线)── P4(mcpp 诚实)── P5(生态 CI)
155+
│ │
156+
└── P2(xlings 形状)──┘ P6(aarch64-linux-gnu 目标)
157+
```
158+
159+
### P0 — 让 aarch64 引导跑通(⏳ 进行中,已提交)
160+
161+
三层,一层盖住一层,每修一层才露出下一层:
162+
163+
| # | 缺陷 | 症状 | 状态 |
164+
|---|---|---|---|
165+
| ① | `bootstrap-mcpp` 按 `uname -s` 取 x86_64 xlings | `Exec format error` 126 | ✅ 已修 |
166+
| ② | 缓存键无 `runner.arch`,aarch64 命中 x86_64 缓存 | ①的修复一次没跑到 | ✅ 已修 |
167+
| ③ | 两个 tarball 内部布局不同 | `No such file or directory` 127 | ✅ 已修(find 而非硬编码) |
168+
169+
**判据**:`invariants (linux-aarch64)` 绿。
170+
171+
### P1 — 建 aarch64 的 LLVM
172+
173+
- **P1.1 可行性判据(先做)**:在 `ubuntu-24.04-arm` 上用
174+
`aarch64-linux-musl-gcc` 静态建一个最小 clang+lld,验证
175+
`import std` 与 libc++ 的 `std.cppm` 可用。⭐ 这一条不过,方案 B 作废,回 A。
176+
- **P1.2**:在 `xlings-res/llvm` 新建 `build-aarch64.yml`,产出
177+
`llvm-22.1.8-linux-aarch64.tar.xz` + `.sha256`,carve 配方写进 workflow。
178+
- **P1.3**:同样产出 20.1.7(target 表两个版本都在用)。
179+
- **判据**:资产可下载,sha256 匹配,解包后 `bin/clang++ --version` 在
180+
aarch64 上退 0。
181+
182+
### P2 — xlings 包形状统一
183+
184+
⚠️ 不改 xlings 的发布(那会动别的消费者)。**mcpp 侧已经改成 find 而非硬编码**,
185+
这是正确的方向:消费者不该假设生产者的内部布局。
186+
187+
- **P2.1**:向 xlings 报一个 issue,说明两个 Linux 包布局不一致。
188+
- **判据**:issue 有编号;mcpp 侧的 find 已经不依赖它被修。
189+
190+
### P3 — `xim-pkgindex` 接线
191+
192+
- **P3.1** `llvm.lua`:把第 178 行的 `linux-x86_64` 改成按 `os.arch()` 取,
193+
并给 linux 分支加 aarch64 的资源条目。
194+
- **P3.2** ⚠️ **`archs` 要按 OS 拆**,或者在 linux 分支缺资产时让 xim 说
195+
「本架构未发布」而不是 404。⭐ 这是「声明 ≠ 安装」在索引侧的同一条:
196+
一个包级 `archs` 覆盖三个 OS,而三个 OS 的资产覆盖面不同。
197+
- **P3.3**(方案 A 才需要)补 zlib / libxml2 / gcc-runtime 的 aarch64。
198+
- **判据**:`xlings install llvm@22.1.8` 在 aarch64 上成功;
199+
失败时的消息点名架构。
200+
201+
### P4 — mcpp 对 aarch64 诚实
202+
203+
- **P4.1** `available_toolchain_indexes()` 目前按 OS 分支不问架构。
204+
⚠️ **不要把索引数据抄进 mcpp** —— 那正是会漂移的形状。
205+
正确做法:让这张表回答「这个 (os, arch) 上这个族有没有载荷」,
206+
而答案来自已有的 `list_available_xpkg_versions()` 查询,
207+
与 `gcc_native_payload_is_musl()` 已经在做的事同型。
208+
- **P4.2** `host_can_serve()` 对裸机无条件 `true`,理由是
209+
「clang/lld 按构造就是交叉编译器」。⚠️ **那个理由预设了 clang 在这台机器上
210+
存在。** 在 llvm 落地前,aarch64 上四个裸机行应报一句可读的拒绝,
211+
带 `reason`,而不是 404。
212+
- **判据**:aarch64 上 `scan.sh` 的 `mismatch` 与 `other` 均为 0。
213+
214+
### P5 — 生态 CI 加 aarch64
215+
216+
- **P5.1** 七个 openkal 仓库的 `ci.yml` 各加一条 `ubuntu-24.04-arm` leg。
217+
⚠️ `openkal-llvm-runtime` 要求 `mcpp:compiler=llvm`,**依赖 P1**;
218+
`openkal-musl` 只 `provides c-abi=musl`,**现在就能跑**。
219+
⇒ 分两批:musl 层先行,llvm 层等 P1。
220+
- **P5.2** `mcpplibs-index` smoke 加 aarch64 leg(源码分发,只需验证能建)。
221+
- **P5.3** 回填 `tests/matrix/expected.tsv` 的 `linux-aarch64` 行。
222+
- **判据**:J2 / J3 / J4。
223+
224+
### P6 — `aarch64-linux-gnu` 作为**目标**(独立,可后置)
225+
226+
今天它是 `planned`。要让它 `verified` 需要 aarch64 的 `glibc` 与
227+
`linux-headers` **目标侧**载荷。⚠️ 与 P1–P5 无依赖关系 —— 那是「aarch64 作为
228+
构建机」,这是「aarch64 作为目标」。**两件事不要混。**
229+
230+
---
231+
232+
## 6. CI 验收
233+
234+
| 层 | 在哪 | 加什么 |
235+
|---|---|---|
236+
| 构建机轴 | `ci-target-matrix.yml` | ✅ 已加 `linux-aarch64` |
237+
| 分母 | `coverage` job | ✅ 已加:发布集 == 矩阵集,双向 |
238+
| 期望表 | `tests/matrix/expected.tsv` | P5.3 回填 aarch64 行 |
239+
| 生态 | 七个 openkal 仓 | P5.1,分两批 |
240+
| 索引 | `mcpplibs-index` | P5.2 |
241+
| 引导 | `ci-aarch64-fresh-install.yml` | ⚠️ 它验的是装得上,**不是**目标矩阵。保留,但不要把它的绿读成覆盖 |
242+
243+
⚠️⚠️ **`expected.tsv` 里不写 `mismatch`。** 写下它就是把缺陷声明成期望,
244+
矩阵会在那一格恒绿。aarch64 的行要么 `ok`,要么带具名 `reason` 的
245+
`unsupported` —— llvm 缺席期间,四个裸机行应当是后者。
246+
247+
---
248+
249+
## 7. 已知陷阱
250+
251+
⚠️ **一层修复会被上一层盖住。** P0 的三条就是这样:缓存键的缺陷让架构修复
252+
一次都没执行。改完一层一定要看下一层的读数,而不是假定它通了。
253+
254+
⚠️ **改共享路径前先查那三台正在工作的宿主。** P0③ 的 find 在 x86_64 上解析到
255+
另一个路径;查过才知道两者是符号链接、同一文件。一个悄悄把 macOS 和 Windows
256+
挪到别的二进制上的修复,比它修的缺陷更糟。
257+
258+
⚠️ **`ci-aarch64-fresh-install.yml` 一直是绿的。** 它走
259+
`quick_install.sh`(自己读架构),所以从来没碰到 P0①。**一条绿的 CI 不证明
260+
另一条路径可用** —— 这正是为什么构建机轴要按 mcpp **发布**的那一组来定,
261+
而不是按「手头有哪几台 runner」。
262+
263+
⚠️ **不要把索引的架构覆盖面抄进 mcpp。** P4.1 的诱惑是写一张
264+
「llvm 没有 aarch64」的表。那张表会在 P1 落地的当天变成错的,而没有任何东西
265+
会提醒你。要问索引,不要记住索引。

0 commit comments

Comments
 (0)