Chapter 9. Project-specific customization

Typical actions you may need to perform for a given project are:

An important note regarding such project-specific customizations: please carefully consider which changes are indeed project-specific and which changes are also useful to developers outside your project. The Buildroot community highly recommends and encourages the upstreaming of improvements, packages and board support to the official Buildroot project. Of course, it is sometimes not possible or desirable to upstream because the changes are highly specific or proprietary.

This chapter describes how to make such project-specific customizations in Buildroot and how to store them in a way that you can build the same image in a reproducible way, even after running make clean. By following the recommended strategy, you can even use the same Buildroot tree to build multiple distinct projects!

9.1. Recommended directory structure

When customizing Buildroot for your project, you will be creating one or more project-specific files that need to be stored somewhere. While most of these files could be placed in any location as their path is to be specified in the Buildroot configuration, the Buildroot developers recommend a specific directory structure which is described in this section.

Orthogonal to this directory structure, you can choose where you place this structure itself: either inside the Buildroot tree, or outside of it using a br2-external tree. Both options are valid, the choice is up to you.

+-- board/
|   +-- <company>/
|       +-- <boardname>/
|           +-- linux.config
|           +-- busybox.config
|           +-- <other configuration files>
|           +-- post_build.sh
|           +-- post_image.sh
|           +-- rootfs_overlay/
|           |   +-- etc/
|           |   +-- <some files>
|           +-- patches/
|               +-- foo/
|               |   +-- <some patches>
|               +-- libbar/
|                   +-- <some other patches>
|
+-- configs/
|   +-- <boardname>_defconfig
|
+-- package/
|   +-- <company>/
|       +-- Config.in (if not using a br2-external tree)
|       +-- <company>.mk (if not using a br2-external tree)
|       +-- package1/
|       |    +-- Config.in
|       |    +-- package1.mk
|       +-- package2/
|           +-- Config.in
|           +-- package2.mk
|
+-- Config.in (if using a br2-external tree)
+-- external.mk (if using a br2-external tree)
+-- external.desc (if using a br2-external tree)

Details on the files shown above are given further in this chapter.

Note: if you choose to place this structure outside of the Buildroot tree but in a br2-external tree, the <company> and possibly <boardname> components may be superfluous and can be left out.

9.1.1. Implementing layered customizations

It is quite common for a user to have several related projects that partly need the same customizations. Instead of duplicating these customizations for each project, it is recommended to use a layered customization approach, as explained in this section.

Almost all of the customization methods available in Buildroot, like post-build scripts and root filesystem overlays, accept a space-separated list of items. The specified items are always treated in order, from left to right. By creating more than one such item, one for the common customizations and another one for the really project-specific customizations, you can avoid unnecessary duplication. Each layer is typically embodied by a separate directory inside board/<company>/. Depending on your projects, you could even introduce more than two layers.

An example directory structure for where a user has two customization layers common and fooboard is:

+-- board/
    +-- <company>/
        +-- common/
        |   +-- post_build.sh
        |   +-- rootfs_overlay/
        |   |   +-- ...
        |   +-- patches/
        |       +-- ...
        |
        +-- fooboard/
            +-- linux.config
            +-- busybox.config
            +-- <other configuration files>
            +-- post_build.sh
            +-- rootfs_overlay/
            |   +-- ...
            +-- patches/
                +-- ...

For example, if the user has the BR2_GLOBAL_PATCH_DIR configuration option set as:

BR2_GLOBAL_PATCH_DIR="board/<company>/common/patches board/<company>/fooboard/patches"

then first the patches from the common layer would be applied, followed by the patches from the fooboard layer.

9.2. Keeping customizations outside of Buildroot

As already briefly mentioned in Section 9.1, “Recommended directory structure”, you can place project-specific customizations in two locations:

  • directly within the Buildroot tree, typically maintaining them using branches in a version control system so that upgrading to a newer Buildroot release is easy.
  • outside of the Buildroot tree, using the br2-external mechanism. This mechanism allows to keep package recipes, board support and configuration files outside of the Buildroot tree, while still having them nicely integrated in the build logic. We call this location a br2-external tree. This section explains how to use the br2-external mechanism and what to provide in a br2-external tree.

One can tell Buildroot to use one or more br2-external trees by setting the BR2_EXTERNAL make variable set to the path(s) of the br2-external tree(s) to use. It can be passed to any Buildroot make invocation. It is automatically saved in the hidden .br2-external.mk file in the output directory. Thanks to this, there is no need to pass BR2_EXTERNAL at every make invocation. It can however be changed at any time by passing a new value, and can be removed by passing an empty value.

Note. The path to a br2-external tree can be either absolute or relative. If it is passed as a relative path, it is important to note that it is interpreted relative to the main Buildroot source directory, not to the Buildroot output directory.

Note: If using an br2-external tree from before Buildroot 2016.11, you need to convert it before you can use it with Buildroot 2016.11 onward. See Section 27.2, “Migrating to 2016.11” for help on doing so.

