@@ -318,6 +318,13 @@ void write_consumer_include_sidecar(const std::filesystem::path& outputDir,
318318std::vector<std::filesystem::path>
319319read_consumer_include_sidecar (const std::filesystem::path& outputDir);
320320
321+ // The compiler cap of the generation a capped graph runs in, beside
322+ // build.ninja and not in it (e2e 861): the fast path replays the graph
323+ // without a plan and opens its generation with this cap. Removed when the
324+ // graph runs no compiler through the capped executor; 0 when absent.
325+ void write_compiler_cap_sidecar (const std::filesystem::path& outputDir, int cap);
326+ int read_compiler_cap_sidecar (const std::filesystem::path& outputDir);
327+
321328// #693 (M4): whether a Ninja reads build.ninja in the encoding mcpp writes it.
322329//
323330// mcpp writes the file in UTF-8: every path in it has a UTF-8 spelling
@@ -1180,6 +1187,21 @@ void write_consumer_include_sidecar(const std::filesystem::path& outputDir,
11801187 for (auto const & d : dirs) f << d.string () << ' \n ' ;
11811188}
11821189
1190+ constexpr std::string_view kCompilerCapSidecar = " .mcpp-compiler-cap" ;
1191+
1192+ void write_compiler_cap_sidecar (const std::filesystem::path& outputDir, int cap) {
1193+ const auto path = outputDir / kCompilerCapSidecar ;
1194+ std::error_code ec;
1195+ if (cap <= 0 ) { std::filesystem::remove (path, ec); return ; }
1196+ std::ofstream (path, std::ios::binary | std::ios::trunc) << cap << ' \n ' ;
1197+ }
1198+
1199+ int read_compiler_cap_sidecar (const std::filesystem::path& outputDir) {
1200+ std::ifstream f (outputDir / kCompilerCapSidecar , std::ios::binary);
1201+ int cap = 0 ;
1202+ return f >> cap && cap > 0 ? cap : 0 ;
1203+ }
1204+
11831205std::vector<std::filesystem::path>
11841206read_consumer_include_sidecar (const std::filesystem::path& outputDir) {
11851207 std::vector<std::filesystem::path> dirs;
@@ -1474,8 +1496,10 @@ std::string emit_ninja_string(const BuildPlan& plan, std::string* placements,
14741496 // larger slot count as their own concurrency bound.
14751497 // Every capped graph (2026.11.7.1): the executor also registers each
14761498 // compiler as a participant of the build's generation.
1499+ // No number of compilers here: the generation carries the cap, so the
1500+ // graph is the same whatever the job count (e2e 861).
14771501 const std::string compilerPrefix = plan.scheduleCompilerCap > 0
1478- ? std::format (" $mcpp __compiler-cap --sem .mcpp-sched --cap {} -- output $out --depfile $out.d -- " , plan. scheduleCompilerCap )
1502+ ? std::string (" $mcpp __compiler-cap --sem .mcpp-sched --output $out --depfile $out.d -- " )
14791503 : std::string{};
14801504 // The other split shape. Clang publishes no BMI early — it writes the BMI
14811505 // at the end of the compile — so the GCC trick of releasing the file
@@ -2113,7 +2137,7 @@ std::string emit_ninja_string(const BuildPlan& plan, std::string* placements,
21132137 // wrong in this file before.
21142138 append (" rule cxx_module_bmi\n " );
21152139 append (" command = $mcpp bmi-compile --bmi $bmi_out --slot $slot"
2116- " --self $mcpp --sem $sched_sem --cap $sched_cap -- object $obj_out"
2140+ " --self $mcpp --sem $sched_sem --object $obj_out"
21172141 " --command-file $out.cmd --dep-from $scan_dep --dep-to $out.d\n " );
21182142 append (std::format (
21192143 " rspfile = $out.cmd\n "
@@ -3204,7 +3228,6 @@ std::string emit_ninja_string(const BuildPlan& plan, std::string* placements,
32043228 e += " slot = " + slot + " \n " ;
32053229 e += " scan_dep = " + escape_ninja_path (ddi) + " .dep\n " ;
32063230 e += " sched_sem = .mcpp-sched\n " ;
3207- e += std::format (" sched_cap = {}\n " , plan.scheduleCompilerCap );
32083231 if (auto inc = local_include_flags (cu, dial); !inc.empty ())
32093232 e += " local_includes =" + inc + " \n " ;
32103233 if (auto fl = unit_cxx_flags (cu); !fl.empty ())
@@ -4545,7 +4568,7 @@ std::expected<BuildResult, BuildError> NinjaBackend::build(const BuildPlan& plan
45454568 plan.outputDir });
45464569 std::optional<mcpp::build::schedule::detach::RunScope> compilerRun;
45474570 if (!opts.dryRun && plan.scheduleCompilerCap > 0 ) {
4548- auto opened = mcpp::build::schedule::detach::RunScope::begin (plan.outputDir );
4571+ auto opened = mcpp::build::schedule::detach::RunScope::begin (plan.outputDir , plan. scheduleCompilerCap );
45494572 if (!opened) return std::unexpected (BuildError{opened.error (), plan.outputDir });
45504573 compilerRun.emplace (std::move (*opened));
45514574 reclaim_stale_schedule_tokens (plan.outputDir );
@@ -4561,6 +4584,7 @@ std::expected<BuildResult, BuildError> NinjaBackend::build(const BuildPlan& plan
45614584 plan.targetSide .cAbiDecl ? plan.targetSide .cAbiDecl ->absent
45624585 : std::vector<mcpp::targetside::CAbiAbsentEntry>{});
45634586 write_consumer_include_sidecar (plan.outputDir , root_include_dirs_of (plan));
4587+ write_compiler_cap_sidecar (plan.outputDir , plan.scheduleCompilerCap );
45644588 write_graph_c_library_sidecar (plan.outputDir , plan.targetSide .cAbi );
45654589 std::string placements;
45664590 mcpp::build::progress::Attribution attribution;
0 commit comments