Skip to content

Commit a4790be

Browse files
committed
fix(toolchain): aarch64 上原生 GCC 的载荷是 musl-gcc,不是 glibc gcc (#367)
`ci-aarch64-fresh-install` 报 `download failed for xim:gcc@16.1.0: HTTP 404`。 **先前的归因是错的**:我判成「`[toolchain]` 缺 arch 轴」,并动手给 manifest 加了 一根选择器轴。生态其实**早就支持 aarch64** —— xlings-res 的 musl-gcc 发行版里 就有 `musl-gcc-16.1.0-linux-aarch64.tar.gz`,而 `xim:gcc` 声明的是 `archs = { "x86_64" }`。所以缺的不是让用户去写 arch,而是 mcpp 问错了包。 同一台机器上 target 一侧是对的: `gcc@16.1.0 → aarch64-linux-musl → xim-x-musl-gcc`。因为 target 一侧把 triple 注入了 spec,`to_xim_package` 的 musl 分支便按「同一 target,两种载荷形态」选中 了原生包。而 build.mcpp 的 **host** 解析刻意不注入 target,于是落到最后那条 glibc 分支,拿到只有 x86_64 资产的 `gcc`。 「这个 spec 在这台机器上对应哪个载荷」正是 `to_xim_package` 存在的意义,上面 那条 musl 分支已经在回答它的另一半。所以修在这里:Linux 上瞄准本机的 GCC spec, 在非 x86_64 架构上解析到 `musl-gcc`。用户不该被要求去编码「某个工具链包是为 哪些架构构建的」。 同时撤回上一版那个「host 工具链装不上就回退到 target 工具链」的兜底 —— 它是 错误归因的产物;正确解析之后它是死代码,而一条失败路径上的隐式回退不该白留。 判定抽成自由函数并接收 host arch,而不是读编译期常量:这样 aarch64 的答案能在 x86_64 机器上被测到 —— 而「测一个手边没有的架构」正是重点。
1 parent 6d354e8 commit a4790be

3 files changed

Lines changed: 85 additions & 29 deletions

File tree

src/build/prepare.cppm

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,34 +1812,6 @@ prepare_build(bool print_fingerprint,
18121812
mcpp::fetcher::InstallProgressHandler progress;
18131813
auto payload = fetcher.resolve_xpkg_path(pkg.target(), /*autoInstall=*/true, &progress);
18141814
if (!payload) {
1815-
// The pinned host toolchain is not obtainable here. Before giving
1816-
// up, ask whether we already HAVE a compiler that runs on this
1817-
// machine: when the resolved target differs from the host only in
1818-
// its libc environment (aarch64-linux-musl on an aarch64 Linux
1819-
// host), the target toolchain is native — it runs here and the
1820-
// binaries it produces run here — so it is a perfectly good
1821-
// compiler for a build program.
1822-
//
1823-
// Without this, a whole architecture is a dead end for any project
1824-
// whose graph contains a build.mcpp: `[toolchain]` is keyed by OS
1825-
// with no arch axis (#367), so `default = "gcc@16.1.0"` is what
1826-
// aarch64 asks for too, and that package declares
1827-
// `archs = { "x86_64" }` — the download 404s and the build stops.
1828-
// A toolchain that is already on disk and already correct should
1829-
// not be unusable because a second, redundant one is missing.
1830-
auto hostT = mcpp::toolchain::triple::host_triple();
1831-
auto tgtT = mcpp::toolchain::triple::parse(overrides.target_triple);
1832-
if (tgtT && tgtT->arch == hostT.arch && tgtT->os == hostT.os) {
1833-
mcpp::diag::warning("toolchain/host-fallback", std::format(
1834-
"the pinned host toolchain '{}' could not be provisioned "
1835-
"({}), so build.mcpp will be compiled with the target "
1836-
"toolchain instead — it is native here ({} vs host {}). "
1837-
"Pin a host toolchain that exists on this architecture to "
1838-
"silence this.",
1839-
*tcSpec, payload.error().message, tgtT->str(), hostT.str()));
1840-
hostTcCache = std::pair{explicit_compiler, *tc};
1841-
return *hostTcCache;
1842-
}
18431815
return std::unexpected(std::format(
18441816
"host toolchain for build.mcpp ('{}'): {}", *tcSpec,
18451817
payload.error().message));

src/toolchain/registry.cppm

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ void print_compat_hint(const ToolchainSpec& spec);
8686
// The (family, target, host) → xim package mapping — the distribution layer.
8787
XimToolchainPackage to_xim_package(const ToolchainSpec& spec);
8888

89+
// #367: does a GCC spec aimed at the NATIVE Linux host resolve to the
90+
// `musl-gcc` payload rather than the glibc `gcc` one?
91+
//
92+
// `xim:gcc` publishes x86_64 assets only (`archs = { "x86_64" }`); the GCC the
93+
// ecosystem ships for other Linux architectures is `musl-gcc`, which does
94+
// publish them (aarch64 included, verified against xlings-res). Asking for
95+
// `gcc` on aarch64 404s.
96+
//
97+
// A free function taking the host arch rather than reading the compile-time
98+
// constant, so the aarch64 answer is testable on an x86_64 machine — the whole
99+
// point being an architecture the developer is not sitting in front of.
100+
bool gcc_native_payload_is_musl(std::string_view hostArch, bool isLinux,
101+
const triple::Triple& target);
102+
89103
ToolchainSpec with_resolved_xim_version(const ToolchainSpec& spec,
90104
std::string_view ximVersion);
91105

@@ -209,6 +223,16 @@ void print_compat_hint(const ToolchainSpec& spec) {
209223
compat::print_hint_once(spec.compatHint);
210224
}
211225

226+
bool gcc_native_payload_is_musl(std::string_view hostArch, bool isLinux,
227+
const triple::Triple& target) {
228+
if (!isLinux) return false;
229+
if (hostArch == "x86_64") return false; // the glibc payload exists here
230+
// "Native" = no explicit target, or one naming this same machine. A CROSS
231+
// target keeps its own payload rule (the `<triple>-gcc` packages above).
232+
return target.empty()
233+
|| (target.os == "linux" && target.arch == hostArch);
234+
}
235+
212236
XimToolchainPackage to_xim_package(const ToolchainSpec& spec) {
213237
XimToolchainPackage pkg;
214238
pkg.displaySpec = spec.display();
@@ -271,7 +295,30 @@ XimToolchainPackage to_xim_package(const ToolchainSpec& spec) {
271295
return pkg;
272296
}
273297

274-
// Host target (or linux-gnu): the glibc gcc package.
298+
// Host target (or linux-gnu): the glibc gcc package — on x86_64.
299+
//
300+
// #367: `xim:gcc` declares `archs = { "x86_64" }` and publishes assets for
301+
// that arch only. The GCC the ecosystem ships for other Linux
302+
// architectures is `musl-gcc`, which does publish them (aarch64 included).
303+
// Asking for `gcc` on aarch64 therefore 404s — and because a `build.mcpp`
304+
// host compile resolves the toolchain spec with NO target injection, it
305+
// landed here and made every project whose graph contains a build program
306+
// unbuildable on aarch64, with `--target aarch64-linux-musl` fixing only
307+
// the target half.
308+
//
309+
// "Which payload backs this spec on this machine" is exactly the question
310+
// this function exists to answer — the musl branch above already answers
311+
// its half the same way ("same target, two payload shapes"). The knowledge
312+
// belongs here rather than in every manifest: a user should not have to
313+
// encode which architectures a toolchain package was built for.
314+
if (gcc_native_payload_is_musl(mcpp::platform::host_arch,
315+
mcpp::platform::is_linux, t)) {
316+
const auto mt = host_musl_triple();
317+
pkg.ximName = "musl-gcc";
318+
pkg.frontendCandidates = { mt.str() + "-g++", "g++" };
319+
return pkg; // no glibc specs fixup: that payload is not glibc-linked
320+
}
321+
275322
pkg.ximName = "gcc";
276323
pkg.frontendCandidates = {"g++"};
277324
pkg.needsGccPostInstallFixup = true;

tests/unit/test_toolchain_registry.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import std;
44
import mcpp.platform;
55
import mcpp.toolchain.registry;
6+
import mcpp.toolchain.triple;
67

78
using namespace mcpp::toolchain;
89

@@ -187,3 +188,39 @@ TEST(ToolchainRegistry, IdentifiesToolchainPayloadsAndSkipsOthers) {
187188
EXPECT_FALSE(identify_xim_payload("python").has_value());
188189
EXPECT_FALSE(identify_xim_payload("linux-headers").has_value());
189190
}
191+
192+
// #367: which GCC payload backs a NATIVE Linux build.
193+
//
194+
// `xim:gcc` declares `archs = { "x86_64" }` and publishes assets for that arch
195+
// only; the GCC the ecosystem ships for other Linux architectures is
196+
// `musl-gcc` (xlings-res carries musl-gcc-16.1.0-linux-aarch64.tar.gz). Asking
197+
// for `gcc` on aarch64 therefore 404s — which is what made every project whose
198+
// graph contains a `build.mcpp` unbuildable there, since a build program's
199+
// host compile resolves the spec with no target injection and landed on the
200+
// glibc package.
201+
//
202+
// Tested through a free function taking the host arch rather than the
203+
// compile-time constant, precisely so the aarch64 answer is checkable from an
204+
// x86_64 machine.
205+
TEST(ToolchainRegistry, NativeGccPayloadFollowsWhatTheArchActuallyPublishes) {
206+
using mcpp::toolchain::gcc_native_payload_is_musl;
207+
const mcpp::toolchain::triple::Triple none{};
208+
209+
// x86_64: unchanged — the glibc package is the one that exists.
210+
EXPECT_FALSE(gcc_native_payload_is_musl("x86_64", true, none));
211+
EXPECT_FALSE(gcc_native_payload_is_musl(
212+
"x86_64", true, {"x86_64", "linux", "gnu"}));
213+
214+
// aarch64: the glibc package has no asset, musl-gcc does.
215+
EXPECT_TRUE(gcc_native_payload_is_musl("aarch64", true, none));
216+
EXPECT_TRUE(gcc_native_payload_is_musl(
217+
"aarch64", true, {"aarch64", "linux", "gnu"}));
218+
219+
// A CROSS target keeps its own payload rule — this is about the native
220+
// one, and `<triple>-gcc` packages answer for the rest.
221+
EXPECT_FALSE(gcc_native_payload_is_musl(
222+
"aarch64", true, {"x86_64", "linux", "gnu"}));
223+
224+
// Non-Linux hosts are out of scope: macOS uses llvm, Windows mingw/msvc.
225+
EXPECT_FALSE(gcc_native_payload_is_musl("aarch64", false, none));
226+
}

0 commit comments

Comments
 (0)