3. Customizing images

There are multiple ways to manipulate the contents of the file system image. The following sections describe some of the ways. Some ways need an overlay of your own. So the first thing we do is to create this overlay.

We kind of created a half overlay already in Section 1.4, “Setting up the environment”. By specifying /develop/oe/build in BBPATH, bitbake is already instructed to look for configuration files in this directory. This includes machine definitions. To make it a full overlay, just replace

BBFILES = " \
		/develop/oe/taskit-overlay/recipes/*/*.bb \
		/develop/oe/openembedded/recipes/*/*.bb \
"

with

BBFILES = " \
		/develop/oe/build/recipes/*/*.bb \
		/develop/oe/taskit-overlay/recipes/*/*.bb \
		/develop/oe/openembedded/recipes/*/*.bb \
"

in your /develop/oe/conf/local.conf

Now, bitbake will also look for recipes (package descriptions) in this directory.

3.1. Creating your own machine

This section overlaps in parts with Section 3.3, “Customizing existing packages”, because you need to customize the kernel package to add a machine.

By adding your board as a new machine, you can add board specific customizations to packages. You could of course do that with the machine, you base your work on, e.g. stamp9g20evb, but than you will not be able to use the original customizations as a reference anymore.

The only disadvantage of creating your own machine is, that you have to replicate customizations you need/want, that have already been done for other machines.

Adding a machine is most of the time a two step process, see Procedure 7.1, “Adding a machine”

Procedure 7.1. Adding a machine

  1. Add a machine definition

  2. Add a kernel recipe/customize an existing kernel recipe

    As you create a machine based on a taskit product, you will most probably just alter the kernel configuration used to build the kernel.

3.1.1. Adding a machine definition

Creating a new machine definition most of time boils down to copying an existing machine definition and modifying it. As your are using a taskit product, copy the corresponding file from /develop/taskit-overlay/conf/machine/ to /develop/build/conf/machine/. Create the directory beforehand.

As an example, we create a new machine called custommachine, based on the Stamp9G20 EVB. We copy the file stamp9g20evb.conf to your overlay and call it custommachine.conf:

cp /develop/oe/taskit-overlay/conf/machine/stamp9g2evb.conf \
/develop/oe/build/conf/machine/custommachine.conf

The contents of the file should now look like Example 7.4, “Custom machine definition before modification”

#@TYPE: Machine
#@Name: taskit Stamp9G20 Evaluation Board 1
#@DESCRIPTION: Machine configuration for the Stamp9G20 Evaluation Board 2

TARGET_ARCH = "arm"

PREFERRED_PROVIDER_virtual/kernel = "linux"

KERNEL_IMAGETYPE = "uImage"

#don't try to access tty1
USE_VT = "0"

MACHINE_FEATURES = "kernel26 ext2 vfat usbhost usbgadget" 3

# used by sysvinit_2
SERIAL_CONSOLE = "115200 ttyS0"
IMAGE_FSTYPES ?= "jffs2" 4
EXTRA_IMAGECMD_jffs2 = "--little-endian --eraseblock=0x20000 -n" 5

require conf/machine/include/tune-arm926ejs.inc

Example 7.4. Custom machine definition before modification


1 2

These are the name and description of your machine. Replace it with approriate text.

3

These features will be used in task based images or in tasks in general. Tasks are packages that do not contain any files but just dependencies. They will add extra dependencies to some general tasks (e.g. task-base). So this is a place, where you can influence to some degree, what packages will be added to the file system image. For available machine features, look into recipes/tasks/task-base.bb in the OpenEmbedded repository.

As an example, let us say you do not need any usb support but want to to have pppd in all images (works only for task based images or images explicitly using these variables). To achieve that, remove usbhost and usbgadget from MACHINE_FEATURES and add ppp.

4

These are the fstypes normally build for this machines. We have overridden this value in Example 7.1, “Bitbake local configuration”.

5

These are extra parameters for the mkfs.jffs2 command and are essential to build correct jffs2 images for the device. Do not change unless you know what you are doing.

You can also add two further variables MACHINE_ESSENTIAL_EXTRA_RDEPENDS and MACHINE_EXTRA_RDEPENDS. You can add package names to both variables. The former is used for packages essential to boot and will be added as dependencies for task-boot. These should land in almost all images (provided they use task-boot). Packages mentioned in the second will be added as dependencies for task-base.

As a last resort, you can also add the variable IMAGE_EXTRA_INSTALL and list packages that should end in all images. It is highly discouraged to use this method. It is better to create your own image if the other described methods are not enough to customize the image. See Section 3.2, “Creating your own image”.

After the mentioned modifications, your machine definition could look like in Example 7.5, “Custom machine definition after modification”.

#@TYPE: Machine
#@Name: Custom Machine
#@DESCRIPTION: My first custom machine

TARGET_ARCH = "arm"

PREFERRED_PROVIDER_virtual/kernel = "linux"

KERNEL_IMAGETYPE = "uImage"

#don't try to access tty1
USE_VT = "0"

