Create your own
Lesson illustration

Creating a Buildroot Package for a CMake-Based Gateway Application

Hello again. In the last lesson, you customized the assembled target filesystem with an overlay and a post-build script. Those mechanisms are appropriate for product-wide configuration and controlled final adjustments, but they are not the right place to compile and install gateway software. A gateway application should instead be a Buildroot package: selectable in configuration, built with the target toolchain, tracked with its dependencies, and installed through the normal target-rootfs flow.

This lesson creates a small CMake-based gateway-agent application and integrates it into a product-owned Buildroot external tree. The result is deliberately modest—a target executable, not yet a complete supervised gateway service—but the packaging pattern is the one you can extend as CAN, Ethernet, telemetry, and security requirements accumulate.


The package boundary: source, metadata, and installed artifact

Think of the package as a contract among three things:

  1. Application source defines what the gateway program does and how CMake builds and installs it.
  2. Kconfig metadata defines whether a product configuration selects the program.
  3. Buildroot Make metadata tells Buildroot where the source is, what it requires, and which package infrastructure manages its build.

When selected, Buildroot does the cross-compilation work. It stages the source into its build area, configures CMake with the correct cross compiler and sysroot, builds the executable, and installs it into the target filesystem. You should not call the host’s cmake, manually set CC, or copy the resulting executable into output/target. Doing so would bypass the dependency and toolchain controls that make the build repeatable.

The package mechanism also gives the application a visible product decision: it appears as a selectable feature in menuconfig.

A Buildroot `menuconfig` External options screen showing three selected packages supplied by a custom external tree. The gateway package created in this lesson will appear in an analogous product-specific menu.

For the automotive/industrial gateway prototype, put product-owned metadata in an external tree rather than editing Buildroot’s own package/ directory. That keeps upstream Buildroot updateable and makes the product integration repository independently reviewable.

// -- mode:doc; -- // vim: set syntax=asciidoc: === Package directory

Read the official Buildroot guidance on package placement, target-side Config.in files, and dependency semantics. It establishes why Kconfig visibility and Make-level build dependencies are separate responsibilities.

In the “Package directory” and “Config.in file” subsections, read from package placement through the Config.in example and its formatting rules. Then read “Choosing depends on or select”, beginning at dependency rules. Focus on the distinction: select enables a library option in Kconfig, while a package Makefile dependency controls build order.

A first version of gateway-agent has no third-party target library dependency, so it needs neither select nor a Makefile dependency. That is intentional: establish the basic pipeline before adding networking and CAN libraries.


CMake’s role in a cross build

CMake is a build-system generator, not the compiler. Your CMakeLists.txt declares targets, compile properties, and installation destinations. Buildroot’s cmake-package infrastructure supplies the target compiler, linker, sysroot, install prefix, and ordinary CMake options.

The important application-side requirement is the install() command. add_executable() makes an executable available to build; install() tells Buildroot where it belongs in the final target filesystem.

adding-packages-cmake.txt

This official CMake-package tutorial is the central reference for the package Makefile you will write. Read it before copying the implementation, because it explains which details Buildroot owns and which values belong in package metadata.

In “cmake-package tutorial”, first examine the complete libfoo.mk example. Then read the line-by-line explanation beginning at tutorial explanation. Continue into “cmake-package reference” from the infrastructure overview. Pay particular attention to INSTALL_STAGING, INSTALL_TARGET, DEPENDENCIES, and CONF_OPTS, along with the options Buildroot already supplies to CMake automatically.

For an executable application, the default target installation is normally correct. Unlike a reusable library, it does not need to install headers and development files into the staging sysroot. Therefore, do not set GATEWAY_AGENT_INSTALL_STAGING = YES.

Also avoid setting values Buildroot already controls:

  • Do not set CMAKE_C_COMPILER, CMAKE_SYSROOT, or CMAKE_INSTALL_PREFIX.
  • Do not invoke a CMake toolchain file designed for a desktop cross-build.
  • Do not hard-code an architecture such as aarch64 in the application’s CMake logic.
  • Do not add -march flags unless they are a reviewed product-level policy; Buildroot’s toolchain configuration owns the ABI and CPU tuning.

Create the product external tree

The following layout separates package metadata from application source. Use a location under your private implementation repository or a private workspace—not inside the upstream Buildroot checkout.

gateway-br2-external/
├── Config.in
├── external.desc
├── external.mk
├── package/
│   └── gateway-agent/
│       ├── Config.in
│       └── gateway-agent.mk
└── src/
    └── gateway-agent/
        ├── CMakeLists.txt
        ├── LICENSE
        └── src/
            └── main.c

Create the directories:

mkdir -p "$HOME/work/gateway-br2-external/package/gateway-agent"
mkdir -p "$HOME/work/gateway-br2-external/src/gateway-agent/src"

