Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When defining a Bitbake recipe, how does PACKAGECONFIG... work?

I have two Bitbake recipes and I'm using Dunfell Yocto.

Both recipes inherit cmake, ie. poky/meta/classes/cmake.bbclass. As I understand it, this CMake Bitbake class effectively just wraps the CMake config, build, and install commands in Yocto/Bitbake-speak -- Bitbake steps do_configure(), do_compile(), and do_install().

One of my recipes is a library. The other recipe is for the main executable, which DEPENDS on the library recipe. The executable needs the async option turned on in the library, which does it using a CMake define, CMAKE_TURN_ON_ASYNC=libasync when building the library.

To do that in my library recipe, I have found I could go several ways. The easiest I think is to just force my recipe to turn on the async option by using EXTRA_OECMAKE = "-DCMAKE_TURN_ON_ASYNC=libasync". However, since the library has the ability to turn it on and off in CMake, it seems only good practice and due diligence to provide that option to the Bitbake recipe.

So I'm trying to use the PACKAGECONFIG feature to do that and failing repeatedly despite giving my best effort to figure it out with both research and just trial and error. So maybe it's a fair time to ask the community.

The canonical example from the Variables Glossary starts with this. And I feel this is most likely where I'm going wrong, since = and ?= and ??= always throw me for a loop:

PACKAGECONFIG ??= "f1 f2 f3 ..."

I take this to mean that, both in and for my current recipe, I'm both defining and turning on features f1, f2, and f3 (unless otherwise set previously by a = or ?=). So, effectively, this recipe will have all these features turned on by default.

Assuming that's right, I came up with this to add to my lib-with-async-option recipe:

PACKAGECONFIG ??= "async"
PACKAGECONFIG[async] ="\
    -DCMAKE_TURN_ON_ASYNC=libasync, \
    -DCMAKE_TURN_ON_ASYNC=, \
    libasync \
"

Which, as I understand it, will effectively both define and turn on the async feature for this recipe, and thus add the -DCMAKE_TURN_ON_ASYNC=libasync to the configure script, and add libasync to the DEPENDS for this recipe.

To my second, executable recipe, I just add:

DEPENDS += "lib-with-async-option"

And that's it -- the executable recipe will get both lib-with-async-option and libasync added to its dependency tree, and compile lib-with-async-option with the async feature turned on.

Well, from the console output, it doesn't seem to work, though as with most things in Yocto/Bitbake, I can't say that for sure. The executable compiles fine, but then fails at the linking stage, with tell-tale signs that it's missing the libasync library. I thought I'd come up for air for a sanity check before further troubleshooting.

Am I using the PACKAGECONFIG feature correctly to both define and enable a feature by default for my lib-with-async-option recipe?

I don't see async in the list of common features, but I wonder if I'm stomping on one not defined there? Or do I need to be setting async in MACHINE_FEATURES, DISTRO_FEATURES, or IMAGE_FEATURES?

like image 847
CivFan Avatar asked Dec 05 '25 13:12

CivFan


1 Answers

Your question already contains a lot of correct information, but has some misconceptions. First, let's simply answer the question title: how does PACKAGECONFIG work? Next, we show an example recipe (I chose dos2unix.bb because it looks simple). Then, we'll look into necessary basics. Finally, we have a look at your specific case.

How does PACKAGECONFIG work?

Using PACKAGECONFIG, recipes can provide optional features for users of that recipe. You need to understand:

  • The features are defined by the recipe via variable flags (e.g. VAR[flag1] = "whatever").
  • In general, variable flags can be independent of the variable content.
  • For PACKAGECONFIG, the variable content is a space-separated list of existing flags (e.g. VAR = "flag1 flag2").
  • You enable features on a per-recipe basis, by changing the variable content of PACKAGECONFIG for that particular recipe.

There are two ways to modify the PACKAGECONFIG variable content of a recipe (see also How to enable a PACKAGECONFIG feature in an image in bitbake?)

  • In a config like local.conf set PACKAGECONFIG:append:some-recipe = " feat1 feat2"
  • In an append file some-recipe_%.bbappend set PACKAGECONFIG:append = " feat1 feat2"

If you want to provide a PACKAGECONFIG from your recipe, the code in the docs and question is correct. Remember the arguments are ordered. Remember to still write a ,, if you omit an argument. Here, feat2 and featX are enabled by default, while feat1 is not.

PACKAGECONFIG ??= "feature2 featureX ..."
PACKAGECONFIG[feature1] = "\
    --with-f1, \
    --without-f1, \
    build-deps-for-f1, \
    runtime-deps-for-f1, \
    runtime-recommends-for-f1, \
    packageconfig-conflicts-for-f1"
