nishikaze 0.1.2

Zephyr build system companion.
Documentation
# kaze CLI Reference

`kaze` is a Zephyr build companion that orchestrates **configure / build / flash / run** for both **single-image** apps and **sysbuild multi-image** projects, with declarative configuration via `kaze.toml`.

This project is heavily inspired by [zync](https://gitlab.com/byarocks/zync) and should follow it for implementation details such as resulting system commands called.

## Synopsis

```bash
kaze [OPTIONS] <COMMAND> [COMMAND_OPTIONS] [-- <EXTRA_ARGS>...]
```

## Global options

| Option | Summary |
|---|---|
| `-h, --help` | Show help |
| `-V, --version` | Show version |
| `-v..., --verbose` | More logs (repeatable optional) |
| `-c, --clean` | Pre-clean the active build dir before running the command |
| `-a, --all` | Run command for all configured profiles (overrides `--profile` and defaults) |
| `-p, --profile <profile>` | Select a configured profile |
| `-b, --board <board>` | Override resolved board |
| `-r, --runner <runner>` | Override resolved runner |
| `--project <path>` | Explicit project root (otherwise auto-detect by traversal) |

### Notes
- `--all` applies only when profiles exist; otherwise it behaves like normal single build.
- `--clean` pre-cleans **only the active build directory** (profile-specific when profiles are enabled).

## Commands

| Command | Aliases | Summary |
|---|---|---|
| `init` | `i` | Initialize project config (`kaze.toml`) |
| `boards` | `bo` | List available Zephyr boards |
| `runners` | `rn` | List available Zephyr runners |
| `profiles` | `p` | List configured profiles |
| `clean` | `c` | Clean build output (root or active profile build dir) |
| `conf` | `cf` | Configure via CMake |
| `build` | `b` | Build via Ninja (auto-configures if needed) |
| `flash` | `f` | Flash using configured/selected runner |
| `run` | `r` | Run simulation (native_sim/qemu), or run target-specific runner behavior |

### Shared command options

All commands support:
- `--board/-b` and `--runner/-r` overrides (precedence over config)

Commands that accept extra args via `--`:
- `conf`, `build`, `flash`, `run`

Example:
```bash
kaze build -- -j 0
kaze conf  -- -DOVERLAY_CONFIG=overlay.conf
kaze flash -- --hex-file path/to/other.hex
kaze run   -- -flash=../../seed/flash.bin
```

## Sysbuild image selection (flash/run)

`flash` and `run` support sysbuild image selection:

| Option | Summary |
|---|---|
| `--list, -l` | List available sysbuild images |
| `--image, -i <name-or-index>` | Select image to flash/run |

Rules:
- If no image selected, default is **first non-bootloader** image (e.g. prefer `app` over `mcuboot`).
- Indexing is 1-based in CLI output.

Example:
```bash
kaze flash --list
kaze flash --image app
kaze flash --image 1
```

## `run` behavior and `--norebuild`

`kaze run`:
- By default, performs a build first, then runs the simulator binary directly if found.
- If simulator binary is not found, falls back to `ninja run` behavior (extra args after `--` are not appended in that fallback mode).

Options:
- `-n, --norebuild` — skip the pre-run rebuild and run immediately.

---

# `kaze.toml` Configuration Spec

`kaze.toml` is a declarative config located at the **project root**.

## File discovery

`kaze` locates the project by:
1) if `--project <path>` is given, use it
2) else traverse upward from `cwd` until `kaze.toml` is found
3) if run from a build directory, traverse upward to find the owning project root

## Configuration layers and precedence

Resolved configuration is produced by merging layers in this order:

1) **Defaults**
2) **User config (optional)**: `~/.config/kaze/config.toml`
3) **Project config**: `<project>/kaze.toml`
4) **Environment variables**: `KAZE_*`
5) **CLI flags** (`--profile`, `--board`, `--runner`, etc.)

Within the project config:
- global values apply first
- active profile values override global values
- CLI overrides override both

## Top-level schema overview

Recommended top-level tables:

- `[project]` — defaults and profile selection
- `[zephyr]` — workspace and base paths
- `[build]` — build directory rules
- `[args]` — extra args per phase (global)
- `[profile.<name>]` — per-profile board/runner + per-profile args

## `[project]`

| Key | Type | Default | Summary |
|---|---|---:|---|
| `board` | string | *(none)* | Default Zephyr board |
| `runner` | string | *(none)* | Default Zephyr runner |
| `default_profile` | string | *(none)* | Default profile name |
| `name` | string | *(optional)* | Display name for the project (optional) |

