Working with Variables

How to set, override, and manipulate variables in the Yocto Project

Variables are one of the most important parts of working with the Yocto Project. They control almost everything:

  • where files are installed
  • which packages are built
  • how recipes behave
  • how images are configured
  • how machine and distro specific behaviour is selected

If you are comfortable with variables, the rest of Yocto starts to make much more sense.

Why Variables Matter

BitBake metadata is mostly about describing build behaviour through variables.

A recipe does not have to hard-code paths such as /usr/bin or /etc. Instead, they often makes use of variables such as ${bindir} and ${sysconfdir}.

Variable Types

There are two types of variables:

  • Global Variables
    • Defined in configuration files (.conf)
    • By convention in uppercase, e.g. MACHINE
  • Local Variables
    • Defined in recipes (.bb and .bbappend)

Variables can be assigned and changed in many different ways. This makes the system flexible, but it also means you need to understand:

  • how to assign a value
  • when a value is expanded
  • how to add to or remove from an existing value
  • how overrides affect the final result

Common Assignment Operators

The most common operators are:

Setting a Value

=

Set a variable absolutely. BitBake will take the value assigned at that point in time. The only way to change it would be to use another absolute assignment in a later recipe.

VAR = "value"
?=

Set a value but only if the variable has not previously been set somewhere else. This is useful for default values, where the user may want to override it in their own recipe.

Use ?= when you want to supply a default only if nobody has already provided a value. This is more useful if you are writing recipes that may be shared and used in different projects.

VAR ?= "value"
??=

Set a weak default value that may still be replaced later. This also sets a default value, but waits until the end of the processing to commit it. This means that a later recipe can also override the value with either = or ?=

VAR ??= "value"

Multi-line Assignments

You can assign a variable value that goes over multiple lines by escaping the ‘new-line’. This is often used to make the assignment easier to read.

VAR = "This value \
  needs multiple lines \
  to make it more readable \
"

Changing Values

Appending values

This is where it can get really confusing as there seem to be so many ways to do the same thing!

These are the options:

:append

Append text to the final value of a variable. It does not automatically add a space - you will need to do that

+=

Append text to the final value of a variable. This will automatically add a space.

:prepend

Prepend text to the final value of a variable.It does not automatically add a space*

=+

Prepend text to the final value of a variable. This will automatically add a space

* If you noticed, yes, that was deliberate.

To summarise:

OperationParse or expansion timeAutomatic spaceTypical use
:appendExpansionNoSafely append
+=ParseYesAdd items immediately
:prependExpansionNoSafely prepend
=+ParseYes (prepend)Prepend items immediately

Automatic Space?

The contents of many variables in a Yocto project are a list of things separated by whitespace. BitBake will then split the string into the individual tokens.

However, there are some variables, such as paths, that use a colon, :, as the separator, so these should not have additional whitespace. This is why there are options not to automatically add the space.

Parse or Expansion time?

What this means is when does the append/prepend actually take place.

At parse time means when BitBake reads the variable. At expansion time means when BitBake uses it.

FOO += "bar"

This modifies the variable immediately.

For example:

FOO = "a"
FOO += "b"

Result:

FOO = "a b"

because += inserts a space automatically. However, if someone later does

FOO = "c"

the previous append is lost.

Example:

FOO =  "a"
FOO += "b"
FOO =  "c"

Final value:

FOO = "c"

When you use

FOO:append = " bar"

This doesn’t modify the variable immediately.

Instead it records:

“When someone finally asks for FOO, append ' bar'.”

Removing values

At least this is much simpler - there is only the one main option:

:remove

Remove matching text from the final value of a variable.

The Yocto Project documentation generally encourages using the modern override syntax (:append, :prepend, :remove) because it is deterministic and avoids subtle ordering problems that occur with operators like += and =+.

Practical Examples

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

This pattern is common in .bbappend files when extending the search path for additional files. Note, in this case, we absolutely do not want any extra spaces, as that would mess up the path.

Use :remove when you need to remove something from a value that has already been built up elsewhere.

DISTRO_FEATURES:remove = "x11"
IMAGE_INSTALL:remove = "nano"

This is much cleaner than trying to reconstruct the whole variable by hand.

Immediate Expansion with :=

Normally, many variables are expanded later when BitBake actually needs them. Sometimes you want the right-hand side to be expanded immediately.

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

