@@ -24,6 +24,22 @@ import mcpp.platform;
2424
2525namespace mcpp ::build {
2626
27+ namespace {
28+ // `thread_local` for the same reason `mcpp::build::refusal`'s sink is
29+ // (refusal.cppm): `prepare_build` recurses for nested host sub-builds on the
30+ // calling thread, and a failure of the INNER call must not leave notes behind
31+ // for an outer call that goes on to succeed. Cleared at the top of every
32+ // `prepare_build` call and on its success path, so only a call that is
33+ // itself failing can leave something here for its caller to take.
34+ thread_local std::vector<PlanNote> g_notesOnFailure;
35+ } // namespace
36+
37+ std::vector<PlanNote> take_notes_on_failure () {
38+ auto notes = std::move (g_notesOnFailure);
39+ g_notesOnFailure.clear ();
40+ return notes;
41+ }
42+
2743std::expected<BuildContext, std::string>
2844prepare_build (bool print_fingerprint,
2945 bool includeDevDeps,
@@ -32,18 +48,29 @@ prepare_build(bool print_fingerprint,
3248 PrepareState state (print_fingerprint, includeDevDeps,
3349 std::move (extraTargets), std::move (overrides));
3450 pending_flag_words_notes ().clear ();
51+ g_notesOnFailure.clear ();
52+
53+ // Every early return below carries `state.planNotes` as they stood at the
54+ // failing phase, so a caller whose only handle on the failure is
55+ // `.error()` (a plain string) can still read what an earlier phase
56+ // recorded — see `take_notes_on_failure`'s declaration in prepare.cppm.
57+ auto fail = [&](std::string message) -> std::unexpected<std::string> {
58+ g_notesOnFailure = state.planNotes ;
59+ return std::unexpected (std::move (message));
60+ };
3561
36- if (auto r = phase0_manifest_and_workspace (state); !r) return std::unexpected (r.error ());
37- if (auto r = phase1_toolchain_spec_and_axes (state); !r) return std::unexpected (r.error ());
38- if (auto r = phase2_define_toolchain_resolver (state); !r) return std::unexpected (r.error ());
39- if (auto r = phase3_xlings_before_graph (state); !r) return std::unexpected (r.error ());
40- if (auto r = phase4a_graph_load (state); !r) return std::unexpected (r.error ());
41- if (auto r = phase4b_graph_worklist (state); !r) return std::unexpected (r.error ());
42- if (auto r = phase5_toolchain_after_graph (state); !r) return std::unexpected (r.error ());
43- if (auto r = phase6_features_and_host_tools (state); !r) return std::unexpected (r.error ());
44- if (auto r = phase9_target_side (state); !r) return std::unexpected (r.error ());
45- if (auto r = phase11_scan (state); !r) return std::unexpected (r.error ());
62+ if (auto r = phase0_manifest_and_workspace (state); !r) return fail (r.error ());
63+ if (auto r = phase1_toolchain_spec_and_axes (state); !r) return fail (r.error ());
64+ if (auto r = phase2_define_toolchain_resolver (state); !r) return fail (r.error ());
65+ if (auto r = phase3_xlings_before_graph (state); !r) return fail (r.error ());
66+ if (auto r = phase4a_graph_load (state); !r) return fail (r.error ());
67+ if (auto r = phase4b_graph_worklist (state); !r) return fail (r.error ());
68+ if (auto r = phase5_toolchain_after_graph (state); !r) return fail (r.error ());
69+ if (auto r = phase6_features_and_host_tools (state); !r) return fail (r.error ());
70+ if (auto r = phase9_target_side (state); !r) return fail (r.error ());
71+ if (auto r = phase11_scan (state); !r) return fail (r.error ());
4672
73+ g_notesOnFailure.clear ();
4774 return phase13_finish (state);
4875}
4976
0 commit comments