Direkt zum Hauptbereich

Making KConfig safe to use with YOCTO

As you may know KConfig is a well-known system for configuring software modules, as used by busybox, linux kernel, u-boot and many more.
If you are unfamiliar you might want to read this nice tutorial.

So as KConfig is well established it is used at different locations throughout YOCTO.
Basically YOCTO is doing it the following way.

You have a base configuration file called "defconfig". This is placed somewhere in the recipe-tree. It describes the absolute minimum of configuration needed.
Depending on the feature you want to add, there are *.cfg-file around the recipe-tree.

The configuration is done with all the *.cfg-files being merged into the defconfig.
This is passed to "make oldconfig" to sanity check and put all the not defined symbols into the configuration. And voila you have a fresh valid configuration to build your software.
It's pretty obvious that this is one stage where things can go very wrong...!

Mostly the basis (KConfig) is changing over the time, so symbols come and go or might change their meaning from version to version.
Which is the second stage where things can go wrong.

And that a fundamental problem.
Most of the developer I know, work like this

  • on version X the define a feature-set and so a set of configuration symbols they want to apply
  • they test this combination
  • they put into the SCM
  • they keep the fingers crossed that any future update will not break anything
  • they are upset/confused/what so ever if a future update is really breaking things
If you look at it in a detail the following scenarios might happen
  1. Symbol set in version A - Symbol set in version b
  2. Symbol set in version A - Symbol has other value or is unset in version B
  3. Symbol in n.a. in version A - Symbol is set in version B
  4. Symbol is unset in version A - Symbol is n.a. in version B
  5. Symbol is n.a. in version A - Symbol is unset in version B
  6. Symbol set in version A - Symbol is n.a. in version B
while points 1, 4 and 5 are definitely uncritical the other you might want to be warned about.

And as YOCTO is based on layer, each layer multiplies the complexity of factors that could change the effective configuration of your software module. Just imagine you have 10 layers (each with the very own release cycle) and in these 10 layers everybody could add/remove/change configuration as they like. Long story short: very bad for you, as you typically just want the feature set that was once defined and tested.

Now one could say, one could always compare the resulting file  against a known configuration placed somewhere in repository - and mismatch just shout out some warning or whatever.
IMHO this is not enough. 

Most of the KConfig files I know include a version-string somewhere in the file, so although nothing effectively changed the effective configuration and the one from your repo look different.
Second point is, that most of the projects using KConfig-based systems tend to "reorganize" the order of the symbols from time to time.

So what you really need to do is check every symbol (and therefore every line) of both files to be 100% sure (also on all the cases I mentioned above).

I've been working on this topic some month ago and implemented a helper class you can find here.

With the help of this class you are able to
  • check if a *.cfg-file is made effective in the resulting configuration
    Which is often a common mistake I did, because I forgot some obscure dependency of a configuration symbol.
    Short example:
    I wanted
    CONFIG_FOO=y
    and I put it into a *.cfg-file, referenced it, was 100% sure that it will make it to resulting config
    but CONFIG_FOO depended on CONFIG_BAR=y which wasn't set anywhere,
    so my CONFIG_FOO was simply ignored
  • you can check the resulting configuration is matching a reference configuration
    (you can see this as a enhanced diff)
  • you can set the action on any finding for the numbered point above
    Sometimes you might don't car if a new feature is automatically activated, sometimes you want to abort the build in the same case
    Quick example:
    in version a we got CONFIG_FOO=1 and in version b CONFIG_FOO=2.
    In this particular case I don't want to warned just informed.
    On the other hand if we got in version a we got CONFIG_FOO=1 and in version b
    # CONFIG_FOO is not set that is clearly a case where I want the build to be aborted
The helper-class makes use of the LibKconfig so it will give you a explanation why a symbol didn't make it into the resulting configuration.

I used rightly this class will always ensure that you can concentrate on the important things, no on regression testing all features you worked on ages ago.
If there is something to look at in detail the build process will tell you (and this hopefully in the right amount).

I wrote the kconfig-sanity.bbclass as part of my collection meta-buildtuils of useful helper functions.

I would be happy to get your input here in the comments or over on GitHub.

Kommentare

Beliebte Posts aus diesem Blog

Speedup python on embedded systems

Have you ever considered to use python as a scripting language in an embedded system? I've been using this on recent projects although it wasn't my first choice. If I had to choose a scripting language to be used in embedded I always had a strong preference for shell/bash or lua, because they are either builtin or designed to have a significant lower footprint compared to others. Nevertheless the choice was python3 (was out of my hands to decide). When putting together the first builds using YOCTO I realized that there are two sides to python. the starting phase, where the app is initializing the execution phase, where the app just processes new data In the 2nd phase python3 has good tradeoffs between maintainability of code vs. execution speed, so there is nothing to moan about. Startup is the worst But the 1st phase where the python3-interpreter is starting is really bad. So I did some research where is might be coming from. Just to give a comparison of

Making go not a no-go

Anyone that dealt with container engines came across go - a wonderful language, that was built to provide a right way of what C++ intended to do. The language itself is pretty straight forward and upstream poky support is given since ages... In the go world one would just run 1 2 go get github.com/foo/bar go build github.com/foo/bar and magically the go ecosystem would pull all the needed sources and build them into an executable. This is where the issues start... In the Openembedded world, one would have  one provider (aka recipe) for each dependency each recipe comes with a (remote) artifact (e.g. tarball, git repo, a.s.o.) which can be archived (so one can build the same software at a later point in time without any online connectivity) dedicated license information all this information is pretty useful when working is an environment (aka company) that has restrictions, such as reproducible builds license compliance security compliance (for instance no unpatched CVE) but when us

Using bbclass based on conditions

When you are working with YOCTO build system, you might be aware of the construct .bbclass. For those who are not: these are code snippets which will be injected into the recipe itself. Mostly they add new tasks or provide some generalization for things you often. A good example might be pypi.bbclass. In your recipe you write usually something likes this SRC_URI = "https://www.foo.org/foo.tar.gz" When it comes to python packages a way more elegant way is to use pypi.org . And here does the pypi.bbclass provide some magic - you recipe will just look like this inherit pypi   PYPI_PACKAGE = "foo" the bbclass pypi will automatically translate the variable name PYPI_PACKAGE into a valid URL to fetch the package. Also it will set some internal variables such as HOMEPAGE or S to the correct settings. You see life can be convenient when you know how to do it. If you want to apply a bbclass you can either insert inherit foo.bbclass into each recipe or you