This is important because THISDIR depends on the file being parsed. If you wait until later expansion, you may no longer get the value you expected.

Recipe Work Directories

When you start reading real recipes, a small set of internal path variables turns up again and again.

They are not usually variables you create yourself. They are the directories BitBake prepares for a recipe while it moves through the normal tasks:

  • fetch
  • unpack
  • patch
  • configure
  • compile
  • install
  • package

The exact paths depend on your machine, distro, recipe name, and version, but a normal target recipe will usually live under something like:

tmp/work/<target-system>/<recipe>/<version>/

For a recipe called hello-recipe_1.0.bb, that might be:

tmp/work/core2-64-poky-linux/hello-recipe/1.0/

The Main Directories

WORKDIR

The recipe’s private work directory. It is the parent area where BitBake keeps temporary files, unpacked sources, build output, install staging, logs, and packaging output for one recipe.

UNPACKDIR

The directory where do_unpack places fetched source files. In modern OpenEmbedded-Core, this normally points to ${WORKDIR}/sources.

S

The source directory used by tasks such as do_patch, do_configure, and do_compile. By default, it is normally ${UNPACKDIR}/${BP}.

B

The build directory used by configure and compile tasks. By default, it is the same as ${S}, but classes such as cmake often use a separate build directory.

D

The destination directory used by do_install(). Files staged here are later split into packages. It normally points to ${WORKDIR}/image.

For the same hello-recipe_1.0.bb example, the important paths would normally look like this:

${WORKDIR}
  tmp/work/core2-64-poky-linux/hello-recipe/1.0

${UNPACKDIR}
  tmp/work/core2-64-poky-linux/hello-recipe/1.0/sources

${S}
  tmp/work/core2-64-poky-linux/hello-recipe/1.0/sources/hello-recipe-1.0

${B}
  usually the same as ${S}, unless a class or recipe changes it

${D}
  tmp/work/core2-64-poky-linux/hello-recipe/1.0/image

The value ${BP} is usually ${BPN}-${PV}. For hello-recipe_1.0.bb, that means:

PN  = hello-recipe
BPN = hello-recipe
PV  = 1.0
BP  = hello-recipe-1.0

PN is the recipe name, including special forms such as -native or multilib prefixes when they apply. BPN is the base recipe name with those special parts removed. BP combines the base name and version.

Why ${S} Sometimes Needs Setting

The default ${S} value works when the unpacked source directory matches ${BP}.

For example, this archive layout is easy:

hello-recipe-1.0.tar.gz
  hello-recipe-1.0/
    Makefile
    hello-recipe.c

The default source directory points to:

${UNPACKDIR}/hello-recipe-1.0

If the source is unpacked somewhere else, set ${S} to match reality. For example, this recipe asks the Git fetcher to unpack into a directory called git:

SRC_URI = "git://github.com/example/hello-recipe.git;branch=main;protocol=https;destsuffix=git"
SRCREV = "0123456789abcdef0123456789abcdef01234567"

S = "${UNPACKDIR}/git"

That tells configure, compile, and patch tasks where the source tree actually is.

Without destsuffix=git, modern OpenEmbedded-Core normally unpacks Git sources under ${UNPACKDIR}/${BP}, so you often do not need to set ${S} for a simple Git recipe.

For recipes made only from local files, there may not be a versioned source directory at all. In that case, it is common to use ${UNPACKDIR} directly:

SUMMARY = "Small local shell tool"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://hello-tool"

S = "${UNPACKDIR}"

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${S}/hello-tool ${D}${bindir}/hello-tool
}

do_unpack stages hello-tool under ${UNPACKDIR}, ${S} points there, and do_install() copies the file into ${D}${bindir}.

How ${B} and ${D} Fit In

${B} matters most when the build system keeps generated files away from the source tree.

A CMake recipe is a common example:

SUMMARY = "Small CMake application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "git://github.com/example/hello-cmake.git;branch=main;protocol=https;destsuffix=git"
SRCREV = "0123456789abcdef0123456789abcdef01234567"

inherit cmake

S = "${UNPACKDIR}/git"

The cmake class can configure from ${S} and build in ${B}. You normally do not need to call CMake yourself. The recipe mostly tells the class where the source is and which options to use.

${D} belongs to installation and packaging:

