Python: Advanced functionality with python

Recipes permit the use of python code in order to perform complex operations which are not possible with the normal recipe syntax and variables. Python can be used in both variable assignments and in the implementation of tasks.

For variable assignments python code is indicated via the use of ${@...}, as shown in the following example:

TAG = ${@bb.data.getVar('PV',d,1).replace('.', '_')}

The above example retrieves the PV variable from the bitbake data object, the replaces any dots with underscores. Therefore if the PV was 0.9.0 then TAG will be set to 0-9-0.

Some of the more common python code in use in existing recipes is shown in the following table:

bb.data.getVar(,d,1)

Retrieve the data for the specified variable from the bitbake database for the current recipe.

.replace(, )

Find each instance of the key and replace it with the replacement value. This can also be used to remove part of a string by specifying '' (two single quotes) as the replacement.

The following example would remove the '-frename-registers' option from the CFLAGS variable:

CFLAGS := "${@'${CFLAGS}'.replace('-frename-registers', '')}"
os.path.dirname()

Return the directory only part of a filename.

This is most commonly seen in existing recipes when settings the FILESDIR variable (as described in the FILESPATH/FILESDIR section). By obtaining name of the recipe file itself, FILE, and then using os.path.dirname to strip the filename part:

FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/make-${PV}"

Note however that this is no longer required as FILE_DIRNAME is automatically set to the dirname of the FILE variable and therefore this would be written in new recipes as:

FILESDIR = "$FILE_DIRNAME/make-${PV}"
.split()[]

Splits are variable around the specified key. Use [] to select one of the matching items from the array generated by the split command.

The following example from the recipe genext2fs_1.3+1.4rc1.bb would take the PV of 1.3+1.4rc1 and split it around the + sign, resulting in an array containing 1.3 and 1.4rc1. It then uses the index of [1] to select the second item from the list (the first item is at index 0). Therefore TRIMMEDV would be set to 1.4rc1 for this recipe:

TRIMMEDV = "${@bb.data.getVar('PV', d, 1).split('+')[1]}"

As well as directly calling built-in python functions, those functions defined by the existing classes may also be called. A set of common functions is provided by the base class in classes/base.bbclass:

base_conditional

This functions is used to set a variable to one of two values based on the definition of a third variable. The general usage is:

${@base_conditional('', '', '', ', d)}"

where:

variable-name

This is the name of a variable to check.

value

This is the value to compare the variable against.

true-result

If the variable equals the value then this is what is returned by the function.

false-result

If the variable does not equal the value then this is what is returned by the function.

The following example from the openssl recipe shows the addition of either -DL_ENDING or -DB_ENDIAN depending on the value of SITEINFO_ENDIANESS which is set to le for little endian targets and to be for big endian targets:

do_compile () {
    ...
    # Additional flag based on target endiness (see siteinfo.bbclass)
    CFLAG="${CFLAG} ${@base_conditional('SITEINFO_ENDIANESS', 'le', '-DL_ENDIAN', '-DB_ENDIAN', d)}"
    ...
base_contains

Similar to base_conditional expect that it is checking for the value being an element of an array. The general usage is:

${@base_contains('', '', '', ', d)}"

where:

array-name

This is the name of the array to search.

value

This is the value to check for in the array.

true-result

If the value is found in the array then this is what is returned by the function.

false-result

If the value is not found in the array then this is what is returned by the function.

The following example from the task-angstrom-x11 recipe shows base_contains being used to add a recipe to the runtime dependency list but only for machines which have a touchscreen:

RDEPENDS_angstrom-gpe-task-base := "\
    ...
    ${@base_contains("MACHINE_FEATURES", "touchscreen", "libgtkstylus", "",d)} \
    ...

Tasks may be implemented in python by prefixing the task function with "python ". In general this should not be needed and should be avoided where possible. The following example from the devshell recipe shows how the compile task is implemented python:

python do_compile() {
    import os
    import os.path

    workdir = bb.data.getVar('WORKDIR', d, 1)
    shellfile = os.path.join(workdir, bb.data.expand("${TARGET_PREFIX}${DISTRO}-${MACHINE}-devshell", d))

    f = open(shellfile, "w")

    # emit variables and shell functions
        devshell_emit_env(f, d, False, ["die", "oe", "autotools_do_configure"])

    f.close()
}