@@ -2106,7 +2106,8 @@ constexpr std::string_view kBuildCacheFile = "target/.build_cache";
21062106void write_build_cache (const std::filesystem::path& projectRoot,
21072107 const std::filesystem::path& outputDir,
21082108 const std::string& ninjaProgram,
2109- const std::string& targetTriple) {
2109+ const std::string& targetTriple,
2110+ const std::string& fingerprintHex = " " ) {
21102111 auto path = projectRoot / kBuildCacheFile ;
21112112 std::error_code ec;
21122113 std::filesystem::create_directories (path.parent_path (), ec);
@@ -2115,6 +2116,7 @@ void write_build_cache(const std::filesystem::path& projectRoot,
21152116 f << outputDir.string () << ' \n ' ;
21162117 f << ninjaProgram << ' \n ' ;
21172118 f << targetTriple << ' \n ' ;
2119+ f << fingerprintHex << ' \n ' ;
21182120 }
21192121}
21202122
@@ -2173,10 +2175,27 @@ int run_build_plan(BuildContext& ctx, bool verbose, bool no_cache,
21732175 }
21742176 }
21752177
2178+ // P1.5: warn if fingerprint changed from last build (explains full rebuild).
2179+ {
2180+ auto cachePath = ctx.projectRoot / kBuildCacheFile ;
2181+ std::ifstream cf (cachePath);
2182+ std::string oldDir;
2183+ if (std::getline (cf, oldDir) && !oldDir.empty ()) {
2184+ auto oldFp = std::filesystem::path (oldDir).filename ().string ();
2185+ auto newFp = ctx.outputDir .filename ().string ();
2186+ if (oldFp != newFp) {
2187+ mcpp::ui::warning (std::format (
2188+ " fingerprint changed ({} → {}), full rebuild" ,
2189+ oldFp, newFp));
2190+ }
2191+ }
2192+ }
2193+
21762194 // P0: save build cache for fast-path on next invocation.
21772195 if (!no_cache && !r->ninjaProgram .empty ()) {
2196+ auto fpHex = ctx.outputDir .filename ().string ();
21782197 write_build_cache (ctx.projectRoot , ctx.outputDir , r->ninjaProgram ,
2179- std::string (targetOverride));
2198+ std::string (targetOverride), fpHex );
21802199 }
21812200
21822201 mcpp::ui::finished (" release" , r->elapsed );
@@ -2204,15 +2223,30 @@ std::optional<int> try_fast_build(const std::filesystem::path& projectRoot,
22042223 if (!std::filesystem::exists (cachePath, ec)) return std::nullopt ;
22052224
22062225 std::ifstream f (cachePath);
2207- std::string outputDirStr, ninjaProgram, cachedTarget;
2226+ std::string outputDirStr, ninjaProgram, cachedTarget, cachedFingerprint ;
22082227 if (!std::getline (f, outputDirStr) || outputDirStr.empty ()) return std::nullopt ;
22092228 if (!std::getline (f, ninjaProgram) || ninjaProgram.empty ()) return std::nullopt ;
2210- std::getline (f, cachedTarget); // may be empty for old cache files
2229+ std::getline (f, cachedTarget); // may be empty for old cache files
2230+ std::getline (f, cachedFingerprint); // may be empty for pre-0.0.15 caches
22112231
22122232 // Reject cache if target triple changed (e.g. previous build used
22132233 // --target x86_64-linux-musl but this one is a default build).
22142234 if (cachedTarget != currentTarget) return std::nullopt ;
22152235
2236+ // P1: verify fingerprint matches the outputDir basename. If someone
2237+ // switched mcpp installations (different toolchain binary), the cached
2238+ // outputDir points to a stale fingerprint directory. Detect and reject.
2239+ if (!cachedFingerprint.empty ()) {
2240+ std::filesystem::path outputDir (outputDirStr);
2241+ auto dirBasename = outputDir.filename ().string ();
2242+ if (dirBasename != cachedFingerprint) {
2243+ // Cache is inconsistent — invalidate it.
2244+ std::error_code ec2;
2245+ std::filesystem::remove (cachePath, ec2);
2246+ return std::nullopt ;
2247+ }
2248+ }
2249+
22162250 std::filesystem::path outputDir (outputDirStr);
22172251
22182252 auto ninjaPath = outputDir / " build.ninja" ;
0 commit comments