do_install:append() {
    install -d ${D}${sysconfdir}/hello-cmake
    install -m 0644 ${UNPACKDIR}/hello-cmake.conf ${D}${sysconfdir}/hello-cmake/hello-cmake.conf
}

That installs into the temporary package image, not into /etc on your build host. Later, do_package reads ${D} and splits its contents into packages.

Other useful directories under ${WORKDIR} include:

  • ${T}, usually ${WORKDIR}/temp, for task logs and generated run scripts
  • ${RECIPE_SYSROOT}, for target headers and libraries from build-time dependencies
  • ${RECIPE_SYSROOT_NATIVE}, for native tools used while building
  • ${WORKDIR}/packages-split, where do_package writes the split package contents

You do not usually write to these directly, but they are very useful when debugging.

BitBake File Search Paths

SRC_URI can refer to remote sources, archives, Git repositories, local files, and patches.

Local files use the file:// form:

SRC_URI = " \
    file://hello-tool \
    file://hello-tool.conf \
    file://0001-fix-startup-message.patch \
"

BitBake then has to answer a simple question:

Where on disk is hello-tool.conf?

That is what FILESPATH is for.

FILESPATH

FILESPATH is a colon-separated list of directories that BitBake searches when it sees a file:// entry in SRC_URI.

You normally do not set FILESPATH directly. The base metadata builds it for you from recipe-related locations. The default pattern is based on:

${FILE_DIRNAME}/${BP}
${FILE_DIRNAME}/${BPN}
${FILE_DIRNAME}/files

${FILE_DIRNAME} is the directory containing the recipe file being processed. For this recipe:

meta-my-software/recipes-apps/hello-tool/hello-tool_1.0.bb

the default recipe-related search locations include:

meta-my-software/recipes-apps/hello-tool/hello-tool-1.0/
meta-my-software/recipes-apps/hello-tool/hello-tool/
meta-my-software/recipes-apps/hello-tool/files/

That is why this recipe can find files in its own files/ directory without extra configuration:

meta-my-software/
  recipes-apps/
    hello-tool/
      hello-tool_1.0.bb
      files/
        hello-tool
        hello-tool.conf
SRC_URI = " \
    file://hello-tool \
    file://hello-tool.conf \
"

BitBake searches the directories in FILESPATH, finds the matching local files, then do_unpack places them under ${UNPACKDIR}.

FILESPATH is also affected by file overrides such as machine and distro overrides. That allows patterns like machine-specific defconfig files, but you do not need to master that immediately. For everyday recipe work, remember that ${BP}, ${BPN}, and files are the normal recipe-local directories.

FILESEXTRAPATHS in .bbappend Files

The default recipe search paths are based on the original .bb file’s directory. They do not automatically include the directory containing your .bbappend in a different layer.

That is why appends commonly add to FILESEXTRAPATHS:

FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

This line has three important parts:

:prepend

Put your directory at the start of the search path so your project files are found before matching files from the original recipe layer.

:=

Expand the right-hand side immediately while BitBake is parsing this append. This matters because ${THISDIR} means “the directory of the file currently being parsed”.

trailing `:`

Keep the value as a colon-separated path list. Without the trailing colon, your new path can be joined to the next path and become one invalid path.

${THISDIR} is different from ${FILE_DIRNAME} in the practical case that matters most:

  • in the original recipe, it points at the recipe directory while the recipe is parsed
  • in your .bbappend, it points at your append directory while the append is parsed

That makes it useful for adding files stored beside an append.

Complete Search Example

Suppose an upstream layer contains:

meta-vendor/
  recipes-apps/
    hello-tool/
      hello-tool_1.0.bb
      files/
        hello-tool

The original recipe says:

SRC_URI = "file://hello-tool"
S = "${UNPACKDIR}"

Your project layer adds a configuration file:

meta-my-distro/
  recipes-apps/
    hello-tool/
      hello-tool_%.bbappend
      hello-tool/
        hello-tool.conf

The append says:

FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

SRC_URI:append = " file://hello-tool.conf"

do_install:append() {
    install -d ${D}${sysconfdir}/hello-tool
    install -m 0644 ${UNPACKDIR}/hello-tool.conf ${D}${sysconfdir}/hello-tool/hello-tool.conf
}

