diff --git a/src/build/prepare/toolchain_env.cpp b/src/build/prepare/toolchain_env.cpp index 51574d7c..13018549 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 0586f799..c79a5539 100644 --- a/src/toolchain/clang.cppm +++ b/src/toolchain/clang.cppm @@ -566,8 +566,13 @@ 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. + std::string ixxFlags = (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 +581,7 @@ std::vector std_compat_build_commands(const Toolchain& tc, crtToken, sysrootFlag, precompileFlags, + ixxFlags, 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 b7e41eab..f25b7edc 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. //