Report cached build steps in wslc build output (#41334)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ggarzia-MSFT committed
Aug 12, 2026 at 17:12 UTC
667d6db2e197d65e9a1407e56c46381dfcd22c82
3 files changed
+37
-1
src/windows/inc/docker_schema.h
+3
-1
@@ -702,9 +702,11 @@ struct BuildKitVertex
702
std::string digest;
703
std::string name;
704
std::string started;
705
+ std::string completed;
706
std::string error;
707
+ bool cached{};
708
707
- NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(BuildKitVertex, digest, name, started, error);
709
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(BuildKitVertex, digest, name, started, completed, error, cached);
710
};
711
712
struct BuildKitStatus
src/windows/wslcsession/WSLCSession.cpp
+28
@@ -1248,6 +1248,7 @@ try
1248
std::string allOutput;
1249
std::string pendingJson;
1250
std::set<std::string> reportedSteps;
1251
+ std::set<std::string> reportedCached;
1252
std::set<std::string> reportedErrors;
1253
std::map<std::string, std::string> digestToStageName;
1254
bool needsNewline = false; // true when the last log chunk didn't end with \n
@@ -1282,6 +1283,23 @@ try
1283
return {};
1284
};
1285
1286
+ // Returns the leading step token from a BuildKit vertex name, e.g. "[2/3]" from "[2/3] RUN make".
1287
+ // Falls back to the full name when there is no bracketed prefix.
1288
+ auto getStepToken = [](const std::string& name) -> std::string {
1289
+ if (name.empty() || name[0] != '[')
1290
+ {
1291
+ return name;
1292
+ }
1293
+
1294
+ auto close = name.find(']');
1295
+ if (close == std::string::npos)
1296
+ {
1297
+ return name;
1298
+ }
1299
+
1300
+ return name.substr(0, close + 1);
1301
+ };
1302
+
1303
auto logPrefix = [](const std::string& name) -> std::string {
1304
if (name.empty())
1305
{
@@ -1347,6 +1365,16 @@ try
1365
reportProgress(vertex.name + "\n");
1366
}
1367
1368
+ if (vertex.cached && reportedCached.insert(vertex.digest).second)
1369
+ {
1370
+ auto stepToken = getStepToken(vertex.name);
1371
+ if (!stepToken.empty())
1372
+ {
1373
+ flushLine();
1374
+ reportProgress(stepToken + " CACHED\n");
1375
+ }
1376
+ }
1377
+
1378
if (!vertex.error.empty() && reportedErrors.insert(vertex.digest).second)
1379
{
1380
flushLine();
test/windows/wslc/e2e/WSLCE2EImageBuildTests.cpp
+6
@@ -1259,12 +1259,18 @@ class WSLCE2EImageBuildTests
1259
cachedBuild.Verify({.Stdout = L"", .ExitCode = 0});
1260
const auto cachedId = InspectImage(BuiltImageNoCache.NameAndTag()).Id;
1261
VERIFY_ARE_EQUAL(firstId, cachedId, L"Repeated build without --no-cache should reuse the cached layer");
1262
+ VERIFY_IS_TRUE(
1263
+ cachedBuild.StderrContainsSubstring(L"[2/2] CACHED"),
1264
+ L"A reused layer must be reported as cached in the build output");
1265
1266
// --no-cache must re-run the non-deterministic step, producing a new id.
1267
auto noCacheBuild = RunWslc(buildCmd + L" --no-cache");
1268
noCacheBuild.Verify({.Stdout = L"", .ExitCode = 0});
1269
const auto noCacheId = InspectImage(BuiltImageNoCache.NameAndTag()).Id;
1270
VERIFY_ARE_NOT_EQUAL(firstId, noCacheId, L"--no-cache must rebuild the non-deterministic RUN step");
1271
+ VERIFY_IS_FALSE(
1272
+ noCacheBuild.StderrContainsSubstring(L"[2/2] CACHED"),
1273
+ L"A step re-run under --no-cache must not be reported as cached");
1274
}
1275
1276
// --iidfile writes the built image's ID to the given host path on success, matching docker build