For hello-tool_1.0.bb, ${PN} is hello-tool. While parsing the append, ${THISDIR} is:

meta-my-distro/recipes-apps/hello-tool

So the prepended search path is:

meta-my-distro/recipes-apps/hello-tool/hello-tool

When BitBake sees:

file://hello-tool.conf

it searches the path list and finds:

meta-my-distro/recipes-apps/hello-tool/hello-tool/hello-tool.conf

After do_unpack, the file is available to tasks at:

${UNPACKDIR}/hello-tool.conf

Then do_install:append() stages it into:

${D}${sysconfdir}/hello-tool/hello-tool.conf

which becomes:

/etc/hello-tool/hello-tool.conf

on the target once the package is installed into an image.

Overrides

Overrides are how the same metadata can behave differently depending on the current build context.

That sounds abstract, but it solves a very practical problem. You may want one kernel fragment for a particular machine, one package dependency only for the main package, or one build option only when a recipe is being built as a native tool. You could copy the whole recipe and maintain several versions of it, but that gets ugly very quickly. Overrides let you keep the shared metadata in one place and apply the difference only when the right context is active.

Overrides can apply to contexts such as:

  • the package
  • the machine
  • the distro
  • the recipe name
  • the class variant
  • the recipe task, where a task-specific value is needed

For example:

SRC_URI:append:qemux86 = " file://qemu-extra.cfg"
IMAGE_INSTALL:append:pn-core-image-minimal = " strace"
RDEPENDS:${PN}:append = " bash"

These lines do not all apply everywhere.

The first one only affects SRC_URI when the qemux86 override is active. The second only affects IMAGE_INSTALL while BitBake is evaluating the core-image-minimal recipe. The third applies to the package named by ${PN}, which is normally the main package produced by the current recipe.

Overrides are one of the most powerful parts of the Yocto Project, but they are also one of the easiest places to create confusing behaviour if you forget the context in which the variable is being evaluated.

OVERRIDES

Conceptually, OVERRIDES is the list of override names that are active for the current context.

It is a colon-separated value built up by the core metadata, machine configuration, distro configuration, classes, and recipe context. You normally do not edit OVERRIDES directly in application recipes. Most of the time, you use the override names that Yocto has already made active for you.

For example, when building for a machine called myboard, machine-related override names may be active. When evaluating a recipe called busybox, a recipe-name override such as pn-busybox is available. When building a native variant of a recipe, class-related overrides such as class-native become relevant.

You can inspect the active set with:

bitbake -e busybox | less

Then search for:

OVERRIDES=

Do not try to memorise the whole value. The useful thing is understanding that BitBake only applies override-specific values when the matching override name is active.

Machine Overrides

Machine overrides are commonly used when one piece of metadata needs a board-specific adjustment.

For example:

SRC_URI:append:myboard = " file://myboard.cfg"

This adds myboard.cfg only when the myboard override is active. That is usually much cleaner than creating a second copy of the recipe just to add one configuration file for one board.

Machine configuration can also extend the set of machine overrides. A common BSP pattern is:

MACHINEOVERRIDES:prepend = "am335x-evm:"

This lets your machine claim compatibility with another machine family for override purposes. It can be useful when several boards share kernel, bootloader, or firmware metadata. It should still describe a real hardware relationship. If you use machine overrides as a general trick to make unrelated metadata apply, future you gets to enjoy the debugging session. Future you will not send a thank you card.

Distro Overrides

Distro overrides are useful when policy differs between distributions.

For example, a recipe might install a different default configuration file for a production distro:

SRC_URI:append:mydistro = " file://production-defaults.conf"

Use this when the difference is genuinely distro policy. If the difference is hardware-related, put it in machine or BSP metadata instead. Keeping that boundary clear makes the project much easier to reason about.

Recipe and Package Overrides

The pn-<recipe> form is useful when you need a setting to apply only while one recipe is being evaluated.

For example:

PACKAGECONFIG:append:pn-busybox = " syslog"

This is different from a package override such as:

FILES:${PN} += "${sysconfdir}/myapp"
RDEPENDS:${PN}:append = " bash"

${PN} refers to a package name in these examples, normally the main package from the current recipe. This distinction matters because recipe-level metadata and package-level metadata are not the same thing.

Class Overrides

Class overrides are useful when a recipe behaves differently depending on which variant is being built.

