Bootstrapper Architecture
The bootstrap command uses an iterative depth-first loop to resolve and build an entire dependency tree. This document describes the phase pipeline, class hierarchy, and interaction model at a high level.
See also
Architecture Overview maps the major subsystems. Bootstrap vs Build covers the difference between bootstrap and build modes.
Bootstrap Phase Flow
Every package passes through a sequence of phases. Source packages traverse the full pipeline; prebuilt wheels skip the build phases.
RESOLVE ──► START ──► PREPARE_SOURCE ─┬─► PREPARE_BUILD ──► BUILD ─┐
│ │ │
│ (prebuilt)└────────────────────────────┤
│ │
│ ┌────────────────────────────────┘
│ ▼
│ PROCESS_INSTALL_DEPS ──► COMPLETE
│
(already seen) ──► drop
Each phase returns a list of new items to push onto the work stack.
Dependency-discovery phases (PREPARE_SOURCE, PREPARE_BUILD,
PROCESS_INSTALL_DEPS) also emit RESOLVE items for newly
discovered dependencies, which drives the recursive traversal.
Phase Class Hierarchy
All phases inherit from the Phase abstract base class, which defines
the contract every phase must satisfy:
run(bt)— execute the phase logic; return new items for the stack.background_work(bt)— optionally return a callable for background I/O (used byResolveandPrepareSource).requires_exclusive_run— returnTrueto drain the thread pool before running (used byBuildfor exclusive builds).
Phase |
Role |
|---|---|
|
Resolve available versions; fan out one |
|
Add to dependency graph; deduplicate already-seen packages |
|
Download source or prebuilt wheel; read build-system deps |
|
Install build-system deps; discover backend and sdist deps |
|
Build sdist and/or wheel; update the local mirror |
|
Run post-bootstrap hooks; extract install deps; record build order |
|
Clean up build directories (terminal phase) |
Each phase wraps a WorkItem dataclass that accumulates per-package
state (resolved version, build environment, build result, etc.) as it
moves through the pipeline.
Bootstrapper and Phase Interaction
The Bootstrapper class owns the work stack and thread pool. It runs
an iterative DFS loop:
┌─────────────────────────────────────────────────────┐
│ Bootstrapper Loop │
│ │
│ while stack: │
│ item = stack.pop() │
│ │
│ if item.requires_exclusive_run: │
│ drain thread pool │
│ │
│ new_items = item.run(self) │
│ │
│ for each new_item in new_items: │
│ stack.push(new_item) │
└─────────────────────────────────────────────────────┘
The Bootstrapper provides services that phases call back into during
run(): dependency graph updates, seen-set tracking, build-order
recording, and work-item creation for discovered dependencies.
Some phases (Resolve and PrepareSource) perform background I/O
in a thread pool. When new items are pushed onto the stack, the
Bootstrapper submits their background_work() immediately so I/O
overlaps with the main thread processing other items. Each phase is
responsible for blocking on its own bg_future inside run() when
it needs the result. Other phases run entirely on the main thread.