### Profile selection rules

When profiles are defined:
1) if `--all` set → all profiles selected
2) else if `--profile` set → that profile selected
3) else if `default_profile` set → selected
4) else → first profile in config file order (or lexicographic if order isn’t preserved)

When profiles are not defined:
- profile selection is ignored and build is profile-less.

## `[profile.<name>]`

Each profile is a partial override of the global project config.

| Key | Type | Summary |
|---|---|---|
| `board` | string | Profile board |
| `runner` | string | Profile runner |
| `args` | table | Per-phase args for the profile |

Example:
```toml
[profile.sim]
board = "native_sim"

[profile.prod]
board = "nucleo_f767zi"
runner = "openocd"
```

## `[zephyr]`

| Key | Type | Default | Summary |
|---|---|---:|---|
| `workspace` | string | auto-detect | Zephyr workspace root |
| `base` | string | `${zephyr_ws}/zephyr` | Zephyr base directory |

Auto-detection order (when not set):
1) `ZEPHYR_BASE` environment variable
2) find `.west/` by traversing upward; if found, treat its parent as workspace
3) (optional) fallback helpers can be added later

## `[build]`

| Key | Type | Default | Summary |
|---|---|---:|---|
| `root` | string | `"build"` | Root build output directory |
| `layout` | string enum | `"auto"` | Build layout mode: `auto`, `profiles`, `single` |
| `link_compile_commands` | bool | `true` | When profiles enabled, create root `build/compile_commands.json` symlink |

### Build output layout

- Profile-less mode: `./<build.root>/`
- Profiles mode: `./<build.root>/<profile>/`

If profiles mode and `link_compile_commands=true`:
- `./<build.root>/compile_commands.json` is a symlink to the selected default/first profile’s `compile_commands.json`

## `[args]` and per-profile args

Args are configurable for phases:

- `conf` (CMake configure)
- `build` (Ninja)
- `flash` (west flash / runner invocation)
- `run` (simulator binary or fallback command)

Value forms:
- string → treated as a single argument string (split behavior is implementation-defined; recommended to use arrays)
- array of strings → preferred, exact args list

Example:
```toml
[args]
conf  = ["-DHG_TEST=ON"]
build = ["-j", "0"]
flash = ["--hex-file", "path/to/other.hex"]
run   = ["-flash_mount=../../seed/sd"]

[profile.sim.args]
run = ["-flash=../../seed/flash.bin", "-wait_uart"]
```

### Arg merge order

For a given phase:
1) global `[args.<phase>]`
2) profile `[profile.<name>.args.<phase>]`
3) CLI passthrough args after `--`

---

# Interpolation Spec (`${key}`)

`kaze` supports `${key}` placeholders in string config values (most commonly in args and paths).

## When interpolation runs
Interpolation runs after:
1) config layers are merged
2) active profile is resolved
3) CLI overrides are applied

So `${board}` reflects the **final** resolved board, etc.

## Supported keys

Resolved from the active configuration + runtime context:

- `board` — resolved board
- `runner` — resolved runner
- `profile` — selected profile name (empty if profile-less)
- `build_dir` — active build directory (profile-specific when profiles enabled)
- `project_dir` — project root directory
- `zephyr_ws` — Zephyr workspace
- `zephyr_base` — Zephyr base (usually `${zephyr_ws}/zephyr`)
- `cwd` — current working directory
- `is_build``"true"`/`"false"` (whether invoked from build dir context)
- `sysbuild``"true"`/`"false"` (whether project detected as sysbuild)

## Rules / limitations
- Missing keys are left unchanged (`${missing}` stays as-is).
- Interpolation applies only to strings (numbers/bools stringify if referenced).
- Cycles are not expanded further (self-references remain `${key}`).
- Interpolation is single-pass (no recursive expansion).

---

# Minimal examples

## 1) Simple project (profile-less)
```toml
[project]
board = "nucleo_f767zi"
runner = "openocd"
```

## 2) Project with profiles
```toml
[project]
runner = "openocd"
default_profile = "sim"

[profile.sim]
board = "native_sim"

[profile.prod]
board = "nucleo_f767zi"
```

## 3) Args + interpolation
```toml
[zephyr]
workspace = "/opt/zephyr"
base = "${zephyr_ws}/zephyr"

[args]
conf = ["-DBOARD=${board}", "-DZEPHYR_BASE=${zephyr_base}"]
build = ["--build-dir", "${build_dir}"]
run = ["-flash_mount=../../seed/sd"]
```