Some examples:

buildroot/ $ make BR2_EXTERNAL=/path/to/foo menuconfig

From now on, definitions from the /path/to/foo br2-external tree will be used:

buildroot/ $ make
buildroot/ $ make legal-info

We can switch to another br2-external tree at any time:

buildroot/ $ make BR2_EXTERNAL=/where/we/have/bar xconfig

We can also use multiple br2-external trees:

buildroot/ $ make BR2_EXTERNAL=/path/to/foo:/where/we/have/bar menuconfig

Or disable the usage of any br2-external tree:

buildroot/ $ make BR2_EXTERNAL= xconfig

9.2.1. Layout of a br2-external tree

A br2-external tree must contain at least those three files, described in the following chapters:

  • external.desc
  • external.mk
  • Config.in

Apart from those mandatory files, there may be additional and optional content that may be present in a br2-external tree, like the configs/ or provides/ directories. They are described in the following chapters as well.

A complete example br2-external tree layout is also described later.

The external.desc file

That file describes the br2-external tree: the name and description for that br2-external tree.

The format for this file is line based, with each line starting by a keyword, followed by a colon and one or more spaces, followed by the value assigned to that keyword. There are two keywords currently recognised:

  • name, mandatory, defines the name for that br2-external tree. That name must only use ASCII characters in the set [A-Za-z0-9_]; any other character is forbidden. Buildroot sets the variable BR2_EXTERNAL_$(NAME)_PATH to the absolute path of the br2-external tree, so that you can use it to refer to your br2-external tree. This variable is available both in Kconfig, so you can use it to source your Kconfig files (see below) and in the Makefile, so that you can use it to include other Makefiles (see below) or refer to other files (like data files) from your br2-external tree.

    Note: Since it is possible to use multiple br2-external trees at once, this name is used by Buildroot to generate variables for each of those trees. That name is used to identify your br2-external tree, so try to come up with a name that really describes your br2-external tree, in order for it to be relatively unique, so that it does not clash with another name from another br2-external tree, especially if you are planning on somehow sharing your br2-external tree with third parties or using br2-external trees from third parties.

  • desc, optional, provides a short description for that br2-external tree. It shall fit on a single line, is mostly free-form (see below), and is used when displaying information about a br2-external tree (e.g. above the list of defconfig files, or as the prompt in the menuconfig); as such, it should relatively brief (40 chars is probably a good upper limit). The description is available in the BR2_EXTERNAL_$(NAME)_DESC variable.

Examples of names and the corresponding BR2_EXTERNAL_$(NAME)_PATH variables:

  • FOOBR2_EXTERNAL_FOO_PATH
  • BAR_42BR2_EXTERNAL_BAR_42_PATH

In the following examples, it is assumed the name to be set to BAR_42.

Note: Both BR2_EXTERNAL_$(NAME)_PATH and BR2_EXTERNAL_$(NAME)_DESC are available in the Kconfig files and the Makefiles. They are also exported in the environment so are available in post-build, post-image and in-fakeroot scripts.

The Config.in and external.mk files

Those files (which may each be empty) can be used to define package recipes (i.e. foo/Config.in and foo/foo.mk like for packages bundled in Buildroot itself) or other custom configuration options or make logic.

Buildroot automatically includes the Config.in from each br2-external tree to make it appear in the top-level configuration menu, and includes the external.mk from each br2-external tree with the rest of the makefile logic.

The main usage of this is to store package recipes. The recommended way to do this is to write a Config.in file that looks like:

source "$BR2_EXTERNAL_BAR_42_PATH/package/package1/Config.in"
source "$BR2_EXTERNAL_BAR_42_PATH/package/package2/Config.in"

Then, have an external.mk file that looks like:

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

And then in $(BR2_EXTERNAL_BAR_42_PATH)/package/package1 and $(BR2_EXTERNAL_BAR_42_PATH)/package/package2 create normal Buildroot package recipes, as explained in Chapter 18, Adding new packages to Buildroot. If you prefer, you can also group the packages in subdirectories called <boardname> and adapt the above paths accordingly.

You can also define custom configuration options in Config.in and custom make logic in external.mk.

The configs/ directory

One can store Buildroot defconfigs in the configs subdirectory of the br2-external tree. Buildroot will automatically show them in the output of make list-defconfigs and allow them to be loaded with the normal make <name>_defconfig command. They will be visible in the make list-defconfigs output, below an External configs label that contains the name of the br2-external tree they are defined in.

Note: If a defconfig file is present in more than one br2-external tree, then the one from the last br2-external tree is used. It is thus possible to override a defconfig bundled in Buildroot or another br2-external tree.

The provides/ directory

For some packages, Buildroot provides a choice between two (or more) implementations of API-compatible such packages. For example, there is a choice to choose either libjpeg or jpeg-turbo; there is one between openssl or libressl; there is one to select one of the known, pre-configured toolchains…

