Gunnar

nordisk/edda

.nornir/build-variants.md

<!-- nornir:class: history -->
# nornir build variants — declarative build "goals" + a Build button

> A product like holger ships several distinct binaries/profiles (embedd /
> mikro / full, plus optional wasm + feature toggles). Instead of remembering
> the `cargo build -p … --bin … --features …` incantations, each product
> **declares** its valid build variants as data; nornir validates a chosen
> variant, materializes it into a `cargo build`, and can present the options +
> a Build button. PLAN task #19. Lives in `src/build/variant.rs`
> (`[build]` in `nornir.toml` or `.nornir/build-variants.toml`).

## Design

- **`BuildVariant`** — one buildable goal: `name`, `package` (→ `-p`), `bin`
  (→ `--bin`), `features` (→ `--features a,b`), `no_default_features`,
  `profile` (`Dev` = cargo default / no flag, `Release`/`Custom(name)` →
  `--profile`), `target` (→ `--target`), `cfg` (→ threaded through `RUSTFLAGS
  --cfg`).
- **`BuildVariantSet`** — the *space of valid choices* a product offers:
  `offers` (the allowed feature universe), `exclusive` (mutually-exclusive
  feature groups), `required` (per-`--bin` required features, mirroring Cargo
  `required-features`), and the concrete `variants`. `validate()` rejects a
  feature not in `offers`, two features from one exclusive group, or a bin
  missing its required features.
- **Materialize** — `cargo_args()` renders argv in a fixed order (`build -p
  --bin --features --no-default-features --profile --target`); `build()`
  validates then runs `cargo` in the repo dir.
- **Load** — from `[build]`/`[[build.variant]]` in `nornir.toml` (inline) or a
  standalone `.nornir/build-variants.toml` (`BuildVariantSet::discover`). The
  descriptor is serde-serializable + enumerable so a **UI can list the variants,
  their option axes, and validity, with a "Build" action** that calls the
  materialize→build path (the nornir-viz presentation).

**Worked example — holger's real products:** `holger-server` (pkg
`holger-server-cli`), `holger-agent` (pkg `holger-agent-cli`),
`holger-mannequin`, plus the fat GUI `holger-ui` (`required-features = ["gui"]`).
Encoded as `offers = ["gui","served","testmatrix"]`,
`exclusive = [["gui","lean"]]`, `required."holger-ui" = ["gui"]`.

## Guide

```bash
nornir build --list-variants        # print each variant → its cargo command line
nornir build --variant server       # validate + materialize + run cargo build
nornir build --variant ui --dry-run # validate + print the argv, build nothing
nornir build --variant full --repo /path/to/holger
```

Add a variant = add a `[[build.variant]]` table (or a row to the set) — no code
change. Invalid combinations are rejected up front with the reason.

## Idiot guide

- **"How do I build the GUI / the server / the tiny one?"** →
  `nornir build --list-variants` shows every buildable target this repo offers;
  pick one with `nornir build --variant <name>`.
- **"Can I just see the command without building?"** → add `--dry-run`.
- **"It refused my variant."** → you asked for a feature the product doesn't
  offer, or two that can't be on together — the error names which.