Skip to content

Commit 627786a

Browse files
committed
fix: redraw action snapshots between Ninja status lines
1 parent 08fb971 commit 627786a

4 files changed

Lines changed: 70 additions & 4 deletions

File tree

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,25 @@ latest,实际执行输出 `mcpp 2026.11.5.1`;安装后的 ELF 与公开安
507507
修复也明确要求 PR、新版本及原发布门禁。新最终提交仍须完成完整 CI 和随后
508508
生态审查;仅在通过后合入并创建新 tag,按用户指定顺序优先 release,及时用
509509
本地 gtc 补 GitCode,完成索引和实际安装后再恢复 main CI。
510+
511+
## 21. 内部快照与界面刷新之间的通知(2026-11-05)
512+
513+
最终版本提交 `08fb971f` 的 CI `42025082619` 在原 1013 E2E 首次终端输出
514+
断言失败。缓存归属记录的首次读取修正仍有效;这次进一步核对发现,异步轮询
515+
接受内部快照后并未请求界面刷新。无 Ninja 输出和其他叙述时,界面依靠一秒
516+
时钟刷新,而原 fixture 的阶段只持续 0.75 秒,因而可能在实际绘制前结束。
517+
此前同一测试通过不能排除该时序缺口,也不能用原样重试代替修正。
518+
519+
快照读取现在报告是否接受了新的序号,轮询在释放模型锁后请求现有 UI frame。
520+
各构建的结果采用非短路聚合,仍读取全部活跃动作;无更新、无效或旧序号不
521+
请求额外 frame。原十帧每秒上限、命令完成、快照非权威性以及锁顺序均保持。
522+
523+
新增回归检查实际渲染输出:先绘制初始 frame,再发布快照,在一秒时钟刷新
524+
之前结束观察;既不提供 Ninja stdout,也不补充其他叙述。仅替换进度模块的
525+
固定对象对照中,上一模块在此检查失败,修正后完整 26 项进度检查通过。
526+
正常 1013 E2E 保持原 0.25 秒更新间隔及全部断言,并通过修正后的诊断二进制。
527+
另有 0.001 秒的历史短动作探针,其失败不代表原 1013 测试失败;局部探针的
528+
时序结果不能替代实际输出回归与最终完整 CI。正式 E2E 未加入等待观察的握手。
529+
530+
该补充继续使用 PR #889 与 2026.11.5.2。新的最终提交仍须完整 CI 以及其后
531+
独立生态审查,通过后才合入和发布;CLion 两仓库的兼容性结果不受此门禁替代。

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
- Cached builds load action ownership when an action starts, so internal progress
1212
appears while Ninja is still buffering stdout. Idle builds retain lazy loading;
1313
command results and output checks remain authoritative.
14+
- New action snapshots request a normal UI frame, so short phases can appear
15+
between Ninja status lines without waiting for the clock redraw.
1416
- The macOS clean bootstrap keeps its stage-one compiler outside the output tree
1517
removed by the second stage.
1618

‎src/build/progress.cppm‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,14 +1035,18 @@ void read_starts(Report& r, Build::Impl& b, std::vector<std::string>& out) {
10351035

10361036
// Read only active graph actions. Snapshot counts never complete an action;
10371037
// the ninja log removes it from running only after the command has finished.
1038-
void read_action_progress(Build::Impl& build) {
1039-
if (build.progressDir.empty()) return;
1038+
bool read_action_progress(Build::Impl& build) {
1039+
if (build.progressDir.empty()) return false;
1040+
bool changed = false;
10401041
for (auto& action : build.running) {
10411042
auto snapshot = action_progress::read(
10421043
action_progress::channel(build.progressDir, action.stamp));
1043-
if (snapshot && (!action.snapshot || snapshot->seq > action.snapshot->seq))
1044+
if (snapshot && (!action.snapshot || snapshot->seq > action.snapshot->seq)) {
10441045
action.snapshot = std::move(snapshot);
1046+
changed = true;
1047+
}
10451048
}
1049+
return changed;
10461050
}
10471051

10481052
// The builds whose Build object is alive: the frame, the poll and the status
@@ -1178,6 +1182,7 @@ mcpp::ui::Frame frame() {
11781182
void poll() {
11791183
auto& r = report();
11801184
std::vector<std::string> out;
1185+
bool progressChanged = false;
11811186
{
11821187
std::lock_guard lock(r.m);
11831188
if (r.game && r.keys)
@@ -1191,10 +1196,13 @@ void poll() {
11911196
for (auto const& b : live_builds(r)) {
11921197
read_starts(r, *b, out);
11931198
read_log(r, *b, out);
1194-
read_action_progress(*b);
1199+
progressChanged |= read_action_progress(*b);
11951200
}
11961201
}
11971202
write_lines(out);
1203+
// A snapshot can arrive between Ninja status lines. Wake the normal
1204+
// frame writer instead of waiting for its one-second clock redraw.
1205+
if (progressChanged) mcpp::ui::touch_region();
11981206
}
11991207

12001208
// The status row's screen, as MCPP_PROGRESS asks: `random` (the default) or

‎tests/unit/test_build_progress.cpp‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,40 @@ TEST(ProgressModel, AnActionStartLoadsTheFastPathRecordBeforeNinjaWritesStdout)
369369
// No Build::status() or completed ninja-log entry was supplied.
370370
}
371371

372+
TEST(ProgressModel, ANewSnapshotRedrawsBeforeTheClockHeartbeat) {
373+
namespace telemetry = mcpp::build::action_progress;
374+
Tmp tmp;
375+
Record rec;
376+
rec.packages = {{"progress-redraw", true, "r", 0, 1, "", "project"}};
377+
rec.owner = {{"gen/redraw.stamp", 0}};
378+
rec.actions = {{"gen/redraw.stamp", "a"}};
379+
Build build(tmp.path);
380+
build.set_record(rec);
381+
build.pass_begin();
382+
const auto env = build.environment();
383+
const auto root = std::ranges::find(env, telemetry::kRootEnv, &decltype(env)::value_type::first);
384+
ASSERT_NE(root, env.end());
385+
const auto unixMs = std::chrono::duration_cast<std::chrono::milliseconds>(
386+
std::chrono::system_clock::now().time_since_epoch()).count();
387+
append(tmp.path / kStartsFile, std::format("gen/redraw.stamp\t{}\n", unixMs));
388+
const bool wasLive = mcpp::ui::live_progress();
389+
mcpp::ui::set_live_progress(true);
390+
testing::internal::CaptureStderr();
391+
open(false);
392+
// Let the initial half-second display guard pass. This line then draws
393+
// the frame immediately before publication, resetting the clock redraw.
394+
std::this_thread::sleep_for(std::chrono::milliseconds(550));
395+
mcpp::ui::info("Probe", "before snapshot publication");
396+
std::ofstream(telemetry::channel(root->second, "gen/redraw.stamp"))
397+
<< R"({"v":1,"seq":1,"phase":"snapshot-redraw","done":1,"total":2})";
398+
std::this_thread::sleep_for(std::chrono::milliseconds(500));
399+
close();
400+
const auto out = testing::internal::GetCapturedStderr();
401+
mcpp::ui::set_live_progress(wasLive);
402+
EXPECT_NE(out.find("snapshot-redraw 1/2"), std::string::npos) << out;
403+
// No Ninja stdout, completion or other narration requested a redraw.
404+
}
405+
372406
TEST(ProgressModel, APackageIsNamedOnceWhenItsFirstStepFinishes) {
373407
mcpp::ui::disable_color();
374408
Tmp tmp;

0 commit comments

Comments
 (0)