ADR 0018 — Distribution and installation model
Status: Accepted (amended 2026-06-25). Phase 4 (CUDA/HIP prebuilt binaries) and Phase 7 (eda-infra-rs upstreaming) remain open — see the amendment below.
Amendment (2026-06-25) — built and shipped. The distribution layer this ADR proposed is now operational; the original "no release artifacts; install = clone + submodules + cargo build" premise is historical. What's live:
- GitHub Releases (macOS arm64/Metal): from v0.2.1 (first attached, working binary) through v0.2.3.
release.ymlbuilds, smoke-tests the relocated tarball, and publishes (prereleases via a draft, for the immutable-releases repo).cargo binstall --git:[package.metadata.binstall]pkg-url override inCargo.toml.- Homebrew tap
gpu-eda/homebrew-tap: formula atpackaging/homebrew/jacquard.rb(v0.2.3);depends_on "llvm".- Staging install-validation (
validate-install.yml): aworkflow_dispatchgate running the documentedcargo binstall+brew installagainst a published (pre)release. RC tags (vX.Y.Z-rc.N) publish as prereleases and gate promotion.netlist-graphon PyPI (publish-netlist-graph.yml, OIDC trusted publishing):netlist-graph 0.1.0published; TestPyPI dry-run job too.- Single shared crate version via
scripts/bump_version.py+ verify-guard; Cargo.lock tracked; repo URLs corrected togpu-eda.- Docs:
docs/installation.md,docs/release-process.md(incl. the staging-validation gate).Still open: Phase 4 (CUDA/HIP prebuilt binaries — gated on self-hosted NVIDIA/AMD release runners; Linux stays source-build), Phase 6 (container image — deferred per the original decision), and Phase 7 (eda-infra-rs upstreaming, the path to a crates.io publish — see
docs/plans/distribution.md § Phase 7). The original proposal follows unchanged.
TL;DR. In the context of shipping Jacquard to users (and to
docs-dogfooding agents), facing the fact that it is a GPU-compiled Rust
binary with vendored path-dependency submodules plus two companion
tools, we chose a tiered, channel-per-artifact model — Rust binaries
via GitHub Releases + cargo-binstall + a Homebrew tap (Metal), and the
Python netlist-graph companion via PyPI — accepting that we maintain
per-GPU-target release binaries and three coordinated release cadences,
rather than forcing everything through one channel.
Context
"Install Jacquard" is not one artifact. The toolset is three pieces:
| Tool | Language | Role | GPU? |
|---|---|---|---|
jacquard | Rust | the simulator (sim / cosim) | yes — Metal / CUDA / HIP, compiled via the ucc build script |
opensta-to-ir | Rust | SDF → timing-IR preprocessing (timing path) | no (CPU) |
netlist-graph | Python | post-synthesis signal-name discovery; the companion the tracing docs (signal-tracing.md, bus-tracing.md) lean on | no |
Constraints that rule options out:
jacquardis not crates.io-publishable. Its dependencies are vendored path deps (vendor/eda-infra-rs/*— a fork carrying in-flight patches), and the build needs git submodules, a GPU SDK, and a C++/CUDA/Metal compile.cargo installfrom source works but is slow and needs the full toolchain — not "easy."jacquardis not a natural PyPI package. It's a GPU binary; a maturin wheel would mean per-backend wheels (Metal macOS-arm64 only, CUDA huge and driver-coupled, HIP).netlist-graphis PyPI-ready today — self-contained (networkx+clickonly), anetlist-graphconsole script, no workspace path deps.
Today there are no release artifacts: install = clone + submodules +
cargo build -r --features <backend>. That blocks easy adoption and
makes docs-dogfooding (a fresh agent following the docs) start from a
heavy source build.
Decision
Distribute each artifact through the channel that fits it:
- Rust binaries (
jacquard+opensta-to-ir) →- GitHub Releases prebuilt binaries, one per GPU target
(
macos-arm64-metal,linux-x64-cuda,linux-x64-hip). cargo-binstallsupport via[package.metadata.binstall]socargo binstall jacquardfetches the release asset.- Homebrew tap (
gpu-eda/homebrew-tap) for the macOS/Metal path:brew install gpu-eda/tap/jacquard. opensta-to-irships alongsidejacquard(same release, same formula) — it's a sibling CPU bin in the same cargo workspace.
- GitHub Releases prebuilt binaries, one per GPU target
(
netlist-graph(Python) → PyPI:uvx netlist-graph/pip install netlist-graph. Versioned independently of the Rust bins.- The simulator never ships via PyPI. PyPI is for the Python companion only.
Versioning: the two Rust bins share the workspace version and a
single tag (coordinated); netlist-graph versions independently. This
is effectively Jacquard's first numbered release — see
release-process.md.
Homebrew scope: the formula installs the Rust bins only;
netlist-graph is documented as a separate uvx/pip line rather than a
formula dependency, keeping the formula simple and the Python tool
independently installable.
Rollout is Metal-first: the macOS-arm64 Metal binary ships now (the self-hosted macOS runner exists). CUDA and HIP release binaries are gated on the NVIDIA / AMD runners being stood up; until then those targets remain source-build.
Install tiers (documented where each is first needed):
- pure functional cosim →
jacquardonly; - signal-name debugging → add
netlist-graph; - timing / post-PnR → add
opensta-to-ir+ a PDK (volare/ciel).
Alternatives considered
- PyPI wheel for the simulator (maturin). Rejected: per-backend wheels, CUDA wheel size and driver coupling, Metal macOS-arm64-only. PyPI fits the pure-Python companion, not the GPU binary. (This is the natural-sounding option; it doesn't survive contact with the GPU backends.)
- crates.io publish +
cargo install. Blocked by the vendored path-dependency fork, and still requires a GPU SDK + a multi-minute compile. Not "easy install." - Container image as the primary channel. Good for reproducible
CUDA/Linux, but a poor fit for the macOS/Metal majority path and heavy
for a quick
jacquard --help. Kept as a possible additional channel (deferred), not the primary.
Consequences
- One-line installs on the platforms that matter; docs-dogfood agents
can
brew installinstead of building from source. - Release CI must build a per-target matrix; the CUDA/HIP rows are runner-gated, so the first release is Metal-only and the matrix fills in as runners land.
- Three artifacts to release: two Rust bins (coordinated, one tag) and
netlist-graph(independent). Coordinated Rust versioning keeps this to two cadences, not three. - New cross-repo surface to own: a
homebrew-taprepo and a PyPI project + publish credentials (trusted publisher). - The stale
repository = "…/ChipFlow/Jacquard"URLs (bothCargo.tomls) must be corrected togpu-edaas part of this work. - Prebuilt binaries require a relocatable kernel. The Metal binary
currently loads its
.metallibfrom a compile-time build-tree path (env!("METALLIB_PATH")), so a shipped binary must embed or bundle the kernel first. Tracked as Phase 1a in the plan.
Walk-back options
- If maintaining per-backend release binaries proves too costly, fall back to container images for CUDA/HIP and keep Releases + Homebrew for Metal only — the channels are independent, so this is a per-target retreat, not a redesign.
- If the Homebrew tap is more upkeep than it's worth, drop it and keep
cargo-binstall+ the release tarballs; the formula is the thinnest layer to remove.
Links
- Implementation phasing:
../plans/distribution.md. - Release mechanics:
../release-process.md.