From 5575cb76aa6b04ae74972778fc9beb35d6cd70b8 Mon Sep 17 00:00:00 2001 From: speak-agent <248744407+speak-agent@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:05:08 +0800 Subject: [PATCH 1/2] the example says what the engine now does, and its pin had gone stale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `examples/06-openkal-cross` documented three target spellings. The fourth — `--target aarch64-linux` — is the one 2026.8.26.2 made writable; before it, the request completed to `aarch64-linux-gnu`, a row registered but not supported, and refused while `aarch64-linux-musl` built. Its `[toolchain] default = "llvm@22.1.8"` is gone. `openkal-llvm-runtime` declares `requires = ["mcpp:compiler=llvm"]`, and mcpp now reads that, so the manifest naming a compiler was stating something the graph already says. Measured on a machine whose global default is gcc: all four targets resolve `llvm@22.1.8`, and `~/.mcpp/config.toml` is byte-identical afterwards. ⚠️ And the dependency floor still read `0.1.1`, whose compiler-rt builtins do not compile for aarch64 — which is what the aarch64 leg actually died on before this. Nothing builds this example in CI, so a pin going stale here has nothing that would say so. --- CHANGELOG.md | 14 ++++++++ examples/06-openkal-cross/README.md | 50 ++++++++++++++++++-------- examples/06-openkal-cross/mcpp.toml | 34 +++++++++++++----- examples/06-openkal-cross/src/main.cpp | 17 ++++++--- 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e11ac6..5d42266c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,20 @@ 测陈述它。⚠️ 这条缺陷是**读出来的**:它需要一台没有工具链的 Windows 机器,而 `config.toml` 的 sha256 判据跑在已配好的环境里,两条分支一条都到不了。 +### 示例 + +- **⭐ `examples/06-openkal-cross` 现在是四个目标,而且不再写 `[toolchain]`。** + + 第四行 `--target aarch64-linux` 正是这次修好的那一条 —— 在此之前它补全成 + `aarch64-linux-gnu`(registered but not supported)并拒绝。同时删掉了那个 + `[toolchain] default = "llvm@22.1.8"` 段:`openkal-llvm-runtime` 自己声明 + `requires = ["mcpp:compiler=llvm"]`,mcpp 现在读它。实测在一台全局默认为 gcc 的 + 机器上,四个目标全部解析出 `llvm@22.1.8`,而 `~/.mcpp/config.toml` 一字未改。 + + ⚠️ 顺带修掉一处失效的钉:该示例的依赖下界还写着 `0.1.1`,而那个版本的 + compiler-rt builtins 在 aarch64 上编不过。这个示例**不被任何 CI 构建**,所以它 + 钉住的版本过期了也没有任何东西会说话。 + ### 兼容性 ⭐ **没有任何一次原本成功的构建换了行为。** 图声明编译器的情形里,原来的结局是 diff --git a/examples/06-openkal-cross/README.md b/examples/06-openkal-cross/README.md index bf9d2ca2..d845a84c 100644 --- a/examples/06-openkal-cross/README.md +++ b/examples/06-openkal-cross/README.md @@ -1,15 +1,21 @@ # 06 — One Source, Several Machines -A program that asks each machine what it is, built for three targets from any +A program that asks each machine what it is, built for four targets from any host without being edited. ```bash mcpp run # this machine -mcpp build --target x86_64-linux # Linux, from any host -mcpp build --target aarch64-macos # macOS, from any host -mcpp build --target x86_64-windows-gnu # Windows, from any host +mcpp build --target x86_64-linux # Linux x86_64, from any host +mcpp build --target aarch64-linux # Linux aarch64, from any host +mcpp build --target aarch64-macos # macOS aarch64, from any host +mcpp build --target x86_64-windows # Windows x86_64, from any host ``` +⭐ Every one of them declines the third segment. The `aarch64-linux` line could +not be written before 2026.8.26.2: it completed to `aarch64-linux-gnu`, a row +registered but not supported, and refused — while `aarch64-linux-musl` built. +A request that names no C library now resolves to a row that exists. + `src/main.cpp` contains no preprocessor directive and no branch on a target name. What differs between the builds is which packages the dependency graph resolved. @@ -18,20 +24,36 @@ resolved. ```toml [dependencies] -openkal-llvm-runtime = "0.1.1" +openkal-llvm-runtime = "0.1.3" +``` + +That is the whole of it. One dependency supplies the compiler runtime and the +C++ runtime, and depends in turn on a C library, which depends on whichever +implementation of the platform interface matches the target being built. One +line therefore selects three of the five target-side layers. -[toolchain] -default = "llvm@22.1.8" +⭐⭐ **And the fourth — the compiler — without the manifest naming it.** There +used to be a `[toolchain] default = "llvm@22.1.8"` here. It is gone, because +`openkal-llvm-runtime` declares `requires = ["mcpp:compiler=llvm"]` — a C++ +runtime is configured for one compiler family and records that in the headers it +ships — and since 2026.8.26.2 mcpp reads that and takes it: + +``` + Resolved llvm@22.1.8 → aarch64-linux-musl → …/xim-x-llvm/22.1.8/bin/clang++ + required by openkal-llvm-runtime@0.1.3 + (`requires = ["mcpp:compiler=llvm"]`), not your gcc@16.1.0 + — this project only ``` -One dependency supplies the compiler runtime and the C++ runtime, and depends in -turn on a C library, which depends on whichever implementation of the platform -interface matches the target being built. One line therefore selects three of -the five target-side layers. +⚠️ **"this project only" is load-bearing.** Nothing is written — not +`~/.mcpp/config.toml`, not this manifest. A requirement is a property of the +package that states it, so it decides this build and no other. A project that +wants a different compiler still writes one here, and that statement outranks +the graph (docs/03). -The toolchain line names a compiler and nothing else. Where the headers, the C -library, the C++ runtime and the platform implementation come from is not stated -in the manifest at all; the build reports what it resolved: +Where the headers, the C library, the C++ runtime and the platform +implementation come from is not stated in the manifest at all; the build reports +what it resolved: ``` Target x86_64-windows-gnu → x86_64-w64-windows-gnu diff --git a/examples/06-openkal-cross/mcpp.toml b/examples/06-openkal-cross/mcpp.toml index 828f4ab1..db23cda4 100644 --- a/examples/06-openkal-cross/mcpp.toml +++ b/examples/06-openkal-cross/mcpp.toml @@ -1,25 +1,41 @@ [package] name = "portable-report" version = "0.1.0" -description = "One source, three hosted targets, over openkal" +description = "One source, four hosted targets, over openkal" # There is no `[build] target` here, and its absence is what the directory is # for. The target is given on the command line so that the same file can be -# built three ways without being edited. +# built four ways without being edited. [dependencies] # One line selects three of the five target-side layers. `openkal-llvm-runtime` # supplies the compiler runtime and the C++ runtime, and depends in turn on # `openkal-musl` for the C library, which depends on whichever implementation of # openkal matches the target being built. -openkal-llvm-runtime = "0.1.1" +openkal-llvm-runtime = "0.1.3" -[toolchain] -# A compiler, and nothing else. Where the headers, the C library, the C++ -# runtime and the platform implementation come from is not stated in this file; -# it follows from the dependency above and is resolved after the graph exists. -# The build reports what it resolved. -default = "llvm@22.1.8" +# ⭐⭐ AND IT SELECTS THE FOURTH — THE COMPILER — WITHOUT THIS FILE SAYING SO. +# +# There used to be a `[toolchain] default = "llvm@22.1.8"` here, with a comment +# explaining that it named "a compiler, and nothing else". It is gone, and the +# example is more accurate for it: `openkal-llvm-runtime` declares +# `requires = ["mcpp:compiler=llvm"]`, because a C++ runtime is configured for +# one compiler family and records that configuration in the headers it ships. +# +# Since 2026.8.26.2 mcpp reads that and takes it. Measured on a machine whose +# global default is gcc, all four targets: +# +# Resolved llvm@22.1.8 → aarch64-linux-musl → …/xim-x-llvm/22.1.8/bin/clang++ +# required by openkal-llvm-runtime@0.1.3 +# (`requires = ["mcpp:compiler=llvm"]`), not your gcc@16.1.0 +# — this project only +# +# ⚠️ "this project only" is load-bearing. Nothing is written: not +# `~/.mcpp/config.toml`, not this file. A requirement is a property of the +# package that states it, so it decides this build and no other. +# +# A project that wants a different compiler still writes one here, and that +# statement outranks the graph — see docs/03. # ⚠️ NO BARE-METAL TARGET SECTION, AND ITS ABSENCE IS DELIBERATE. # diff --git a/examples/06-openkal-cross/src/main.cpp b/examples/06-openkal-cross/src/main.cpp index 12cdd153..3dc2017d 100644 --- a/examples/06-openkal-cross/src/main.cpp +++ b/examples/06-openkal-cross/src/main.cpp @@ -1,13 +1,20 @@ -// One source, three machines, and a program that asks each of them what it is. +// One source, four machines, and a program that asks each of them what it is. // // mcpp run this machine -// mcpp build --target x86_64-linux Linux, any host -// mcpp build --target aarch64-macos macOS, any host -// mcpp build --target x86_64-windows Windows, any host +// mcpp build --target x86_64-linux Linux x86_64, any host +// mcpp build --target aarch64-linux Linux aarch64, any host +// mcpp build --target aarch64-macos macOS aarch64, any host +// mcpp build --target x86_64-windows Windows x86_64, any host +// +// ⭐ EVERY ONE OF THEM DECLINES THE THIRD SEGMENT, AND THE FOURTH LINE COULD +// NOT BE WRITTEN UNTIL 2026.8.26.2. `aarch64-linux` used to complete to +// `aarch64-linux-gnu` — a row registered but not supported — and refuse, while +// `aarch64-linux-musl` built. A request that names no C library now resolves to +// a row that exists; see docs/16. // // Nothing below is conditional on a platform. There is no preprocessor // directive in this file, and no branch on a target name. What differs between -// the three builds is which packages the dependency graph resolved, and the +// the four builds is which packages the dependency graph resolved, and the // only trace of that difference in the source is that the program ASKS about // capabilities instead of assuming them. // From 2bd20c785171e62fa478ce029d2c7d1bd86db3cc Mon Sep 17 00:00:00 2001 From: speak-agent <248744407+speak-agent@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:14:14 +0800 Subject: [PATCH 2/2] the matrix's compiler axis follows the declaration, not the cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same commit: the PR's `scan (windows-x86_64)` was green and the same job on main was red. That run's restored cache carried an extra `gcc@16.1.0`, so the scan produced 24 cells where the expected table declares 16 for that host, and all eight extras were reported as cells the table does not mention. What a runner happens to have installed is not what this repository declares, and the criterion has to be the second. The compiler axis now comes from expected.tsv's own column, computed by the workflow from the same column the install step reads — so "install these" and "scan these" cannot disagree. Installed versions outside the declaration are named on stderr; a measurement that did not run and one that passed are indistinguishable from an exit code. --- .github/workflows/ci-target-matrix.yml | 13 +++++++++++++ CHANGELOG.md | 13 +++++++++++++ tests/matrix/scan.sh | 25 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/.github/workflows/ci-target-matrix.yml b/.github/workflows/ci-target-matrix.yml index 3e6e0455..6f1c1873 100644 --- a/.github/workflows/ci-target-matrix.yml +++ b/.github/workflows/ci-target-matrix.yml @@ -309,6 +309,19 @@ jobs: export MCPP="$MCPP_UNDER_TEST" export MCPP_VENDORED_XLINGS="$XLINGS_BIN" "$MCPP" self config --mirror GLOBAL 2>/dev/null || true + # ⚠️⚠️ 编译器轴跟着**声明**走,不跟着「这台机器上装了什么」走。 + # + # 实测 2026-08-26,同一个提交:PR 上这个 job 绿,合入 main 后红 —— 那 + # 次 windows-2022 恢复出来的缓存里多了一个 `gcc@16.1.0`,扫描产出 24 + # 格而期望表声明 16 格,八格全部报成「表里没有这一格」。缓存里有什么 + # 不是这个仓库声明了什么,而判据必须是后者。 + # + # 与上一步取自同一列,所以「装它」和「扫它」不可能各说各话。 + MATRIX_COMPILERS="$(awk -F'\t' -v h='${{ matrix.host }}' \ + 'NF>=11 && $2==h {print $4}' tests/matrix/expected.tsv \ + | sort -u | tr '\n' ' ')" + export MATRIX_COMPILERS + echo "declared compiler axis: ${MATRIX_COMPILERS:-}" # ⚠️ 两种体系各自成表。scan 把 mode 写进第一列,而比对必须按 mode 分开 # 做 —— 拿一种体系的测量去比整张表,另一种的每一行都会被报成「没跑到」。 bash tests/matrix/scan.sh payload > measured-payload.tsv diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d42266c..fed795b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,19 @@ compiler-rt builtins 在 aarch64 上编不过。这个示例**不被任何 CI 构建**,所以它 钉住的版本过期了也没有任何东西会说话。 +### CI + +- **⚠️⚠️ 目标矩阵的编译器轴跟着「装了什么」走,于是缓存能决定判据。** + + 实测:同一个提交,PR 的 `scan (windows-x86_64)` 绿,合入 main 后同一个 job 红 —— + 那次 runner 恢复出来的缓存里多了一个 `gcc@16.1.0`,扫描产出 24 格而期望表为这台 + 宿主声明的是 16 格,八格全部报成「表里没有这一格」。 + + ⭐ **期望表是一份声明**,编译器轴现在跟着它走(`MATRIX_COMPILERS`,由 workflow + 从 expected.tsv 的同一列算出,与「装哪些」那一步同源,所以两者不可能各说各话)。 + 装着却不在声明里的版本写到 stderr —— 一次没跑的测量和一次通过的测量,在退出码上 + 没有区别。 + ### 兼容性 ⭐ **没有任何一次原本成功的构建换了行为。** 图声明编译器的情形里,原来的结局是 diff --git a/tests/matrix/scan.sh b/tests/matrix/scan.sh index 93d4b0fb..c53f98c2 100755 --- a/tests/matrix/scan.sh +++ b/tests/matrix/scan.sh @@ -111,7 +111,32 @@ targets() { printf '%s' "$LIST" | jq_r '.data.targets[].target' | sort -u; } # # ⚠️ 但收窄必须说出来。被丢掉的版本写到 stderr —— 一次没跑的测量和一次通过的 # 测量,在退出码上没有区别。 +# +# ⚠️⚠️ 而「装了哪些」不是「这台宿主声明了哪些」,把前者当成后者会让缓存决定判据。 +# +# 实测 2026-08-26,同一个提交:PR 的 `scan (windows-x86_64)` 绿,合入 main 后同一 +# 个 job 红 —— 因为那次 runner 恢复出来的缓存里多了一个 `gcc@16.1.0`,扫描于是产出 +# 24 格,而期望表为这台宿主声明的是 16 格,八格全部报成「表里没有这一格」。 +# +# 这台宿主装了什么随缓存变(297 的具名跳过就是围着这个事实写的),而**期望表是一 +# 份声明**。让编译器轴跟着声明走,这一整类假红就消失了:`MATRIX_COMPILERS` 由 +# workflow 从 expected.tsv 自己那一列算出来传进来。 +# +# ⭐ 不传时退回「装了什么扫什么」,因为本机跑没有期望表可依。 compilers() { + if [ -n "${MATRIX_COMPILERS:-}" ]; then + # ⚠️ 收窄仍然要说出来。装着却不在声明里的那些,写到 stderr —— 一次没跑的 + # 测量和一次通过的测量,在退出码上没有区别。 + printf '%s' "$LIST" | jq_r '.data.toolchains[] | .family + "@" + .version' \ + | while IFS= read -r have; do + case " $MATRIX_COMPILERS " in + *" $have "*) ;; + *) echo "scan: 略过 $have —— 本宿主的期望表没有声明它" >&2 ;; + esac + done + printf '%s\n' $MATRIX_COMPILERS + return + fi printf '%s' "$LIST" | jq_r '.data.toolchains[] | .family + "@" + .version' \ | awk -F@ '{ if (seen[$1]++) print "scan: 略过 " $0 " —— 每族只取最新" > "/dev/stderr" else print $0 }'