PACKAGECONFIG[feature2] = ...

Example: dos2unix

For a simple example, have a look at poky/meta/recipes-support/dos2unix/dos2unix.bb. Let's play around:

  • Run bitbake dos2unix. The recipe succeeds, because the default (??=) is "".
  • Enable the nls feature via PACKAGECONFIG:append = " nls" inside a bbappend file for dos2unix.
  • Run bitbake dos2unix. You should see it fail due with Nothing PROVIDES 'po4a-native'. That's because the nls feature adds a dependency on po4a-native, which is only available in meta-perl (all assuming not having meta-perl in the build).

Run bitbake -e dos2unix | grep PACKAGECONFIG instead to see the variable content.

Understanding related Topics

Now, let's dive into the nitty-gritty details. PACKAGECONFIG makes heavy use of Yocto and bitbake features, so feel free to skip ahead if you already understand a topic I explain below.

The naming

It's called PACKAGECONFIG, but I wrote "recipes can provide options". Technically, I'm wrong. Yocto project has named the result of recipes "packages", but 1. .deb or .rpm files are called packages as well and 2. recipes produce those "other packages" as well. If this sounds confusing, I think you're right. Normally, however, the package name is inferred from the recipe name, and stuff "just works".

In practice, package name (PN), recipe name and file name of the recipe are identical for most recipes. If you're unsure for your recipe, look into PROVIDES type variables.

Default Variable Assignments with ??=

The ??= weak default assignment operator only sets a variable, when nothing else sets it (no =, no ?=, and counter intuitively, no +=). For multiple ??= the last one wins.

The ?= default assignment operator only sets a variable, if it's empty or set by ??=. For multiple ?=, the first one wins.

Here are the docs. Here's an answer of mine, where I try to make sense of the docs.

You might have noticed a pitfall: when some-recipe.bb sets, say PACKAGECONFIG ??= "foo", it will be entirely overwritten by += assignments, not only by = assignments. You have to use :append, because they apply at a later stage. So only PACKAGECONFIG:append = " bar" achieves:

PACKAGECONFIG = "foo bar"

Variable Flags

Each bitbake "variable" has a dictionary associated with it, which is called the variable's variable flags. That means you can store something inside the variable as well as in the flags associated with that variable. Here's an explanation in another answer as well.

For PACKAGECONFIG:

  • the available options are stored as flags (key value pairs), e.g. PACKAGECONFIG[feat1] = "magic,csv,string"
  • the active options are put into the variable, e.g. PACKAGECONFIG ??= "feat1 feat2"

In above examples, the name (aka key) of the feature is feat1, the value is "magic,csv,string". Annoyingly, variable flags are incompatible with overrides.

Your specific case

I'm both defining and turning on features

As you probably understand by now, definition happens via PACKAGECONFIG[flag], setting a default ("turing on") happens via ??=.

If you're referring to this libasync, there's no recipe for it in the recipe index. I expect bitbake libasync to show a "Nothing PROVIDES libasync" error. You need to write a recipe for libasync to use it within bitbake. Also, you should RDEPEND on libasync if you need it at runtime (i.e. when linking dynamically).

libasync is neither MACHINE, nor DISTRO nor IMAGE feature, it's just a library. Those features have nothing to with PACKAGECONFIG features of recipes. Maybe you fell for the wrong answer on this question you linked. So don't worry, others constantly confuse them as well. If the docs aren't enough, most Yocto intros on Youtube explain this as well.

Calling the recipe libsomething-with-async.bb would be not the Yocto way. The recipe would be called libsomething.bb and the (hopefully documented) default choice could be async turned on. What's the default is a decision of you as a packager, AFAIK.

If you do not expect (m)any consumers of that recipe to disable the async option, IMHO adding a PACKAGECONFIG for it needlessly complicates the recipe. In fact, the above-mentioned way to change PACKAGECONFIG (bbappend and local.conf) is not unique to PACKAGECONFIG, you can use it for the cmake flags and DEPENDS as well. So in the end it's an architectural decision and depends on the use case of the recipe.

  1. PACKAGECONFIG:
  • Pro: Implementation details are hidden. Dependency management.
  • Con: More complex (as your question demonstrates).
  1. Manually changing cmake flags:
  • Pro: Simple implementation. Less Yocto knowledge needed to understand it.
  • Con: Implementation details visible => Doesn't scale if many people use the recipe

Recipes you see in poky and other public layers have many users. Hence, for their use case, sticking with PACKAGECONFIG makes a lot of sense.

like image 199
Mo_ Avatar answered Dec 11 '25 12:12

Mo_