Identify the external tree

Create gateway-br2-external/external.desc:

name: GATEWAY
desc: Automotive and industrial gateway product integration

The name field determines the Buildroot variable prefix. Here it creates BR2_EXTERNAL_GATEWAY_PATH, which points to this external tree during a Buildroot invocation.

Create gateway-br2-external/Config.in:

menu "Gateway product packages"

source "$BR2_EXTERNAL_GATEWAY_PATH/package/gateway-agent/Config.in"

endmenu

Create gateway-br2-external/external.mk:

include $(sort $(wildcard $(BR2_EXTERNAL_GATEWAY_PATH)/package/*/*.mk))

These two files form the external-tree integration points:

  • Config.in makes product package options visible to Kconfig.
  • external.mk includes the package Makefiles that define how selected packages build.

The wildcard is appropriate for this controlled product tree. Its sorting makes the include order deterministic.


Write the small CMake application

Create gateway-br2-external/src/gateway-agent/CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)

project(gateway_agent VERSION 0.1.0 LANGUAGES C)

add_executable(gateway-agent
    src/main.c
)

target_compile_features(gateway-agent PRIVATE c_std_11)

target_compile_options(gateway-agent PRIVATE
    -Wall
    -Wextra
    -Wpedantic
)

install(TARGETS gateway-agent
    RUNTIME DESTINATION sbin
)

Create gateway-br2-external/src/gateway-agent/src/main.c:

#include <stdio.h>

int main(void)
{
    puts("gateway-agent: prototype application installed");
    return 0;
}

Create gateway-br2-external/src/gateway-agent/LICENSE:

Gateway Agent Prototype License

This source is proprietary to the product development project.
Redistribution is not permitted without written authorization.

This license is intentionally simple for a private prototype. The important Buildroot practice is that the package metadata identifies the license and points to a file containing it. That allows later license-information generation to discover it.

Notice two details in the CMake file:

  • gateway-agent is the installed executable name. The hyphen is convenient for an executable and package name.
  • RUNTIME DESTINATION sbin is relative to Buildroot’s install prefix. With Buildroot’s conventional prefix, the executable will normally appear at /usr/sbin/gateway-agent in the target filesystem.

At this stage, the program exits immediately. Do not add an init script merely to make it seem more “embedded.” A continuously running daemon needs explicit decisions about configuration, restart behavior, logging, privileges, and shutdown handling. We will make those decisions later when the gateway has real CAN-to-IP work to perform.


Make the package selectable

Create gateway-br2-external/package/gateway-agent/Config.in:

config BR2_PACKAGE_GATEWAY_AGENT
	bool "gateway-agent"
	help
	  Build the prototype gateway application.

	  This package installs the gateway-agent executable.

Use a literal tab before bool, help, and the help text indentation shown above. Kconfig is whitespace-sensitive in conventions that Buildroot checks and maintainers expect.

The Kconfig symbol is uppercase with hyphens converted to underscores:

ConceptValue
Directory and package namegateway-agent
Kconfig symbolBR2_PACKAGE_GATEWAY_AGENT
Makefile variable prefixGATEWAY_AGENT
Installed executablegateway-agent

This naming correspondence is worth keeping strict. A mismatched prefix is one of the most common reasons a new Buildroot package fails mysteriously.

For a later version that links to a Buildroot library, declare the relationship twice, for different reasons:

select BR2_PACKAGE_SOME_LIBRARY
GATEWAY_AGENT_DEPENDENCIES += some-library

The Kconfig statement ensures the user’s configuration selects the library. The Makefile statement ensures Buildroot builds and installs it before configuring gateway-agent. Neither replaces the other.


Define the CMake package metadata

Create gateway-br2-external/package/gateway-agent/gateway-agent.mk:

################################################################################
#
# gateway-agent
#
################################################################################

GATEWAY_AGENT_VERSION = 0.1.0
GATEWAY_AGENT_SITE = $(BR2_EXTERNAL_GATEWAY_PATH)/src/gateway-agent
GATEWAY_AGENT_SITE_METHOD = local

GATEWAY_AGENT_LICENSE = Proprietary
GATEWAY_AGENT_LICENSE_FILES = LICENSE

$(eval $(cmake-package))

Read this file as a concise declaration rather than a conventional procedural Makefile:

  • GATEWAY_AGENT_VERSION identifies this source version in Buildroot’s build directories and package metadata.
  • GATEWAY_AGENT_SITE locates the source tree.
  • GATEWAY_AGENT_SITE_METHOD = local tells Buildroot that the source is a local directory. Buildroot copies it into its controlled build area rather than compiling directly in your working tree.
  • GATEWAY_AGENT_LICENSE and GATEWAY_AGENT_LICENSE_FILES declare legal information.
  • $(eval $(cmake-package)) invokes Buildroot’s CMake infrastructure, which supplies the standard configure, build, and target-install steps.

There is deliberately no GATEWAY_AGENT_INSTALL_STAGING = YES. This is an application, not a development library consumed by another target package.

There is also no .hash file in this learning version. A live local source directory changes while you develop, so a fixed archive hash would be false assurance and would constantly need updating. For a release-oriented package, replace the mutable local source with an immutable versioned source archive or a pinned revision, then record strong source and license-file hashes. That transition is essential before treating a product build as reproducible release evidence.


Select, build, and verify the package

From the root of the Buildroot tree used in the preceding labs, export the external-tree path:

export BR2_EXTERNAL="$HOME/work/gateway-br2-external"

Open Buildroot configuration:

make BR2_EXTERNAL="$BR2_EXTERNAL" menuconfig

Navigate to:

External options
    Gateway product packages
        [*] gateway-agent

Save and exit, then build:

make BR2_EXTERNAL="$BR2_EXTERNAL"

Buildroot should perform these relevant actions:

  1. Read the external tree’s Kconfig and Make metadata.
  2. Copy the local source into its package build workspace.
  3. Run CMake using Buildroot’s cross-compilation environment.
  4. Compile gateway-agent with the configured target toolchain.
  5. Run the CMake install rule against Buildroot’s target filesystem tree.
  6. Include the executable in the generated filesystem image.

Verify the target-tree installation before booting:

test -x output/target/usr/sbin/gateway-agent && \
    echo "gateway-agent installed for target"

You can also inspect the installed artifact:

file output/target/usr/sbin/gateway-agent

For an ARM64 QEMU configuration, file should identify an ARM aarch64 ELF executable, not an x86-64 host executable. That is a valuable early cross-compilation check.

Boot the QEMU image using the launch method already established in your Buildroot lab, then run:

/usr/sbin/gateway-agent

Expected output:

gateway-agent: prototype application installed

The executable’s presence in output/target confirms the install phase; successfully running it in QEMU confirms that it is compatible with the target userspace and dynamic loader.


Rebuilding during active development

Buildroot does not continuously watch a local package source tree for every edit. After changing main.c, request a package rebuild:

make BR2_EXTERNAL="$BR2_EXTERNAL" gateway-agent-rebuild

If you modify CMakeLists.txt, add files, or need to eliminate stale CMake cache state, use the more conservative sequence:

make BR2_EXTERNAL="$BR2_EXTERNAL" gateway-agent-dirclean
make BR2_EXTERNAL="$BR2_EXTERNAL"

Use dirclean selectively. It discards this package’s build directory, not the entire Buildroot output tree. That is usually the right first response when a CMake configuration change does not seem to take effect.

A compact diagnostic guide:

SymptomLikely causeFirst check
gateway-agent is absent from menuconfigExternal Config.in or package Config.in is not sourcedConfirm both source paths and the BR2_EXTERNAL value
Package is selectable but does not buildThe package is not selected in the saved configurationReopen menuconfig, save, and inspect .config for BR2_PACKAGE_GATEWAY_AGENT=y
CMake builds but the binary is absent from output/targetMissing or incorrect install(TARGETS ...) commandCheck CMakeLists.txt; add_executable() alone is insufficient
Build uses host headers or produces an x86-64 executableBuild was invoked with host CMake outside BuildrootRebuild through make, never from the source directory
Source edits appear ignoredExisting package stamps or CMake cacheUse gateway-agent-rebuild, then gateway-agent-dirclean if needed
CMake cannot find a target libraryThe Buildroot dependency and CMake discovery setup are incompleteAdd the library dependency in package metadata and use its supported CMake or pkg-config discovery path

Keep application source and package metadata under version control together. A source change that requires a new dependency, package option, install destination, or license update should be reviewed as one coherent change—not as an undocumented modification to a build machine.


Key takeaways

A Buildroot package is the proper home for compiled product software. For the CMake-based gateway-agent, you created:

  • an external-tree Kconfig menu and Makefile include point;
  • a minimal CMake application with an explicit target installation rule;
  • a target-side Kconfig option, BR2_PACKAGE_GATEWAY_AGENT;
  • a gateway-agent.mk file using $(cmake-package);
  • a local-source development workflow that preserves Buildroot’s cross-toolchain control.

The critical boundary is simple: the application declares how it installs; Buildroot declares where, with which toolchain, and under what product configuration it is built.

Next, you will generate and boot both ext4 and SquashFS Buildroot images, then compare their mutability, size, and recovery implications for a gateway intended to survive field power loss.

Can't find a good explanation? Sign up and we'll make it for you

Sign up