For example:

DEPENDS:class-target += "openssl"
DEPENDS:class-native += "openssl-native"
DEPENDS:class-nativesdk += "nativesdk-openssl"

The target variant needs target OpenSSL. The native variant needs a build-host tool or library. The SDK variant needs the SDK form. The names are similar, but they are not interchangeable. This is one of those places where being precise saves a lot of strange build failures.

Override Syntax in Older Code

Current metadata should use colon override syntax:

RDEPENDS:${PN}:append = " bash"

Older layers may still contain underscore override syntax, such as:

RDEPENDS_${PN}_append = " bash"

You only need to recognise that when maintaining old code. Do not copy that style into Wrynose-era training examples or new project metadata.

Metadata Precedence

When people first hit a precedence problem, the explanation they often hear is:

The highest-priority layer wins.

That is sometimes true, but it is also an oversimplification. It mainly applies when more than one layer provides a recipe with the same name. It does not explain all variable behaviour, and it definitely does not mean that only one layer’s metadata matters.

For a real recipe, the final value can be affected by:

  • the original recipe
  • inherited classes
  • configuration files
  • .bbappend files from several layers
  • weak defaults such as ?= and ??=
  • normal assignments with =
  • active overrides
  • :append, :prepend, and :remove
  • the order in which metadata is parsed

That is why debugging precedence by reading one file is often misleading. You may be looking at the line that set a variable, while another file appended to it later, removed one item, or changed it only for one machine.

Layer Priority Is Not Override Behaviour

BBFILE_PRIORITY controls which layer wins when BitBake has to choose between competing recipe files with the same recipe name. It is about file selection.

Overrides control whether a variable operation applies in a particular context. They are about conditional metadata.

For example, if two layers both provide busybox_1.36.1.bb, layer priority helps BitBake choose which recipe file is the provider. But if one layer provides a recipe and three other layers provide matching .bbappend files, those appends can all apply. The highest-priority layer does not simply erase the lower-priority append files.

This is why “the highest-priority layer wins” can lead you in the wrong direction. The better question is:

Which metadata files were used, and what operations did they apply to this variable?

Inspect the Final Value

When a variable is not doing what you expect, inspect what BitBake actually calculated:

bitbake -e busybox | less

Then search for the variable:

PACKAGECONFIG=
SRC_URI=
FILESEXTRAPATHS=
OVERRIDES=

bitbake -e shows the final expanded environment for a recipe. For many variables, it also shows the history of where the value was set, appended, prepended, or removed. That is much more reliable than guessing from layer priority.

For a single variable, bitbake-getvar is often quicker:

bitbake-getvar -r busybox PACKAGECONFIG

Use whichever tool gives you the clearest evidence for the question you are asking.

Basic Expansion

Variables are usually referenced using ${...} syntax.

MYDIR = "/opt/demo"
MYFILE = "${MYDIR}/config.txt"

In that example, MYFILE expands to /opt/demo/config.txt.

You will see this style everywhere in Yocto metadata.

Variable Expansion in Shell Tasks

Inside shell tasks, BitBake variables are expanded before the shell runs.

do_install() {
    install -d ${D}${bindir}
    install -m 0755 mytool ${D}${bindir}/mytool
}

In that example:

  • ${D} is the destination staging area
  • ${bindir} is usually /usr/bin

So the file is installed into the package staging area under /usr/bin.

Inline Python Expansion

Sometimes the value you need depends on logic rather than a fixed string.

You can use the ${@...} to evaluate a Python expression.

An example of the type of expression commonly seen:

ROOTFS_DEVICE = "${@'/dev/mmcblk0' if d.getVar('IMAGE_BASENAME') \
  == 'dev-image' else '/dev/mmcblk1'}"

This makes use of the Python syntax

VALUE equals X if (thing is true) else VALUE equals Y

Notes:

  • d.getVar() reads a BitBake variable
  • the expression returns the string that becomes the final value

Use this carefully. It is useful, but large amounts of inline Python can make metadata hard to read.

Order of Evaluation

This diagram is deliberately simplified. BitBake’s real parsing rules have more detail than this, but this is enough to avoid the most common mistake: assuming one assignment in one file explains the final value.