It is possible for a br2-external to extend those choices, by providing a set of files that define those alternatives:

  • provides/toolchains.in defines the pre-configured toolchains, which will then be listed in the toolchain selection;
  • provides/jpeg.in defines the alternative libjpeg implementations;
  • provides/openssl.in defines the alternative openssl implementations;
  • provides/skeleton.in defines the alternative skeleton implementations;
  • provides/init.in defines the alternative init system implementations, this can be used to select a default skeleton for your init.

Free-form content

One can store all the board-specific configuration files there, such as the kernel configuration, the root filesystem overlay, or any other configuration file for which Buildroot allows to set the location (by using the BR2_EXTERNAL_$(NAME)_PATH variable). For example, you could set the paths to a global patch directory, to a rootfs overlay and to the kernel configuration file as follows (e.g. by running make menuconfig and filling in these options):

BR2_GLOBAL_PATCH_DIR=$(BR2_EXTERNAL_BAR_42_PATH)/patches/
BR2_ROOTFS_OVERLAY=$(BR2_EXTERNAL_BAR_42_PATH)/board/<boardname>/overlay/
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE=$(BR2_EXTERNAL_BAR_42_PATH)/board/<boardname>/kernel.config

Additional Linux kernel extensions

Additional Linux kernel extensions (see Section 18.22.2, “linux-kernel-extensions”) can be added by storing them in the linux/ directory at the root of a br2-external tree.

Example layout

Here is an example layout using all features of br2-external (the sample content is shown for the file above it, when it is relevant to explain the br2-external tree; this is all entirely made up just for the sake of illustration, of course):

