Skip to content

Commit a31c762

Browse files
fix: a cross target's toolchain section is not the build program's; a test name is not another's directory; CI findings
- Under a cross `--target`, a `[target.<t>] toolchain` names the target's compiler; the build program takes the spec it replaced, as under a row's pin (mcpp's own tree built on Windows for x86_64-linux-musl compiled its build program with the musl cross gcc). e2e 1051. - A test name that is another's directory (`unit`, `unit/own`) is refused, naming both. - The rows' snapshot keeps LF on every checkout (.gitattributes). - e2e 298 leaves out x86_64-windows-gnu, whose MinGW-w64 payload llvm pairs with; openkal-cross names the openkal macOS identity, aarch64-macos-musl. - The stray empty `.edited` is removed; e2e 859 names a missing record. Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent cd18669 commit a31c762

13 files changed

Lines changed: 132 additions & 14 deletions

‎.edited‎

Whitespace-only changes.

‎.gitattributes‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
# The cross-host admission compares the exact source bytes compiled by each host.
22
tests/fixtures/openkal-hosted-threads/src/main.cpp.in text eol=lf
3+
# The rows' snapshot is compared line by line with what the engine answers, in its own bytes.
4+
tests/fixtures/target-rows.snapshot text eol=lf

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,9 @@ jobs:
292292
# `openkal-llvm-runtime`'s own CI under qemu; it has no runner here to
293293
# execute on, and a build-only cell in a workflow whose point is
294294
# running would be the weaker claim.
295-
for t in x86_64-linux-gnu aarch64-macos x86_64-windows-gnu; do
295+
# The macOS identity over openkal is `aarch64-macos-musl` on every host (SPEC-016 §4): on a
296+
# macOS host `aarch64-macos` is the Apple SDK's.
297+
for t in x86_64-linux-gnu aarch64-macos-musl x86_64-windows-gnu; do
296298
rm -rf target
297299
# The upstream manifest may pin an older LLVM line. The invocation
298300
# selects this candidate explicitly, including on the ARM64 host.
@@ -301,12 +303,12 @@ jobs:
301303
[ -n "$a" ] || { echo "::error::$t produced no artefact on ${{ matrix.host }}"; exit 1; }
302304
case "$t" in
303305
x86_64-windows-gnu) cp "$a" "$RUNNER_TEMP/out/windows.exe" ;;
304-
aarch64-macos) cp "$a" "$RUNNER_TEMP/out/macos" ;;
306+
aarch64-macos-musl) cp "$a" "$RUNNER_TEMP/out/macos" ;;
305307
*) cp "$a" "$RUNNER_TEMP/out/linux" ;;
306308
esac
307309
"$READOBJ" --file-headers "$a" > "$RUNNER_TEMP/out/$t.headers.txt"
308310
case "$t" in
309-
aarch64-macos) arch=aarch64 ;;
311+
aarch64-macos-musl) arch=aarch64 ;;
310312
*) arch=x86_64 ;;
311313
esac
312314
grep -q "Arch: $arch" "$RUNNER_TEMP/out/$t.headers.txt" \
@@ -319,7 +321,7 @@ jobs:
319321
thread_bin=$(cat "$RUNNER_TEMP/threads-binary.txt")
320322
case "$t" in
321323
x86_64-windows-gnu) thread_output=windows-threads.exe ;;
322-
aarch64-macos) thread_output=macos-threads ;;
324+
aarch64-macos-musl) thread_output=macos-threads ;;
323325
*) thread_output=linux-threads ;;
324326
esac
325327
cp "$thread_bin" "$RUNNER_TEMP/out/$thread_output"
@@ -355,7 +357,7 @@ jobs:
355357
# costs.
356358
#
357359
# LEG 3 IS THE ENGINE: the same idiom, over the openkal stack, for
358-
# `aarch64-macos`, built by the mcpp under test. Measured on the token
360+
# `aarch64-macos-musl`, built by the mcpp under test. Measured on the token
359361
# this step was written for, the two readings are
360362
#
361363
# -fno-builtin-memset_pattern16 1 reference, and the link fails
@@ -444,10 +446,10 @@ jobs:
444446
[ "$(refs "$W/narrow.o")" = 1 ] || { echo "::error::leg 2: clang now honours -fno-builtin-memset_pattern16, and mcpp.toolchain.cenv can emit the narrower token"; exit 1; }
445447
[ "$(refs "$W/blunt.o")" = 0 ] || { echo "::error::leg 2: -fno-builtin no longer withdraws the pattern fill"; exit 1; }
446448
447-
(cd "$W" && "$MCPP_UNDER_TEST" build --target aarch64-macos)
449+
(cd "$W" && "$MCPP_UNDER_TEST" build --target aarch64-macos-musl)
448450
obj=$(find "$W/target" -name 'main.o' | head -1)
449451
[ -n "$obj" ] || { echo "::error::leg 3 produced no object to read"; exit 1; }
450-
echo "engine, aarch64-macos over openkal: $(refs "$obj")"
452+
echo "engine, aarch64-macos-musl over openkal: $(refs "$obj")"
451453
[ "$(refs "$obj")" = 0 ] || { echo "::error::leg 3: builtins = \"iso\" did not withdraw memset_pattern16"; exit 1; }
452454
453455
- uses: actions/upload-artifact@v4

