@@ -447,7 +447,8 @@ export int run_build_plan(BuildContext& ctx, bool verbose, bool no_cache,
447447// caller's broader glob; and it's the same choke-point fix as expand_glob
448448// itself, see scanner.cppm).
449449bool sources_newer_than (const std::filesystem::path& projectRoot,
450- std::filesystem::file_time_type ninjaTime) {
450+ std::filesystem::file_time_type ninjaTime,
451+ const std::vector<std::filesystem::path>& resourceScripts = {}) {
451452 std::error_code ec;
452453 // The root build.mcpp is a build input too — its directives shape
453454 // build.ninja (flags, generated/selected sources). A changed program must
@@ -465,6 +466,25 @@ bool sources_newer_than(const std::filesystem::path& projectRoot,
465466 // "Finished dev in 0.00s" while the new file is never generated. Same
466467 // question as the build.mcpp check above, different kind of input.
467468 if (mcpp::build::glob_inputs_stale (projectRoot)) return true ;
469+ // mcpp#365: an author-written `.rc` is a third input of the same kind. It
470+ // is not under src/ and has no C++ extension, so the sweep below cannot see
471+ // it — and unlike the icon or a header the script includes, editing it can
472+ // change WHAT THE GRAPH SHOULD BE: the implicit-input set comes from
473+ // scanning the script, and the "your VERSIONINFO is named by string"
474+ // diagnostic is produced while scanning. Both happen in prepare_build, so a
475+ // fresh build.ninja made the edit invisible — the resource itself rebuilt
476+ // (ninja tracks it), but a newly added `#include "ids.h"` went untracked and
477+ // the diagnostic never fired again after the first build.
478+ //
479+ // Only `files` is swept. `icon` and `extra-inputs` are already ninja
480+ // implicit inputs and changing them cannot change the shape of the graph,
481+ // so forcing a full prepare on every icon tweak would buy nothing.
482+ for (auto const & f : resourceScripts) {
483+ auto p = f.is_absolute () ? f : (projectRoot / f);
484+ auto ft = std::filesystem::last_write_time (p, ec);
485+ if (ec) { ec.clear (); continue ; } // missing → prepare_build reports it
486+ if (ft > ninjaTime) return true ;
487+ }
468488 for (auto & f : mcpp::modgraph::expand_glob (projectRoot, " src/**/*" )) {
469489 auto ext = f.extension ().string ();
470490 if (ext != " .cppm" && ext != " .cpp" && ext != " .cc" &&
@@ -556,6 +576,11 @@ std::optional<int> run_ninja_fast(const std::string& ninjaProgram,
556576struct FastPathIdentity {
557577 std::string profile;
558578 std::string cacheMode;
579+ // mcpp#365: author-written resource scripts, for the freshness sweep. They
580+ // ride along here because this is the one place on the fast path that
581+ // already parses the manifest — re-reading it to answer a second question
582+ // would be a second derivation of the same fact.
583+ std::vector<std::filesystem::path> resourceScripts;
559584};
560585
561586std::optional<FastPathIdentity>
@@ -567,6 +592,7 @@ fast_path_identity(const std::filesystem::path& projectRoot,
567592 mcpp::build::resolve_profile_name (*m, profileOverride),
568593 std::string (mcpp::build::cache_mode_name (
569594 mcpp::build::resolve_cache_mode (*m, " " ))),
595+ m->resources .files ,
570596 };
571597}
572598
@@ -631,7 +657,7 @@ export std::optional<int> try_fast_build(const std::filesystem::path& projectRoo
631657
632658 // mcpp#225: bounded + vcs/build-dir-excluded walk (see sources_newer_than)
633659 // instead of a hand-rolled recursive_directory_iterator over src/.
634- if (sources_newer_than (projectRoot, ninjaTime)) return std::nullopt ;
660+ if (sources_newer_than (projectRoot, ninjaTime, want-> resourceScripts )) return std::nullopt ;
635661
636662 // All inputs are older than build.ninja → fast-path: just run ninja.
637663 std::chrono::milliseconds elapsed{};
@@ -706,7 +732,7 @@ std::optional<int> try_fast_run(const std::filesystem::path& projectRoot,
706732 auto tomlTime = std::filesystem::last_write_time (tomlPath, ec);
707733 if (ec || tomlTime > ninjaTime) return std::nullopt ;
708734
709- if (sources_newer_than (projectRoot, ninjaTime)) return std::nullopt ;
735+ if (sources_newer_than (projectRoot, ninjaTime, want-> resourceScripts )) return std::nullopt ;
710736
711737 // Fresh → run ninja (picks up any incremental object/link work) then
712738 // exec the cached exe path directly.
0 commit comments