Recipes
Introduction
A bitbake recipe is a set of instructions that describes what needs
to be done to retrieve the source code for some application, apply any
necessary patches, provide any additional files (such as init scripts),
compile it, install it and generate binary packages. The end result is a
binary package that you can install on your target device, and maybe some
intermediate files, such as libraries and headers, which can be used when
building other applications.
In many ways the process is similar to creating .deb or .rpm
packages for your standard desktop distributions with one major difference
- in OpenEmbedded everything is being cross-compiled. This often makes the
task far more difficult (depending on how well suited the application is
to cross compiling), than it is for other packaging systems and sometimes
impossible.
This chapter assumes that you are familiar with working with
bitbake, including the work flow, required directory structures and bitbake
configuration. If you are not familiar with these
then first take a look at the chapter on bitbake usage.
Syntax of recipes
The basic items that make up a bitbake recipe file are:
functions
Functions provide a series of actions to be performed.
Functions are usually used to override the default implementation of
a task function, or to compliment (append or prepend to an existing
function) a default function. Standard functions use sh shell
syntax, although access to OpenEmbedded variables and internal
methods are also available.
The following is an example function from the sed
recipe:
do_install () {
autotools_do_install
install -d ${D}${base_bindir}
mv ${D}${bindir}/sed ${D}${base_bindir}/sed.${PN}
}It is also possible to implement new functions, that are not
replacing or complimenting the default functions, which are called
between existing tasks. It is also possible to implement functions
in python instead of sh. Both of these options are not seen in the
majority of recipes.
variable assignments and manipulations
Variable assignments allow a value to be assigned to a
variable. The assignment may be static text or might include the
contents of other variables. In addition to assignment, appending
and prepending operations are also supported.
The following example shows some of the ways variables can be
used in recipes:S = "${WORKDIR}/postfix-${PV}"
PR = "r4"
CFLAGS += "-DNO_ASM"
SRC_URI_append = "file://fixup.patch;patch=1"
keywords
Only a few keywords are used in bitbake recipes. They are used
for things such as including common functions
(inherit), loading parts of a recipe from other
files (include and
require) and exporting variables to the
environment (export).
The following example shows the use of some of these
keywords:export POSTCONF = "${STAGING_BINDIR}/postconf"
inherit autoconf
require otherfile.inc
comments
Any lines that begin with a # are treated as comment lines and
are ignored.# This is a comment
The following is a summary of the most important (and most commonly
used) parts of the recipe syntax:
Line continuation: \
To split a statement over multiple lines you should place a \ at
the end of the line that is to be continued on the next line.
VAR = "A really long \
line"
Note that there must not be anything (no spaces or tabs) after
the \.
Using variables: ${...}
To access the contents of a variable you need to access it via
${<varname>}:SRC_URI = "${SOURCEFORGE_MIRROR}/libpng/zlib-${PV}.tar.gz"
Quote all assignments
All variable assignments should be quoted with double quotes.
(It may work without them at present, but it will not work in the
future).VAR1 = "${OTHERVAR}"
VAR2 = "The version is ${PV}"
Conditional assignment
Conditional assignment is used to assign a value to a
variable, but only when the variable is currently unset. This is
commonly used to provide a default value for use when no specific
definition is provided by the machine or distro configuration of the
user's local.conf configuration.
The following example:VAR1 ?= "New value"will
set VAR1 to "New
value" if its currently empty. However if it was already
set it would be unchanged. In the following VAR1 is left with the value
"Original value":VAR1 = "Original value"
VAR1 ?= "New value"
Appending: +=
You can append values to existing variables using the
+= operator. Note that this operator will add a
space between the existing content of the variable and the new
content.SRC_URI += "file://fix-makefile.patch;patch=1"
Prepending: =+
You can prepend values to existing variables using the
=+ operator. Note that this operator will add a
space between the new content and the existing content of the
variable.VAR =+ "Starts"
Appending: _append
You can append values to existing variables using the
_append method. Note that this operator does
not add any additional space, and it is applied after all the
+=, and =+ operators have
been applied.
The following example shows the space being explicitly added to
the start to ensure the appended value is not merged with the
existing value:SRC_URI_append = " file://fix-makefile.patch;patch=1"The
_append method can also be used with overrides,
which results in the actions only being performed for the specified
target or machine: [TODO: Link to section on overrides]SRC_URI_append_sh4 = " file://fix-makefile.patch;patch=1"Note
that the appended information is a variable itself, and therefore
it's possible to used += or
=+ to assign variables to the
_append information:SRC_URI_append = " file://fix-makefile.patch;patch=1"
SRC_URI_append += "file://fix-install.patch;patch=1"
Prepending: _prepend
You can prepend values to existing variables using the
_prepend method. Note that this operator does not add any additional
space, and it is applied after all the +=, and
=+ operators have been applied.
The following example shows the space being explicitly added to
the end to ensure the prepended value is not merged with the
existing value:CFLAGS_prepend = "-I${S}/myincludes "The
_prepend method can also be used with
overrides, which result in the actions only being performed for the
specified target or machine: [TODO: Link to section on
overrides]CFLAGS_prepend_sh4 = " file://fix-makefile.patch;patch=1"Note
that the appended information is a variable itself, and therefore
it's possible to used += or
=+ to assign variables to the
_prepend information:CFLAGS_prepend = "-I${S}/myincludes "
CFLAGS_prepend += "-I${S}/myincludes2 "Note also the lack of a space
when using += to append to a prepend value - remember that the +=
operator adds space itself.
Spaces vs tabs
Spaces should be used for indentation, not hard tabs. Both
currently work, however it is a policy decision of OE that spaces
always be used.
Style: oe-stylize.py
To help with using the correct style in your recipes there is
a python script in the contrib directory called
oe-stylize.py which can be used to reformat
your recipes to the correct style. The output will contain a list of
warnings (to let you know what you did wrong) which should be edited
out before using the new file.$ contrib/oe-stylize.py myrecipe.bb > fixed-recipe.bb
vi fixed-recipe.bb
mv fixed.recipe.bb myrecipe.bb
Using python for complex operations: ${@...}
For more advanced processing it is possible to use python code
during variable assignments, for doing search and replace on a
variable for example.
Python code is indicated by a proceeding @ sign in the
variable assignment.CXXFLAGS := "${@'${CXXFLAGS}'.replace('-frename-registers', '')}"More
information about using python is available in the section.
Shell syntax
When describing a list of actions to take, shell syntax is used
(as if you were writing a shell script). You should ensure that your
script works with a generic sh and not require any bash (or
other shell) specific functionality. The same applies to various
system utilities (sed, grep, awk etc) that you may wish to use. If
in doubt you should check with multiple implementations - including
those from busybox.
For a detailed description of the syntax for the bitbake recipe
files you should refer to the bitbake use manual.
Recipe naming: Names, versions and releases
Recipes in OpenEmbedded use a standard naming convention that
includes the package name and version number in the filename. In addition
to the name and version there is also a release number, which indicates
changes to the way the package is built and/or packaged. The release
number is contained within the recipe itself.
The expected format of recipe name is:<package-name>_<version>.bb
where <package-name> is the name of the
package (application, library, module, or whatever it is that is being
packaged) and version is the version number.
So a typical recipe name would be:strace_4.5.14.bbwhich
would be for version 4.5.14 of the
strace application.
The release version is defined via the package release variable, PR,
contained in the recipe. The expected format is:r<n>where
<n> is an integer number starting from 0
initially and then incremented each time the recipe, or something that
effects the recipe, is modified. So a typical definition of the release
would be:PR = "r1"to specify release number
1 (the second release, the first would have been
0). If there is no definition of PR in the recipe
then the default value of "r0" is used.
It is good practice to always define PR in your recipes, even
for the "r0" release, so that when editing the
recipe it is clear that the PR number needs to be updated.
You should always increment PR when modifying a recipe.
Sometimes this can be avoided if the change will have no effect on the
actual packages generated by the recipe, such as updating the SRC_URI
to point to a new host. If in any doubt then you should increase the
PR regardless of what has been changed.
The PR value should never be decremented. If you accidentally
submit a large PR value for example then it should be left at the
value and just increased for new releases, not reset back to a lower
version.
When a recipe is being processed some variables are automatically
set based on the recipe file name and can be used for other purposes from
within the recipe itself. These include:
PN
The package name. Determined from the recipe filename -
everything up until the first underscore is considered to be the
package name. For the strace_4.5.14.bb recipe the
PN variable would be set to "strace".
PV
The package version. Determined from the recipe filename -
everything between the first underscore and the final .bb is
considered to be the package version. For the
strace_4.5.14.bb recipe the PV variable would be
set to "4.5.14".
PR
The package release. This is explicitly set in the recipe.
It defaults to "r0" if not set.
P
The package name and versions separated by a hyphen.P = "${PN}-${PV}"
For the strace_4.5.14.bb recipe the P
variable would be set to
"strace-4.5.14".
PF
The package name, version and release separated by
hyphens.PF = "${PN}-${PV}-${PR}"
For the strace_4.5.14.bb recipe, with PR
set to "r1" in the recipe, the PF variable
would be set to "strace-4.5.14-r1".
While some of these variables are not commonly used in recipes (they
are used internally though) both PN and PV are used a lot.
In the following example we are instructing the packaging system to
include an additional directory in the package. We use PN to refer to the
name of the package rather than spelling out the package name:FILES_${PN} += "${sysconfdir}/myconf"
In the next example we are specifying the URL for the package
source, by using PV in place of the actual version number it is possible
to duplicate, or rename, the recipe for a new version without having to
edit the URL:SRC_URI = "ftp://ftp.vim.org/pub/vim/unix/vim-${PV}.tar.bz2"
Variables
One of the most confusing part of bitbake recipes for new users is
the large amount of variables that appear to be available to change and/or
control the behaviour of some aspect of the recipe. Some variables, such
as those derived from the file name are reasonably obvious, others are not
at all obvious.
There are several places where these variables are derived from
and/or used:
A large number of variables are defined in the bitbake
configuration file conf/bitbake.conf - it's often a good idea to look
through that file when trying to determine what a particular variable
means.
Machine and distribution configuration files in conf/machine and
conf/distro will sometimes define some variables specific to the
machine and/or distribution. You should look at the appropriate files
for your targets to see if anything is being defined that effects the
recipes you are building.
Bitbake itself will define some variables. The FILE variable
that defines the name of the bitbake recipe being processed is set by
bitbake itself for example. Refer to the bitbake manual for more
information on the variables that bitbake sets.
The classes, that are used via the inherit keyword, define
and/or use the majority of the remaining variables. A class is like
a library that contains parts of a bitbake recipe that is used by
multiple recipes. To make them usable in more situations they often
include a large number of variables to control how the class
operates.
Another important aspect is that there are three different types of
things that binaries and libraries are used for and they often have
different variables for each. These include:
target
Refers to things built for the target and are expected to be run
on the target device itself.
native
Refers to things built to run natively on the build host
itself.
cross
Refers to things built to run natively on the build host
itself, but produce output which is suitable for the target device.
Cross versions of packages usually only exist for things like
compilers and assemblers - i.e. things which are used to produce
binary applications themselves.
Sources: Downloading, patching and additional files
A recipe's purpose is to describe how to take a software package and
build it for your target device. The location of the source file (or
files) is specified via the in the
recipe. This can describe several types of URIs, the most common
are:
http and https
Specifies files to be downloaded. A copy is stored locally so
that future builds will not download the source again.
cvs, svn and git
Specifies that the files are to be retrieved using the
specified version control system.
files
Plain files which are included locally. These can be used for
adding documentation, init scripts or any other files that need to
be added to build the package under openembedded.
patches
Plain files which are treated as patches and
automatically applied.
If an http, https or file URI refers to a compressed file, an archive
file or a compressed archive file, such as .tar.gz or .zip, then the files
will be uncompressed and extracted from the archive automatically.
Archive files will be extracted from with the working directory,
${WORKDIR} and plain files will be copied
into the same directory. Patches will be applied from within the unpacked
source directory, ${S}. (Details on these
directories is provided in the next section.)
The following example from the havp recipe shows a typical SRC_URI definition:SRC_URI = "http://www.server-side.de/download/havp-${PV}.tar.gz \
file://sysconfdir-is-etc.patch;patch=1 \
file://havp.init \
file://doc.configure.txt \
file://volatiles.05_havp"
This describes several files
http://www.server-side.de/download/havp-${PV}.tar.gz
This is the URI of the havp source code. Note the use of the
${PV} variable to specify the
version. This is done to enable the recipe to be renamed for a new
version without the need to edit the recipe itself. Because this is
a .tar.gz compressed archive the file will be decompressed and
extracted in the working dir ${WORKDIR}.
file://sysconfdir-is-etc.patch;patch=1
This is a local file that is used to patch the extracted
source code. The patch=1 is what specifies that this is a patch. The
patch will be applied from the unpacked source directory, ${S}. In this case ${S} will be ${WORKDIR}/havp-0.82, and luckily the
havp-0.82.tar.gz file extracts
itself into that directory (so no need to explicitly change
${S}).
file://havp.init file://doc.configure.txt
file://volatiles.05_havp"
These are plain files which are just copied into the working
directory ${WORKDIR}. These are
then used during the install task in the recipe to provide init
scripts, documentation and volatiles configuration information for
the package.
Full details on the SRC_URI
variable and all the support URIs are available in the section of the reference chapter.
Directories: What goes where
A large part of the work of a recipe is involved with specifying
where files are found and where they have to go. It's important for
example that programs do not try and use files from /usr/include or /usr/lib since they are for the host system, not
the target. Similarly you don't want programs installed into /usr/bin since that may overwrite your host system
programs with versions that don't work on the host!
The following are some of the directories commonly referred to in
recipes and will be described in more detail in the rest of this
section:
Working directory: WORKDIR
This working directory for a recipe is where archive files
will be extracted, plain files will be placed, subdirectories for
logs, installed files etc will be created.
Unpacked source code directory: S
This is where patches are applied and where the program is
expected to be compiled in.
Destination directory: D
The destination directory. This is where your package should
be installed into. The packaging system will then take the files
from directories under here and package them up for installation on
the target.
Installation directories: bindir, docdir, ...
There are a set of variables available to describe all of the
paths on the target that you may want to use. Recipes should use
these variables rather than hard coding any specific paths.
Staging directories: STAGING_LIBDIR, STAGING_INCDIR, ...
Staging directories are a special area for headers, libraries
and other files that are generated by one recipe that may be needed
by another recipe. A library package for example needs to make the
library and headers available to other recipes so that they can link
against them.
File path directories: FILE, FILE_DIRNAME, FILESDIR,
FILESPATH
These directories are used to control where files are found.
Understanding these can help you separate patches for different
versions or releases of your recipes and/or use the same patch over
multiple versions etc.
WORKDIR: The working directory
The working directory is where the source code is extracted,
plain files (not patches) are copied and where the logs and
installation files are created. A typical reason for needing to
reference the work directory is for the handling of non patch
files.
If we take a look at the recipe for quagga we can see example
non patch files for configuration and init scripts:SRC_URI = "http://www.quagga.net/download/quagga-${PV}.tar.gz \
file://fix-for-lib-inpath.patch;patch=1 \
file://quagga.init \
file://quagga.default \
file://watchquagga.init \
file://watchquagga.default"The recipe has two init files
and two configuration files, which are not patches, but are actually
files that it wants to include in the generated packages. Bitbake will
copy these files into the work directory. So to access them during the
install task we refer to them via the WORKDIR variable:do_install () {
# Install init script and default settings
install -m 0755 -d ${D}${sysconfdir}/default ${D}${sysconfdir}/init.d ${D}${sysconfdir}/quagga
install -m 0644 ${WORKDIR}/quagga.default ${D}${sysconfdir}/default/quagga
install -m 0644 ${WORKDIR}/watchquagga.default ${D}${sysconfdir}/default/watchquagga
install -m 0755 ${WORKDIR}/quagga.init ${D}${sysconfdir}/init.d/quagga
install -m 0755 ${WORKDIR}/watchquagga.init ${D}${sysconfdir}/init.d/watchquagga
...
S: The unpacked source code directory
Bitbake expects to find the extracted source for a package in a
directory called <packagename>-<version> in the
WORKDIR directory. This is the
directory in which it will change into before patching, compiling and
installing the package.
For example, we have a package called widgets_1.2.bb which we are extracting from the
widgets-1.2.tar.gz file. Bitbake
expects the source to end up in a directory called widgets-1.2 within the work directory. If the
source does not end up in this directory then bitbake needs to be told
this by explicitly setting S.
If widgets-1.2.tar.gz actually
extracts into a directory called widgets, without the version number, instead of
widgets-1.2 then the S variable will be wrong and patching and/or
compiling will fail. Therefore we need to override the default value of
S to specify the directory the source
was actually extracted into:SRC_URI = "http://www.example.com/software/widgets-${PN}.tar.gz"
S = "${WORKDIR}/widgets"
D: The destination directory
The destination directory is where the completed application and
all of it's files are installed into in preparation for packaging.
Typically an installation would place files in directories such as
/etc and /usr/bin by default. Since those directories are
used by the host system we do not want the packages to install into
those locations. Instead they need to install into the directories below
the destination directory.
So instead of installing into /usr/bin the package needs to install into
${D}/usr/bin.
The following example from arpwatch shows the make install command
being passed a ${D} as the DESTDIR variable to control where the makefile
installs everything:do_install() {
...
oe_runmake install DESTDIR=${D}
The following example from quagga shows the use of the destination
directory to install the configuration files and init scripts for the
package:do_install () {
# Install init script and default settings
install -m 0755 -d ${D}${sysconfdir}/default ${D}${sysconfdir}/init.d ${D}${sysconfdir}/quagga
install -m 0644 ${WORKDIR}/quagga.default ${D}${sysconfdir}/default/quagga
install -m 0755 ${WORKDIR}/quagga.init ${D}${sysconfdir}/init.d/quagga
You should not use directories such as /etc and /usr/bin directly in your recipes. You should
use the variables that define these locations. The full list of
these variables can be found in the section of the reference
chapter.
Staging directories
Staging is used to make libraries, headers and binaries available
for the build of one recipe for use by another recipe. Building a
library for example requires that packages be created containing the
libraries and headers for development on the target as well as making
them available on the host for building other packages that need the
libraries and headers.
Making the libraries, headers and binaries available for use by
other recipes on the host is called staging and is performed by the
stage task in the recipe. Any recipes that contain
items that are required to build other packages should have a
stage task to make sure the items are all correctly
placed into the staging area. The following example from clamav shows the
clamav library and header being placed into the staging area:do_stage () {
oe_libinstall -a -so libclamav ${STAGING_LIBDIR}
install -m 0644 libclamav/clamav.h ${STAGING_INCDIR}
}
The following from the p3scan recipe shows the path to the clamav
library and header being passed to the configure script. Without this
the configure script would either fail to find the library, or worse
still search the host system's directories for the library. Passing in
the location results in it searching the correct location and finding
the clamav library and headers:EXTRA_OECONF = "--with-clamav=${STAGING_LIBDIR}/.. \
--with-openssl=${STAGING_LIBDIR}/.. \
--disable-ripmime"While the staging directories are
automatically added by OpenEmbedded to the compiler and linking commands
it is sometimes necessary, as in the p3scan example above, to explicitly
specify the location of the staging directories. Typically this is
needed for autoconf scripts that search in multiple places for the
libraries and headers.
Many of the helper classes, such as pkgconfig and autotools add
appropriate commands to the stage task for you. Check with the
individual class descriptions in the reference section to determine
what each class is staging automatically for you.
A full list of staging directories can be found in the section in the reference
chapter.
FILESPATH/FILESDIR: Finding local files
The file related variables are used by bitbake to determine where
to look for patches and local files.
Typically you will not need to modify these, but it is useful to
be aware of the default values. In particular when searching for patches
and/or files (file:// URIs), the default search path is:
${FILE_DIRNAME}/${PF}
This is the package name, version and release, such as
"strace-4.5.14-r1". This is very
rarely used since the patches would only be found for the one
exact release of the recipe.
${FILE_DIRNAME}/${P}
This is the package name and version, such as "strace-4.5.14". This is by far the most
common place to place version specific patches.
${FILE_DIRNAME}/${PN}
This is the package name only, such as "strace". This is not commonly used.
${FILE_DIRNAME}/files
This is just the directory called "files". This is commonly used for patches
and files that apply to all versions of the package.
${FILE_DIRNAME}/
This is just the base directory of the recipe. This is very
rarely used since it would just clutter the main directory.
Each of the paths is relative to ${FILE_DIRNAME} which is the directory in which
the recipe that is being processed is located.
The full set of variables that control the file locations
are:
FILE
The path to the .bb file which is currently being
processed.
FILE_DIRNAME
The path to the directory which contains the FILE which is
currently being processed.FILE_DIRNAME = "${@os.path.dirname(bb.data.getVar('FILE', d))}"
FILESPATH
The default set of directories which are available to use
for the file:// URIs. Each directory is searched, in the
specified order, in an attempt to find the file specified by each
file:// URI: FILESPATH = "${FILE_DIRNAME}/${PF}:${FILE_DIRNAME}/${P}:\
${FILE_DIRNAME}/${PN}:${FILE_DIRNAME}/files:${FILE_DIRNAME}"
FILESDIR
The default directory to search for file:// URIs. Only used
if the file is not found in FILESPATH. This can be used to easily
add one additional directory to the search path without having to
modify the default FILESPATH setting. By default this is just the
first directory from FILESPATH. FILESDIR = "${@bb.which(bb.data.getVar('FILESPATH', d, 1), '.')}"
Sometimes recipes will modify the FILESPATH or FILESDIR variables to change the default search
path for patches and files. The most common situation in which this is
done is when one recipe includes another one in which the default values
will be based on the name of the package doing the including, not the
included package. Typically the included package will expect the files
to be located in a directory based on it's own name.
As an example the m4-native recipe includes the m4 recipe. This is
fine, except that the m4 recipe expects its files and patches to be
located in a directory called m4,
while the native file name results in them being searched for
in m4-native. So the m4-native recipe
sets the FILESDIR variable to the value
of the actual m4 directory (where m4 itself has its files
stored): include m4_${PV}.bb
inherit native
FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/m4"
Basic examples
By now you should know enough about the bitbake recipes to be able
to create a basic recipe. We'll cover a simple single file recipe and then
a more advanced example that uses the autotools helper class (to be
described later) to build an autoconf based package.
Hello world
Now it's time for our first recipe. This is going to be one of the
simplest possible recipes: all code is included and there's only one
file to compile and one readme file. While this isn't all that common,
it's a useful example because it doesn't depend on any of the helper
classes which can sometime hide a lot of what is going on.
First we'll create the myhelloworld.c file and a readme file.
We'll place this in the files subdirectory, which is one of the places
that is searched for file:// URIs:$ mkdir recipes/myhelloworld
$ mkdir recipes/myhelloworld/files
$ cat > recipes/myhelloworld/files/myhelloworld.c
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello world!\n");
return 0;
}
^D
$ cat > recipes/myhelloworld/files/README.txt
Readme file for myhelloworld.
^D
Now we have a directory for our recipe, recipes/myhelloworld, and
we've created a files subdirectory in there to store our local files.
We've created two local files, the C source code for our helloworld
program and a readme file. Now we need to create the bitbake
recipe.
First we need the header section, which will contain a description
of the package and the release number. We'll leave the other header
variables out for now:DESCRIPTION = "My hello world program"
PR = "r0"
Next we need to tell it which files we want to be included in the
recipe, which we do via file:// URIs and the SRC_URI variable:SRC_URI = "file://myhelloworld.c \
file://README.txt"
Note the use of the \ to continue a file and the use of file://
local URIs, rather than other types such as http://.
Now we need to provide a compile task which tells bitbake how to
compile this program. We do this by defining a do_compile function in
the recipe and providing the appropriate commands:
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/myhelloworld.c -o myhelloworld
}
Note the:
use of the pre-defined compiler variables, ${CC}, ${CFLAGS} and ${LDFLAGS}. These are set up automatically to
contain the settings required to cross-compile the program for the
target.
use of ${WORKDIR} to find the
source file. As mentioned previously all files are copied into the
working directory and can be referenced via the ${WORKDIR} variable.
And finally we want to install the program and readme file into
the destination directory so that it'll be packaged up correctly. This
is done via the install task, so we need to define a do_install function
in the recipe to describe how to install the package:do_install() {
install -m 0755 -d ${D}${bindir} ${D}${docdir}/myhelloworld
install -m 0644 ${S}/myhelloworld ${D}${bindir}
install -m 0644 ${WORKDIR}/README.txt ${D}${docdir}/myhelloworld
}
Note the:
use of the install command to
create directories and install the files, not cp.
way directories are created before we attempt to install any
files into them. The install command takes care of any
subdirectories that are missing, so we only need to create the full
path to the directory - no need to create the subdirectories.
way we install everything into the destination directory via
the use of the ${D}
variable.
way we use variables to refer to the target directories, such
as ${bindir} and ${docdir}.
use of ${WORKDIR} to get
access to the README.txt file,
which was provided via a file:// URI.
We'll consider this release 0 and version 0.1 of a program called
helloworld. So we'll name the recipe myhelloworld_0.1.bb:$ cat > recipes/myhelloworld/myhelloworld_0.1.bb
DESCRIPTION = "Hello world program"
PR = "r0"
SRC_URI = "file://myhelloworld.c \
file://README.txt"
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/myhelloworld.c -o myhelloworld
}
do_install() {
install -m 0755 -d ${D}${bindir} ${D}${docdir}/myhelloworld
install -m 0644 ${S}/myhelloworld ${D}${bindir}
install -m 0644 ${WORKDIR}/README.txt ${D}${docdir}/myhelloworld
}
^DNow we are ready to build our package, hopefully it'll all work
since it's such a simple example:$ bitbake -b recipes/myhelloworld/myhelloworld_0.1.bb
NOTE: package myhelloworld-0.1: started
NOTE: package myhelloworld-0.1-r0: task do_fetch: started
NOTE: package myhelloworld-0.1-r0: task do_fetch: completed
NOTE: package myhelloworld-0.1-r0: task do_unpack: started
NOTE: Unpacking /home/lenehan/devel/oe/local-recipes/myhelloworld/files/helloworld.c to /home/lenehan/devel/oe/build/titan-glibc-25/tmp/work/myhelloworld-0.1-r0/
NOTE: Unpacking /home/lenehan/devel/oe/local-recipes/myhelloworld/files/README.txt to /home/lenehan/devel/oe/build/titan-glibc-25/tmp/work/myhelloworld-0.1-r0/
NOTE: package myhelloworld-0.1-r0: task do_unpack: completed
NOTE: package myhelloworld-0.1-r0: task do_patch: started
NOTE: package myhelloworld-0.1-r0: task do_patch: completed
NOTE: package myhelloworld-0.1-r0: task do_configure: started
NOTE: package myhelloworld-0.1-r0: task do_configure: completed
NOTE: package myhelloworld-0.1-r0: task do_compile: started
NOTE: package myhelloworld-0.1-r0: task do_compile: completed
NOTE: package myhelloworld-0.1-r0: task do_install: started
NOTE: package myhelloworld-0.1-r0: task do_install: completed
NOTE: package myhelloworld-0.1-r0: task do_package: started
NOTE: package myhelloworld-0.1-r0: task do_package: completed
NOTE: package myhelloworld-0.1-r0: task do_package_write: started
NOTE: Not creating empty archive for myhelloworld-dbg-0.1-r0
Packaged contents of myhelloworld into /home/lenehan/devel/oe/build/titan-glibc-25/tmp/deploy/ipk/sh4/myhelloworld_0.1-r0_sh4.ipk
Packaged contents of myhelloworld-doc into /home/lenehan/devel/oe/build/titan-glibc-25/tmp/deploy/ipk/sh4/myhelloworld-doc_0.1-r0_sh4.ipk
NOTE: Not creating empty archive for myhelloworld-dev-0.1-r0
NOTE: Not creating empty archive for myhelloworld-locale-0.1-r0
NOTE: package myhelloworld-0.1-r0: task do_package_write: completed
NOTE: package myhelloworld-0.1-r0: task do_populate_staging: started
NOTE: package myhelloworld-0.1-r0: task do_populate_staging: completed
NOTE: package myhelloworld-0.1-r0: task do_build: started
NOTE: package myhelloworld-0.1-r0: task do_build: completed
NOTE: package myhelloworld-0.1: completed
Build statistics:
Attempted builds: 1
$
The package was successfully built, the output consists of two
.ipkg files, which are ready to be installed on the target. One contains
the binary and the other contains the readme file:$ ls -l tmp/deploy/ipk/*/myhelloworld*
-rw-r--r-- 1 lenehan lenehan 3040 Jan 12 14:46 tmp/deploy/ipk/sh4/myhelloworld_0.1-r0_sh4.ipk
-rw-r--r-- 1 lenehan lenehan 768 Jan 12 14:46 tmp/deploy/ipk/sh4/myhelloworld-doc_0.1-r0_sh4.ipk
$
It's worthwhile looking at the working directory to see where
various files ended up:$ find tmp/work/myhelloworld-0.1-r0
tmp/work/myhelloworld-0.1-r0
tmp/work/myhelloworld-0.1-r0/myhelloworld-0.1
tmp/work/myhelloworld-0.1-r0/myhelloworld-0.1/patches
tmp/work/myhelloworld-0.1-r0/myhelloworld-0.1/myhelloworld
tmp/work/myhelloworld-0.1-r0/temp
tmp/work/myhelloworld-0.1-r0/temp/run.do_configure.21840
tmp/work/myhelloworld-0.1-r0/temp/log.do_stage.21840
tmp/work/myhelloworld-0.1-r0/temp/log.do_install.21840
tmp/work/myhelloworld-0.1-r0/temp/log.do_compile.21840
tmp/work/myhelloworld-0.1-r0/temp/run.do_stage.21840
tmp/work/myhelloworld-0.1-r0/temp/log.do_configure.21840
tmp/work/myhelloworld-0.1-r0/temp/run.do_install.21840
tmp/work/myhelloworld-0.1-r0/temp/run.do_compile.21840
tmp/work/myhelloworld-0.1-r0/install
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-locale
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-dbg
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-dev
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-doc
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-doc/usr
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-doc/usr/share
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-doc/usr/share/doc
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-doc/usr/share/doc/myhelloworld
tmp/work/myhelloworld-0.1-r0/install/myhelloworld-doc/usr/share/doc/myhelloworld/README.txt
tmp/work/myhelloworld-0.1-r0/install/myhelloworld
tmp/work/myhelloworld-0.1-r0/install/myhelloworld/usr
tmp/work/myhelloworld-0.1-r0/install/myhelloworld/usr/bin
tmp/work/myhelloworld-0.1-r0/install/myhelloworld/usr/bin/myhelloworld
tmp/work/myhelloworld-0.1-r0/image
tmp/work/myhelloworld-0.1-r0/image/usr
tmp/work/myhelloworld-0.1-r0/image/usr/bin
tmp/work/myhelloworld-0.1-r0/image/usr/share
tmp/work/myhelloworld-0.1-r0/image/usr/share/doc
tmp/work/myhelloworld-0.1-r0/image/usr/share/doc/myhelloworld
tmp/work/myhelloworld-0.1-r0/myhelloworld.c
tmp/work/myhelloworld-0.1-r0/README.txt
$Things to note here are:
The two source files are in tmp/work/myhelloworld-0.1-r0, which is the
working directory as specified via the ${WORKDIR} variable;
There's logs of the various tasks in tmp/work/myhelloworld-0.1-r0/temp which you
can look at for more details on what was done in each task;
There's an image directory at tmp/work/myhelloworld-0.1-r0/image which
contains just the directories that were to be packaged up. This is
actually the destination directory, as specified via the ${D} variable. The two files that we
installed were originally in here, but during packaging they were
moved into the install area into a subdirectory specific to the
package that was being created (remember we have a main package and
a -doc package being created.
The program was actually compiled in the tmp/work/myhelloworld-0.1-r0/myhelloworld-0.1
directory, this is the source directory as specified via the
${S} variable.
There's an install directory at tmp/work/myhelloworld-0.1-r0/install which
contains the packages that were being generated and the files that
go in the package. So we can see that the myhelloworld-doc package
contains the single file /usr/share/doc/myhelloworld/README.txt, the
myhelloworld package contains the single file /usr/bin/myhelloworld and the -dev, -dbg and
-local packages are all empty.
At this stage it's good to verify that we really did produce a
binary for the target and not for our host system. We can check that
with the file command:$ file tmp/work/myhelloworld-0.1-r0/install/myhelloworld/usr/bin/myhelloworld
tmp/work/myhelloworld-0.1-r0/install/myhelloworld/usr/bin/myhelloworld: ELF 32-bit LSB executable, Hitachi SH, version 1 (SYSV), for GNU/Linux 2.4.0, dynamically linked (uses shared libs), for GNU/Linux 2.4.0, not stripped
$ file /bin/ls
/bin/ls: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.4.0, dynamically linked (uses shared libs), for GNU/Linux 2.4.0, stripped
$This shows us that the helloworld program is for an SH
processor (obviously this will change depending on what your target
system is), while checking the /bin/ls
program on the host shows us that the host system is an AMD X86-64 system.
That's exactly what we wanted.
An autotools package
Now for an example of a package that uses autotools. These are
programs that you need to run a configure script for, passing various
parameters, and then make. To make these work when cross-compiling you
need to provides a lot of variables to the configure script. But all the
hard work as already been done for you. There's an which takes care of most of the complexity
of building an autotools based package.
Let's take a look at the tuxnes recipe which is an example of a
very simple autotools based recipe:$ cat recipes/tuxnes/tuxnes_0.75.bb
DESCRIPTION = "Tuxnes Nintendo (8bit) Emulator"
HOMEPAGE = "http://prdownloads.sourceforge.net/tuxnes/tuxnes-0.75.tar.gz"
LICENSE = "GPLv2"
SECTION = "x/games"
PRIORITY = "optional"
PR = "r1"
SRC_URI = "http://heanet.dl.sourceforge.net/sourceforge/tuxnes/tuxnes-0.75.tar.gz"
inherit autotools
This is a really simple recipe. There's the standard header that
describes the package. Then the SRC_URI, which in this case is an http
URL that causes the source code to be downloaded from the specified URI.
And finally there's an "inherit
autotools" command which loads the autotools class. The
autotools class will take care of generating the required configure,
compile and install tasks. So in this case there's nothing else to do -
that's all there is to it.
It would be nice if it was always this simple. Unfortunately
there's usually a lot more involved for various reasons including the
need to:
Pass parameters to configure to enable and disable
features;
Pass parameters to configure to specify where to find
libraries and headers;
Make modifications to prevent searching for headers and
libraries in the normal locations (since they belong to the host
system, not the target);
Make modifications to prevent the configure script from tying
to compile and run programs - any programs it compiles will be for
the target and not the host and so cannot be run.
Manually implement staging scripts;
Deal with lots of other more complex issues;
Some of these items are covered in more detail in the advanced
autoconf section.
Dependencies: What's needed to build and/or run the
package?
Dependencies should be familiar to anyone who has used an .rpm and
.deb based desktop distribution. A dependency is something that a package
requires either to run the package (a run-time dependency) or to build the
package (a build-time or compile-time, dependency).
There are two variables provided to allow the specifications of
dependencies:
DEPENDS
Specifies build-time dependencies, via a list of bitbake
recipes to build prior to building the recipe. These are programs
(flex-native) or libraries (libpcre) that are required in order to
build the package.
RDEPENDS
Specifies run-time dependencies, via a list of packages to
install prior to installing the current package. These are programs
or libraries that are required in order to run the program. Note
that libraries which are dynamically linked to an application will
be automatically detected and added to RDEPENDS and therefore do not need to be
explicitly declared. If a library was dynamically loaded then it
would need to be explicitly listed.
If we take openssh for an example, it requires zlib and openssl in
order to both build and run. In the recipe we have:DEPENDS = "zlib openssl"This
tells bitbake that it will need to build and stage zlib and openssl prior
to trying to build openssh, since openssh requires both of them. Note that
there is no RDEPENDS even though openssh
requires both of them to run. The run time dependencies on libz1 (the name
of the package containing the zlib library) and libssl0 (the name of the
package containing the ssl library) are automatically determined and added
via the auto shared libs dependency code.
Methods: Inbuilt methods to make your life easier
There are several helper functions defined by the base class, which
is included by default for all recipes. Many of these are used a lot in
both recipes and other classes.