Skip to content

Commit cb91861

Browse files
committed
toolchain: a stated linker reaches the link on every shape, and gcc says it cannot
`--ld-path` was appended inside the Linux clang branch, the only one that consumes `link_toolchain_flags`. On macOS the stated linker therefore entered the fingerprint -- touching the wrapper declined the fast path -- and took no part in the link, with nothing said; the toolchain lab measured it on macos-15, where build.ninja held no `--ld-path`. e2e 875 asserts that flag but skips on a host without the llvm payload, which macOS CI is. The flag belongs to the driver, not to a platform, so it is added once after every shape has built its line. For a gcc toolchain the declaration is now refused where it is read: gcc selects a linker by the name `ld` inside a `-B` directory, so a program named anything else could not be chosen, and a silent `-B` would be the same defect in the other direction.
1 parent 49abb10 commit cb91861

3 files changed

Lines changed: 41 additions & 8 deletions

File tree

‎.agents/docs/2026-10-01-tool-and-toolchain-sources-design.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,11 @@ action 描述的是一次还不存在的构建;一个载荷请求也无法回答
150150
的预算是 128 KiB,而只有 Windows 上 `capture_exec` 所经的 shell 是 8191 字节。覆盖它的是
151151
mcpp-plugins 的 `all-rules-compile`(导入十五个宿主模块)与两个单测——每种 tokenize 语法
152152
一个,因为 clang 与 GCC 把反斜杠当转义,cl 与 clang-cl 不当。
153+
- `tools = { ld = ... }` 只对 clang 驱动成立(`--ld-path`);gcc 按 `-B` 目录里的名字 `ld`
154+
选链接器,所以一个别名程序无法那样被选中,声明处直接拒绝而不是在链接时忽略。这条起初写成了
155+
Linux clang 分支里的一行,于是 macOS 的 Apple 链接形状拿不到它——被陈述的链接器进了指纹
156+
(改动 wrapper 会让快速路径失效),却不参与链接,而且什么都不说。现在它在所有形状之后追加
157+
一次。覆盖它的是 toolchain-lab:e2e 875 在没有装 llvm 载荷的宿主上 SKIP,而 macOS CI 正是
158+
这样的宿主。
153159
- 工具链描述数据化(核心读描述文件、不再硬编码 `to_xim_package`)不在本次范围;
154160
`[toolchain] { path }` 的字段已经与那份描述同形,所以它是后续的第三种载体,而不是改写。

‎src/build/flags.cppm‎

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -975,14 +975,6 @@ CompileFlags compute_flags(const BuildPlan& plan) {
975975
link_toolchain_flags +=
976976
mcpp::toolchain::ClangDriverModel::kLinkDriverFlags;
977977
}
978-
// A LINKER STATED BY ROLE (mcpp#755, `tools = { ld = ... }`): clang's
979-
// `--ld-path` names the program, after `-fuse-ld` has chosen the
980-
// flavour, so the flags that follow still speak to the same linker.
981-
if (auto* ld = plan.toolchain.tool_override("ld")) {
982-
const auto opt = " --ld-path=" + escape_path(*ld);
983-
link_toolchain_flags += opt;
984-
link_toolchain_flags_c += opt;
985-
}
986978
f.sysroot = link_toolchain_flags;
987979
} else if (lm.mode != mcpp::toolchain::CLibMode::None) {
988980
// GCC (or Clang without cfg): --sysroot from probe, or the payload
@@ -2401,6 +2393,27 @@ CompileFlags compute_flags(const BuildPlan& plan) {
24012393
}
24022394
}
24032395

2396+
// A LINKER STATED BY ROLE (mcpp#755, `tools = { ld = ... }`), AFTER EVERY
2397+
// SHAPE HAS BUILT ITS LINE.
2398+
//
2399+
// It was appended inside the Linux clang branch, the only one that consumes
2400+
// `link_toolchain_flags`: on macOS the stated linker entered the fingerprint
2401+
// -- the fast path declined when the wrapper changed -- and took no part in
2402+
// the link, with nothing said (measured in the toolchain lab on macos-15,
2403+
// where `build.ninja` held no `--ld-path`). The flag belongs to the driver,
2404+
// not to a platform, so it is added once here, for every shape.
2405+
//
2406+
// Clang only: `--ld-path` is clang's. GCC chooses its linker by name inside a
2407+
// `-B` directory, so a program named anything else could not be selected that
2408+
// way, and a silent `-B` would be the same defect in the other direction. A
2409+
// gcc toolchain that states `ld` is refused where the declaration is read.
2410+
if (plan.toolchain.compiler == mcpp::toolchain::CompilerId::Clang)
2411+
if (auto* ld = plan.toolchain.tool_override("ld")) {
2412+
const auto opt = " --ld-path=" + escape_path(*ld);
2413+
if (f.ld.find("--ld-path=") == std::string::npos) f.ld += opt;
2414+
if (f.ldC.find("--ld-path=") == std::string::npos) f.ldC += opt;
2415+
}
2416+
24042417
return f;
24052418
}
24062419

‎src/build/prepare/local_toolchain.cpp‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,20 @@ step2_use_local_toolchain(PrepareState& state, const mcpp::toolchain::ToolchainS
240240
for (auto const& [role, p] : lt.tools)
241241
if (!fs::exists(p, ec))
242242
return refuse(std::format("tools.{} names '{}', which does not exist", role, p));
243+
// A STATED LINKER REACHES THE LINK THROUGH CLANG'S `--ld-path`, which gcc
244+
// does not have: gcc selects a linker by the name `ld` inside a `-B`
245+
// directory, so a program named anything else cannot be chosen that way.
246+
// Refused here rather than ignored in the link: the alternative is a stated
247+
// tool that enters the fingerprint and takes no part in the build, which is
248+
// what this mechanism exists to prevent.
249+
if (family == "gcc")
250+
for (auto const& [role, p] : lt.tools)
251+
if (role == "ld")
252+
return refuse(std::format(
253+
"tools.ld names '{}', and this is a gcc toolchain: a linker is stated "
254+
"through clang's `--ld-path`, which gcc has no counterpart for. Either "
255+
"name an llvm toolchain, or place a program called `ld` in the tree "
256+
"gcc searches", p));
243257
if (!lt.sysroot.empty() && !fs::is_directory(lt.sysroot, ec))
244258
return refuse(std::format("sysroot '{}' is not a directory", lt.sysroot));
245259
state.explicit_compiler = driver;

0 commit comments

Comments
 (0)