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

Installation

nano-ros is distributed as source, vendored into the consumer’s project tree (git submodule, west manifest, ESP-IDF component, etc.) and built in-tree via add_subdirectory(nano-ros). There is no binary tarball, no system-wide install step, no find_package(NanoRos).

activate.sh is the after-install step, NOT a prereq. Sourcing activate.sh (or activate.fish, or direnv allow) only wires the workspace env into a new shell — it presumes the nros binary already exists at packages/cli/target/release/nros. The three bootstrap paths below (A/B/C) are what produce that binary; the activate file is what makes it findable in every subsequent shell.

See build-as-subdirectory.md for the canonical user incantation (4-line CMakeLists.txt). This page walks the surrounding workspace + per-target setup choices.

Pattern A: nano-ros lives inside your ROS 2 workspace

The recommended layout is to clone nano-ros into your workspace’s src/ directory alongside your packages:

~/ros2_ws/
├── src/
│   ├── nano-ros/             # <-- this repo
│   ├── pkg_a/                # your package(s)
│   ├── pkg_b/
│   └── …
└── (build/, install/, log/ — generated by colcon)

colcon build discovers nano-ros + each user package via package.xml, builds them in dependency order, and shares one nano-ros build across every consuming package. Users never run cmake manually — the per-user package’s CMakeLists.txt does add_subdirectory(../nano-ros nano_ros) under the hood.

A complete working example lives at examples/templates/multi-package-workspace/.

Pattern B: nano-ros as a third-party subdirectory

For C/C++ projects that don’t use colcon at all:

~/my_project/
├── CMakeLists.txt
├── src/main.c
└── third_party/
    └── nano-ros/             # git submodule

The top-level CMakeLists.txt:

cmake_minimum_required(VERSION 3.22)
project(my_app LANGUAGES C)

# `NROS_RMW` is the user-facing cache var (overridable via
# `-DNROS_RMW=<rmw>`); forward it to `NANO_ROS_RMW`, the var the
# nano-ros add_subdirectory reads. Matches the canonical example
# shape in examples/native/c/talker/CMakeLists.txt.
set(NANO_ROS_PLATFORM posix)
set(NROS_RMW "zenoh" CACHE STRING
    "Active RMW (zenoh|xrce|cyclonedds) — selects the backend linked into my_app.")
set(NANO_ROS_RMW "${NROS_RMW}")
add_subdirectory(third_party/nano-ros nano_ros)

add_executable(my_app src/main.c)
target_link_libraries(my_app PRIVATE NanoRos::NanoRos)
nros_platform_link_app(my_app)

nros_platform_link_app transitively wires the selected RMW backend on POSIX — no explicit nano_ros_link_rmw() call is needed. That is the entire consumption shape. See build-as-subdirectory.md for the full walkthrough.

Provision your toolchain with nros setup

nros setup is the single command that prepares a machine to build nano-ros for a given board. It ships prebuilt toolchains per platform per RMW — the cross-compiler, emulator, RMW host daemon, and any SDK sources a board needs are fetched from a pinned index and placed in a shared store (~/.nros/sdk). You do not install cross-toolchains by hand, and you do not need ROS 2 on the machine.

1. Get the nros CLI onto PATH

Pick one path from a fresh checkout — just is NOT a prereq.

A. Bare machine (no Rust, no just, no cargo):

git clone https://github.com/NEWSLabNTU/nano-ros.git
cd nano-ros
./scripts/bootstrap.sh base

Installs rustup, just, builds the in-tree nros CLI at packages/cli/target/release/nros, leaves the binary on PATH for this shell.

B. Already have cargo (most contributors):

cargo build --release --manifest-path packages/cli/Cargo.toml --bin nros
export PATH="$PWD/packages/cli/target/release:$PATH"

C. Tagged release, no Rust at all:

./scripts/install-nros-prebuilt.sh

Downloads the matching nros-<triple>.tar.gz from the GitHub release, sha256-verifies, installs to packages/cli/target/release/nros.

All three paths produce the per-checkout binary at packages/cli/target/release/nros. One checkout = one CLI version = one runtime ABI — no global install, no ~/.nros/bin PATH skew across worktrees.

2. Activate the workspace (every subsequent shell)

The bootstrap path you ran in §1 left nros on PATH for that shell only. Every new shell needs the workspace env wired in. Pick whichever fits your shell — all three wire the same env exports + PATH entries from the Phase 218.C SSoT activate.sh:

direnv allow                  # auto-activates on `cd nano-ros` (recommended)
source ./activate.sh          # bash / zsh, one-shot per shell
source ./activate.fish        # fish, one-shot per shell

The activate file also sources /opt/ros/humble/setup.bash if present (required by nros generate-rust + cyclonedds codegen + rmw_zenoh interop tests) and exports NROS_REPO_DIR.

