Skip to content

Commit 03042a3

Browse files
committed
test(hostflags): pin the rendered flag strings
Byte-equality with what earlier releases emitted is a compatibility surface here, not an implementation detail: stdmod folds its compile command into std_build_commands and the std cache directory name is derived from the metadata containing it, so a reordered flag invalidates every user's std BMIs. Verifying that by inspecting the cache after a build turned out to be unreliable — the cache is shared and accumulates entries from every toolchain used since, so the first check compared stale entries alongside fresh ones and passed while the string had in fact moved. These tests assert the spelling directly from a synthetic model, deterministically, and keep asserting it.
1 parent 015d929 commit 03042a3

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

tests/unit/test_hostflags.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,56 @@ TEST(HostFlags, ProducerAnswersForEveryFamily) {
7979
}
8080
}
8181

82+
// ── The rendered string must not move ───────────────────────────────────────
83+
//
84+
// stdmod folds its compile command into `std_build_commands`, and the std
85+
// cache DIRECTORY NAME is derived from the metadata containing it. Reordering
86+
// a flag therefore invalidates every user's std BMIs for no behavioural gain
87+
// — so the exact spelling is a compatibility surface, not an implementation
88+
// detail. This pins it; the first attempt at this refactor moved
89+
// `-stdlib=libc++` after the include flags and would have shipped exactly
90+
// that invalidation.
91+
TEST(HostFlags, ClangCfgBypassStringIsStable) {
92+
mcpp::toolchain::ClangDriverModel dm;
93+
dm.hasCfg = true;
94+
dm.cxxIncludes = { "/llvm/include/c++/v1", "/llvm/include/tgt/c++/v1" };
95+
96+
EXPECT_EQ(dm.compile_flags(mcpp::toolchain::no_escape),
97+
" --no-default-config -nostdinc++"
98+
" -isystem/llvm/include/c++/v1"
99+
" -isystem/llvm/include/tgt/c++/v1");
100+
101+
// With the stdlib selection the std module has always asked for, it lands
102+
// immediately after -nostdinc++ — not at the end.
103+
EXPECT_EQ(mcpp::toolchain::render_tokens(
104+
dm.compile_tokens(mcpp::toolchain::no_escape, true)),
105+
" --no-default-config -nostdinc++ -stdlib=libc++"
106+
" -isystem/llvm/include/c++/v1"
107+
" -isystem/llvm/include/tgt/c++/v1");
108+
}
109+
110+
TEST(HostFlags, LinkModelStringsAreStable) {
111+
mcpp::toolchain::ToolchainLinkModel lm;
112+
lm.mode = mcpp::toolchain::CLibMode::PayloadFirst;
113+
lm.clangDriver = true;
114+
lm.crtDir = "/glibc/lib";
115+
lm.libDirs = { "/glibc/lib" };
116+
lm.loader = "/glibc/lib/ld.so";
117+
lm.systemIncludes = { "/glibc/include" };
118+
119+
EXPECT_EQ(lm.compile_flags(mcpp::toolchain::no_escape),
120+
" -isystem/glibc/include");
121+
EXPECT_EQ(lm.link_flags(mcpp::toolchain::no_escape),
122+
" -B/glibc/lib -L/glibc/lib -Wl,-rpath,/glibc/lib"
123+
" -Wl,--dynamic-linker=/glibc/lib/ld.so");
124+
125+
// GCC takes -idirafter so libstdc++'s #include_next wrappers can still
126+
// reach libc.
127+
lm.clangDriver = false;
128+
EXPECT_EQ(lm.compile_flags(mcpp::toolchain::no_escape),
129+
" -idirafter/glibc/include");
130+
}
131+
82132
// ── bmi_reference_tokens ────────────────────────────────────────────────────
83133
//
84134
// The traits store these for the ninja STRING channel, where one word vs two

0 commit comments

Comments
 (0)