MACHINE_FEATURES = "kernel26 ext2 vfat ppp"

# used by sysvinit_2
SERIAL_CONSOLE = "115200 ttyS0"
IMAGE_FSTYPES ?= "jffs2"
EXTRA_IMAGECMD_jffs2 = "--little-endian --eraseblock=0x20000 -n"

require conf/machine/include/tune-arm926ejs.inc

Example 7.5. Custom machine definition after modification


3.1.2. Customizing the kernel recipe

Now it is time to customize the kernel image so that you can let bitbake build the kernel and the file system. This is especially useful if you want to include kernel modules in the file system.

The first step is to create a kernel config as described in Chapter 6, Compiling a new Linux kernel.

After doing so, we copy the kernel recipe and corresponding files used for the Stamp9G20 Evaluation Board to your overlay.

cp -r /develop/oe/taskit-overlay/recipes/linux /develop/oe/build/recipes

Now create the directory /develop/oe/build/recipes/linux/linux-2.6.29/custommachine. In this directory, place the file .config from the kernel configuration process renamed to defconfig.

Finally edit the kernel recipe itself (/develop/oe/build/recipes/linux/linux_2.6.29.bb). You should duplicate all Stamp9G20 specific lines, in this case DEFAULT_PREFERENCE and SRC_URI_append. DEFAULT_PREFERENCE tells bitbake, which recipe it should build. The recipe with the highest DEFAULT_PREFERENCE for a given machine is built (if the recipe version is not fixed with PREFERRED_VERSION_recipename in one of the config files). See Example 7.6, “Customized kernel recipe” for reference.

require recipes/linux/linux.inc

S = "${WORKDIR}/linux-2.6.29"

# Mark archs/machines that this kernel supports
DEFAULT_PREFERENCE = "-1"
DEFAULT_PREFERENCE_stamp9g20evb = "1"
DEFAULT_PREFERENCE_custommachine = "1"

SRC_URI = "${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/linux-2.6.29.tar.bz2 \
					 file://defconfig"

SRC_URI_append_stamp9g20evb = " \
		file://stamp9g20.patch;patch=1 \
"
SRC_URI_append_custommachine = " \
		file://stamp9g20.patch;patch=1 \
"

Example 7.6. Customized kernel recipe


3.2. Creating your own image

Creating your own image is relatively easy and the most straightforward way to get exactly what you want into the root file system. You can do it by either extending an existing image or creating a completely new one.

New image recipes should be created in your overlay in the recipes/images directory. We will call our image custom-image so create the file custom-image.bb in this directory.

As a first example, we extend taskit-image to get additional packages into it. See Example 7.7, “custom-image.bb: Extending taskit-image” for reference.

require recipes/images/taskit-image.bb 1

IMAGE_INSTALL += " \
		iptables \ 2
"

export IMAGE_BASENAME="custom-image" 3

Example 7.7. custom-image.bb: Extending taskit-image


1

This includes the contents of the taskit-image. The full path ensures, that it is found although it is in another overlay.

2

This adds iptables to the contents of your image.

3

This tells the build system how to name the file system image. If you do not set this variable, the image will be called the same as the extended image.

Now imagine you want to base your image on taskit-image, but it does contain stuff you do not want, e.g. the java stack, and you still want to add iptables. This can only be solved by copying, renaming and editing the image file. Example 7.8, “taskit-image.bb” shows you the contents of the taskit-image (at the time of this writing).

IMAGE_PREPROCESS_COMMAND = "create_etc_timestamp" 1

DISTRO_SSH_DAEMON ?= "dropbear"
DISTRO_PACKAGE_MANAGER ?= "opkg-nogpg opkg-collateral"

IMAGE_LINGUAS = "en-gb de-de fr-fr" 2

IMAGE_INSTALL += " \
		busybox \
		modutils-initscripts \
		netbase \
		base-files \
		base-passwd \
		update-alternatives \
		${MACHINE_ESSENTIAL_EXTRA_RDEPENDS} \
		\
		${DISTRO_PACKAGE_MANAGER} \
		${DISTRO_SSH_DAEMON} \
		mtd-utils \
		u-boot-utils \
		jamvm \
		librxtx-java \
		librxtx-jni \
		gdbserver \
		strace \
		libstdc++ \
		${@base_contains('MACHINE_FEATURES', 'ext2', 'task-base-ext2', '', d)} \
		${@base_contains('MACHINE_FEATURES', 'vfat', 'dosfstools', '', d)} \
		${@base_contains('MACHINE_FEATURES', 'ppp', 'task-base-ppp', '', d)} \
"

export IMAGE_BASENAME="taskit-image"

inherit image 3

Example 7.8. taskit-image.bb


1

This instructs the build system to create a timestamp in /etc in the root file system, so that you always know, when the file system image was created.

2

This variable contains all locales, for which the binary versions should be installed. If you do not need localization in your application, you can leave this variable empty.

3

This statement has to be in all images, that do not derive from other images. It inherits the image bitbake class, which includes all code needed for building images.

