Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Workflow by Platform and Language

You have a target and a language. This page is the sequence of commands that pair implies, and the one step whose absence is the most common first failure.

The rest of the book is organized by where you are going — a starter page per platform. This page is organized by what you type, because the commands vary along a different axis than the pages do: the builder follows from your language, and the toolchain follows from your platform. Neither table below is a summary of the other.

The step that is easy to miss

Rust leaves need nros sync before their first build. C and C++ leaves do not.

A Rust leaf resolves its nano-ros dependencies through a [patch.crates-io] table in its own .cargo/config.toml, and that table is generated — .gitignore excludes it, so a fresh clone does not have it. Sixty-seven Rust example leaves have such a file on a synced checkout; fifty of them are committed shells that include the generated central table:

include = [ "../../../../../nros-patch.toml", "nros-board.toml"]

Skipping the sync fails in two different ways, and the quieter one is worse. Where the config is committed, cargo treats the missing include as a hard error while parsing the manifest — before it builds anything, and before any message about nano-ros could appear:

error: failed to parse manifest at `<leaf>/Cargo.toml`

Caused by:
  could not load Cargo configuration

Caused by:
  failed to load config include `../../../../../nros-patch.toml` from `<leaf>/.cargo/config.toml`

Caused by:
  failed to read configuration file `<...>/nros-patch.toml`

Caused by:
  No such file or directory (os error 2)

Nothing in those five frames says nros sync. If you see it, this is what it means.

Where the whole config is generated — the native leaves, for instance — there is no file at all in a fresh clone, so there is no error to read. The patch table simply is not there, and the nano-ros crates your leaf names go looking for themselves on crates.io instead of in your checkout. That one does not announce itself.

Contributors (in-tree checkout): the just recipes — just <module> build-fixtures and friends — run nros sync for you, so they work from a fresh clone. It is the hand-run cd <leaf> && cargo build that needs you to run it yourself.

C and C++ leaves have no .cargo/config.toml at all — not committed, not generated. Their message bindings are produced inside CMake by nros_find_interfaces(), and the cargo builds CMake drives resolve against the repo-root config, which carries no include. Running nros sync for a C/C++ build is harmless but buys nothing.

You need it once per checkout location, not once per build. Re-run it after editing a .msg, .srv, or .action file, and — because the central table it writes holds absolute paths — after moving the checkout, or after one of the patched crates moves within it.

Which builder your cell uses

Each cell is the builder that nano-ros’s own CI uses for that pair, read from examples/fixtures.toml — the manifest the fixture builds and the staleness probe both consume. A dash means the pair has no in-tree coverage today, not that it is forbidden.

The row names are the manifest’s, which are shorter than the ones you type: freertos here is the platform whose board is qemu-arm-freertos and whose examples live in examples/qemu-arm-freertos/. The per-platform table further down maps all three spellings.

platformrustccppmixed
linuxcargocmakecmakecmake
freertoscargocmakecmakecmake
nuttxcargocmakecmake
nuttx-riscvcargocmakecmake
threadx-linuxcargocmakecmakecmake
threadx-riscv64cargo, cmakecmakecmake
zephyrcargo, westcmake, westcmake, westcargo
zephyr-cortex-mwestwestwest
esp32cargo
qemu-arm-baremetalcargo
qemu-esp32-baremetalcargo

Where a row lists two builders, both are real and they build different things: on Zephyr, west builds the single-node examples under examples/zephyr/<lang>/, and cargo/cmake build the workspace examples under examples/workspaces/.

The three command shapes

Every cell above is one of these three, plus platform-specific flags that live on the platform’s own starter page.

cargo — Rust, every platform

./scripts/bootstrap.sh          # builds the in-tree nros CLI
source ./activate.sh            # OR: direnv allow / source ./activate.fish
nros setup <board> --rmw zenoh  # toolchain + SDK for the target

