Skip to content

Commit 7192039

Browse files
committed
fix: load cached action metadata before buffered ninja output
1 parent 11dc0d0 commit 7192039

3 files changed

Lines changed: 64 additions & 2 deletions

File tree

‎.agents/docs/2026-11-05-heterogeneous-build-implementation-plan.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,3 +467,24 @@ latest,实际执行输出 `mcpp 2026.11.5.1`;安装后的 ELF 与公开安
467467
索引分别经 PR #713、#714 合入、发布并通过普通客户端验证。musl 0.27.7 复用
468468
版本组推进的 PR #85;runtime 0.18.8 与 platform 0.23.8 继续按公开前置依赖
469469
依次验证。源码对照诊断不能替代引擎正式生产者与联合 UX 的资格验证。
470+
471+
## 19. 缓存构建的内部进度首次读取(2026-11-05)
472+
473+
收尾 CI 的 `1013_optional_action_progress_preserves_outputs` 暴露了一个核心显示
474+
缺口。缓存构建不重新规划,其步骤归属记录原先只在 Ninja 首次输出状态时读取;
475+
异步轮询在记录尚未加载时直接跳过 action-start 文件。Ninja 缓冲长动作输出时,
476+
合法进度文件虽已存在,界面仍不能显示正在执行的动作和内部阶段。该问题不说明
477+
动作本身停止,也不能解释引擎冷启动 CPU 的性能差额。
478+
479+
修正使轮询先检查 action-start 是否新增内容,再按需读取已有归属记录。无工作
480+
的缓存构建仍不预读记录;快照计数仍不能完成动作,输出后置条件、命令退出码和
481+
取消处理保持原契约。没有通过延长工作、隐藏断言或调低门槛修复正式测试。
482+
483+
固定原构建的 448 个对象,仅重编译进度模块并链接诊断二进制。新增测试在没有
484+
任何 Ninja stdout 或完成记录时要求观察到动作及内部阶段:原模块在两秒期限内
485+
失败,修正模块通过;完整进度测试文件的 25 项全部通过。原 1013 E2E 与额外的
486+
短动作观测控制均通过,包含各 role 的输出、增量不重跑、失败和取消。初次八路
487+
全量本地编译曾被资源回收终止,不记为通过;单模块控制也不替代正式 CI。
488+
489+
源代码和回归测试继续复用 PR #889。其最新完整 CI 和随后独立生态审查仍是合入
490+
前置条件,已发布 tag 的源码与资产不作改写。

‎src/build/progress.cppm‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,8 @@ struct Build::Impl {
733733
namespace {
734734

735735
// The fast path has no plan: it reads the record the plan wrote, when ninja
736-
// reports its first step, so a build with nothing to do reads nothing.
736+
// reports its first step or an action writes its start, so a build with
737+
// nothing to do reads nothing. Ninja may buffer stdout until an action ends.
737738
void ensure_record(Build::Impl& b) {
738739
if (b.record || b.recordTried) return;
739740
b.recordTried = true;
@@ -1004,11 +1005,13 @@ void read_log(Report& r, Build::Impl& b, std::vector<std::string>& out) {
10041005
// Reads the start file of an open pass; model lock held. A check or prepare
10051006
// action that starts names its package: that is work.
10061007
void read_starts(Report& r, Build::Impl& b, std::vector<std::string>& out) {
1007-
if (!b.inPass || !b.record) return;
1008+
if (!b.inPass) return;
10081009
std::error_code ec;
10091010
const auto path = b.dir / kStartsFile;
10101011
const auto size = std::filesystem::file_size(path, ec);
10111012
if (ec || size <= b.startsOffset) return;
1013+
ensure_record(b);
1014+
if (!b.record) return;
10121015
std::ifstream f(path, std::ios::binary);
10131016
f.seekg(static_cast<std::streamoff>(b.startsOffset));
10141017
std::string text(static_cast<std::size_t>(size - b.startsOffset), '\0');

‎tests/unit/test_build_progress.cpp‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import std;
44
import mcpp.ui;
55
import mcpp.build.progress;
6+
import mcpp.build.action_progress;
67

78
// The build's report (.agents/docs/2026-09-29-build-progress-display-design.md,
89
// revised by .agents/docs/2026-09-30-build-output-refinement-design.md): its
@@ -331,6 +332,43 @@ TEST(ProgressText, ThePhaseIsAlignedWithTheVerbs) {
331332

332333
// ─── When a package is named (revision 3, §5.3) ──────────────────────────
333334

335+
TEST(ProgressModel, AnActionStartLoadsTheFastPathRecordBeforeNinjaWritesStdout) {
336+
namespace telemetry = mcpp::build::action_progress;
337+
Tmp tmp;
338+
Record rec;
339+
rec.packages = {{"progress-no-stdout", true, "progress-no-stdout", 0, 1, "v0.0.0 (.)", "project"}};
340+
rec.owner = {{"gen/check.stamp", 0}};
341+
rec.step = {{"gen/check.stamp", 0}};
342+
rec.actions = {{"gen/check.stamp", "check-action"}};
343+
rec.steps = 1;
344+
write_record(tmp.path, rec);
345+
Build build(tmp.path); // The cached path has a record on disk, not set_record().
346+
build.pass_begin();
347+
const auto env = build.environment();
348+
const auto root = std::ranges::find(env, telemetry::kRootEnv, &decltype(env)::value_type::first);
349+
ASSERT_NE(root, env.end());
350+
std::ofstream(telemetry::channel(root->second, "gen/check.stamp"))
351+
<< R"({"v":1,"seq":1,"phase":"internal-check","done":1,"total":2})";
352+
const auto unixMs = std::chrono::duration_cast<std::chrono::milliseconds>(
353+
std::chrono::system_clock::now().time_since_epoch()).count();
354+
append(tmp.path / kStartsFile, std::format("gen/check.stamp\t{}\n", unixMs));
355+
testing::internal::CaptureStderr();
356+
open(false);
357+
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
358+
std::string row;
359+
do {
360+
row = status_row();
361+
if (row.contains("internal-check 1/2")) break;
362+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
363+
} while (std::chrono::steady_clock::now() < deadline);
364+
close();
365+
(void)testing::internal::GetCapturedStderr();
366+
EXPECT_TRUE(build.has_record());
367+
EXPECT_NE(row.find("check-action"), std::string::npos) << row;
368+
EXPECT_NE(row.find("internal-check 1/2"), std::string::npos) << row;
369+
// No Build::status() or completed ninja-log entry was supplied.
370+
}
371+
334372
TEST(ProgressModel, APackageIsNamedOnceWhenItsFirstStepFinishes) {
335373
mcpp::ui::disable_color();
336374
Tmp tmp;

0 commit comments

Comments
 (0)