/path/to/br2-ext-tree/
  |- external.desc
  |     |name: BAR_42
  |     |desc: Example br2-external tree
  |     `----
  |
  |- Config.in
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/toolchain/toolchain-external-mine/Config.in.options"
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/package/pkg-1/Config.in"
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/package/pkg-2/Config.in"
  |     |source "$BR2_EXTERNAL_BAR_42_PATH/package/my-jpeg/Config.in"
  |     |
  |     |config BAR_42_FLASH_ADDR
  |     |    hex "my-board flash address"
  |     |    default 0x10AD
  |     `----
  |
  |- external.mk
  |     |include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/package/*/*.mk))
  |     |include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/toolchain/*/*.mk))
  |     |
  |     |flash-my-board:
  |     |    $(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/flash-image \
  |     |        --image $(BINARIES_DIR)/image.bin \
  |     |        --address $(BAR_42_FLASH_ADDR)
  |     `----
  |
  |- package/pkg-1/Config.in
  |     |config BR2_PACKAGE_PKG_1
  |     |    bool "pkg-1"
  |     |    help
  |     |      Some help about pkg-1
  |     `----
  |- package/pkg-1/pkg-1.hash
  |- package/pkg-1/pkg-1.mk
  |     |PKG_1_VERSION = 1.2.3
  |     |PKG_1_SITE = /some/where/to/get/pkg-1
  |     |PKG_1_LICENSE = blabla
  |     |
  |     |define PKG_1_INSTALL_INIT_SYSV
  |     |    $(INSTALL) -D -m 0755 $(PKG_1_PKGDIR)/S99my-daemon \
  |     |                          $(TARGET_DIR)/etc/init.d/S99my-daemon
  |     |endef
  |     |
  |     |$(eval $(autotools-package))
  |     `----
  |- package/pkg-1/S99my-daemon
  |
  |- package/pkg-2/Config.in
  |- package/pkg-2/pkg-2.hash
  |- package/pkg-2/pkg-2.mk
  |
  |- provides/jpeg.in
  |     |config BR2_PACKAGE_MY_JPEG
  |     |    bool "my-jpeg"
  |     `----
  |- package/my-jpeg/Config.in
  |     |config BR2_PACKAGE_PROVIDES_JPEG
  |     |    default "my-jpeg" if BR2_PACKAGE_MY_JPEG
  |     `----
  |- package/my-jpeg/my-jpeg.mk
  |     |# This is a normal package .mk file
  |     |MY_JPEG_VERSION = 1.2.3
  |     |MY_JPEG_SITE = https://example.net/some/place
  |     |MY_JPEG_PROVIDES = jpeg
  |     |$(eval $(autotools-package))
  |     `----
  |
  |- provides/init.in
  |     |config BR2_INIT_MINE
  |     |    bool "my custom init"
  |     |    select BR2_PACKAGE_MY_INIT
  |     |    select BR2_PACKAGE_SKELETON_INIT_MINE if BR2_ROOTFS_SKELETON_DEFAULT
  |     `----
  |
  |- provides/skeleton.in
  |     |config BR2_ROOTFS_SKELETON_MINE
  |     |    bool "my custom skeleton"
  |     |    select BR2_PACKAGE_SKELETON_MINE
  |     `----
  |- package/skeleton-mine/Config.in
  |     |config BR2_PACKAGE_SKELETON_MINE
  |     |    bool
  |     |    select BR2_PACKAGE_HAS_SKELETON
  |     |
  |     |config BR2_PACKAGE_PROVIDES_SKELETON
  |     |    default "skeleton-mine" if BR2_PACKAGE_SKELETON_MINE
  |     `----
  |- package/skeleton-mine/skeleton-mine.mk
  |     |SKELETON_MINE_ADD_TOOLCHAIN_DEPENDENCY = NO
  |     |SKELETON_MINE_ADD_SKELETON_DEPENDENCY = NO
  |     |SKELETON_MINE_PROVIDES = skeleton
  |     |SKELETON_MINE_INSTALL_STAGING = YES
  |     |$(eval $(generic-package))
  |     `----
  |
  |- provides/toolchains.in
  |     |config BR2_TOOLCHAIN_EXTERNAL_MINE
  |     |    bool "my custom toolchain"
  |     |    depends on BR2_some_arch
  |     |    select BR2_INSTALL_LIBSTDCPP
  |     `----
  |- toolchain/toolchain-external-mine/Config.in.options
  |     |if BR2_TOOLCHAIN_EXTERNAL_MINE
  |     |config BR2_TOOLCHAIN_EXTERNAL_PREFIX
  |     |    default "arch-mine-linux-gnu"
  |     |config BR2_PACKAGE_PROVIDES_TOOLCHAIN_EXTERNAL
  |     |    default "toolchain-external-mine"
  |     |endif
  |     `----
  |- toolchain/toolchain-external-mine/toolchain-external-mine.mk
  |     |TOOLCHAIN_EXTERNAL_MINE_SITE = https://example.net/some/place
  |     |TOOLCHAIN_EXTERNAL_MINE_SOURCE = my-toolchain.tar.gz
  |     |$(eval $(toolchain-external-package))
  |     `----
  |
  |- linux/Config.ext.in
  |     |config BR2_LINUX_KERNEL_EXT_EXAMPLE_DRIVER
  |     |    bool "example-external-driver"
  |     |    help
  |     |      Example external driver
  |     |---
  |- linux/linux-ext-example-driver.mk
  |
  |- configs/my-board_defconfig
  |     |BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL_BAR_42_PATH)/patches/"
  |     |BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/overlay/"
  |     |BR2_ROOTFS_POST_IMAGE_SCRIPT="$(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/post-image.sh"
  |     |BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_BAR_42_PATH)/board/my-board/kernel.config"
  |     `----
  |
  |- patches/linux/0001-some-change.patch
  |- patches/linux/0002-some-other-change.patch
  |- patches/busybox/0001-fix-something.patch
  |
  |- board/my-board/kernel.config
  |- board/my-board/overlay/var/www/index.html
  |- board/my-board/overlay/var/www/my.css
  |- board/my-board/flash-image
  `- board/my-board/post-image.sh
        |#!/bin/sh
        |generate-my-binary-image \
        |    --root ${BINARIES_DIR}/rootfs.tar \
        |    --kernel ${BINARIES_DIR}/zImage \
        |    --dtb ${BINARIES_DIR}/my-board.dtb \
        |    --output ${BINARIES_DIR}/image.bin
        `----

The br2-external tree will then be visible in the menuconfig (with the layout expanded):

External options  --->
    *** Example br2-external tree (in /path/to/br2-ext-tree/)
    [ ] pkg-1
    [ ] pkg-2
    (0x10AD) my-board flash address

If you are using more than one br2-external tree, it would look like (with the layout expanded and the second one with name FOO_27 but no desc: field in external.desc):

External options  --->
    Example br2-external tree  --->
        *** Example br2-external tree (in /path/to/br2-ext-tree)
        [ ] pkg-1
        [ ] pkg-2
        (0x10AD) my-board flash address
    FOO_27  --->
        *** FOO_27 (in /path/to/another-br2-ext)
        [ ] foo
        [ ] bar

Additionally, the jpeg provider will be visible in the jpeg choice:

Target packages  --->
    Libraries  --->
        Graphics  --->
            [*] jpeg support
                jpeg variant ()  --->
                    ( ) jpeg
                    ( ) jpeg-turbo
                        *** jpeg from: Example br2-external tree ***
                    (X) my-jpeg
                        *** jpeg from: FOO_27 ***
                    ( ) another-jpeg

And similarly for the toolchains:

Toolchain  --->
    Toolchain ()  --->
        ( ) Custom toolchain
            *** Toolchains from: Example br2-external tree ***
        (X) my custom toolchain

Note. The toolchain options in toolchain/toolchain-external-mine/Config.in.options will not appear in the Toolchain menu. They must be explicitly included from within the br2-external’s top-level Config.in and will thus appear in the External options menu.

9.3. Storing the Buildroot configuration

The Buildroot configuration can be stored using the command make savedefconfig.

This strips the Buildroot configuration down by removing configuration options that are at their default value. The result is stored in a file called defconfig. If you want to save it in another place, change the BR2_DEFCONFIG option in the Buildroot configuration itself, or call make with make savedefconfig BR2_DEFCONFIG=<path-to-defconfig>.

The recommended place to store this defconfig is configs/<boardname>_defconfig. If you follow this recommendation, the configuration will be listed in make list-defconfigs and can be set again by running make <boardname>_defconfig.

Alternatively, you can copy the file to any other place and rebuild with make defconfig BR2_DEFCONFIG=<path-to-defconfig-file>.

9.4. Storing the configuration of other components

The configuration files for BusyBox, the Linux kernel, Barebox, U-Boot and uClibc should be stored as well if changed. For each of these components, a Buildroot configuration option exists to point to an input configuration file, e.g. BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE. To store their configuration, set these configuration options to a path where you want to save the configuration files, and then use the helper targets described below to actually store the configuration.

As explained in Section 9.1, “Recommended directory structure”, the recommended path to store these configuration files is board/<company>/<boardname>/foo.config.

Make sure that you create a configuration file before changing the BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE etc. options. Otherwise, Buildroot will try to access this config file, which doesn’t exist yet, and will fail. You can create the configuration file by running make linux-menuconfig etc.

Buildroot provides a few helper targets to make the saving of configuration files easier.

  • make linux-update-defconfig saves the linux configuration to the path specified by BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE. It simplifies the config file by removing default values. However, this only works with kernels starting from 2.6.33. For earlier kernels, use make linux-update-config.
  • make busybox-update-config saves the busybox configuration to the path specified by BR2_PACKAGE_BUSYBOX_CONFIG.
  • make uclibc-update-config saves the uClibc configuration to the path specified by BR2_UCLIBC_CONFIG.
  • make barebox-update-defconfig saves the barebox configuration to the path specified by BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE.
  • make uboot-update-defconfig saves the U-Boot configuration to the path specified by BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE.
  • For at91bootstrap3, no helper exists so you have to copy the config file manually to BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_CONFIG_FILE.

9.5. Customizing the generated target filesystem

Besides changing the configuration through make *config, there are a few other ways to customize the resulting target filesystem.

The two recommended methods, which can co-exist, are root filesystem overlay(s) and post build script(s).

Root filesystem overlays (BR2_ROOTFS_OVERLAY)

A filesystem overlay is a tree of files that is copied directly over the target filesystem after it has been built. To enable this feature, set config option BR2_ROOTFS_OVERLAY (in the System configuration menu) to the root of the overlay. You can even specify multiple overlays, space-separated. If you specify a relative path, it will be relative to the root of the Buildroot tree. Hidden directories of version control systems, like .git, .svn, .hg, etc., files called .empty and files ending in ~ are excluded from the copy.

When BR2_ROOTFS_MERGED_USR is enabled, then the overlay must not contain the /bin, /lib or /sbin directories, as Buildroot will create them as symbolic links to the relevant folders in /usr. In such a situation, should the overlay have any programs or libraries, they should be placed in /usr/bin, /usr/sbin and /usr/lib.

As shown in Section 9.1, “Recommended directory structure”, the recommended path for this overlay is board/<company>/<boardname>/rootfs-overlay.

Post-build scripts (BR2_ROOTFS_POST_BUILD_SCRIPT)

Post-build scripts are shell scripts called after Buildroot builds all the selected software, but before the rootfs images are assembled. To enable this feature, specify a space-separated list of post-build scripts in config option BR2_ROOTFS_POST_BUILD_SCRIPT (in the System configuration menu). If you specify a relative path, it will be relative to the root of the Buildroot tree.

Using post-build scripts, you can remove or modify any file in your target filesystem. You should, however, use this feature with care. Whenever you find that a certain package generates wrong or unneeded files, you should fix that package rather than work around it with some post-build cleanup scripts.

As shown in Section 9.1, “Recommended directory structure”, the recommended path for this script is board/<company>/<boardname>/post_build.sh.

The post-build scripts are run with the main Buildroot tree as current working directory. The path to the target filesystem is passed as the first argument to each script. If the config option BR2_ROOTFS_POST_SCRIPT_ARGS is not empty, these arguments will be passed to the script too. All the scripts will be passed the exact same set of arguments, it is not possible to pass different sets of arguments to each script.

Note that the arguments from +BR2_ROOTFS_POST_SCRIPT_ARGS+ will also be
passed to post-image and post-fakeroot scripts. If you want to use
arguments that are only used for the post-build scripts you can use
+BR2_ROOTFS_POST_BUILD_SCRIPT_ARGS+.

+ In addition, you may also use these environment variables:

  • BR2_CONFIG: the path to the Buildroot .config file
  • CONFIG_DIR: the directory containing the .config file, and therefore the top-level Buildroot Makefile to use (which is correct for both in-tree and out-of-tree builds)
  • HOST_DIR, STAGING_DIR, TARGET_DIR: see Section 18.6.2, “generic-package reference”
  • BUILD_DIR: the directory where packages are extracted and built
  • BINARIES_DIR: the place where all binary files (aka images) are stored
  • BASE_DIR: the base output directory
  • PARALLEL_JOBS: the number of jobs to use when running parallel processes.

Below three more methods of customizing the target filesystem are described, but they are not recommended.

Direct modification of the target filesystem

For temporary modifications, you can modify the target filesystem directly and rebuild the image. The target filesystem is available under output/target/. After making your changes, run make to rebuild the target filesystem image.

This method allows you to do anything to the target filesystem, but if you need to clean your Buildroot tree using make clean, these changes will be lost. Such cleaning is necessary in several cases, refer to Section 8.2, “Understanding when a full rebuild is necessary” for details. This solution is therefore only useful for quick tests: changes do not survive the make clean command. Once you have validated your changes, you should make sure that they will persist after a make clean, using a root filesystem overlay or a post-build script.

Custom target skeleton (BR2_ROOTFS_SKELETON_CUSTOM)

The root filesystem image is created from a target skeleton, on top of which all packages install their files. The skeleton is copied to the target directory output/target before any package is built and installed. The default target skeleton provides the standard Unix filesystem layout and some basic init scripts and configuration files.

If the default skeleton (available under system/skeleton) does not match your needs, you would typically use a root filesystem overlay or post-build script to adapt it. However, if the default skeleton is entirely different than what you need, using a custom skeleton may be more suitable.

To enable this feature, enable config option BR2_ROOTFS_SKELETON_CUSTOM and set BR2_ROOTFS_SKELETON_CUSTOM_PATH to the path of your custom skeleton. Both options are available in the System configuration menu. If you specify a relative path, it will be relative to the root of the Buildroot tree.

Custom skeletons don’t need to contain the /bin, /lib or /sbin directories, since they are created automatically during the build. When BR2_ROOTFS_MERGED_USR is enabled, then the custom skeleton must not contain the /bin, /lib or /sbin directories, as Buildroot will create them as symbolic links to the relevant folders in /usr. In such a situation, should the skeleton have any programs or libraries, they should be placed in /usr/bin, /usr/sbin and /usr/lib.

This method is not recommended because it duplicates the entire skeleton, which prevents taking advantage of the fixes or improvements brought to the default skeleton in later Buildroot releases.

Post-fakeroot scripts (BR2_ROOTFS_POST_FAKEROOT_SCRIPT)

When aggregating the final images, some parts of the process requires root rights: creating device nodes in /dev, setting permissions or ownership to files and directories… To avoid requiring actual root rights, Buildroot uses fakeroot to simulate root rights. This is not a complete substitute for actually being root, but is enough for what Buildroot needs.

Post-fakeroot scripts are shell scripts that are called at the end of the fakeroot phase, right before the filesystem image generator is called. As such, they are called in the fakeroot context.

Post-fakeroot scripts can be useful in case you need to tweak the filesystem to do modifications that are usually only available to the root user.

Note: It is recommended to use the existing mechanisms to set file permissions or create entries in /dev (see Section 9.5.1, “Setting file permissions and ownership and adding custom devices nodes”) or to create users (see Section 9.6, “Adding custom user accounts”)

Note: The difference between post-build scripts (above) and fakeroot scripts, is that post-build scripts are not called in the fakeroot context.

Note: Using fakeroot is not an absolute substitute for actually being root. fakeroot only ever fakes the file access rights and types (regular, block-or-char device…) and uid/gid; these are emulated in-memory.

9.5.1. Setting file permissions and ownership and adding custom devices nodes

Sometimes it is needed to set specific permissions or ownership on files or device nodes. For example, certain files may need to be owned by root. Since the post-build scripts are not run as root, you cannot do such changes from there unless you use an explicit fakeroot from the post-build script.

Instead, Buildroot provides support for so-called permission tables. To use this feature, set config option BR2_ROOTFS_DEVICE_TABLE to a space-separated list of permission tables, regular text files following the makedev syntax.

If you are using a static device table (i.e. not using devtmpfs, mdev, or (e)udev) then you can add device nodes using the same syntax, in so-called device tables. To use this feature, set config option BR2_ROOTFS_STATIC_DEVICE_TABLE to a space-separated list of device tables.

As shown in Section 9.1, “Recommended directory structure”, the recommended location for such files is board/<company>/<boardname>/.

It should be noted that if the specific permissions or device nodes are related to a specific application, you should set variables FOO_PERMISSIONS and FOO_DEVICES in the package’s .mk file instead (see Section 18.6.2, “generic-package reference”).

9.6. Adding custom user accounts

Sometimes it is needed to add specific users in the target system. To cover this requirement, Buildroot provides support for so-called users tables. To use this feature, set config option BR2_ROOTFS_USERS_TABLES to a space-separated list of users tables, regular text files following the makeusers syntax.

As shown in Section 9.1, “Recommended directory structure”, the recommended location for such files is board/<company>/<boardname>/.

It should be noted that if the custom users are related to a specific application, you should set variable FOO_USERS in the package’s .mk file instead (see Section 18.6.2, “generic-package reference”).

9.7. Customization after the images have been created

While post-build scripts (Section 9.5, “Customizing the generated target filesystem”) are run before building the filesystem image, kernel and bootloader, post-image scripts can be used to perform some specific actions after all images have been created.

Post-image scripts can for example be used to automatically extract your root filesystem tarball in a location exported by your NFS server, or to create a special firmware image that bundles your root filesystem and kernel image, or any other custom action required for your project.

To enable this feature, specify a space-separated list of post-image scripts in config option BR2_ROOTFS_POST_IMAGE_SCRIPT (in the System configuration menu). If you specify a relative path, it will be relative to the root of the Buildroot tree.

Just like post-build scripts, post-image scripts are run with the main Buildroot tree as current working directory. The path to the images output directory is passed as the first argument to each script. If the config option BR2_ROOTFS_POST_SCRIPT_ARGS is not empty, these arguments will be passed to the script too. All the scripts will be passed the exact same set of arguments, it is not possible to pass different sets of arguments to each script.

Note that the arguments from BR2_ROOTFS_POST_SCRIPT_ARGS will also be passed to post-build and post-fakeroot scripts. If you want to use arguments that are only used for the post-image scripts you can use BR2_ROOTFS_POST_IMAGE_SCRIPT_ARGS.

Again just like for the post-build scripts, the scripts have access to the environment variables BR2_CONFIG, HOST_DIR, STAGING_DIR, TARGET_DIR, BUILD_DIR, BINARIES_DIR, CONFIG_DIR, BASE_DIR, and PARALLEL_JOBS.

The post-image scripts will be executed as the user that executes Buildroot, which should normally not be the root user. Therefore, any action requiring root permissions in one of these scripts will require special handling (usage of fakeroot or sudo), which is left to the script developer.

9.8. Adding project-specific patches and hashes

9.8.1. Providing extra patches

It is sometimes useful to apply extra patches to packages - on top of those provided in Buildroot. This might be used to support custom features in a project, for example, or when working on a new architecture.

The BR2_GLOBAL_PATCH_DIR configuration option can be used to specify a space separated list of one or more directories containing package patches.

For a specific version <packageversion> of a specific package <packagename>, patches are applied from BR2_GLOBAL_PATCH_DIR as follows:

  1. For every directory - <global-patch-dir> - that exists in BR2_GLOBAL_PATCH_DIR, a <package-patch-dir> will be determined as follows:

    • <global-patch-dir>/<packagename>/<packageversion>/ if the directory exists.
    • Otherwise, <global-patch-dir>/<packagename> if the directory exists.
  2. Patches will then be applied from a <package-patch-dir> as follows:

    • If a series file exists in the package directory, then patches are applied according to the series file;
    • Otherwise, patch files matching *.patch are applied in alphabetical order. So, to ensure they are applied in the right order, it is highly recommended to name the patch files like this: <number>-<description>.patch, where <number> refers to the apply order.

For information about how patches are applied for a package, see Section 19.2, “How patches are applied”

The BR2_GLOBAL_PATCH_DIR option is the preferred method for specifying a custom patch directory for packages. It can be used to specify a patch directory for any package in buildroot. It should also be used in place of the custom patch directory options that are available for packages such as U-Boot and Barebox. By doing this, it will allow a user to manage their patches from one top-level directory.

The exception to BR2_GLOBAL_PATCH_DIR being the preferred method for specifying custom patches is BR2_LINUX_KERNEL_PATCH. BR2_LINUX_KERNEL_PATCH should be used to specify kernel patches that are available at a URL. Note: BR2_LINUX_KERNEL_PATCH specifies kernel patches that are applied after patches available in BR2_GLOBAL_PATCH_DIR, as it is done from a post-patch hook of the Linux package.

9.8.2. Providing extra hashes

Buildroot bundles a list of hashes against which it checks the integrity of the downloaded archives, or of those it generates locally from VCS checkouts. However, it can only do so for the known versions; for packages where it is possible to specify a custom version (e.g. a custom version string, a remote tarball URL, or a VCS repository location and changeset), Buildroot can’t carry hashes for those.

For users concerned with the integrity of such downloads, it is possible to provide a list of hashes that Buildroot can use to check arbitrary downloaded files. Those extra hashes are looked up similarly to the extra patches (above); for each directory in BR2_GLOBAL_PATCH_DIR, the first file to exist is used to check a package download:

  • <global-patch-dir>/<packagename>/<packageversion>/<packagename>.hash
  • <global-patch-dir>/<packagename>/<packagename>.hash

The utils/add-custom-hashes script can be used to generate these files.

9.9. Adding project-specific packages

In general, any new package should be added directly in the package directory and submitted to the Buildroot upstream project. How to add packages to Buildroot in general is explained in full detail in Chapter 18, Adding new packages to Buildroot and will not be repeated here. However, your project may need some proprietary packages that cannot be upstreamed. This section will explain how you can keep such project-specific packages in a project-specific directory.

As shown in Section 9.1, “Recommended directory structure”, the recommended location for project-specific packages is package/<company>/. If you are using the br2-external tree feature (see Section 9.2, “Keeping customizations outside of Buildroot”) the recommended location is to put them in a sub-directory named package/ in your br2-external tree.

However, Buildroot will not be aware of the packages in this location, unless we perform some additional steps. As explained in Chapter 18, Adding new packages to Buildroot, a package in Buildroot basically consists of two files: a .mk file (describing how to build the package) and a Config.in file (describing the configuration options for this package).

Buildroot will automatically include the .mk files in first-level subdirectories of the package directory (using the pattern package/*/*.mk). If we want Buildroot to include .mk files from deeper subdirectories (like package/<company>/package1/) then we simply have to add a .mk file in a first-level subdirectory that includes these additional .mk files. Therefore, create a file package/<company>/<company>.mk with following contents (assuming you have only one extra directory level below package/<company>/):

include $(sort $(wildcard package/<company>/*/*.mk))