cd <leaf>
nros sync                       # ← the step above; once per checkout
cargo build --release

Some leaves also pin their cross target in that same .cargo/config.toml ([build] target = "thumbv7m-none-eabi" on the Cortex-M ones), so no --target on the command line. Others get it from the platform’s recipe instead — contributors: in an in-tree checkout, just --list <module> shows which recipe builds what, and using the recipe avoids having to know.

cmake — C and C++

./scripts/bootstrap.sh
source ./activate.sh
nros setup <board> --rmw zenoh

cd <leaf>
cmake -B build -DCMAKE_TOOLCHAIN_FILE=<toolchain> -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel

No nros sync. The toolchain file is per platform — see the starter page. -D_NANO_ROS_CODEGEN_TOOL= is not needed when nros is on PATH; CMake resolves it.

west — Zephyr single-node examples

Zephyr owns the build. See Zephyr (west module) for the module wiring; the Rust leaves under examples/zephyr/rust/ still need nros sync first, because west drives cargo and cargo reads the leaf config either way.

In the checkout, or copied out?

The commands above are written as cd <leaf> inside the nano-ros checkout, which is the fastest way to see something run. For anything beyond that, copy the example directory out — examples are standalone copy-out projects with no workspace walk-up, so a copied one builds on its own.

The distinction matters for one reason: a bare cargo build writes target/ next to the leaf. In your project that is exactly right. In the nano-ros checkout it is residue the repo’s own gate rejects (check-example-leaf-target-dirs) — in-tree builds are expected to go through the contributor recipes (just <module> …), which write into a shared build directory instead. One in-repo cargo build --release of a Cortex-M example leaves 269 MB behind.

So: exploring in the checkout, prefer the recipe. Building your own thing, copy the example out and cargo build normally.

Per platform

platformgrid rownros setup <board>examples underrecipes (contributors)starter page
Linux hostlinuxnativeexamples/native/just native …Native host build
FreeRTOS (QEMU MPS2-AN385)freertosqemu-arm-freertosexamples/qemu-arm-freertos/just freertos …FreeRTOS
NuttX (Arm)nuttxqemu-arm-nuttxexamples/qemu-arm-nuttx/just nuttx …NuttX
NuttX (RISC-V)nuttx-riscvqemu-riscv-nuttxexamples/qemu-riscv-nuttx/just nuttx …NuttX
ThreadX (Linux sim)threadx-linuxthreadx-linuxexamples/threadx-linux/just threadx_linux …ThreadX
ThreadX (QEMU RISC-V 64)threadx-riscv64qemu-riscv64-threadxexamples/qemu-riscv64-threadx/just threadx_riscv64 …ThreadX
Zephyrzephyr, zephyr-cortex-mzephyrexamples/zephyr/just zephyr …Zephyr
ESP32qemu-esp32-baremetal (single-node), esp32 (workspace)qemu-esp32-baremetalexamples/qemu-esp32-baremetal/just esp32 …ESP32
Bare-metal Cortex-M3qemu-arm-baremetalqemu-arm-baremetalexamples/qemu-arm-baremetal/just qemu …Bare-metal
Arm FVP (Cortex-A SMP)zephyr + a license-gated FVP binaryARM FVP

Multi-node workspace examples do not follow that directory rule: they all live under examples/workspaces/, selected by fixture row rather than by directory. That is why ESP32 has two grid rows — qemu-esp32-baremetal for its single-node examples and esp32 for its share of the workspace ones. Zephyr’s two rows split differently: zephyr and zephyr-cortex-m build the SAME examples for different boards.

Contributors (in-tree checkout): each module’s recipes are discoverable rather than memorized:

just --list freertos          # every FreeRTOS recipe, grouped
just --list zephyr

More than one node

The sequence above builds one leaf. A project with several nodes adds a Bringup package and an Entry package on top of it, and the build is still one of the three shapes above — see Project layout.

If it does not work