Skip to content

Commit 52c19ef

Browse files
committed
fix: payload C headers via -idirafter for GCC (include_next reachability)
Ground truth from CI (command now in the error): -isystem'd payload glibc include was present yet #include_next <stdlib.h> still failed. GCC's libstdc++ wraps libc headers with #include_next, which only searches directories AFTER the current header's dir — gcc's built-in dirs are last, so -isystem (inserted before them) is unreachable. Use -idirafter (appended to the very end) for GCC payload headers in both the std-module precompile and per-TU flags; clang keeps -isystem. Verified with a faithful fresh-home repro (no subos => invalid sysroot): first-run installs glibc default and builds clean.
1 parent 4541b05 commit 52c19ef

2 files changed

Lines changed: 24 additions & 6 deletions

File tree

src/build/flags.cppm

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,18 @@ CompileFlags compute_flags(const BuildPlan& plan) {
173173
}
174174
f.sysroot = link_toolchain_flags;
175175
} else if (plan.toolchain.payloadPaths) {
176-
// No sysroot but have payload paths: use -isystem.
176+
// No usable sysroot: wire the C library headers from the payload.
177+
// For GCC use -idirafter (appended after the built-in dirs) so that
178+
// libstdc++'s #include_next wrappers can reach them; -isystem would
179+
// place them BEFORE the built-ins, invisible to #include_next.
177180
auto& pp = *plan.toolchain.payloadPaths;
178-
compile_toolchain_flags += " -isystem" + escape_path(pp.glibcInclude);
181+
const bool clangTc = mcpp::toolchain::is_clang(plan.toolchain);
182+
auto inc_flag = [&](const std::filesystem::path& p) {
183+
return (clangTc ? " -isystem" : " -idirafter") + escape_path(p);
184+
};
185+
compile_toolchain_flags += inc_flag(pp.glibcInclude);
179186
if (!pp.linuxInclude.empty())
180-
compile_toolchain_flags += " -isystem" + escape_path(pp.linuxInclude);
187+
compile_toolchain_flags += inc_flag(pp.linuxInclude);
181188
}
182189

183190
// Binutils -B flag

src/toolchain/stdmod.cppm

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,21 @@ std::expected<StdModule, StdModError> ensure_built(
236236
sysroot_flag += std::format(" -isystem'{}'", tc.payloadPaths->linuxInclude.string());
237237
}
238238
} else if (tc.payloadPaths) {
239-
// No sysroot: use payload -isystem paths.
240-
sysroot_flag += std::format(" -isystem'{}'", tc.payloadPaths->glibcInclude.string());
239+
// No usable sysroot: wire the C library headers from the payload.
240+
// GCC's libstdc++ wraps libc headers via #include_next, which only
241+
// searches directories AFTER the one the current header came from —
242+
// and gcc's built-in dirs are LAST in the search order, so an
243+
// -isystem payload dir (inserted before the built-ins) is unreachable
244+
// from #include_next. -idirafter appends the payload to the very end,
245+
// exactly where #include_next looks.
246+
const bool clang = is_clang(tc);
247+
auto add_inc = [&](const std::filesystem::path& p) {
248+
if (clang) sysroot_flag += std::format(" -isystem'{}'", p.string());
249+
else sysroot_flag += std::format(" -idirafter'{}'", p.string());
250+
};
251+
add_inc(tc.payloadPaths->glibcInclude);
241252
if (!tc.payloadPaths->linuxInclude.empty())
242-
sysroot_flag += std::format(" -isystem'{}'", tc.payloadPaths->linuxInclude.string());
253+
add_inc(tc.payloadPaths->linuxInclude);
243254
}
244255

245256
std::vector<std::string> stdCommands;

0 commit comments

Comments
 (0)