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:
- Application source defines what the gateway program does and how CMake builds and installs it.
- Kconfig metadata defines whether a product configuration selects the program.
- 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.

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.
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, orCMAKE_INSTALL_PREFIX. - Do not invoke a CMake toolchain file designed for a desktop cross-build.
- Do not hard-code an architecture such as
aarch64in the application’s CMake logic. - Do not add
-marchflags 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.inmakes product package options visible to Kconfig.external.mkincludes 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-agentis the installed executable name. The hyphen is convenient for an executable and package name.RUNTIME DESTINATION sbinis relative to Buildroot’s install prefix. With Buildroot’s conventional prefix, the executable will normally appear at/usr/sbin/gateway-agentin 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:
| Concept | Value |
|---|---|
| Directory and package name | gateway-agent |
| Kconfig symbol | BR2_PACKAGE_GATEWAY_AGENT |
| Makefile variable prefix | GATEWAY_AGENT |
| Installed executable | gateway-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_VERSIONidentifies this source version in Buildroot’s build directories and package metadata.GATEWAY_AGENT_SITElocates the source tree.GATEWAY_AGENT_SITE_METHOD = localtells 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_LICENSEandGATEWAY_AGENT_LICENSE_FILESdeclare 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:
- Read the external tree’s Kconfig and Make metadata.
- Copy the local source into its package build workspace.
- Run CMake using Buildroot’s cross-compilation environment.
- Compile
gateway-agentwith the configured target toolchain. - Run the CMake install rule against Buildroot’s target filesystem tree.
- 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:
| Symptom | Likely cause | First check |
|---|---|---|
gateway-agent is absent from menuconfig | External Config.in or package Config.in is not sourced | Confirm both source paths and the BR2_EXTERNAL value |
| Package is selectable but does not build | The package is not selected in the saved configuration | Reopen menuconfig, save, and inspect .config for BR2_PACKAGE_GATEWAY_AGENT=y |
CMake builds but the binary is absent from output/target | Missing or incorrect install(TARGETS ...) command | Check CMakeLists.txt; add_executable() alone is insufficient |
| Build uses host headers or produces an x86-64 executable | Build was invoked with host CMake outside Buildroot | Rebuild through make, never from the source directory |
| Source edits appear ignored | Existing package stamps or CMake cache | Use gateway-agent-rebuild, then gateway-agent-dirclean if needed |
| CMake cannot find a target library | The Buildroot dependency and CMake discovery setup are incomplete | Add 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.mkfile 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