Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion dag/gunbc/regen_round_cost.dag
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import std.types { String, Int, List, Bool, NonEmptyStr }
import std.nat { Nat }
import std.measure { ClockBasis, CpuClock, WallClock, Nanosecond, nanosecond, nanosecond_count }
import std.observation { TimedMeasurement, Measured, MeasuredValue, MeasuredUnavailable, observation_duration_of }
import gunbc.stage0_partition_rebuild_scope {
stage0_partition_rebuild_decision_line,
stage0_partition_rebuild_decision_today
}

// ONE REGEN ROUND, PRICED BY PHASE, ON TWO CLOCKS -- the instrument behind
// `claim_executor --regen-round-cost`. A round is the convergence recipe that
Expand Down Expand Up @@ -109,6 +113,7 @@ type RegenRoundCostReceipt {
rustfmt_spawns: Nat
marks: List<TraceMark>
changed_paths: List<String>
installed_mirrors: List<String>
}

fn regen_round_phase_tag(phase: RegenRoundPhase) -> String {
Expand Down Expand Up @@ -231,6 +236,24 @@ fn regen_round_total_line(receipt: RegenRoundCostReceipt) -> String {
)
}

// WHAT THE REBUILD WOULD HAVE COST IF IT WERE CHANGE-DENOMINATED, printed beside what it did
// cost. `installed_mirrors` is the regen's own drift answer -- the basenames it just copied into
// the seed tree -- so the scope is derived from the round's real input, not from a fixture. The
// line reports which packages the changed mirrors own, the reverse package closure those force,
// and whether an executable can be assembled from them at all; today it reports
// `executable_assembly=unavailable`, which is why the phase above it is still a whole-crate
// rebuild. The empty `unlocatable` argument is a fact and not a convenience: every element of the
// drift answer is a basename the installer resolved to a file it copied, so at this boundary
// there is nothing the selection could fail to name.
fn regen_round_partition_rebuild_line(receipt: RegenRoundCostReceipt) -> String {
stage0_partition_rebuild_decision_line(
decision: stage0_partition_rebuild_decision_today(
changed_mirrors: receipt.installed_mirrors,
unlocatable: []
)
)
}

fn regen_round_changed_paths_line(receipt: RegenRoundCostReceipt) -> String {
let listed = fold(receipt.changed_paths, init: "", f: fn(acc, path) {
if acc == "" { path } else { concat(concat(acc, ", "), path) }
Expand All @@ -249,7 +272,10 @@ fn regen_round_cost_render(receipt: RegenRoundCostReceipt) -> String {
})
concat(
concat(concat(regen_round_header_line(receipt: receipt), "\n"), mark_lines),
concat(concat(regen_round_total_line(receipt: receipt), "\n"), concat(regen_round_changed_paths_line(receipt: receipt), "\n"))
concat(
concat(concat(regen_round_total_line(receipt: receipt), "\n"), concat(regen_round_changed_paths_line(receipt: receipt), "\n")),
concat(regen_round_partition_rebuild_line(receipt: receipt), "\n")
)
)
}

Expand Down
55 changes: 55 additions & 0 deletions dag/gunbc/stage0/stage0_partition_package_graph.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module gunbc.stage0_partition_package_graph

import std.types { String, List, Bool }
import gunbc.stage0_crate_partition_generated {
GeneratedEmitCoreCrate,
GeneratedFoundationCrate,
GeneratedLayeredCoreCrate,
GeneratedPartitionCrateRow,
generated_partition_crate_rows
}

// THE PACKAGE DEPENDENCY GRAPH OF THE STAGE0 PARTITION, in one place, as package NAMES.
//
// Two consumers ask the same question and must not answer it twice. v1.compiler.stage0_crates
// renders each partition crate's Cargo manifest from these edges, and
// gunbc.stage0_partition_rebuild_scope walks them in reverse to decide which packages one changed
// mirror forces cargo to recompile. If the renderer kept its own copy, the selector could compute
// a closure over edges cargo was never handed -- and the failure direction is the dangerous one:
// a missing edge NARROWS the closure, so a package that must recompile is skipped and the next
// pass runs stale code. One authority makes that state unwritable rather than checked.
//
// NAMES, NOT MANIFEST ROWS, because the two consumers need different things from an edge: the
// renderer needs a LocalPathDep carrying the sibling path it resolves from the row's crate_dir,
// and the selector needs only the identity of the package on the other end. Publishing the
// manifest row here would push the renderer's transport into the graph (DESIGN section 3:
// interface, realization and policy are three facts), and publishing the path would make the
// selector carry a directory it has no use for.
//
// THE KIND DECIDES THE EDGES, and each arm is the rule the partition already lives by:
// a foundation crate is the bottom of the layering and depends on no partition package; a
// layered core crate depends on exactly the packages it re-exports, which is what makes the
// `crate::` references in its included mirrors keep resolving; and the emit shell re-exports a
// SURFACE assembled from every module-bearing package, so it depends on all of them regardless
// of which modules it names.
fn stage0_partition_row_is_module_bearing_package(row: GeneratedPartitionCrateRow) -> Bool {
match row.kind {
GeneratedEmitCoreCrate => false
GeneratedFoundationCrate => true
GeneratedLayeredCoreCrate => true
}
}

fn stage0_partition_module_bearing_package_names() -> List<String> {
generated_partition_crate_rows
|> filter(row => stage0_partition_row_is_module_bearing_package(row: row))
|> map(row => row.package_name)
}

fn stage0_partition_package_dependency_names(row: GeneratedPartitionCrateRow) -> List<String> {
match row.kind {
GeneratedFoundationCrate => []
GeneratedLayeredCoreCrate => row.reexport_packages
GeneratedEmitCoreCrate => stage0_partition_module_bearing_package_names()
}
}
Loading
Loading