3. Provision a board (+ RMW)

nros setup <board> --rmw <zenoh|xrce|cyclonedds>

nros setup resolves the board’s package set union the RMW’s host packages and fetches them all — prebuilt cross-toolchain + emulator + RMW daemon + board SDK sources. --rmw defaults to zenoh.

CommandProvisions
nros setup nativehost build; the zenoh router (zenohd)
nros setup native --rmw xrcehost build; the Micro-XRCE-DDS agent
nros setup native --rmw cycloneddshost build; Cyclone DDS runtime + idlc
nros setup qemu-arm-freertosarm-none-eabi-gcc, patched qemu-system-arm, FreeRTOS + lwIP sources, zenohd
nros setup qemu-arm-nuttxarm-none-eabi-gcc, qemu, NuttX sources
nros setup qemu-riscv64-threadxriscv64-*-gcc, qemu, ThreadX/NetX sources
nros setup threadx-linuxThreadX POSIX-sim sources
nros setup esp32the Espressif toolchain bits the esp-hal path needs
nros setup zephyrthe Zephyr west workspace + Zephyr SDK bits
nros setup mps2-an385 / stm32f4 / qemu-arm-baremetalbare-metal arm-none-eabi-gcc + qemu

Useful flags:

nros setup --list            # every package in the index + its version
nros setup <board> --dry-run # resolve + print the plan, fetch nothing
nros setup --licenses        # license-gated packages + how to install them
nros setup --tool qemu       # one tool by name
nros setup --source freertos-kernel   # one source package by name

Each board’s exact package set lives in the index; run nros setup <board> --dry-run to see precisely what a board pulls. See Supported Boards for the full board list and nros CLI for every subcommand.

Heads-up before your first example. Every nano-ros example (Linux talker, FreeRTOS talker, …) connects to its RMW host daemon at startup — zenohd for zenoh, the Micro-XRCE-DDS agent for xrce. Cyclone DDS is in-process — no separate daemon — so the heads-up below doesn’t apply if you ran nros setup … --rmw cyclonedds. nros setup … --rmw <rmw> installs the daemon into the nros store (~/.nros/sdk/<tool>/<version>/bin/; the cache root is ~/.nros/sdk/ — toolchains, transports, and daemons all land under there); you must then run it in a dedicated terminal before launching any example. For zenoh, put the store binary on PATH once and run it:

export PATH="$(dirname "$(ls -d ~/.nros/sdk/zenohd/*/bin/zenohd | tail -1)")":$PATH
zenohd        # leave running for the whole session

Without it the talker blocks forever on Executor::open with no output. Default ports: tcp/127.0.0.1:7447 on POSIX, tcp/10.0.2.2:7451 on QEMU FreeRTOS (Slirp forwards to host). Mismatch = silent hang; see Troubleshooting — First 10 Minutes.

After provisioning, follow the per-platform starter page (FreeRTOS, Zephyr, NuttX, …) for the board’s build + run steps.

Rust-only consumers

nano-ros is source-only — nothing is published to crates.io (decision 2026-05-14). The full nros crate can’t be published because it depends transitively on C/C++ submodules (zenoh-pico, mbedtls); nros-core isn’t carved out for crates.io either, to avoid a hybrid distribution model with version drift between the crates.io snapshot and in-repo HEAD.

Rust packages consume nano-ros via path dependency on the in-workspace checkout:

[dependencies]
nros = { path = "src/nano-ros/packages/core/nros",
         default-features = false,
         features = ["std", "rmw-cffi", "platform-posix", "ros-humble"] }

Each Rust package carries its own .cargo/config.toml patch entries when needed — see examples/templates/multi-package-workspace/src/pkg_rust_publisher/ for the pattern.

Contributor setup (working on nano-ros itself)

Contributors clone the repo and drive everything through just. The just setup recipes are thin wrappers over the same nros setup index — just <module> setup calls nros setup <board> under the hood, so the toolchains a contributor gets are identical to a user’s.

git clone https://github.com/NEWSLabNTU/nano-ros.git
cd nano-ros
./scripts/bootstrap.sh base   # path A from §1 above; gets rustup + just + nros
source ./activate.sh          # OR: direnv allow / source ./activate.fish
just setup all                # provision every supported board's SDK/toolchain

Diagnose missing tools (read-only):

just doctor tier=all

Provision one module:

just freertos setup           # → nros setup qemu-arm-freertos
just nuttx setup              # → nros setup qemu-arm-nuttx
just threadx_linux setup      # → nros setup threadx-linux

Docker environment

For a containerized environment with QEMU 7.2+:

just docker build
just docker shell      # Interactive shell with all tools
just docker test-qemu  # Run QEMU tests in container

Migrating from a pre-140 checkout

If you were on a nano-ros version that still had just install-local, see migration-install-local-removal.md for the one-page rewrite.

Next Steps