‎CHANGELOG.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@
9797
the conflict is reported (B2).
9898
- `mcpp pack` no longer packs one program of several without a word (B3).
9999
- A static ELF is not run to learn its closure.
100+
- A build program under a cross `--target` whose `[target.<t>] toolchain`
101+
names the target's compiler is compiled by the spec that section replaced,
102+
as under a row's pin: mcpp's own tree, built on Windows for
103+
`x86_64-linux-musl`, compiled its build program with the musl cross gcc.
104+
- A test name that is another's directory (`unit` and `unit/own`) is refused,
105+
naming both; the two programs were a file and a directory of one path.
106+
- The repository's stray empty `.edited` is removed; e2e 859 fails naming the
107+
missing record rather than writing one in the working directory.
100108

101109
## [2026.11.8.1] - 2026-11-08
102110

‎docs/08-testing.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ below a glob's fixed directory one program, named by the directory
6161
it, its cases are all of theirs, and it begins at the file that defines `main`
6262
when one does, else as a file that defines none does. Two files of one directory
6363
that define `main` are refused, naming both; a file directly in the fixed
64-
directory stays a program of its own. A program of many files is linked once
64+
directory stays a program of its own. A test's name is the path of its program,
65+
so a name that is another's directory (`unit` and `unit/own`) is refused,
66+
naming both (2026.11.9.1+). A program of many files is linked once
6567
where a program per file is linked once per file, which is what a suite of many
6668
small files spends its build time on.
6769

‎docs/zh/08-testing.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ discover = ["checks/**/*.cpp", "!checks/fixtures/**"]
5454
一个程序,以目录命名(`tests/unit/*.cpp` 为程序 `unit`):每个文件是它的一个翻译单元,
5555
它的用例是这些文件的全部用例;有文件定义 `main` 时以该文件为起点,否则按不定义 `main`
5656
的文件的规则开始。同一目录中两个定义 `main` 的文件会被拒绝并点出两者;直接位于固定
57-
目录中的文件仍各自是一个程序。多文件的程序只链接一次,而每文件一个程序时每个文件链接
57+
目录中的文件仍各自是一个程序。测试的名字是其程序的路径,因此一个名字是另一个名字的目录
58+
(`unit` 与 `unit/own`)时被拒绝并点出两者(2026.11.9.1+)。多文件的程序只链接一次,而每文件一个程序时每个文件链接
5859
一次,后者正是由许多小文件组成的套件在构建上花费的时间。
5960

6061
使用测试协议的程序**按其用例所在文件各起一个进程**运行(2026.11.9.1+):mcpp 先列出多文件程序的用例,

‎src/build/prepare/toolchain.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,12 @@ static void step1_apply_target_section(PrepareState& state,
550550
: state.overrides.target_triple,
551551
state.m->sourcePath.filename().string()));
552552
if (!e.toolchain.empty() && !state.tcFromCommandLine && !state.tcFromConsumer) {
553+
// A CROSS TARGET'S SECTION NAMES THE TARGET'S COMPILER, NOT THE HOST'S:
554+
// the spec it replaced is what the build program resolves, as a row's
555+
// pin's is (`[target.x86_64-linux-musl] toolchain = "gcc@16.1.0"` on a
556+
// Windows host whose `[toolchain] windows` is llvm).
557+
if (!state.overrides.target_triple.empty() && state.tcSpec && *state.tcSpec != e.toolchain)
558+
state.hostSpecBeforeRowPin = *state.tcSpec;
553559
state.tcSpec = e.toolchain;
554560
state.tcOrigin = TcOrigin::TargetSection;
555561
}

