nishikaze 0.3.2

Zephyr build system companion.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
# Nishikaze - The West Wind
[![crates.io](https://img.shields.io/crates/v/nishikaze.svg)](https://crates.io/crates/nishikaze)
[![docs.rs](https://img.shields.io/docsrs/nishikaze)](https://docs.rs/nishikaze)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
[![pipeline](https://gitlab.com/byacrates/nishikaze/badges/master/pipeline.svg)](https://gitlab.com/byacrates/nishikaze/-/commits/master)
[![Coverage Status](https://coveralls.io/repos/gitlab/byacrates/nishikaze/badge.svg?branch=master)](https://coveralls.io/gitlab/byacrates/nishikaze?branch=master)


A CLI companion that orchestrates the whole lifecycle of Zephyr-based projects
using a declarative config.

## Index

<!-- toc -->

- [Why?]#why
- [Features]#features
- [Installation]#installation
- [Requirements]#requirements
- [Usage]#usage
- [Kaze config]#kaze-config
  * [File discovery]#file-discovery
  * [Configuration layers and precedence]#configuration-layers-and-precedence
  * [Top-level schema overview]#top-level-schema-overview
  * [`[project]`]#project
  * [`[profile.]`]#profile
  * [Profile selection rules]#profile-selection-rules
  * [`[build]`]#build
  * [Build output layout]#build-output-layout
  * [`[zephyr]`]#zephyr
  * [`[project.args]` and per-profile args]#projectargs-and-per-profile-args
  * [Arg merge order]#arg-merge-order
  * [`[bom]`]#bom
  * [Minimal config examples]#minimal-config-examples
    + [1) Simple project (profile-less)]#1-simple-project-profile-less
    + [2) Project with profiles]#2-project-with-profiles
    + [3) Project with both profiles and extra args]#3-project-with-both-profiles-and-extra-args
  * [Verbosity]#verbosity
  * [Typical workflow]#typical-workflow
  * [Sysbuild projects]#sysbuild-projects
    + [Sysbuild image selection]#sysbuild-image-selection
  * [SPDX BoM generation]#spdx-bom-generation
  * [Workspace management]#workspace-management
- [Example app]#example-app
- [Similar projects]#similar-projects
- [License]#license
- [Contribution]#contribution
- [Development]#development
- [Upcomming features]#upcomming-features

<!-- tocstop -->

## Why?

Zephyr OS is a dream to work with. Zephyr's `west`, however, is a different story
(at least for multi image sysbuild projects). Even though at first glance this
meta tool looks exactly like west, you have the option to configure your project
declaratively. I admit, when building a simple Zephyr app, you can just use pure
CMake + ninja/make and configure everything in CMake. However, when building a
multi-image app using sysbuild (e.g. updatehub OTA + mcuboot + app image) then
you're stuck with passing a mile of args into west as you cannot configure it
declaratively and cannot modify CMake config of external modules.

## Features

1. Manage the whole lifecycle of a Zephyr app - config, build, flash, sim runs
2. Supports both simple Zephyr apps and multi-image apps with sysbuild
3. Automatic Zephyr workspace detection
4. Declarative config through `kaze.toml` @ project root
5. Automatic project dir navigation (can be run from both project root or build dir)
6. Automatically detects `sysbuild` projects and adjusts the build accordingly
7. Supports multiple build profiles (can build multiple binaries for a specific app, useful for simulations or multiple board targets)
8. SPDX BoM generation
9. Zephyr workspace management

## Installation

Install using cargo:
```bash
cargo install nishikaze
```

## Requirements

As it's intended to manage Zephyr OS apps, you'll need a workspace setup with
[Zephyr
OS](https://docs.zephyrproject.org/latest/develop/getting_started/index.html)
and [Zephyr
SDK](https://docs.zephyrproject.org/latest/develop/toolchains/zephyr_sdk.html)
installed.

## Usage

```bash
Usage: kaze [OPTIONS] <COMMAND>

Commands:
  init        Initializes project with kaze.toml
  boards      Lists available boards
  runners     Lists available runners
  profiles    Lists profiles configured in kaze.toml
  clean       Cleans build dir
  conf        Configure project
  build       Build binary
  run         Run simulation
  flash       Flash binary
  menuconfig  Run menuconfig
  bom         Generate SPDX `BoM`
  workspace   Workspace management
  help        Print this message or the help of the given subcommand(s)

Options:
  -c, --clean              Pre-clean the active build dir
  -a, --all                Run command for all configured profiles (overrides --profile/default)
  -p, --profile <PROFILE>  Select project profile defined in kaze.toml
  -b, --board <BOARD>      Zephyr OS board/target (overrides config)
  -r, --runner <RUNNER>    Zephyr OS flash runner (overrides config)
      --project <PROJECT>  Explicit project root
  -v, --verbose...         Verbosity (repeatable)
  -d, --dry-run            Dry run
  -h, --help               Print help
  -V, --version            Print version
```

## Kaze config

`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) **Project config**: `<project>/kaze.toml`
3) **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

`kaze.toml` supports these configuration tables:

| Table   | Description    |
|--------------- | --------------- |
| `[project]`   | Global project configuration |
| `[build]`   | Build layout configuration   |
| `[zephyr]`   | Zephyr workspace overrides   |
| `[bom]`   | SPDX BoM generation config   |
| `[profile.<name>]` | Build profile |

Recommended top-level tables:

- `[project]` — defaults and profile selection
- `[build]` — build directory rules

### `[project]`

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

### `[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"
```

### Profile selection rules

When profiles are defined:
1) if `--all` arg provided -> all profiles selected (kaze runs selected command for all profiles)
2) else if `default_profile` defined -> selects that profile
3) else if `--profile` provided -> selects that profile
4) else -> selects 1st profile defined in config file

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

### `[build]`

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

### Build output layout

- Profile-less layout with no profiles configured: `./<build.root>/`
- Profiles layout with profiles configured: `./<build.root>/<profile>/`

If building with `profiles` layout and `link_compile_commands=true`:
- `./<build.root>/compile_commands.json` is a symlink to the selected default/first profile’s `compile_commands.json`
If building a sysbuild project and `link_compile_commands=true`:
- `./<build.root>/compile_commands.json` is a symlink to the project app image's `compile_commands.json` while perserving profile logic.
Linking is skipped when building in profile-less layout or when `link_compile_commands=false`.

### `[zephyr]`

| Key | Type | Default | Summary |
|---|---|---:|---|
| `workspace` | string | auto-detect | Zephyr workspace root |
| `base` | string | `${workspace}/zephyr` | Zephyr base directory |
| `url` | string | *(none)* | Zephyr workspace manifest repository url |
| `manifest` | string | *(none)* | Zephyr workspace local manifest file path |

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

### `[project.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
[project.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 `[project.args.<phase>]`
2) profile `[profile.<name>.args.<phase>]`
3) CLI passthrough args after `--`

### `[bom]`

| Key | Type | Default | Summary |
|---|---|---:|---|
| `build` | bool | `false` | Enables SPDX BoM generation |
| `version` | string | `"2.2"` | SPDX version (`"2.2"` / `"2.3"`) |

If BoM generation is enabled, calling `kaze bom` will configure the project for
BoM generation and generates SPDX BoM.

For sysbuild projects, you have to manually enable `CONFIG_BUILD_OUTPUT_META=y`
for all sysbuild images in their respective Kconfigs.

### Minimal config examples

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

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

[profile.sim]
board = "native_sim"
runner = "native"

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

#### 3) Project with both profiles and extra args
```toml
[project]
# global extra args (applies to all profiles)
args = {
    build = ["-j", "0"],
    run = ["wait_uart"]
}

[profile.dev]
board = "native_sim"
runner = "native"
# dev profile specific extra args
args = {
    build = ["-DKAZE_TEST=ON"],
    run = ["-flash=seed/flash.bin"],
}

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

### Verbosity

Kaze emits user-facing logs prefixed with `kaze:` and uses colored logs if
colour output is supported by the terminal.

Verbosity levels:
- `-v` (1): quiet — no kaze logs, no command output
- default / `-vv` (2): normal — kaze logs only; command output only on error
- `-vvv`+ (3+): verbose — kaze logs + command output always

Environment variables:
- `KAZE_TESTING=1` suppresses logs (useful for tests).
- `KAZE_LOGS=1` re-enables logs during tests.

### Typical workflow

Let's say you have a Zephyr app like this:

```bash
app
├── boards
│   └── ...
├── include
│   └── ...
├── src
│   └── ...
├── CMakeLists.txt
├── LICENSE
├── prj.conf
└── README.md
```

1. Initialize the project with a `kaze.toml`, either manually or use:

```bash
kaze init
```

This will create a `kaze.toml` file with some default settings:

```bash
app
├── boards
│   └── ...
.
.
.
├── README.md
└── kaze.toml # <- kaze project config
```

```toml
# kaze.toml
#edit this configuration at your discretion
# Project config
[project]
board = "nucleo_f767zi"
runner = "openocd"
# args = { conf = [""], build = [""], run = [""], flash = [""] }
# default_profile = "sim"

# Build dir config
[build]
root = "build"

# Zephyr workspace override
# [zephyr]
# workspace = ""
# base = ""

# SPDX BoM generation
# [bom]
# build = true
# version = "2.2"

# Build profiles
# [profile.sim]
# board = "native_sim"
# runner = "native"
# args = { conf = [""], build = [""], run = [""], flash = [""] }

# [profile.prod]
# args = { conf = [""], build = [""], run = [""], flash = [""] }

```

2. Configure and build the app:

```bash
kaze conf # <- optional step, as kaze build will configure project as well if not configured
kaze build
```

3. Flash app

```bash
kaze flash
```

or if using one of Zephyr's simulator boards (`native_sim` or `qemu` boards) run
simulation:

```bash
kaze run
```

### Sysbuild projects

When building a sysbuild project the workflow looks pretty much the same:

1. Init project with kaze config
2. Config + build project
3. Flash project images / run simulations

#### Sysbuild image selection

Sysbuild projects often consist of multiple standalone apps all built by
sysbuild. Let's consider a sysbuild project consisting of the `mcuboot`
bootloader and a simple app compatible with `mcuboot` (let's call it just
`app`), so the project would look something like this:

```bash
app
├── boards
│   └── ...
├── keys                # keys to sign our app and mcuboot
│   └── ...
├── src
│   └── ...
├── sysbuild            # additional sysbuild image configs
│   └── mcuboot.conf    # in our case that is mcuboot
├── CMakeLists.txt      # app cmake config
├── LICENSE
├── prj.conf            # app kconfig
├── README.md
├── sysbuild.conf       # sysbuild config
└── kaze.toml           # already initialized with kaze config
```

This app, when built, would produce 2 separate images:

| Image | Description |
| -------------- | --------------- |
| `app` | Our app compatible with mcuboot |
| `mcuboot` | Mcuboot bootloader image |

You can list available images with the `--list` option passed to `run`, `flash`,
`menuconfig` and `bom` commands:

```bash
kaze flash --list
```

Output:
```bash
...
Available sysbuild images:

1: app
2: mcuboot
```

To select an image to flash/run use the `--image` option and pass in either the
image name (e.g. `app`) or the image number (e.g. `1`):

```bash
kaze flash --image app
```

or

```bash
kaze flash --image 1
```

If no image selected, kaze will default to the first non-bootloader image
available.

### SPDX BoM generation

To be able to generate SPDX BoMs, enable the `[bom.build]` option in `kaze`
config. BoMs can only be genrated from built binaries pre-configured for BoM
generation. If a binary is built without BoM support and you enable it, `kaze`
automatically configures and rebuilds it when running `kaze bom`.

### Workspace management

`kaze` is able to manage Zephyr workspaces through `west.yml` manifest files or
repositories using `west`. To manage a zephyr workspace use the `kaze workspace`
command:

```bash
Usage: kaze workspace <COMMAND>

Commands:
  init    Initialize workspace from a url or a manifest file
  update  Update workspace
  export  Exports workspace cmake package
  apply   Apply workspace by running init, update and export
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version
```

`kaze` supports all workspace topologies, given the `[zephyr]` table of kaze
config is set up correctly.

> 🔔 **Note:** `[zephyr.url]` and `[zephyr.manifest]` settings are mutually exclusive.

## Example app

For a simple example Zephyr app managed by `kaze` see
[kaze-demo](https://gitlab.com/byacrates/kaze-demo)

## Similar projects
- [Zync]https://gitlab.com/byarocks/zync

## License

This project is licensed under either of:
* Apache License, Version 2.0, ([LICENSE-APACHE] or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT] or http://opensource.org/licenses/MIT)

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed under the Apache-2.0 and
MIT license, without any additional terms or conditions.

[LICENSE-APACHE]: ./LICENSE-APACHE
[LICENSE-MIT]: ./LICENSE-MIT

## Development

See [contribution guidelines](CONTRIBUTING.md).

TLDR:

Requires `just` to bootstrap all tools and configuration
```bash
cargo install just
just init # setup repo, install hooks and all required tools
```

To run:
```bash
just run
```

To test:
```bash
just test
```

Before committing work:
```bash
just pre-commit
```

To see all available commands:
```bash
just list
```

## Upcomming features

- [x] Menuconfig
- [x] SPDX BoMs
- [x] Zephyr workspace management
- [ ] Key generation for mcuboot bootloader and images
- [ ] Manual image signing
- [ ] Debugging apps
- [ ] Project template generation (app, module, sysbuild project, ...)
- [ ] Reference config values in string config options