When debugging, always ask:

  • which metadata files applied?
  • which overrides were active?
  • was the value set, appended, prepended, or removed?
  • am I looking at a recipe variable, a package-specific variable, or an image variable?

Common Patterns

Add a Package to an Image

IMAGE_INSTALL:append = " i2c-tools"

Add a Distro Feature

DISTRO_FEATURES:append = " systemd"

Remove a Distro Feature

DISTRO_FEATURES:remove = "x11"

Set a Package-Specific Variable

RDEPENDS:${PN} += "bash"

${PN} means “the package name for this recipe”.

Add Files to a Package

FILES:${PN} += " /usr/local/bin/mytool"

This is a common pattern when a recipe installs something outside the usual default packaging locations.

Common Mistakes

Forgetting the Leading Space

This is one of the most common mistakes:

IMAGE_INSTALL:append = "htop"

That will usually produce a broken combined value.

The correct form is:

IMAGE_INSTALL:append = " htop"

Overwriting Instead of Extending

If you write:

DISTRO_FEATURES = "systemd"

you replace the whole variable.

That may be what you want, but often what you really meant was:

DISTRO_FEATURES:append = " systemd"

Using Immediate Expansion Without Needing It

:= is useful, but do not use it everywhere. If you do not need immediate expansion, prefer the simpler form.

How to Inspect Variable Values

One of the most useful debugging commands is:

bitbake-getvar <variable-name>

For example:

bitbake-getvar DISTRO_FEATURES
NOTE: Starting bitbake server...
#
# $DISTRO_FEATURES [6 operations]
#   set? /home/ming/Projects/demos/YoctoTraining/build/../layers/project/meta-ecw/meta-ecw-core/conf/distro/microforge.conf:20
#     "${DISTRO_FEATURES_DEFAULT} ${MICROFORGE_DEFAULT_DISTRO_FEATURES}"
#   :remove /home/ming/Projects/demos/YoctoTraining/build/../layers/project/meta-ecw/meta-ecw-core/conf/distro/microforge.conf:21
#     "x11"
#   set? /home/ming/Projects/demos/YoctoTraining/build/../layers/third-party/poky/meta/conf/distro/include/default-distrovars.inc:29
#     "${DISTRO_FEATURES_DEFAULT}"
#   :append /home/ming/Projects/demos/YoctoTraining/build/../layers/third-party/poky/meta/conf/distro/include/init-manager-systemd.inc:2
#     " systemd usrmerge"
#   set /home/ming/Projects/demos/YoctoTraining/build/../layers/third-party/poky/meta/conf/documentation.conf:145
#     [doc] "The features enabled for the distribution."
#   set? /home/ming/Projects/demos/YoctoTraining/build/../layers/third-party/poky/meta/conf/bitbake.conf:903
#     ""
# pre-expansion value:
#   "${DISTRO_FEATURES_DEFAULT} ${MICROFORGE_DEFAULT_DISTRO_FEATURES} systemd usrmerge"
DISTRO_FEATURES="acl alsa bluetooth debuginfod ext2 ipv4 ipv6 pcmcia usbgadget usbhost wifi xattr nfs zeroconf pci 3g nfc  vfat seccomp usrmerge systemd systemd-resolved networkd  systemd usrmerge"

This will show you not only the final value of variable, but also, importantly, which file(s) set the values.

If a variable is not behaving the way you expect, bitbake-getvar is usually the best place to start.

For full recipe context, use:

bitbake -e <recipe>

For example:

bitbake -e busybox | less

This is noisier than bitbake-getvar, but it is useful when you need to inspect several related values together, such as OVERRIDES, SRC_URI, FILESEXTRAPATHS, and PACKAGECONFIG.

Summary

  • use = for normal assignment
  • use ?= and ??= for defaults
  • use :append, :prepend, and :remove to manipulate existing values
  • use := only when immediate expansion is required
  • remember that overrides change values depending on context
  • remember that layer priority and override behaviour are different mechanisms
  • use bitbake-getvar or bitbake -e to inspect the final value when debugging

Quick quiz: Yocto variables

A few short checks before you move on.

Question 1You want to add `htop` to an existing image package list without replacing what is already there. Which form is most appropriate?
Question 2What is the actual problem with writing `IMAGE_INSTALL:append = "htop"` for a space-separated list?
Question 3Which case is the best reason to use `:=` instead of normal deferred expansion?