Базовые примеры

К данному моменту вам должно быть достаточно известно о bitbake для того, чтобы создать базовый рецепт. Сначала мы рассмотрим простой рецепт одного файла, а затем более сложный, использующий вспомогательный класс autotools (описываемый в разделе справочника autotools class) для построения пакета, основанного на autoconf.

Hello world

Пришло время для нашего первого рецепта. Это будет наверное один из самых простых возможных рецептов: весь код включен, только один файл для компиляции и один readme файл. Хотя в рецепте и не представлено все, что чаще всего встречается в рецептах, он является является весьма полезным так как не зависит ни от одного вспомогательного класса, которые чаще всего скрывают достаточно много информации о процессе сборки.

Для начала создадим файлы myhelloworld.c, readme и разместим их в поддиректорию files, которая входит в путь поиска file:// URI:

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

Теперь у нас есть директория для рецепта, recipes/myhelloworld, и мы создали поддиректорию files, в которой будут храниться локальные файлы. Также мы создали два локальных файлы, исходный код на C для программы helloworld и файл readme. Теперь осталось только создать bitbake рецепт.

Для начала необходимо описать заголовок, в котором будет содержаться описание пакета и номер релиза. Остальные заголовочные переменные пока оставим без изменения:

DESCRIPTION = "My hello world program"
      PR = "r0"

Теперь нужно указать, какие файлы включаются в рецепт, с помощью URI file:// URI и переменной SRC_URI:

SRC_URI = "file://myhelloworld.c \
           file://README.txt"

Обратите внимание на то, что мы используем \ для продолжения линии и локальный URI file:// вместо таких, как http://.

Теперь нужно описать задачу compile, в которой указываются инструкции по компиляции программы. Мы делаем это определением функции do_compile в рецепте, содержащей соответствующие команды:

do_compile() {
        ${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/myhelloworld.c -o myhelloworld
}

Обратите внимание на:

  • использование предопределенных переменных компилятора, ${CC}, ${CFLAGS} и ${LDFLAGS}. Они устанавливаются автоматически и содержат настройки, необходимые для кросс-компиляции программы для целевого устройства.

  • использование ${WORKDIR} для указания на файл исходного кода. Как было сказано ранее, все файлы копируются в рабочий каталог и доступ к ним должен осуществляться через переменную ${WORKDIR}.

И, наконец, мы хотим установить программу и файл readme в директории назначения, чтобы они были корректно упакованы. Это осуществляется задачей install, так что в рецепте должна быть определена функция do_install, описывающая, каким образом устанавливается пакет:

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
      }
      

Обратите внимание на:

  • использование команды install вместо cp для создания директорий и установки файлов.

  • способ, которым создаются директории перед попыткой установки файлов в них. Команда install заботится о всех отсутствующих поддиректориях, так что достаточно просто указать полный путь - нет необходимости создавать поддиректории.

  • то, как происходит установка в директорию назначения через переменную ${D}variable.

  • способ использования переменных для указания на целевые директории, такие как ${bindir} и ${docdir}.

  • использование ${WORKDIR} для доступа к README.txt файлу, обеспенному с помощью 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
}
^D
      

Теперь мы готовы собрать наш первый пакет, надеемся все заработает, так как это очень простой пример:

      ~/oe%> 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
~/oe%>
      

Пакет был успешно собран, на выходе было получено два ipkg файла, готовые к установке на целевую систему. В одном находится бинарник, а в другом readme файл:

~/oe%> 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
~/oe%>
      

Также стоит посмотреть, какие файлы были созданы в рабочей директории:

~/oe%> 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
~/oe%>
      

Здесь стоит обратить внимание на следующее:

  • Два исходных файла в рабочей директории tmp/work/myhelloworld-0.1-r0, на которую указывает переменная ${WORKDIR};

  • Для выполняемых задач велись логи в tmp/work/myhelloworld-0.1-r0/temp, которые можно посмотреть для детального изучения того, как проходило выполнение каждой задачи;

  • Была создана image директория tmp/work/myhelloworld-0.1-r0/image в которой просто расположены директории, из которых формировался пакет. Это и есть директория назначения, указанная в переменной ${D}. Два наших файла сначала были здесь, но в процессе упаковки были перемещены в область установки в поддиректорию, специфичную для создаваемого пакета (не забывайте, что мы создали основной пакет и пакет с документацией.

  • Программа компилировалась в директории tmp/work/myhelloworld-0.1-r0/myhelloworld-0.1, это директория исходного кода, описываемая переменной ${S}.

  • Была создана директория установки tmp/work/myhelloworld-0.1-r0/install, с содержащая сгенерированные пакеты и все файлы, которые были включены в пакет. То есть мы можем видеть myhelloworld-doc пакет, содержащий файл /usr/share/doc/myhelloworld/README.txt, myhelloworld пакет, содержащий файл /usr/bin/myhelloworld и пустые dev, dbg, local пакеты.

Теперь было бы неплохо проверить, что мы на самом деле создали бинарный файл для целевой системы, а не для хоста. Проверку можно сделать с помощью команды file:

~/oe%> 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
~/oe%> 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
~/oe%>
      

Вывод показал, что программа helloworld предназначена для SH процессора (очевидно, что это может измениться в зависимости от того, какова ваша целевая система), в то время как проверка /bin/ls программы на хосте показала, что установлена система AMD X86-64. То есть мы получили именно то, что хотели.

Основанный на autotools пакет

Теперь рассмотрим пакет, использующий autotools. Это программы, необходимые для запуска скрипта configure с различными параметрами, а затем запуска программы make. Чтобы это все работало при кросс-компиляции необходимо установить передать множество переменных configure скрипту. По счастью, вся эта сложная работа уже сделана за вас. Существует autotools class, который берет на себя всю сложность сборки пакетов, основанных на autotools.

Рассмотрим пример tuxnes рецепта, который является примером очень простого основанного на autotools рецепта:

%~oe> 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

Это на самом деле простой рецепт. В нем есть стандартный заголовок, описывающий пакет. Затем SRC_URI переменная, которая в нашем случае содержит http URL и поэтому требующая загрузки исходного кода с указанного URI. И, наконец, команда "inherit autotools", загружающая класс autotools. Этот класс обеспечивает создание необходимых configure, compile и install задач. Так что в нашем случае больше ничего делать не нужно.

Было бы хорошо, если на самом деле все обстояло так просто. К сожалению, часто возникают более сложные сложные задачи, например:

  • Передача параметров configure для включения и выключения некоторых свойств;

  • Передача параметров для указания того, где расположены библиотеки и заголовочные файлы;

  • Запрет поиска заголовков и библиотек по стандартным путям (так как они предназначены для хост-системы, а не для целевой);

  • Запрет скрипту configure компилировать и выполнять программы - так как то, что он компилирует, предназначено для целевой машины, а не для хоста, и, соответственно, не может быть запущено.

  • Реализация "staging" скриптов вручную;

  • Решение прочих сложных задач;

Некоторые из этих задач более подробно освещены в разделе autotools class.