Skip to main content

Module builders

Module builders 

Source
Expand description

All builders for the site (ex: cargo build, cargo doc, etc.). Builder types, the [Builder] trait, and the parallel execution model.

Each [[builders]] entry in abbaye.toml deserialises into a BuilderEntry and is run as an independent Tokio task. This module owns the AnyBuilder enum (the TOML type-tag dispatch), the [LogEvent] channel used for progress-bar updates, and the [Builder] trait that every concrete builder must implement.


§Parallel Builder Execution

This section explains how Abbaye runs the [[builders]] entries defined in abbaye.toml in parallel, how inter-builder dependencies are enforced, and how the progress UI is kept in sync with the running tasks.

§Overview

Every [[builders]] entry is executed as an independent Tokio async task. Tasks are launched at the same time (via a shared JoinSet) and may therefore run fully concurrently. When a builder declares depends_on, its task simply waits — without blocking any thread — until all named prerequisites have completed successfully before invoking the underlying build logic.

§Configuration Model

§BuilderEntry

Each [[builders]] table in the TOML file deserialises into a BuilderEntry:

FieldTypePurpose
builderAnyBuilderType-tagged union holding the builder’s own config (flattened).
idOption<String>Stable name that other builders can reference in depends_on.
depends_onVec<String>IDs of builders that must succeed before this one starts.

Example:

[[builders]]
type    = "cargo"
id      = "compile"

[[builders]]
type       = "script"
script     = ["strip target/release/mybin"]
outputs    = ["target/release/mybin"]
depends_on = ["compile"]          # waits for the cargo builder above

§AnyBuilder variants

TOML typeRust variantWhat it does
archiveArchiveCreates a .tar.gz of the source tree.
cargoCargocargo build --release, optionally for multiple targets.
cargo_docCargoDoccargo doc.
markdownMarkdownRenders a directory of .md files to HTML.
scriptScriptRuns an arbitrary sequence of sh -c commands.

§Orchestration in build_site (src/site.rs)

The entire builder pipeline lives inside build_site, in three phases.

§Phase 1 — Validation

Before any task is spawned, two checks are performed:

  1. Reference check — every string in depends_on must match an id of some other builder in the same config. Unknown IDs are reported as a hard error immediately.

  2. Cycle detection — a depth-first search visits the dependency graph and returns an error if a cycle is found.

The DFS tracks three states per node: 0 = unvisited, 1 = in the current stack, 2 = fully processed. Encountering a node in state 1 means there is a back-edge, i.e. a cycle.

§Phase 2 — Completion channels

For each builder that carries an id, a tokio::sync::watch channel is created:

watch::channel(None::<bool>)
         ↑
         └─ initial value: None  (pending)

The channel is later sent Some(true) (success) or Some(false) (failure). Dependents hold a cloned Receiver and call wait_for(|v| v.is_some()), which suspends the task cooperatively until the value changes.

If a dependency’s watch::Sender is dropped without ever sending a value (e.g. the task panicked), wait_for returns an Err, which is treated as a failure and the dependent is skipped.

§Phase 3 — Task spawning and collection

All tasks are submitted to a single tokio::task::JoinSet. Every task follows the same lifecycle:

  • Spawned → waits for all declared dependencies.
  • Running → all deps succeeded; the builder executes.
  • Skipped → a dependency failed; returns Ok(vec![]) and signals Some(false) so its own dependents also skip.
  • Done / Failed → signals Some(true) or Some(false).

After all tasks finish, collected [ArtifactPath]s are classified:

  • File artifacts → dist/ (distribution binaries, archives, etc.).
  • Directory artifacts → docs/ (rustdoc output, rendered Markdown, …).

If any builder returned an error, the first error is propagated to the caller after all spinners have settled, so the full UI is always rendered to completion.

§Progress UI

The UI is built with the indicatif crate. Each builder task owns:

  • A parent spinner inserted above a shared summary bar.
  • An mpsc log channel ([LogSender]) over which the builder streams events.
  • A log-consumer task that receives [LogEvent]s and updates the spinner.

§LogEvent variants

VariantMeaning
Line(String)Update the parent spinner message.
ChildStart { id, label }Create a sub-spinner below the parent.
ChildLine { id, line }Update a child spinner’s message.
ChildFinish { id, … }Close a child spinner with ✓ or ✗.

The ChildStart / ChildLine / ChildFinish events are used only by the cargo builder when it compiles for multiple targets in parallel (see below).

§Inner Parallelism: CargoBuilder

When a cargo builder lists multiple targets, it spawns one Tokio task per target inside its own inner JoinSet — a second level of concurrency nested inside the outer builder task.

Each inner task:

  1. Creates a tempfile::TempDir and passes it as --target-dir so the cargo build process does not contend with sibling builds on cargo’s file lock.
  2. Emits a ChildStart event so the UI creates a dedicated sub-spinner.
  3. Uses line_bridge to adapt the plain-string stderr stream from run_cargo_build into ChildLine events on the parent [LogSender].
  4. After the build, copies artifacts to stable target/<triple>/release/ paths before the TempDir is dropped.
  5. Emits a ChildFinish event when done.

Within run_cargo_build itself, stderr and the JSON stdout are consumed concurrently in separate tokio::spawn tasks so neither stream blocks the other.

§Other Builders

BuilderAsync strategy
archivetokio::task::spawn_blocking for the CPU-bound tar walk; sends Line events.
cargo_docSpawns one tokio::spawn to stream stderr; awaits cargo doc exit.
markdownSequential per-file render loop; spawn_blocking for the directory walk.
scriptRuns commands sequentially; streams stdout and stderr in parallel tasks.

Modules§

archive
cargo
markdown
script

Structs§

ArtifactPath
BuilderEntry
A single [[builders]] entry: the builder itself plus optional dependency metadata.

Enums§

AnyBuilder
LogEvent
Events emitted by builders and forwarded to the progress-bar manager in crate::site.

Traits§

Builder
Implementing a New Builder

Type Aliases§

LogSender
A sender used to stream LogEvents from a builder back to the progress-bar manager in crate::site. Errors sending are silently ignored because the receiver may already be gone when a builder finishes.