Documentation
¶
Overview ¶
Example 12_build_dependency — model a software build-dependency graph, derive a valid build order with search.TopologicalSort (Kahn's algorithm), and detect a circular dependency with search.TarjanSCC.
It generates a realistic, seeded, scale-parametrised build graph: a layered module DAG in which every module depends on a few modules in strictly lower layers. That layering is what guarantees acyclicity — along any dependency path the layer index strictly decreases, so no directed cycle can form and TopologicalSort always succeeds. The example then exercises the two algorithms it teaches and reports both the deterministic shape of the data and the volatile telemetry — wall-clock and live heap — that make it a benchmark rather than a demonstration.
Model ¶
module mNNNN // one node per module (a)-[depends-on]->(b) // edge a->b: a depends on b
Edge direction reads "a depends on b", so b must be built before a. Modules are partitioned into L layers; a module in layer k draws a few distinct dependencies from layers strictly below it ([0, k)). Layer 0 modules are leaves (no dependencies — the foundation libraries). The layer widths follow a pyramid (widest at the leaves), apportioned with a floor pass so every layer is non-empty and the counts sum exactly to the requested module total. A single dependency chain is planted from the top layer down to layer 0 so the graph always has a known longest chain and a known forward path to close into a cycle.
Two stages ¶
Build order. The DAG is frozen into a CSR snapshot and sorted with search.TopologicalSort. The result is verified in-code against the validity invariant — for every edge u->v the source precedes the destination in the order — and the build order (dependencies first) is the reverse of that linear extension. The longest dependency chain (the build's critical-path depth) is computed by a linear-time DP over the topological order.
Cycle detection. A back-edge from the bottom of the planted chain to its top is injected, closing a circular dependency. search.TopologicalSort then fails with search.ErrCycle, and search.TarjanSCC reports the single strongly connected component that contains the cycle. Because the input was a DAG (whose every SCC is a singleton), the injected back-edge produces exactly one component of size greater than one, and that component contains both endpoints of the back-edge.
Scale ¶
Run with no flags, the example builds a small deterministic default (a few thousand modules) that the regression test pins. Every dimension is a flag, so the same binary scales up to where the algorithms' cost is observable:
go run ./examples/12_build_dependency -modules 1000000 -layers 40 -seed 7
The deterministic data shape is reproducible for a fixed -seed; only the telemetry (lines prefixed with "# ") varies between runs and machines.
Why CSR ¶
TopologicalSort and TarjanSCC are read-only analytics, so the graph is built once in a mutable adjlist.AdjList and then frozen into an immutable csr.CSR snapshot — the lock-free, cache-friendly surface the search package runs against. This is the canonical build -> snapshot -> query flow; persistence is orthogonal to what this example measures and is demonstrated by examples 04, 17, 24 and 25.