Now let us say, you want to get rid of the java stack, do not need the flexibility of MACHINE_FEATURES, don't use localization and want to include iptables. Additionally, you do not need the C++ library (it would be added anyway, if it was needed by a package). The resulting image recipe would look like in Example 7.9, “custom-image.bb: Complete new image”

IMAGE_PREPROCESS_COMMAND = "create_etc_timestamp"

DISTRO_SSH_DAEMON ?= "dropbear"
DISTRO_PACKAGE_MANAGER ?= "opkg-nogpg opkg-collateral"

IMAGE_LINGUAS = ""

IMAGE_INSTALL += " \
		busybox \
		modutils-initscripts \
		netbase \
		base-files \
		base-passwd \
		update-alternatives \
		${MACHINE_ESSENTIAL_EXTRA_RDEPENDS} \
		\
		${DISTRO_PACKAGE_MANAGER} \
		${DISTRO_SSH_DAEMON} \
		mtd-utils \
		u-boot-utils \
		gdbserver \
		strace \
		iptables \
"

export IMAGE_BASENAME="custom-image"

inherit image

Example 7.9. custom-image.bb: Complete new image


3.3. Customizing existing packages

This section will only discuss customization for a specific device. You can of course make modifications to all recipes as you like without keeping them specific to one machine.

Machine specific customization of a recipe can be done in two ways:

  • overriding variables and functions in the recipe

  • overriding files (given in the SRC_URI variable) in the recipe subdirectories

Overriding of variables and functions is easy. You just take the variable/function name, add the machine name separated with an underscore to the end. We have already used this in Example 7.6, “Customized kernel recipe”.

In this example, we have also overridden one file. To do that, there can be multiple directories, where you can place the file. If you have a bitbake called foo_1.0.bb (meaning it is package foo, version 1.0), your file to override can be placed in the following subdirectories in the recipe directory (ordered by priority):

  1. foo-1.0: Files in this directory are only used for the recipe foo, version 1.0.

  2. foo: Files in this directory are used for all recipes called foo without looking at the version.

  3. files: Files in this directory are used for all recipes in the directory, without looking at the name or version (You can have multiple completely different recipes in each directory. Directories are just used for categorizing recipes.).

So to actually override one of the files, create a directory custommachine in one of these directories and place a file with the name you want to override there. It will be used instead of the default ones.

The only thing you have to keep in mind when you customize a package by copying it to your overlay is, that you also have to copy the default files, or at least the ones, you do not override.

3.4. Adding own packages

Sooner or later, you want to add your own application to the root file system. This section will tell you how to do it for a simple application (some source files resulting in one standalone binary, no data files, no extra libraries needed).

The sample project contains three files:

  • main.c

  • util1.c

  • util2.c

To build this project we use the Makefile shown in Example 7.10, “Makefile”. It will create a binary called app from the sources.

app: main.o util1.o util2.o
	${LINK.c} $? -o $@

clean:
	rm -r app *.o

install:
	install -d ${DESTDIR}/bin
	install -m755 app ${DESTDIR}/bin/app

Example 7.10. Makefile


To build the binary with this make file, just enter the following command:

make CC=arm-angstrom-linux-gnueabi-gcc

To install the binary, the make file includes an install rule. This rule is basically there to ease the writing of the bitbake recipe later. The destination directory will be given in the DESTDIR variable, e.g.:

make DESTDIR=/develop/appinstall install

To make the recipe even more easy to write, put the sources and the make file in a directory called app-1.0 (we assume the project is called app and is the first release version).

Now we create a tarball from this directory.

tar -cvjf app-1.0.tar.bz2 app-1.0

It is now time to create the recipe. To do that, create a directory called recipes/app in your overlay and put a file called app_1.0.bb in there, see Example 7.11, “app_1.0.bb” for reference. Additionally create a files directory and put the tarball there.

SRC_URI="file://${PN}-${PV}.tar.bz2" 1
PR="r0" 2

do_install () {
	oe_runmake install DESTDIR=${D} 3
}

Example 7.11. app_1.0.bb


1

This line tells bitbake where it can find the sources. The variables PN and PV are automatically expanded to the package name and package version. To use these variable has the advantage, that you can update the recipe to a new version by just renaming it.

If you do not want to copy the tarball to the overlay or have it on a webserver, you can of course replace the path with either an absolute path or the URL, e.g.:

SRC_URI="file:///develop/${PN}-${PV}.tar.bz2"

or

SRC_URI="http://hostname/${PN}-${PV}.tar.bz2"

2

This is the package revision. If you leave it out, it defaults to r0. The value should be incremented every time you modify this recipe so that bitbake knows it has to rebuild it and the package manager opkg knows it needs to update the package in the root file system after it is build.

3

To install any files, you have to implement the install task (do_install). We use the function oe_runmake to call the install target of our make file. The variable D holds the temporary directory, where all files, that should go into packages, should be installed, so we pass the contents of it to the DESTDIR variable.

Apart from that, we do not need anything else in the recipe, as the defaults of OpenEmbedded handle the rest for us.