@@ -27,9 +27,10 @@ struct CompileFlags {
2727 std::string cxx; // full cxxflags string
2828 std::string cc; // full cflags string
2929 std::string ld; // ldflags string
30- std::filesystem::path cxxBinary; // g++ / clang++
31- std::filesystem::path ccBinary; // gcc / clang (derived)
32- std::filesystem::path arBinary; // ar path (may be empty → use PATH)
30+ std::filesystem::path cxxBinary; // g++ / clang++ / cl.exe
31+ std::filesystem::path ccBinary; // gcc / clang (derived; cl.exe = same)
32+ std::filesystem::path arBinary; // ar / llvm-ar / lib.exe (empty → PATH)
33+ std::filesystem::path ldBinary; // link.exe (SeparateLinker dialects only)
3334 std::string sysroot; // --sysroot=... (for ninja ldflags)
3435 std::string bFlag; // -B<binutils> (for ninja ldflags)
3536 bool staticStdlib = true ;
@@ -142,15 +143,17 @@ CompileFlags compute_flags(const BuildPlan& plan) {
142143 f.cxxBinary = plan.toolchain .binaryPath ;
143144 f.ccBinary = mcpp::toolchain::derive_c_compiler (plan.toolchain );
144145
145- // PIC?
146+ const bool isMsvcDialect = (d.id == " msvc" );
147+
148+ // PIC? (GNU-only concept; PE code is position independent by design.)
146149 bool need_pic = false ;
147150 for (auto & lu : plan.linkUnits ) {
148151 if (lu.kind == LinkUnit::SharedLibrary) {
149152 need_pic = true ;
150153 break ;
151154 }
152155 }
153- std::string pic_flag = need_pic ? " -fPIC" : " " ;
156+ std::string pic_flag = ( need_pic && !isMsvcDialect) ? " -fPIC" : " " ;
154157
155158 // Include dirs
156159 std::string include_flags;
@@ -235,9 +238,21 @@ CompileFlags compute_flags(const BuildPlan& plan) {
235238 // unless the profile pins -O0.
236239 auto & prof = plan.manifest .buildConfig ;
237240 std::string opt_flag = isMuslTc && prof.optLevel != " 0"
238- ? " -Og" : std::format (" {}{}" , d.optPrefix , prof.optLevel );
241+ ? " -Og"
242+ : (isMsvcDialect && prof.optLevel == " 0" )
243+ ? " /Od" // MSVC's no-opt spelling (there is no /O0)
244+ : std::format (" {}{}" , d.optPrefix , prof.optLevel );
239245 if (prof.debug ) opt_flag += std::format (" {}" , d.debugFlags );
240- if (prof.lto ) opt_flag += " -flto" ;
246+ if (prof.lto && !isMsvcDialect) opt_flag += " -flto" ;
247+
248+ // MSVC baseline: /nologo /EHsc /utf-8 (dialect alwaysFlags) + the CRT
249+ // model — /MD default, /MT under static linkage (portable-by-default is
250+ // impossible on MSVC-ABI; /MT at least removes the vcruntime DLL dep).
251+ std::string msvc_base;
252+ if (isMsvcDialect) {
253+ msvc_base = std::format (" {}" , d.alwaysFlags );
254+ msvc_base += (plan.manifest .buildConfig .linkage == " static" ) ? " /MT" : " /MD" ;
255+ }
241256
242257 // User link flags
243258 std::string user_ldflags;
@@ -264,9 +279,8 @@ CompileFlags compute_flags(const BuildPlan& plan) {
264279 }
265280 std::string std_compat_module_flag;
266281 if (!traits.stdCompatBmiUsePrefix .empty () && !plan.stdCompatBmiPath .empty ()) {
267- // NOTE: staging path is Clang's today; registry-dispatch when the
268- // MSVC backend lands (std.compat.ixx staging).
269- auto compatDst = mcpp::toolchain::clang::staged_std_compat_bmi_path (plan.outputDir );
282+ auto compatDst = mcpp::toolchain::staged_std_compat_bmi_path (
283+ plan.toolchain , plan.outputDir );
270284 std_compat_module_flag = std::string (traits.stdCompatBmiUsePrefix )
271285 + escape_path (compatDst);
272286 }
@@ -287,11 +301,20 @@ CompileFlags compute_flags(const BuildPlan& plan) {
287301 std::string cxx_std_flag =
288302 plan.cppStandardFlag .empty ()
289303 ? std::format (" {}c++23" , d.stdPrefix ) : plan.cppStandardFlag ;
290- f.cxx = std::format (" {}{}{}{}{}{}{}{}{}{}" , cxx_std_flag, module_flag, std_module_flag,
304+ // plan.dialectFlags rides right behind -std= (issue #210): module-graph-
305+ // global dialect flags reach every TU (deps included) via this global
306+ // cxxflags string, exactly like the standard flag itself.
307+ f.cxx = std::format (" {}{}{}{}{}{}{}{}{}{}{}{}" , cxx_std_flag, plan.dialectFlags ,
308+ msvc_base, module_flag, std_module_flag,
291309 std_compat_module_flag, prebuilt_module_flag,
292310 opt_flag, pic_flag, compile_toolchain_flags, b_flag, include_flags);
293- f.cc = std::format (" {}{}{}{}{}{}{}" , d.stdPrefix , c_std, opt_flag, pic_flag,
294- compile_toolchain_flags, b_flag, include_flags);
311+ // MSVC compiles C with cl.exe too; /std: for C uses cN spellings — skip
312+ // the C standard flag there (cl defaults are fine for the C entry TUs).
313+ f.cc = isMsvcDialect
314+ ? std::format (" {}{}{}{}{}" , msvc_base, opt_flag, compile_toolchain_flags,
315+ b_flag, include_flags)
316+ : std::format (" {}{}{}{}{}{}{}" , d.stdPrefix , c_std, opt_flag, pic_flag,
317+ compile_toolchain_flags, b_flag, include_flags);
295318
296319 // Link flags
297320 f.staticStdlib = plan.manifest .buildConfig .staticStdlib ;
@@ -339,6 +362,18 @@ CompileFlags compute_flags(const BuildPlan& plan) {
339362 if (prof.strip ) link_extra += " -s" ;
340363
341364 if constexpr (mcpp::platform::is_windows) {
365+ if (isMsvcDialect) {
366+ // Native cl.exe: link.exe does the link (SeparateLinker). Search
367+ // paths for dependency runtime import libs via /LIBPATH; user
368+ // ldflags pass through verbatim; GNU link_extra (-flto/-s) does
369+ // not apply.
370+ f.ldBinary = mcpp::toolchain::link_tool (plan.toolchain );
371+ std::string libpaths;
372+ for (auto & dir : plan.depRuntimeLibraryDirs )
373+ libpaths += " /LIBPATH:" + escape_path (dir);
374+ f.ld = libpaths + user_ldflags;
375+ return f;
376+ }
342377 // PE link: no rpath/loader/payload model. MSVC-ABI Clang needs
343378 // nothing extra (MSVC STL/SDK via the driver); MinGW adds the static
344379 // libstdc++/libgcc pair (static_stdlib above) and -B so its own
0 commit comments