Yann works for Orange and develops set-top boxes in three teams in two locations. Most are application developers, not Linux or embedded experts. The main part of the firmware comes from third parties. To put this together, they need a generic build system that is not dependent on the target and middleware. It should be easy to use and not take too much build time. A home-grown build system was tried before but no success. The build system provided by the provider of the middleware is very specific, for a specific target, not generic enough.
Evaluated build systems:
- OpenEmbedded: distro generator, steep learning curve and no in-house knowledge.
- Buildroot: firmware generator (= what they were looking for), moderate learning curve and in-house knowledge (Yann), extendable (BR2_EXTERNAL)
- Others: no community (except OpenWRT).
Buildroot is simple (package in a few lines) and efficient (doesn’t take longer than absolutely necessary), is entirely community-driven (no companies behind it), community resources like website and manual.
Build process: make .config file, build toolchain, build packages, run finalize step that cleans up unnecessary cruft, generate filesystem images (with hooks at various steps). Package build process: download, extract, patch, configure, build, install (with hooks before and after each step).
External is the place for costumisations. Use Buildroot as a git submodule.
Config files are saved as defconfigs under the configs/ directory. Some people use defconfig snippets and stack them together to make different variants of the board. However, that way you can’t use the Kconfig UI, it only allows to save a total defconfig.
New packages go in packages/, new filesystems use go in fs/, all use the same Buildroot syntax.
For fine-tuning, you can use a custom skeleton and overlays. However, don’t use overlays too heavily. Preferably create a package for them, even if it just copies stuff to the target. That allows to use all of the possibilities of Buildroot, e.g. taking into account size.
In the external tree, it is also possible to add extra logic, e.g. a custom make rule to check that there are no circular dependencies. Or add a hook that is run after every package build or just before creating the filesystem. Preferably move things to helper scripts and don’t do it directly in make, much easier to maintain (e.g. syntax highlighting). It is even possible to add infrastructure. Orange added a orange-package infrastructure that adds some features, like installation of documentation.
To avoid creating too many make variables (which makes ‘make’ slower), avoid defining new variables in the infrastructure. Instead, use as much as possible generic variables. In addition, it is more readable since you don’t have to double-dollar to escape the call to eval. Adding such internal infra makes it much easier for developers to add packages.
UIDs can be created automatically by Buildroot. However, that means that adding a package could change the UID chosen. To avoid that, Orange packages must declare the UID explicitly. The orange infra checks that it is explicit.
For D-Bus, there have to be authorisation files that allow a specific application to access specific objects on the bus. That is tedious to declare. So Orange has a script that scans the source code and looks which access each package needs, and creates the authorisation file automatically. There are some exceptions that have to be handled, that is done with extra variables in the package .mk file. AppArmor uses a similar approach. This is also a reason to not put things in an overlay but in a package: that way, all this magic can work.
Orange has added a lot of infrastructure to automate things without need for the developer to take (much) action. It makes sure that these things are done systematically, reproducibly and maintainable.