@@ -23,6 +23,8 @@ import mcpp.build.plan;
2323import mcpp.build.flags;
2424import mcpp.build.compile_commands;
2525import mcpp.dyndep;
26+ import mcpp.toolchain.detect;
27+ import mcpp.toolchain.registry;
2628import mcpp.xlings;
2729
2830export namespace mcpp ::build {
@@ -67,6 +69,16 @@ std::string escape_ninja_path(const std::filesystem::path& p) {
6769 return out;
6870}
6971
72+ std::string escape_ninja_variable_value (std::string_view s) {
73+ std::string out;
74+ out.reserve (s.size ());
75+ for (char c : s) {
76+ if (c == ' $' ) out += " $$" ;
77+ else out.push_back (c);
78+ }
79+ return out;
80+ }
81+
7082void write_file (const std::filesystem::path& p, std::string_view content) {
7183 std::filesystem::create_directories (p.parent_path ());
7284 std::ofstream os (p);
@@ -107,47 +119,15 @@ std::filesystem::path mcpp_exe_path() {
107119 return " mcpp" ; // fall back to PATH lookup
108120}
109121
110- // Derive a sibling C compiler from a C++ compiler binary path. Used so .c
111- // sources can be compiled by the actual C frontend (cc1), not g++ which
112- // rejects implicit `void*` conversions and `restrict` etc.
113- // .../bin/g++ → .../bin/gcc
114- // .../bin/x86_64-linux-musl-g++ → .../bin/x86_64-linux-musl-gcc
115- // .../bin/clang++ → .../bin/clang
116- // .../bin/c++ → .../bin/cc
117- // If no sibling exists, return "gcc" so PATH lookup is the final fallback
118- // (this also keeps unit tests that don't touch a real toolchain happy).
119- std::filesystem::path derive_c_compiler (const std::filesystem::path& cxx) {
120- auto fname = cxx.filename ().string ();
121- auto try_replace = [&](std::string_view from,
122- std::string_view to) -> std::optional<std::filesystem::path> {
123- auto pos = fname.rfind (from);
124- if (pos == std::string::npos)
125- return std::nullopt ;
126- std::string repl = fname;
127- repl.replace (pos, from.size (), to);
128- auto p = cxx.parent_path () / repl;
129- std::error_code ec;
130- if (std::filesystem::exists (p, ec))
131- return p;
132- return std::nullopt ;
133- };
134- if (auto p = try_replace (" clang++" , " clang" ))
135- return *p;
136- if (auto p = try_replace (" g++" , " gcc" ))
137- return *p;
138- if (auto p = try_replace (" c++" , " cc" ))
139- return *p;
140- return " gcc" ;
141- }
142-
143122bool is_c_source (const std::filesystem::path& src) {
144123 return src.extension () == " .c" ;
145124}
146125
147126} // namespace
148127
149128std::string emit_ninja_string (const BuildPlan& plan) {
150- bool dyndep = dyndep_mode_enabled ();
129+ bool dyndep = dyndep_mode_enabled ()
130+ && mcpp::toolchain::is_gcc (plan.toolchain );
151131 std::string out;
152132 auto append = [&](std::string s) { out += std::move (s); };
153133
@@ -167,6 +147,7 @@ std::string emit_ninja_string(const BuildPlan& plan) {
167147
168148 append (std::format (" cxx = {}\n " , escape_ninja_path (flags.cxxBinary )));
169149 append (std::format (" cxxflags = {}\n " , flags.cxx ));
150+ append (std::format (" toolenv = {}\n " , escape_ninja_variable_value (flags.toolEnv )));
170151 if (need_c_rule) {
171152 append (std::format (" cc = {}\n " , escape_ninja_path (flags.ccBinary )));
172153 append (std::format (" cflags = {}\n " , flags.cc ));
@@ -208,7 +189,7 @@ std::string emit_ninja_string(const BuildPlan& plan) {
208189 " if [ -n \" $bmi_out\" ] && [ -f \" $bmi_out\" ]; then "
209190 " cp -p \" $bmi_out\" \" $bmi_out.bak\" ; "
210191 " fi && "
211- " $cxx $cxxflags -c $in -o $out && "
192+ " $toolenv $ cxx $cxxflags -c $in -o $out && "
212193 " if [ -n \" $bmi_out\" ] && [ -f \" $bmi_out.bak\" ] && "
213194 " cmp -s \" $bmi_out\" \" $bmi_out.bak\" ; then "
214195 " mv \" $bmi_out.bak\" \" $bmi_out\" ; "
@@ -221,38 +202,38 @@ std::string emit_ninja_string(const BuildPlan& plan) {
221202 append (" \n " );
222203
223204 append (" rule cxx_object\n " );
224- append (" command = $cxx $cxxflags -c $in -o $out\n " );
205+ append (" command = $toolenv $ cxx $cxxflags -c $in -o $out\n " );
225206 append (" description = OBJ $out\n " );
226207 if (dyndep)
227208 append (" restat = 1\n " );
228209 append (" \n " );
229210
230211 if (need_c_rule) {
231212 append (" rule c_object\n " );
232- append (" command = $cc $cflags -c $in -o $out\n " );
213+ append (" command = $toolenv $ cc $cflags -c $in -o $out\n " );
233214 append (" description = CC $out\n " );
234215 if (dyndep)
235216 append (" restat = 1\n " );
236217 append (" \n " );
237218 }
238219
239220 append (" rule cxx_link\n " );
240- append (" command = $cxx $in -o $out $ldflags\n " );
221+ append (" command = $toolenv $ cxx $in -o $out $ldflags\n " );
241222 append (" description = LINK $out\n\n " );
242223
243224 append (" rule cxx_archive\n " );
244- append (" command = $ar rcs $out $in\n " );
225+ append (" command = $toolenv $ ar rcs $out $in\n " );
245226 append (" description = AR $out\n\n " );
246227
247228 append (" rule cxx_shared\n " );
248- append (" command = $cxx -shared $in -o $out $ldflags\n " );
229+ append (" command = $toolenv $ cxx -shared $in -o $out $ldflags\n " );
249230 append (" description = SHARED $out\n\n " );
250231
251232 if (dyndep) {
252233 // Scan rule: produce P1689 .ddi for one TU.
253234 // -E -M -MM -MF gives us the dep file; -fdeps-* gives us the .ddi.
254235 append (" rule cxx_scan\n " );
255- append (" command = $cxx $cxxflags -fdeps-format=p1689r5 "
236+ append (" command = $toolenv $ cxx $cxxflags -fdeps-format=p1689r5 "
256237 " -fdeps-file=$out -fdeps-target=$compile_target "
257238 " -M -MM -MF $out.dep -E $in -o $compile_target\n " );
258239 append (" description = SCAN $out\n\n " );
@@ -264,14 +245,17 @@ std::string emit_ninja_string(const BuildPlan& plan) {
264245 append (" restat = 1\n\n " );
265246 }
266247
267- // Stage prebuilt std artifacts into our gcm. cache/
268- auto std_bmi_dst = std::filesystem::path ( " gcm.cache " ) / " std.gcm " ;
248+ // Stage prebuilt std artifacts into the compiler-specific BMI cache.
249+ auto std_bmi_dst = mcpp::toolchain::staged_std_bmi_path (plan. toolchain , {}) ;
269250 auto std_o_dst = std::filesystem::path (" obj" ) / " std.o" ;
270251
271- append (std::format (" build {} : cp_bmi {}\n " , escape_ninja_path (std_bmi_dst),
272- escape_ninja_path (plan.stdBmiPath )));
273- append (std::format (" build {} : cp_bmi {}\n\n " , escape_ninja_path (std_o_dst),
274- escape_ninja_path (plan.stdObjectPath )));
252+ bool has_std_artifacts = !plan.stdBmiPath .empty () && !plan.stdObjectPath .empty ();
253+ if (has_std_artifacts) {
254+ append (std::format (" build {} : cp_bmi {}\n " , escape_ninja_path (std_bmi_dst),
255+ escape_ninja_path (plan.stdBmiPath )));
256+ append (std::format (" build {} : cp_bmi {}\n\n " , escape_ninja_path (std_o_dst),
257+ escape_ninja_path (plan.stdObjectPath )));
258+ }
275259
276260 auto bmi_path = [](std::string_view name) {
277261 std::string s = " gcm.cache/" ;
@@ -366,7 +350,8 @@ std::string emit_ninja_string(const BuildPlan& plan) {
366350 if (rule != " c_object" ) {
367351 for (auto & imp : cu.imports ) {
368352 if (imp == " std" || imp == " std.compat" ) {
369- implicit += " gcm.cache/std.gcm" ;
353+ if (has_std_artifacts)
354+ implicit += " " + escape_ninja_path (std_bmi_dst);
370355 continue ;
371356 }
372357 implicit += " " + bmi_path (imp);
@@ -397,14 +382,16 @@ std::string emit_ninja_string(const BuildPlan& plan) {
397382 switch (lu.kind ) {
398383 case LinkUnit::Binary:
399384 case LinkUnit::TestBinary:
400- ins += " " + escape_ninja_path (std_o_dst);
385+ if (has_std_artifacts)
386+ ins += " " + escape_ninja_path (std_o_dst);
401387 rule = " cxx_link" ;
402388 break ;
403389 case LinkUnit::StaticLibrary:
404390 rule = " cxx_archive" ;
405391 break ;
406392 case LinkUnit::SharedLibrary:
407- ins += " " + escape_ninja_path (std_o_dst);
393+ if (has_std_artifacts)
394+ ins += " " + escape_ninja_path (std_o_dst);
408395 rule = " cxx_shared" ;
409396 break ;
410397 }
0 commit comments