For the Config.in files, create a file package/<company>/Config.in that includes the Config.in files of all your packages. An exhaustive list has to be provided since wildcards are not supported in the source command of kconfig. For example:

source "package/<company>/package1/Config.in"
source "package/<company>/package2/Config.in"

Include this new file package/<company>/Config.in from package/Config.in, preferably in a company-specific menu to make merges with future Buildroot versions easier.

If using a br2-external tree, refer to Section 9.2, “Keeping customizations outside of Buildroot” for how to fill in those files.

9.10. Quick guide to storing your project-specific customizations

Earlier in this chapter, the different methods for making project-specific customizations have been described. This section will now summarize all this by providing step-by-step instructions to storing your project-specific customizations. Clearly, the steps that are not relevant to your project can be skipped.

  1. make menuconfig to configure toolchain, packages and kernel.
  2. make linux-menuconfig to update the kernel config, similar for other configuration like busybox, uclibc, …
  3. mkdir -p board/<manufacturer>/<boardname>
  4. Set the following options to board/<manufacturer>/<boardname>/<package>.config (as far as they are relevant):

    • BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE
    • BR2_PACKAGE_BUSYBOX_CONFIG
    • BR2_UCLIBC_CONFIG
    • BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_CONFIG_FILE
    • BR2_TARGET_BAREBOX_CUSTOM_CONFIG_FILE
    • BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE
  5. Write the configuration files:

    • make linux-update-defconfig
    • make busybox-update-config
    • make uclibc-update-config
    • cp <output>/build/at91bootstrap3-*/.config board/<manufacturer>/<boardname>/at91bootstrap3.config
    • make barebox-update-defconfig
    • make uboot-update-defconfig
  6. Create board/<manufacturer>/<boardname>/rootfs-overlay/ and fill it with additional files you need on your rootfs, e.g. board/<manufacturer>/<boardname>/rootfs-overlay/etc/inittab. Set BR2_ROOTFS_OVERLAY to board/<manufacturer>/<boardname>/rootfs-overlay.
  7. Create a post-build script board/<manufacturer>/<boardname>/post_build.sh. Set BR2_ROOTFS_POST_BUILD_SCRIPT to board/<manufacturer>/<boardname>/post_build.sh
  8. If additional setuid permissions have to be set or device nodes have to be created, create board/<manufacturer>/<boardname>/device_table.txt and add that path to BR2_ROOTFS_DEVICE_TABLE.
  9. If additional user accounts have to be created, create board/<manufacturer>/<boardname>/users_table.txt and add that path to BR2_ROOTFS_USERS_TABLES.
  10. To add custom patches to certain packages, set BR2_GLOBAL_PATCH_DIR to board/<manufacturer>/<boardname>/patches/ and add your patches for each package in a subdirectory named after the package. Each patch should be called <packagename>-<num>-<description>.patch.
  11. Specifically for the Linux kernel, there also exists the option BR2_LINUX_KERNEL_PATCH with as main advantage that it can also download patches from a URL. If you do not need this, BR2_GLOBAL_PATCH_DIR is preferred. U-Boot, Barebox, at91bootstrap and at91bootstrap3 also have separate options, but these do not provide any advantage over BR2_GLOBAL_PATCH_DIR and will likely be removed in the future.
  12. If you need to add project-specific packages, create package/<manufacturer>/ and place your packages in that directory. Create an overall <manufacturer>.mk file that includes the .mk files of all your packages. Create an overall Config.in file that sources the Config.in files of all your packages. Include this Config.in file from Buildroot’s package/Config.in file.
  13. make savedefconfig to save the buildroot configuration.
  14. cp defconfig configs/<boardname>_defconfig