Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci-target-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:-<none — scanning what is installed>}"
# ⚠️ 两种体系各自成表。scan 把 mode 写进第一列,而比对必须按 mode 分开
# 做 —— 拿一种体系的测量去比整张表,另一种的每一行都会被报成「没跑到」。
bash tests/matrix/scan.sh payload > measured-payload.tsv
Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,33 @@
测陈述它。⚠️ 这条缺陷是**读出来的**:它需要一台没有工具链的 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 构建**,所以它
钉住的版本过期了也没有任何东西会说话。

### CI

- **⚠️⚠️ 目标矩阵的编译器轴跟着「装了什么」走,于是缓存能决定判据。**

实测:同一个提交,PR 的 `scan (windows-x86_64)` 绿,合入 main 后同一个 job 红 ——
那次 runner 恢复出来的缓存里多了一个 `gcc@16.1.0`,扫描产出 24 格而期望表为这台
宿主声明的是 16 格,八格全部报成「表里没有这一格」。

⭐ **期望表是一份声明**,编译器轴现在跟着它走(`MATRIX_COMPILERS`,由 workflow
从 expected.tsv 的同一列算出,与「装哪些」那一步同源,所以两者不可能各说各话)。
装着却不在声明里的版本写到 stderr —— 一次没跑的测量和一次通过的测量,在退出码上
没有区别。

### 兼容性

⭐ **没有任何一次原本成功的构建换了行为。** 图声明编译器的情形里,原来的结局是
Expand Down
50 changes: 36 additions & 14 deletions examples/06-openkal-cross/README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
Expand Down
34 changes: 25 additions & 9 deletions examples/06-openkal-cross/mcpp.toml
Original file line number Diff line number Diff line change
@@ -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.
#
Expand Down
17 changes: 12 additions & 5 deletions examples/06-openkal-cross/src/main.cpp
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down
25 changes: 25 additions & 0 deletions tests/matrix/scan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 }'
Expand Down
Loading