From 7760e6e8ac89799a63c5885d95ba610443ac887c Mon Sep 17 00:00:00 2001 From: julixian Date: Mon, 5 Oct 2026 15:03:58 +0800 Subject: [PATCH 1/2] fix: build std.compat with Clang and MSVC STL --- src/build/prepare/toolchain_env.cpp | 11 ++++++----- src/toolchain/clang.cppm | 7 ++++++- tests/unit/test_toolchain_stdmod.cpp | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/build/prepare/toolchain_env.cpp b/src/build/prepare/toolchain_env.cpp index 51574d7c8..130185498 100644 --- a/src/build/prepare/toolchain_env.cpp +++ b/src/build/prepare/toolchain_env.cpp @@ -231,21 +231,22 @@ bind_msvc_sysroot(mcpp::toolchain::Toolchain& tc, // by its own search. A toolset without one leaves `import std` unavailable // rather than borrowing another toolset's. // - // Only `std` is rebound. Detection never gave this row a `std.compat` - // source, and the clang builder for it passes the file without - // `-x c++-module`: given `std.compat.ixx`, clang takes it for linker - // input, `--precompile` writes nothing and exits 0, and the next command - // fails on the missing BMI (measured on the Windows runners). + // Bind both standard modules to this toolset. A compat module from another + // STL cannot safely import the selected std BMI. std::error_code ec; const auto ixx = choice->toolsDir / "modules" / "std.ixx"; const bool msvcStl = tc.stdModuleSource.empty() || tc.stdModuleSource.filename() == "std.ixx"; if (msvcStl && std::filesystem::exists(ixx, ec)) { tc.stdModuleSource = ixx; + const auto compat = ixx.parent_path() / "std.compat.ixx"; + tc.stdCompatSource = std::filesystem::exists(compat, ec) + ? compat : std::filesystem::path{}; tc.hasImportStd = true; tc.importStdMinLevel = msvc::std_module_min_level_for_stl(ixx); } else if (msvcStl && !tc.stdModuleSource.empty()) { tc.stdModuleSource.clear(); + tc.stdCompatSource.clear(); tc.hasImportStd = false; } diff --git a/src/toolchain/clang.cppm b/src/toolchain/clang.cppm index 0586f7999..856d25248 100644 --- a/src/toolchain/clang.cppm +++ b/src/toolchain/clang.cppm @@ -566,8 +566,12 @@ std::vector std_compat_build_commands(const Toolchain& tc, auto absBmi = (cacheDir / relBmi).string(); auto absStdBmi = (cacheDir / relStdBmi).string(); auto absObj = (cacheDir / "std.compat.o").string(); + // MSVC STL ships .ixx, which Clang does not infer as C++ module input. + // Apply the override only when parsing source, never when compiling a BMI. + const std::string sourceFlags = tc.stdCompatSource.extension() == ".ixx" + ? " -x c++-module -Wno-include-angled-in-module-purview" : ""; return { - std::format("{}{} {}{} -Wno-reserved-module-identifier{}{} " + std::format("{}{} {}{} -Wno-reserved-module-identifier{}{}{} " "-fmodule-file=std={} " "--precompile {} -o {} 2>&1", mcpp::toolchain::compiler_env_prefix(tc), @@ -576,6 +580,7 @@ std::vector std_compat_build_commands(const Toolchain& tc, crtToken, sysrootFlag, precompileFlags, + sourceFlags, absStdBmi, mcpp::xlings::shq(tc.stdCompatSource.string()), mcpp::xlings::shq(absBmi)), diff --git a/tests/unit/test_toolchain_stdmod.cpp b/tests/unit/test_toolchain_stdmod.cpp index b7e41eaba..f25b7edcb 100644 --- a/tests/unit/test_toolchain_stdmod.cpp +++ b/tests/unit/test_toolchain_stdmod.cpp @@ -71,6 +71,27 @@ TEST(ToolchainStdmod, ClangStdCompatCommandsUseRequestedStandard) { } } +TEST(ToolchainStdmod, MsvcCompatSourceIsParsedAsAModuleButItsBmiIsNot) { + auto tc = clang_toolchain(); + tc.stdModuleSource = "std.ixx"; + tc.stdCompatSource = "std.compat.ixx"; + auto cmds = clang::std_compat_build_commands( + tc, "cache", "cache/pcm.cache/std.compat.pcm", + "cache/pcm.cache/std.pcm", "", "-std=c++23", "-fms-runtime-lib=dll"); + ASSERT_EQ(cmds.size(), 2u); + EXPECT_NE(cmds[0].find("-x c++-module"), std::string::npos); + EXPECT_EQ(cmds[1].find("-x c++-module"), std::string::npos); + for (auto const& cmd : cmds) { + EXPECT_NE(cmd.find("-fmodule-file=std="), std::string::npos); + EXPECT_NE(cmd.find("-fms-runtime-lib=dll"), std::string::npos); + } + tc.stdCompatSource = "std.compat.cppm"; + cmds = clang::std_compat_build_commands( + tc, "cache", "cache/pcm.cache/std.compat.pcm", + "cache/pcm.cache/std.pcm", "", "-std=c++23"); + EXPECT_EQ(cmds[0].find("-x c++-module"), std::string::npos); +} + // THE PRECOMPILE HAS TO KNOW WHICH MACHINE, AND ONLY ONE OF TWO SOURCES EVER // CARRIES IT. // From 54483c283d736119c8b0f6a041fb1cbf7a6df06b Mon Sep 17 00:00:00 2001 From: julixian Date: Mon, 5 Oct 2026 15:23:14 +0800 Subject: [PATCH 2/2] style: align compat module flags with std module builder --- src/toolchain/clang.cppm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/toolchain/clang.cppm b/src/toolchain/clang.cppm index 856d25248..c79a55399 100644 --- a/src/toolchain/clang.cppm +++ b/src/toolchain/clang.cppm @@ -568,8 +568,9 @@ std::vector std_compat_build_commands(const Toolchain& tc, auto absObj = (cacheDir / "std.compat.o").string(); // MSVC STL ships .ixx, which Clang does not infer as C++ module input. // Apply the override only when parsing source, never when compiling a BMI. - const std::string sourceFlags = tc.stdCompatSource.extension() == ".ixx" - ? " -x c++-module -Wno-include-angled-in-module-purview" : ""; + std::string ixxFlags = (tc.stdCompatSource.extension() == ".ixx") + ? " -x c++-module -Wno-include-angled-in-module-purview" + : ""; return { std::format("{}{} {}{} -Wno-reserved-module-identifier{}{}{} " "-fmodule-file=std={} " @@ -580,7 +581,7 @@ std::vector std_compat_build_commands(const Toolchain& tc, crtToken, sysrootFlag, precompileFlags, - sourceFlags, + ixxFlags, absStdBmi, mcpp::xlings::shq(tc.stdCompatSource.string()), mcpp::xlings::shq(absBmi)),