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.
Header Practically all recipes start with a header section which describes various aspects of the package that is being built. This information is typically used directly by the package format (such as ipkg or deb) as it's meta data used to describe the package. Variables used in the header include: DESCRIPTION Describes what the software does. Hopefully this gives enough information to a user to know if it's the right application for them. The default description is: "Version ${PV}-${PR} of package ${PN}". HOMEPAGE The URL of the home page of the application where new releases and more information can be found. The default homepage is "unknown". SECTION The section is used to categorise the application into a specific group. Often used by GUI based installers to help users when searching for software. See for a list of the available sections. The default section is "base". PRIORITY The default priority is "optional". LICENSE The license for the application. If it is not one of the standard licenses then the license itself must be included (where?). As well as being used in the package meta-data the license is also used by the src_distribute class. The default license is "unknown".
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.