Skip to content

Commit dd30d64

Browse files
committed
test: require native ARM64 runtime and ecosystem evidence
Exercise standard modules, exceptions, threads, 16-byte atomics, a C shared-library consumer and relocated self-contained deployment. Record managed loader/header resolution, require native openkal execution and preserve fresh-install Windows failure diagnostics. Document the explicit compiler-and-target migration command and its publication prerequisites. Refs: #784
1 parent 42e8b88 commit dd30d64

9 files changed

Lines changed: 231 additions & 28 deletions

‎.agents/docs/2026-10-08-llvm-2313-part2-execution-and-dependencies.md‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ LLVM 与 glibc 是原生 aarch64 的新默认组合;x86_64 保持 GCC,显式
3838
|---|---|---|---|---|
3939
| T1 review 修正与引擎默认 | [mcpp #784](https://github.com/mcpp-community/mcpp/issues/784),沿用 #781 | T4 公开资源后完整 CI | 冷 home、默认双轴、GNU native、自举、modules、Windows 诊断 | 实现中 |
4040
| T2 ARM64 配套与 carve | [xim-pkgindex #937](https://github.com/openxlings/xim-pkgindex/issues/937) | 原生构建机 | 架构、来源摘要、loader/runtime、编译运行 | 原生构建已启动 |
41-
| T3 配方元数据上下文 | [xlings #646](https://github.com/openxlings/xlings/issues/646) | 现有 libxpkg LoaderContext | metadata 与 install 相同架构;三平台回归 | 已复现并实现,构建中 |
41+
| T3 配方元数据上下文 | [xlings #647](https://github.com/openxlings/xlings/pull/647),关联 #646 | 现有 libxpkg LoaderContext | metadata 与 install 相同架构;三平台回归 | 本地验证通过,CI 中 |
4242
| T4 索引接线与镜像 | [xim-pkgindex #938](https://github.com/openxlings/xim-pkgindex/pull/938) | T2、T3 发布 | 每架构哈希、旧版本拒绝、双镜像 GET、消费门 | draft |
4343
| T5 原生与生态消费 | mcpp、xlings、mcpp-index、openkal | T1、T4 | 原生 ARM64、CN SubOS sandbox、真实 build/test/run/pack | 待资源与客户端就绪 |
4444
| T6 自审与发布 | mcpp、xlings、资源与索引 | T1–T5 | 最终头 CI、发版产物、指针传播、消费审计 | 待前置门 |
@@ -75,3 +75,21 @@ ubuntu-24.04-arm,生成 UAPI、zlib、libxml2、gcc-runtime、glibc 和 LLVM
7575

7676
本地测试按变更契约聚焦执行,避免用重复测试占据资源制作与集成时间。
7777
最终闭环审计仍须逐项覆盖方案 G0–G9;局部通过不能替代整个生态已可用。
78+
79+
## 5. 集中 PR 当前证据
80+
81+
2026-10-08:mcpp #781 已推送 `42e8b884`,包含 Part 2 默认接线、
82+
review 修正与候选发布版本 2026.10.8.1。ARM64 native GNU 仍为 preview;
83+
实测矩阵与完整生态消费尚未准入。临时报告及本地 dist 未纳入提交。
84+
85+
xlings #647 的 `276fce5` 统一配方上下文,版本两处均为 2026.10.8.1。
86+
本地构建通过,catalog 套件 48 通过、9 项因索引 fixture 缺席跳过。
87+
完整单测执行得到 57 个测试程序通过、1 个失败;失败来自既有 progress
88+
测试在当前 TERM=dumb 下的颜色断言。该程序以 TERM=xterm 复验 7/7 通过。
89+
这些局部证据不替代发布版三平台 CI。
90+
91+
xim-pkgindex #938 已推送 `193f910e`,冻结五种依赖源码的下载摘要,
92+
加入许可证、provenance 与 ELF 清单。静态及隔离套件 4053 通过,
93+
15 跳过、952 未选入、3 项既有 xpass;架构与客户端门 11/11 通过。
94+
原生资源首轮 CI 已完成依赖构建并进入 glibc;新提交将生成包含完整
95+
来源记录的资源。尚未写入 ARM64 公开路由或占位摘要。

‎.github/tools/check_aarch64_llvm_payload.sh‎

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,138 @@ file -L "$LLVM_ROOT/bin/clang++" | grep -q 'ARM aarch64' || {
1313
}
1414
"$LLVM_ROOT/bin/clang++" --version
1515
work="$(mktemp -d)"
16+
report="${MCPP_NATIVE_REPORT_DIR:-${RUNNER_TEMP:-$work}/native-llvm-arm64}"
17+
mkdir -p "$report"
1618
trap 'rm -rf "$work"' EXIT
1719
export MCPP_HOME="$work/mcpp-home"
1820
"$MCPP" self config --mirror "${MCPP_E2E_MIRROR:-GLOBAL}"
1921
"$MCPP" self env --format json | python3 -c 'import json,sys; d=json.load(sys.stdin); assert d["data"]["defaultToolchain"] == "llvm@23.1.3", d'
2022
"$MCPP" new "$work/native-probe"
23+
mkdir -p "$work/nativeabi/src"
24+
cat > "$work/nativeabi/mcpp.toml" <<'TOML'
25+
[package]
26+
name = "nativeabi"
27+
version = "0.1.0"
28+
[build]
29+
sources = ["src/*.c"]
30+
[targets.nativeabi]
31+
kind = "shared"
32+
soname = "libnativeabi.so.1"
33+
TOML
34+
printf 'int native_answer(int x) { return x + 1; }\n' > "$work/nativeabi/src/answer.c"
2135
cd "$work/native-probe"
36+
cat >> mcpp.toml <<'TOML'
37+
38+
[dependencies]
39+
nativeabi = { path = "../nativeabi" }
40+
TOML
41+
cat > src/main.cpp <<'CPP'
42+
import std;
43+
import std.compat;
44+
extern "C" int native_answer(int);
45+
int native_headers();
46+
struct alignas(16) Pair { unsigned long a, b; };
47+
static_assert(sizeof(Pair) == 16);
48+
int main() {
49+
auto memory = std::make_unique<int>(41);
50+
int value = 0;
51+
std::thread worker([&] { value = native_answer(*memory); });
52+
worker.join();
53+
bool caught = false;
54+
try { throw std::runtime_error("native"); }
55+
catch (const std::runtime_error&) { caught = true; }
56+
std::atomic<Pair> atom;
57+
atom.store(Pair{0, 0});
58+
Pair expected{0, 0};
59+
bool exchanged = atom.compare_exchange_strong(expected, Pair{42, 7});
60+
auto result = atom.load();
61+
void* raw = ::malloc(32);
62+
bool allocated = raw != nullptr;
63+
::free(raw);
64+
bool ok = value == 42 && caught && exchanged && result.a == 42
65+
&& result.b == 7 && allocated && native_headers() == 1;
66+
::printf("native-stdlib-cabi=%s\n", ok ? "ok" : "failed");
67+
return ok ? 0 : 1;
68+
}
69+
CPP
70+
cat > src/headers.cpp <<'CPP'
71+
#include <features.h>
72+
#include <stddef.h>
73+
#include <unistd.h>
74+
#include <pthread.h>
75+
int native_headers() { return 1; }
76+
CPP
2277
"$MCPP" build
23-
"$MCPP" run
78+
"$MCPP" run | tee "$report/runtime.log"
79+
grep -qF 'native-stdlib-cabi=ok' "$report/runtime.log"
2480
binary="$(find target/aarch64-linux-gnu -type f -path '*/bin/native-probe' | head -1)"
2581
[[ -n "$binary" ]] || { echo 'FAIL: default did not produce a native GNU artifact'; exit 1; }
26-
readelf -l "$binary" | grep -q 'ld-linux-aarch64.so.1' || {
82+
readelf -l "$binary" > "$report/program-headers.txt"
83+
readelf -d "$binary" > "$report/dynamic.txt"
84+
grep -q 'ld-linux-aarch64.so.1' "$report/program-headers.txt" || {
2785
echo 'FAIL: native default is not glibc-linked'; exit 1;
2886
}
87+
interpreter="$(sed -n 's/.*Requesting program interpreter: \(.*\)]/\1/p' "$report/program-headers.txt")"
88+
case "$interpreter" in
89+
"$MCPP_HOME"/registry/data/xpkgs/xim-x-glibc/*/lib*/ld-linux-aarch64.so.1) ;;
90+
*) echo "FAIL: GNU default uses an ambient loader: $interpreter"; exit 1 ;;
91+
esac
92+
"$interpreter" --list "$binary" > "$report/loader-resolution.txt"
93+
python3 - "$report/loader-resolution.txt" <<'PYLOADER'
94+
import pathlib, re, sys
95+
text = pathlib.Path(sys.argv[1]).read_text()
96+
assert not re.search(r'=> /(?:usr/lib|lib64?|usr/local/lib)/', text), text
97+
print('PASS: the native loader resolves no ambient system library')
98+
PYLOADER
99+
grep -q 'libnativeabi.so.1' "$report/dynamic.txt" || {
100+
echo 'FAIL: the C ABI consumer does not depend on the shared library'; exit 1;
101+
}
102+
# Replay the engine's effective header compile as a preprocess trace. The trace
103+
# must resolve libc headers through the payload, without an ambient /usr tree.
104+
python3 - "$report" <<'PYTRACE'
105+
import json, pathlib, re, shlex, subprocess, sys
106+
report = pathlib.Path(sys.argv[1])
107+
cdb = next(pathlib.Path('target/aarch64-linux-gnu').rglob('compile_commands.json'))
108+
entries = json.loads(cdb.read_text())
109+
entry = next(e for e in entries if e['file'].endswith('/headers.cpp'))
110+
args = entry.get('arguments') or shlex.split(entry['command'])
111+
clean = []
112+
i = 0
113+
while i < len(args):
114+
arg = args[i]
115+
if arg in ('-o', '-MF', '-MT', '-MQ'):
116+
i += 2
117+
continue
118+
if arg in ('-c', '-MMD', '-MD', '-MP'):
119+
i += 1
120+
continue
121+
clean.append(arg)
122+
i += 1
123+
result = subprocess.run(clean + ['-E', '-H'], cwd=entry['directory'],
124+
text=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
125+
(report / 'include-trace.txt').write_text(result.stderr)
126+
assert result.returncode == 0, result.stderr
127+
headers = [m.group(1) for line in result.stderr.splitlines()
128+
if (m := re.match(r'^\.+ (.+)$', line))]
129+
assert any('xim-x-glibc' in path and path.endswith('/features.h') for path in headers), headers
130+
assert not any(re.match(r'/usr/(include|lib/gcc|local/include)(/|$)', path) for path in headers), headers
131+
print('PASS: libc include trace uses the managed payload')
132+
PYTRACE
133+
cat "$report/program-headers.txt" "$report/loader-resolution.txt" "$report/include-trace.txt"
134+
"$MCPP" pack --mode self-contained --format dir --message-format json > "$report/pack.json"
135+
bundle="$(python3 - "$report/pack.json" <<'PYPACK'
136+
import json, sys
137+
artifact = json.load(open(sys.argv[1]))['data']['artifacts'][0]
138+
assert artifact['type'] == 'directory', artifact
139+
print(artifact['path'])
140+
PYPACK
141+
)"
142+
# Directory deployment exercises the documented portable bundle entry point.
143+
cp -a "$bundle" "$work/deployed"
144+
(cd / && env -u LD_LIBRARY_PATH "$work/deployed/native-probe") | tee "$report/deployed.log"
145+
grep -qF 'native-stdlib-cabi=ok' "$report/deployed.log"
146+
"$MCPP" new "$work/musl-probe"
147+
cd "$work/musl-probe"
29148
"$MCPP" build --target aarch64-linux-musl --toolchain gcc@16.1.0-musl
30149
"$MCPP" run --target aarch64-linux-musl --toolchain gcc@16.1.0-musl
31150
printf '%s\n' 'PASS: native ARM64 LLVM installs, builds and runs'

‎.github/workflows/ci-fresh-install.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,3 +762,10 @@ jobs:
762762
shell: bash
763763
run: |
764764
MCPP="$(command -v mcpp)" bash tests/e2e/182_windows_no_msvc_fallback.sh
765+
766+
- uses: actions/upload-artifact@v4
767+
if: failure()
768+
with:
769+
name: bare-windows-fresh-install-diagnostics
770+
path: ${{ runner.temp }}/bare-windows-diagnostics
771+
if-no-files-found: warn

‎.github/workflows/ci-target-matrix.yml‎

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ jobs:
106106
MCPP_VENDORED_XLINGS: ${{ env.XLINGS_BIN }}
107107
run: bash .github/tools/check_aarch64_llvm_payload.sh
108108

109+
- uses: actions/upload-artifact@v4
110+
if: ${{ always() && matrix.host == 'linux-aarch64' }}
111+
with:
112+
name: native-llvm-arm64-admission
113+
path: ${{ runner.temp }}/native-llvm-arm64
114+
if-no-files-found: warn
115+
109116
- name: Each invariant RAN
110117
run: |
111118
set -euo pipefail
@@ -166,42 +173,27 @@ jobs:
166173
check 298_overriding_a_convention_requires_replacing_it.sh \
167174
"OK: a convention may be overridden, but not merely removed" || fail=1
168175
else
169-
# THE MIRROR OF 297's EXEMPTION, AND FOR THE OTHER FAMILY.
170-
#
171-
# 298 declares llvm, and there is no llvm payload for aarch64 Linux —
172-
# upstream stopped publishing linux-aarch64 after 19.x and the index
173-
# has none. Measured on `ubuntu-24.04-arm`:
174-
#
175-
# SKIP: llvm is not installed here, and this test is about
176-
# declaring it
177-
#
178-
# Granting it by REASON rather than by host is what makes it
179-
# retire itself: the day
180-
# `.agents/docs/2026-08-26-aarch64-linux-ecosystem-closure.md`
181-
# lands an aarch64 llvm, this stops being a skip and starts being
182-
# the assertion, with nothing here to change.
176+
# Non-Linux rows may restore a cache with no declared LLVM.
177+
# Both Linux hosts require the assertion after native provisioning.
183178
check_or_declared_skip 298_overriding_a_convention_requires_replacing_it.sh \
184179
"OK: a convention may be overridden, but not merely removed" \
185180
"llvm is not installed here" || fail=1
186181
fi
187182
188183
# ── 2026.8.26.2: an answer mcpp already had, now used ─────────────
189184
#
190-
# 299/300/303 CARRY NO SKIP AT ALL, so they use `check` on every
191-
# host. They read the vocabulary and the query's own document —
192-
# neither depends on which payloads this machine happens to hold, and
193-
# a version of them that skipped anywhere would be a version that
194-
# could skip everywhere.
185+
# Vocabulary completion is host-independent. The C-library query
186+
# additionally checks graph layering on both GNU Linux hosts.
195187
check 299_a_request_that_named_no_c_library_resolves_to_a_row_that_exists.sh \
196188
"OK: a request that named no C library resolves to a row that exists" || fail=1
197189
check 300_a_registered_family_is_not_reported_unknown.sh \
198190
"OK: a registered family is not reported unknown" || fail=1
199191
# 303's third half stacks a musl c-abi over this host's own target,
200192
# and not every host stacks that — an MSVC-ABI host answers the
201193
# layering question first, correctly, and there is then no
202-
# two-answer document to check. Granted by reason; linux-x86_64 below
203-
# is the denominator that must run the whole file.
204-
if [ "${{ matrix.host }}" = linux-x86_64 ]; then
194+
# two-answer document to check. Both GNU Linux hosts must run all
195+
# three halves; a skip there is a failed assertion.
196+
if [[ "${{ matrix.host }}" = linux-* ]]; then
205197
check 303_the_query_gives_one_answer_for_the_c_library.sh \
206198
"OK: the query gives one answer for the C library" || fail=1
207199
else
@@ -213,8 +205,8 @@ jobs:
213205
# 301/302 NEED TWO COMPILER FAMILIES, AND THAT IS A PROPERTY OF THE
214206
# MACHINE RATHER THAN OF THE CLAIM. "A requirement that DIFFERS from
215207
# mcpp's own answer is applied" cannot be stated where only one family
216-
# exists — macOS installs llvm only, and aarch64 Linux has no llvm
217-
# payload at all (see 298's note).
208+
# exists — macOS ordinarily installs LLVM only. Both Linux hosts
209+
# now have LLVM and GCC payloads.
218210
#
219211
# AND THE DENOMINATOR IS linux-x86_64, WHICH HAS BOTH. Without a
220212
# host required to actually run these, a reason accepted everywhere is

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ jobs:
223223

224224
# Pull requests and dispatch only, as when it was a workflow of its own.
225225
openkal:
226-
needs: [build-linux, build-macos, build-windows]
226+
needs: [build-linux, build-linux-arm, build-macos, build-windows]
227227
if: github.event_name != 'push'
228228
uses: ./.github/workflows/openkal-cross.yml
229229

‎.github/workflows/openkal-cross.yml‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,3 +633,41 @@ jobs:
633633
check 778_a_graph_link_searches_no_host_directory.sh \
634634
"ok: a host directory in ldflags is refused, by name" || fail=1
635635
[ "$fail" = 0 ] || exit 1
636+
637+
638+
ecosystem-native-arm64:
639+
name: openkal native stack (Linux ARM64, LLVM 23.1.3)
640+
runs-on: ubuntu-24.04-arm
641+
timeout-minutes: 45
642+
defaults:
643+
run:
644+
shell: bash
645+
steps:
646+
- uses: actions/checkout@v4
647+
- uses: ./.github/actions/bootstrap-mcpp
648+
- uses: ./.github/actions/use-built-mcpp
649+
with:
650+
host: linux-aarch64
651+
652+
- name: Native platform and graph stack
653+
env:
654+
MCPP_VENDORED_XLINGS: ${{ env.XLINGS_BIN }}
655+
MCPP_E2E_EXPECT_ARCH: aarch64
656+
MCPP_E2E_LLVM_VERSION: 23.1.3
657+
run: |
658+
set -euo pipefail
659+
test "$(uname -m)" = aarch64
660+
export MCPP="$MCPP_FRESH"
661+
"$MCPP" toolchain install llvm 23.1.3
662+
# The existing test builds real openkal-musl and LLVM runtime packages,
663+
# verifies every graph layer, checks static ELF closure and runs it.
664+
bash tests/e2e/286_the_openkal_stack_still_builds.sh 2>&1 | tee native-openkal.log
665+
grep -qF "ok native aarch64 ELF" native-openkal.log
666+
grep -qF "OK: the openkal stack builds, links statically and runs" native-openkal.log
667+
668+
- uses: actions/upload-artifact@v4
669+
if: always()
670+
with:
671+
name: openkal-native-arm64
672+
path: native-openkal.log
673+
if-no-files-found: warn

‎docs/20-toolchains.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ The pair persists as `[toolchain] default = "gcc@16.1.0"` +
9292
configs with combined spellings like `default = "gcc@15.1.0-musl"` keep
9393
working unchanged.)
9494

95+
On native Linux aarch64 (2026.10.8.1+), an explicit migration selects both
96+
LLVM 23.1.3 and the GNU target after the native payload is published:
97+
98+
```bash
99+
mcpp toolchain install llvm 23.1.3
100+
mcpp toolchain default llvm@23.1.3 --target aarch64-linux-gnu
101+
```
102+
103+
The command records `llvm@23.1.3` and `aarch64-linux-gnu` as the default pair.
104+
Existing configurations remain effective until explicitly changed; project
105+
manifest declarations retain their precedence.
106+
95107
### Compiler selection for a build
96108

97109
Five things can name it. They are ranked, and the rank is what makes the two

‎docs/zh/20-toolchains.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ mcpp toolchain default gcc@16 --target x86_64-linux-musl # "default to fully-s
9393
`default_target = "x86_64-linux-musl"`。(存量配置里 `default =
9494
"gcc@15.1.0-musl"` 这类合并拼写原样可用,不受影响。)
9595

96+
在原生 Linux aarch64 上(2026.10.8.1+),原生载荷发布后,显式迁移同时
97+
选择 LLVM 23.1.3 与 GNU 目标:
98+
99+
```bash
100+
mcpp toolchain install llvm 23.1.3
101+
mcpp toolchain default llvm@23.1.3 --target aarch64-linux-gnu
102+
```
103+
104+
该命令将 `llvm@23.1.3` 与 `aarch64-linux-gnu` 记录为默认值对。存量配置在
105+
显式修改前保持有效;工程清单中的声明保留原有优先级。
106+
96107
### 一次构建的编译器选定
97108

98109
有五种来源可以给它命名。它们分级排列,而正是这套分级,让工程能够写下的那两条

‎tests/e2e/286_the_openkal_stack_still_builds.sh‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ bin="$(find target -type f -name okstack | head -1)"
100100

101101
# ── The artefact is what a graph-supplied target side produces ──────────────
102102
desc="$(file -b "$bin")"
103+
if [ "${MCPP_E2E_EXPECT_ARCH:-}" = aarch64 ]; then
104+
case "$desc" in
105+
*"ELF 64-bit"*"ARM aarch64"*) echo " ok native aarch64 ELF" ;;
106+
*) echo "FAIL: native ARM64 job produced a different architecture: $desc"; exit 1 ;;
107+
esac
108+
fi
103109
case "$desc" in
104110
*"statically linked"*) echo " ok statically linked" ;;
105111
*) echo "FAIL: not static — the payload's C library was linked instead"

0 commit comments

Comments
 (0)