Skip to content

Commit 6739b7f

Browse files
committed
fix(bench): the generated fixture needs the payload flags too, and a shim is not an engine
CI 上 fixture 的 cmake/xmake 全红,`cannot find crt1.o / crti.o / -lm`。 **我的公平性修复只做了一半。** `--compiler payload:gcc` 把 mcpp registry 里的 编译器交给了每个引擎,但**生成的** fixture 描述里没有任何 payload 接线 —— `bench_hermetic_payload()` 只存在于 checked-in 的 bench/projects/ 里。一个裸的 registry gcc 不知道自己的汇编器、链接器和 libc 在哪,而 cmake 把这个报成 「编译器无法编译一个简单程序」,只字不提 sysroot。 `bench.toolchain::payload_flags()` 现在算出这些 flag,`emit_cmake` 在 **`project()` 之前**写入(cmake 的编译器探测就发生在 project() 里), `emit_xmake` 通过 add_cxflags/add_ldflags 写入。gcc 拿 -B + --sysroot, clang 拿显式的 libc++ include 链 —— 与 hermetic_payload.cmake 同一套判断, 注释里写明了「同一个决策在两处」以及为什么不能靠 include 一个绝对路径。 **另一条:xlings 的 shim 会替一个没装的程序回答。** 问它 `--version`,它打印 `[error] xlings: 'bazel' is not installed` 然后**退出 0**。照单全收就是 「present,版本 = 那句错误」,于是该引擎的每个格子都跑了、都失败了,并被记成 **对引擎的发现**,而不是「这台机器上没装」—— macOS 每个 job 18 个格子。 `probe_program` 现在把这种 banner 当作 absent。 本机验证:fixture 的 cmake/xmake modules cold 从「configure 失败」变成 4.29s / 3.07s。
1 parent 674a462 commit 6739b7f

3 files changed

Lines changed: 118 additions & 6 deletions

File tree

bench/src/engines/engine.cppm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ inline Availability probe_program(std::string_view program,
131131
version_argv.size() > 1 ? version_argv[1] : "--version",
132132
r.exit_code)};
133133
auto banner = first_line(*captured);
134+
// ⚠️ A SHIM THAT ANSWERS FOR A PROGRAM IT DOES NOT HAVE. xlings installs
135+
// `bazel`, `mcpp` and friends as shims on PATH; ask one for its version
136+
// when the package is not installed and it prints
137+
//
138+
// [error] xlings: 'bazel' is not installed
139+
//
140+
// and exits ZERO. Taken at face value that is "present, version =
141+
// <error message>", so every cell for that engine ran, failed, and was
142+
// recorded as a FINDING against the engine rather than as "not installed
143+
// here" — 18 cells per macOS job.
144+
if (banner.find("is not installed") != std::string::npos)
145+
return {false, std::format("{} resolves to a shim that reports it is not "
146+
"installed: {}", program, banner)};
134147
return {true, banner.empty() ? std::string(program) : banner};
135148
}
136149

bench/src/fixture/buildfiles.cppm

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ inline std::string join(const std::vector<std::string>& v, std::string_view sep,
6262
return out;
6363
}
6464

65+
66+
// Leading space is convenient when concatenating flag strings and wrong inside
67+
// a quoted xmake argument.
68+
inline std::string trim_copy(std::string_view s) {
69+
while (!s.empty() && s.front() == ' ') s.remove_prefix(1);
70+
while (!s.empty() && s.back() == ' ') s.remove_suffix(1);
71+
return std::string(s);
72+
}
73+
6574
} // namespace detail
6675

