---
title: "Applications"
canonical_url: https://developer.repebble.com/pebbleos/docs/development/build_system/applications/
last_updated: 2026-11-13
sdk_version: 4.44
---
# Applications

Source: https://developer.repebble.com/pebbleos/docs/development/build_system/applications/

PebbleOS is the kernel, the drivers, the subsystems and the libraries. What runs on top of it is an application: the code that provides `main()`, built against PebbleOS from its own directory. The watch firmware under `pebble/` is one, and so is anything else, from a driver test to a product that is not a watch.

## A minimal application

`samples/hello_world` is the smallest one:

```
samples/hello_world/
  CMakeLists.txt
  src/main.c
```

```
cmake\_minimum\_required(VERSION 3.20)find\_package(PebbleOS REQUIRED HINTS $ENV{PEBBLEOS\_BASE})project(hello\_world)target\_sources(app PRIVATE src/main.c)
```

```
#include \<pbl/logging/logging.h\>int main(void) {PBL\_LOG\_ALWAYS("Hello World! %s", CONFIG\_BOARD);return 0;}
```

`find_package(PebbleOS)` configures the build for the board, adds PebbleOS and defines `app`, the library the application’s sources go to. It comes before `project()`, as it also selects the toolchain. The image is linked once the application’s `CMakeLists.txt` has been read, so the application can use every `pbl_*` helper described in [CMake](https://developer.repebble.com/pebbleos/docs/development/build_system/cmake.md).

## Building and running

[pbl configure](https://developer.repebble.com/pebbleos/docs/pbl/configure.md) takes the application’s directory:

```
pbl configure --board qemu_emery samples/hello_world
pbl build
pbl run
```

The message goes to the console once, at boot. Attach [pbl console](https://developer.repebble.com/pebbleos/docs/pbl/console.md) from another shell, then restart the emulator with `system_reset` in the QEMU monitor:

```
* - 00:00:00.000 init.c:38> ***Booting PebbleOS v4.45.0***
* - 00:00:00.004 main.c:7> Hello World! qemu_emery
```

Plain CMake needs to be told where PebbleOS is, with the `PEBBLEOS_BASE` environment variable or with `-DPebbleOS_DIR=`:

```
cmake -B build -GNinja -S samples/hello_world -DBOARD=qemu_emery \-DPebbleOS_DIR=$PWD/cmake
cmake --build build
```

The image is `pebbleos.elf`, `.hex` and `.bin` in the build directory, whatever the application calls its project.

## Before main()

The kernel brings the system up before it calls `main()`, on the main thread: logging and the task watchdog are running, when enabled (`CONFIG_LOG`, `CONFIG_TASK_WDT`), and every device the board defines has been initialized (see [Device model](https://developer.repebble.com/pebbleos/docs/drivers/device_model.md)). `main()` may return; the other threads go on.

The kernel and the task watchdog call a few functions an application may override, to decide what a failure does. Their defaults log and reset:

| 

Function

 | 

Called when

 |
| --- | --- |
| 

`pbl_fatal_error_handler()`

 | 

An assertion or a panic stops the system

 |
| 

`pbl_heap_oom_handler()`

 | 

An allocation that must not fail does

 |
| 

`pbl_kernel_syscall_exit()`, `pbl_kernel_syscall_fault()`

 | 

A syscall made from unprivileged code returns, or is given bad arguments

 |
| 

`pbl_task_wdt_stalled()`, `pbl_task_wdt_recovered()`, `pbl_task_wdt_expired()`

 | 

A watched thread stops making progress

 |

## Configuration

An application configures PebbleOS with files in its own directory, applied on top of the board’s defconfig (see [Kconfig](https://developer.repebble.com/pebbleos/docs/development/build_system/kconfig.md)):

| 

File

 | 

Purpose

 |
| --- | --- |
| 

`prj.conf`

 | 

What the application enables on every board

 |
| 

`prj_<variant>.conf`

 | 

What a variant (`--variant`) adds

 |
| 

`boards/<board>.conf`

 | 

What it enables or tunes on one board

 |
| 

`boards/<board>_<variant>.conf`

 | 

The same, for one variant only

 |
| 

`Kconfig`

 | 

The application’s own options

 |
| 

`roles.yml`

 | 

The device of each role, over the board’s; see [Roles](https://developer.repebble.com/pebbleos/docs/development/build_system/devicetree.md#devicetree-roles)

 |
| 

`boards/<board>.roles.yml`

 | 

The same, on one board

 |
| 

`app.overlay`

 | 

Changes to the board’s devicetree; see [Overlays](https://developer.repebble.com/pebbleos/docs/development/build_system/devicetree.md#devicetree-overlays)

 |
| 

`boards/<board>.overlay`

 | 

The same, on one board

 |

An application with a `Kconfig` makes it the root of the configuration tree: it defines its options and sources PebbleOS’s with `source "Kconfig.pebbleos"`. Defaults it gives before that line take precedence over PebbleOS’s own.

Driver classes, subsystems and optional kernel features are off unless the application asks for them, e.g. `CONFIG_ACCEL=y` or `CONFIG_BT=y`: a board’s defconfig only selects its SoC, platform, boot chain and console UART, so `hello_world` builds little more than the kernel. Even the console and logging are the application’s choice: `hello_world` enables them with `CONFIG_CONSOLE=y` and `CONFIG_LOG=y` in its `prj.conf`. The drivers follow from the board’s [Devicetree](https://developer.repebble.com/pebbleos/docs/development/build_system/devicetree.md), and the buses and supplies they use from the drivers. What an application does with the hardware, such as the firmware’s backlight levels on `obelix`, goes in the application’s `boards/obelix.conf`.

The firmware is built the same way, from `pebble/CMakeLists.txt`, and its configuration follows the same layout: `pebble/prj.conf`, `pebble/prj_normal.conf`, `pebble/prj_prf.conf`, `pebble/boards/` and `pebble/Kconfig`.

Generated from PebbleOS [f4926c952](https://github.com/coredevices/pebbleos/commit/f4926c952bf9b82bce9e3cdb44645a59531c0ee9). This page is maintained in the pebbleos repository: [docs/development/build\_system/applications.md](https://github.com/coredevices/pebbleos/blob/f4926c952bf9b82bce9e3cdb44645a59531c0ee9/docs/development/build_system/applications.md).