‎src/build/prepare/xlings.cpp‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,16 @@ step3_define_host_tc_closures_and_refresh_index(PrepareState& state) {
121121
if (!state.bootstrapSpec.empty()) return state.bootstrapSpec;
122122
if (!state.tcSpec) return {};
123123
if (state.overrides.target_triple.empty()) return *state.tcSpec;
124-
return (state.tcOrigin == TcOrigin::TargetPin && state.hostSpecBeforeRowPin.has_value()
125-
&& !state.hostSpecBeforeRowPin->empty() && *state.hostSpecBeforeRowPin != "system")
126-
? *state.hostSpecBeforeRowPin
127-
: (state.tcOrigin == TcOrigin::TargetPin ? state.native_first_run_spec() : *state.tcSpec);
124+
// The target's own choice -- a row's pin or a `[target.<t>] toolchain`
125+
// -- is not the host's: the spec it replaced is. A pin that replaced
126+
// nothing gives the native default (#622); a section that replaced
127+
// nothing names the only compiler the project states.
128+
const bool targetChose = state.tcOrigin == TcOrigin::TargetPin
129+
|| state.tcOrigin == TcOrigin::TargetSection;
130+
if (targetChose && state.hostSpecBeforeRowPin.has_value() && !state.hostSpecBeforeRowPin->empty()
131+
&& *state.hostSpecBeforeRowPin != "system")
132+
return *state.hostSpecBeforeRowPin;
133+
return state.tcOrigin == TcOrigin::TargetPin ? state.native_first_run_spec() : *state.tcSpec;
128134
};
129135
state.host_tc_for_build_program = [&]() -> std::expected<
130136
std::pair<std::filesystem::path, mcpp::toolchain::Toolchain>, std::string> {

‎src/build/test_targets.cppm‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,17 @@ discover_test_targets(const std::filesystem::path& manifestRoot,
192192
}
193193
result.targets.push_back(std::move(target));
194194
}
195+
// A TEST'S NAME IS A PATH OF ITS PROGRAM (`bin/<name>`): one name that is
196+
// another's directory would make one program a directory of the other's.
197+
// The names are sorted: the first name below `<name>/` is the one to ask.
198+
for (auto it = seenNames.begin(); it != seenNames.end(); ++it) {
199+
auto next = seenNames.lower_bound(it->first + "/");
200+
if (next == seenNames.end() || !next->first.starts_with(it->first + "/")) continue;
201+
return std::unexpected(std::format(
202+
"test '{}' ({}) is a directory of test '{}' ({}): a test's name is the path of its "
203+
"program, and one cannot be both; move one of them",
204+
it->first, it->second.generic_string(), next->first, next->second.generic_string()));
205+
}
195206
return result;
196207
}
197208

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
# requires: elf llvm gcc musl
3+
source "$(dirname "${BASH_SOURCE[0]}")/_toolchain_env.sh"
4+
# 1051 -- a cross target's `[target.<t>] toolchain` names the target's compiler, not the one the
5+
# build program is compiled with.
6+
#
7+
# A build program is compiled and run on this machine. `[target.x86_64-linux-musl] toolchain =
8+
# "gcc@16.1.0"` replaces the project's `[toolchain]` for the target; the build program takes the
9+
# spec it replaced, as it takes the one a row's pin replaced (657). Measured on mcpp's own tree,
10+
# built on Windows for x86_64-linux-musl: the build program was compiled by the musl cross gcc and
11+
# the host could not start it (error 8, executable format error).
12+
#
13+
# Criteria: under `--target x86_64-linux-musl` the build program's compiler is the `[toolchain]`
14+
# one (llvm), the target's is the section's (gcc), and the build program runs.
15+
set -e
16+
t=$(mktemp -d); trap 'rm -rf "$t"' EXIT
17+
fail() { echo "FAIL: $1"; [ -n "${2:-}" ] && printf '%s\n' "$2" | tail -20; exit 1; }
18+
[ "$(uname -m)" = x86_64 ] || { echo "SKIP: an x86_64 host"; exit 0; }
19+
mkdir -p "$t/src" && cd "$t"
20+
cat > mcpp.toml <<EOF
21+
[package]
22+
name = "crossprog"
23+
version = "0.1.0"
24+
25+
[toolchain]
26+
default = "llvm@${LLVM_VERSION:-23.1.3}"
27+
28+
[target.x86_64-linux-musl]
29+
toolchain = "gcc@16.1.0"
30+
EOF
31+
cat > build.mcpp <<'EOF'
32+
import std;
33+
import mcpp;
34+
int main() {
35+
std::ofstream(std::string(mcpp::out_dir()) + "/ran.txt") << "ran\n";
36+
}
37+
EOF
38+
echo 'int main() { return 0; }' > src/main.cpp
39+
out=$("$MCPP" build --target x86_64-linux-musl 2>&1) || fail "the cross build failed" "$out"
40+
grep -q 'host toolchain for build.mcpp: llvm' <<<"$out" \
41+
|| grep -Eq 'Resolved host toolchain for build.mcpp: clang' <<<"$out" \
42+
|| fail "the build program is not compiled by the [toolchain] compiler" "$out"
43+
grep -q 'x86_64-linux-musl-g++' <<<"$out" || fail "the target is not compiled by the section's gcc" "$out"
44+
find target -name ran.txt | grep -q . || fail "the build program did not run" "$(find target -maxdepth 4 | head)"
45+
echo " ok the build program takes the [toolchain] compiler; the target takes its section's"
46+
echo "PASS: 1051 a cross target's toolchain section is not the build program's"

0 commit comments

Comments
 (0)