6776
// --- mcpp -----------------------------------------------------------------
@@ -102,12 +111,24 @@ inline void emit_mcpp(const std::filesystem::path& root, Variant variant, const
102111

103112
// --- cmake ----------------------------------------------------------------
104113

105-
inline void emit_cmake(const std::filesystem::path& root, Variant variant, const Shape& s) {
114+
inline void emit_cmake(const std::filesystem::path& root, Variant variant, const Shape& s,
115+
std::string_view compiler = {}) {
106116
const auto set = source_set(variant, s);
117+
// The payload flags go in BEFORE project(), because cmake's "can the
118+
// compiler build a trivial program" probe runs during project() — and a
119+
// bare registry gcc fails that probe at the LINK with `cannot find crt1.o`,
120+
// reported as a configure error that never mentions a sysroot.
121+
const auto pf = toolchain::payload_flags(compiler);
107122
std::string cm =
108123
"# Generated by bench.fixture.buildfiles — do not edit.\n"
109-
"cmake_minimum_required(VERSION 3.28)\n"
110-
"project(fx CXX)\n"
124+
"cmake_minimum_required(VERSION 3.28)\n";
125+
if (!pf.compile.empty() || !pf.link.empty()) {
126+
cm += std::format("set(CMAKE_CXX_FLAGS \"${{CMAKE_CXX_FLAGS}}{}\")\n"
127+
"set(CMAKE_C_FLAGS \"${{CMAKE_C_FLAGS}}{}\")\n"
128+
"set(CMAKE_EXE_LINKER_FLAGS \"${{CMAKE_EXE_LINKER_FLAGS}}{}\")\n",
129+
pf.compile, pf.compile, pf.link);
130+
}
131+
cm += "project(fx CXX)\n"
111132
"set(CMAKE_CXX_STANDARD 23)\n"
112133
"set(CMAKE_CXX_STANDARD_REQUIRED ON)\n"
113134
"set(CMAKE_CXX_EXTENSIONS OFF)\n"
@@ -128,7 +149,9 @@ inline void emit_cmake(const std::filesystem::path& root, Variant variant, const
128149

129150
// --- xmake ----------------------------------------------------------------
130151

131-
inline void emit_xmake(const std::filesystem::path& root, Variant variant, const Shape&) {
152+
inline void emit_xmake(const std::filesystem::path& root, Variant variant, const Shape&,
153+
std::string_view compiler = {}) {
154+
const auto pf = toolchain::payload_flags(compiler);
132155
std::string lua =
133156
"-- Generated by bench.fixture.buildfiles — do not edit.\n"
134157
"set_project(\"fx\")\n"
@@ -150,6 +173,14 @@ inline void emit_xmake(const std::filesystem::path& root, Variant variant, const
150173
// cost none of the others do.
151174
lua += " set_policy(\"build.c++.modules.std\", false)\n";
152175
}
176+
// Same payload flags as the cmake arm: xmake is handed the driver through
177+
// CXX, and a registry gcc without -B/--sysroot cannot link.
178+
if (!pf.compile.empty())
179+
lua += std::format(" add_cxflags(\"{}\", {{force = true}})\n",
180+
detail::trim_copy(pf.compile));
181+
if (!pf.link.empty())
182+
lua += std::format(" add_ldflags(\"{}\", {{force = true}})\n",
183+
detail::trim_copy(pf.link));
153184
detail::write(root / "xmake.lua", lua);
154185
}
155186

@@ -206,8 +237,8 @@ inline void emit_bazel(const std::filesystem::path& root, Variant variant, const
206237
inline void emit_all(const std::filesystem::path& root, Variant variant, const Shape& s,
207238
std::string_view compiler = {}) {
208239
emit_mcpp(root, variant, s, compiler);
209-
emit_cmake(root, variant, s);
210-
emit_xmake(root, variant, s);
240+
emit_cmake(root, variant, s, compiler);
241+
emit_xmake(root, variant, s, compiler);
211242
emit_bazel(root, variant, s);
212243
}
213244

bench/src/toolchain.cppm

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,72 @@ inline Resolved payload_cxx(std::string_view compiler) {
115115
driver.string(), clang ? "llvm" : "gcc", ver)};
116116
}
117117

118+
// The flags a FOREIGN engine needs so that a payload compiler can actually
119+
// build and link — the generated fixture's counterpart of
120+
// bench/projects/common/cmake/hermetic_payload.cmake.
121+
//
122+
// ⚠️ WHY THIS EXISTS AT ALL, given that file exists. The checked-in project
123+
// descriptions `include()` it; the fixture is GENERATED into a scratch
124+
// directory by a binary that may live anywhere, so it has no path to include.
125+
// The two are the same decision in two places and must be kept in step — the
126+
// alternative considered (emit an `include()` of an absolute path) makes every
127+
// generated fixture depend on this checkout still being where it was.
128+
//
129+
// It is needed because `--compiler payload:gcc` hands cmake and xmake a
130+
// compiler out of mcpp's registry, and a bare registry gcc has no idea where
131+
// its assembler, linker or libc are:
132+
//
133+
// /usr/bin/ld: cannot find crt1.o: No such file or directory
134+
// /usr/bin/ld: cannot find -lm: No such file or directory
135+
//
136+
// which cmake reports as "the C++ compiler is not able to compile a simple
137+
// test program", i.e. as a configure failure with no mention of a sysroot.
138+
struct PayloadFlags {
139+
std::string compile;
140+
std::string link;
141+
};
142+
143+
inline PayloadFlags payload_flags(std::string_view compiler) {
144+
PayloadFlags f;
145+
// Only a compiler FROM the registry gets these; a host compiler already
146+
// knows where its own runtime is, and adding a registry sysroot to it
147+
// produces a mixed build that fails somewhere unrelated.
148+
if (compiler.find("xpkgs") == std::string_view::npos) return f;
149+
150+
const auto xpkgs = registry_xpkgs();
151+
if (xpkgs.empty()) return f;
152+
std::error_code ec;
153+
154+
if (!is_clang_request(compiler)) {
155+
// gcc: -B for `as`/`ld`, --sysroot for headers and startup files, and
156+
// BOTH must reach compile and link — the driver spawns `as` at compile
157+
// time and `ld` at link time, so one side alone silently falls back.
158+
for (const auto& e : std::filesystem::directory_iterator(
159+
xpkgs / "xim-x-binutils", ec)) {
160+
f.compile += " -B" + (e.path() / "bin").string();
161+
f.link += " -B" + (e.path() / "bin").string();
162+
break;
163+
}
164+
auto sysroot = registry_xpkgs().parent_path().parent_path()
165+
/ "subos" / "default";
166+
if (std::filesystem::is_directory(sysroot, ec)) {
167+
f.compile += " --sysroot=" + sysroot.string();
168+
f.link += " --sysroot=" + sysroot.string();
169+
}
170+
return f;
171+
}
172+
173+
// clang: an explicit libc++ chain rather than --sysroot, which is what mcpp
174+
// itself drives clang with. Handing clang gcc's sysroot is the mirror of the
175+
// bug above — one arm on the payload libc, the other on the host's.
176+
const std::string ver{on_windows() ? kLlvmWindows : kLlvm};
177+
const auto root = xpkgs / "xim-x-llvm" / ver;
178+
if (std::filesystem::is_directory(root / "include" / "c++" / "v1", ec)) {
179+
f.compile += " --no-default-config -nostdinc++"
180+
" -isystem" + (root / "include" / "c++" / "v1").string();
181+
f.link += " -nostdlib++ -L" + (root / "lib").string() + " -lc++ -lc++abi";
182+
}
183+
return f;
184+
}
185+
118186
} // namespace bench::toolchain

0 commit comments

Comments
 (0)