Build and run projects with Zephyr
Zephyr is a small-footprint, scalable real-time OS for resource-constrained devices, maintained under the Linux Foundation. It's Apache-licensed, supports 450+ boards out of the box, and ships with a coherent build system (west), a Linux-style devicetree, and Kconfig. This post walks through the shortest possible path: from nothing built to "Hello World" running on QEMU x86, then how to retarget the same sample to real hardware.
Pre-requisites
Before you build anything, make sure you have:
- The Zephyr SDK — a bundle containing arm-none-eabi-gcc, riscv-gcc, and the host tools.
- A Zephyr workspace cloned via
west init. - Python 3.10+ with
westand the requirements installed (pip install -r zephyr/scripts/requirements.txt). - CMake 3.20+ and Ninja on your PATH.
The official getting-started guide for macOS / Linux / Windows covers the SDK install, west bootstrap, and the first workspace clone:
→ docs.zephyrproject.org/latest/develop/getting_started
About west
west is Zephyr's meta-tool. It wraps Git (for managing the workspace's many repos), CMake (for builds), and the toolchain glue (for flash/debug). You almost never call cmake or make directly — you call west and it does the right thing.
Build and run Hello World on QEMU x86
QEMU lets you run a Zephyr binary on your host CPU — no board, no flash cable. Perfect for first-time exploration.
cd zephyr/
west build -b qemu_x86 samples/hello_world
west build -t run
The first command builds the hello_world sample for the qemu_x86 target. The second invokes the run CMake target, which boots the resulting ELF inside QEMU. You should see:
*** Booting Zephyr OS build v3.x.x ***
Hello World! qemu_x86
Press Ctrl+A then X to exit QEMU.
Retarget to real hardware
Same sample, swap the board. For an STM32F4 Discovery:
west build -p auto -b stm32f4_disco samples/hello_world
west flash
For a Nordic nRF52840 DK:
west build -p auto -b nrf52840dk_nrf52840 samples/hello_world
west flash
The -p auto flag triggers a pristine build whenever the board changes — without it, CMake caches stale settings and you get cryptic errors.
Useful west commands
west build -t menuconfig— interactive Kconfig (terminal UI).west build -t guiconfig— same thing, GUI version.west flash— program the connected board.west debug— launch a GDB session attached to the running target.west list— show every repo in the workspace and its checked-out revision.west update— sync all repos to the versions pinned inwest.yml.west boards— list every supported board.
Build directory layout
After a build, the build/ directory contains everything CMake produced:
build/
├── CMakeCache.txt
├── zephyr/
│ ├── zephyr.elf ← linked firmware (debug symbols)
│ ├── zephyr.bin ← raw binary for flashing
│ ├── zephyr.hex ← Intel HEX for some flashers
│ ├── zephyr.map ← link map
│ └── .config ← resolved Kconfig (final values)
└── ...
The .config file is gold for debugging "why is this option not enabled?" questions — it shows the actual values applied after all defaults, board overlays, and prj.conf merges.
Common troubleshooting
ZEPHYR_TOOLCHAIN_VARIANT not set— you forgot to source the SDK setup script (~/zephyr-sdk-x.y.z/setup.shor setZEPHYR_SDK_INSTALL_DIR).cmake: command not found— install CMake (Homebrew on macOS, apt on Debian, scoop on Windows).- Stale build artefacts — when in doubt,
west build -p alwaysforces a from-scratch build. - Wrong board after switching — delete the
build/directory entirely;-p autocovers most cases but not all.
Where to go next
- Sample reference: samples/hello_world/README
- Browse other samples: docs.zephyrproject.org/latest/samples
- Devicetree intro: docs.zephyrproject.org/latest/build/dts