Упаковка: Определение пакетов и их содержимого

Bitbake рецепт представляет собой набор инструкций по созданию одного или более пакетов для установки на целевое устройство. Обычно это бывают ipkg или deb пакеты (хотя bitbake сам по себе не связан ни с каким конкретным форматом пакетов).

По умолчанию несколько пакетов автоматически создается без каких-либо дополнительных указаний со стороны автора рецепта. Следующий пример вывода процесса упаковки из helloworld примера выше показывает создание таких пакетов:

[NOTE: package helloworld-0.1-r0: task do_package_write: started
NOTE: Not creating empty archive for helloworld-dbg-0.1-r0
Packaged contents of helloworld into /home/lenehan/devel/oe/build/titan-glibc-25/tmp/deploy/ipk/sh4/helloworld_0.1-r0_sh4.ipk
Packaged contents of helloworld-doc into /home/lenehan/devel/oe/build/titan-glibc-25/tmp/deploy/ipk/sh4/helloworld-doc_0.1-r0_sh4.ipk
NOTE: Not creating empty archive for helloworld-dev-0.1-r0
NOTE: Not creating empty archive for helloworld-locale-0.1-r0
NOTE: package helloworld-0.1-r0: task do_package_write: completed

Из приведенного примера видно, что при упаковке происходит следующее:

Здесь происходит несколько вещей, которые важно понять:

  1. Существует стандартный набор пакетов, которые должны быть установлены. Этот набор пакетов определяется переменной PACKAGES.

  2. Для каждого пакета существует стандартный набор файлов и/или директорий, которые считаются относящимися к тем или иным пакетам. Пакеты документации, например, включают все файлы из /usr/share/doc. Набор файлов и директорий определяется переменными FILES_<package-name>.

  3. По умолчанию пакеты, не содержащие никаких файлов, не создаются и ошибок при этом не выводится. Будут пустые пакеты создаваться или нет, определяется переменной ALLOW_EMPTY.

Философия

Раздельная упаковка, где это возможно, имеет большое значение в OpenEmbedded. Многие целевые устройства имеют ограниченную память (как оперативную, так и дисковую) и возможность не устанавливать ту часть пакета, которая не нужна, позволяет уменьшить количество требуемой памяти.

Например, практически нет дистрибутивов, включающих пакеты с документацией и пакеты для разработчиков, так как они не требуются для повседневной работы устройств. В частности, если пакет предоставляет несколько бинарных файлов, и чаще всего будут использоваться либо одни из них, либо другие, то стоит задуматься о разделении этого пакета на несколько.

По умолчанию некоторые группы файлов автоматически отделяются, например:

dev

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

doc

Любые файлы, относящиеся к документации, включая man страницы. Эти файлы служат только для информационных целей, а в большинстве случаев у пользователя даже нет возможности посмотреть какую-либо документацию на встраиваемом устройстве, поэтому нет смысла тратить память под нее. Благодаря отделению документации по умолчанию она не будет занимать место, но дистрибутивы и/или пользователи могут при необходимости выбрать установку документации по определенному пакету.

locale

Во многих случаях в файлах локализации нет необходимости, либо локализуются только элементы, относящиеся к пользовательскому интерфейсу, а не к системным бинарным файлам. Благодаря выделению таких файлов в другой пакет решение, устанавливать их или нет, остается за дистрибутивом или пользователем.

Пакеты и файлы по умолчанию

Настройки пакетов по умолчанию определены в conf/bitbake.conf и должны подходить для большинства рецептов без каких-либо изменений. Ниже приведен список значений по умолчанию для переменных, имеющих отношение к упаковке:

PACKAGES

В этом пакете содержатся имена всех пакетов, которые будут генерироваться.

              PACKAGES = "${PN}-dbg ${PN} ${PN}-doc ${PN}-dev ${PN}-locale"
              

Обратите внимание, что порядок пакетов важен: пакеты обрабатываются в указанном порядке. Так что если два пакета содержат один и тот же файл, то только первый пакет из списка получит его. Это бывает важно в том случае, когда пакеты используют символы подстановки.

Например, если основной пакет, ${PN}, содержит /usr/bin/* (то есть все файлы в /usr/bin), но вы хотите выделить /usr/bin/tprogram в отдельный пакет, ${PN}-tpackage, то нужно либо убедиться, что ${PN}-tpackage указан до ${PN} в переменной PACKAGES, либо изменить FILES_${PN} так, чтобы выражение подстановки не соответствовало /usr/bin/tprogram.

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

FILES_${PN}

Базовые пакет, включает все нужное для запуска приложения на целевой системе.

FILES_${PN} = "\
    ${bindir}/* \
    ${sbindir}/* \
    ${libexecdir}/* \
    ${libdir}/lib*.so.* \
    ${sysconfdir} \
    ${sharedstatedir} \
    ${localstatedir} \
    /bin/* \
    /sbin/* \
    /lib/*.so* \
    ${datadir}/${PN} \
    ${libdir}/${PN}/* \
    ${datadir}/pixmaps \
    ${datadir}/applications \
    ${datadir}/idl \
    ${datadir}/omf \
    ${datadir}/sounds \
    ${libdir}/bonobo/servers"
FILES_${PN}-dbg

Отладочная информация, извлеченная из неочищенных (non-stripped) версий библиотек и исполняемых файлов. OpenEmbedded автоматически извлекает отладочную информацию в файлы в .debug директориях и затем выполняет strip для оригинальных файлов.

FILES_${PN}-dbg = "\
    ${bindir}/.debug \
    ${sbindir}/.debug \
    ${libexecdir}/.debug \
    ${libdir}/.debug \
    /bin/.debug \
    /sbin/.debug \
    /lib/.debug \
    ${libdir}/${PN}/.debug"
FILES_${PN}-doc

Файлы документации. Вся документация выделяется в отдельный пакет так, что он не устанавливается без явного указания.

FILES_${PN}-doc = "\
    ${docdir} \
    ${mandir} \
    ${infodir} \
    ${datadir}/gtk-doc \
    ${datadir}/gnome/help"
FILES_${PN}-dev

Файлы, необходимые для разработки. Любые заголовочные файлы, библиотеки и файлы поддержки, необходимые для работ по разработке на целевой машине.

FILES_${PN}-dev = "\
    ${includedir} \
    ${libdir}/lib*.so \
    ${libdir}/*.la \
    ${libdir}/*.a \
    ${libdir}/*.o \
    ${libdir}/pkgconfig \
    /lib/*.a \
    /lib/*.o \
    ${datadir}/aclocal"
FILES_${PN}-locale

Файлы локализации.

FILES_${PN}-locale = "${datadir}/locale"

Символы подстановки

Символы подстановки, используемые в FILES переменных, обрабатываются python функцией fnmatch. Об этой функции следует знать следующие вещи:

  • /<dir>/*: Это соответствует всем файлам и директориям в dir - но не будет соответствовать другим директориям.

  • /<dir>/a*: Только файлы, но не директории.

  • /dir: Включает в пакет директорию dir, все файлы и поддиректории в ней.

Обратите внимание, что порядок пакетов влияет на то, как файлы будут сравниваться с шаблоном подстановки. Обратите внимание, что в том случае, когда есть три бинарных файла в директории /usr/bin и мы хотим поместить программу test в отдельный пакет:

/usr/bin/programa /usr/bin/programb /usr/bin/test
      

Тогда мы определяем новый пакет и указываем bitbake включить /usr/bin/test в него:

FILES-${PN}-test = "${bindir}/test"
PACKAGES += "FILES-${PN}-test"

Когда будет производиться упаковка, пакета ${PN}-test не будет создано. Причиной этому является то, что переменная PACKAGES имеет следующий вид:

{PN}-dbg ${PN} ${PN}-doc ${PN}-dev ${PN}-locale ${PN}-test
      

Обратите внимание на то, что ${PN} указан до ${PN}-test, и если посмотреть на определение FILES-${PN}, то можно увидеть, что оно содержит подстановку ${bindir}/*. Так как ${PN} указан ранее, все файлы, соответствующие этой подстановке будут перемещены в пакет ${PN} еще до обработки ${PN}-test.

Чтобы получить то, что мы хотели, есть два способа:

  1. Модифицировать определение ${PN} так, чтобы программа test под него не попадала.

    Это можно сделать например следующим образом:

    FILES-${PN} = "${bindir}/p*"

    Таким образом, под шаблон попадут только те файлы в bindir, имена которых начинаются с p, и соответственно test под него не попадет. Обратите внимание, что FILES-${PN} содержит значительно больше записей и стоило бы добавить в определение остальные. Но в нашем случае других файлов просто нет, поэтому можно оставить и такое простое определение.

  2. Изменить порядок пакетов так, чтобы ${PN}-test был первым.

    Наиболее простым способом сделать это является добавить test пакет перед списком пакетов вместо добавления после него:

    PACKAGES =+ "FILES-${PN}-test"

    В некоторых случаях это будет работать нормально, однако может возникнуть проблема с пакетами, включающими бинарные фейлы. Пакет test в этом случае будет стоять перед dbg пакетом и результатом может стать включение .debug директорий в неотладочный пакет. Поэтому нужно быть очень аккуратным с символами подстановки, а лучше вообще явно перечислять файлы.

    В общем случае лучше переопределять список пакетов целиком для добавления нового пакета и тех стандартных пакетов, которые вам необходимы:

              PACKAGES = "${PN}-dbg ${PN}-test ${PN} ${PN}-doc ${PN}-dev ${PN}-locale"
              

Проверка пакетов

В процессе разработки рецептов бывает полезно проверять, какие файлы были установлены в каждый пакет, какие файлы не были упакованы и какие пакеты оказались пустыми.

Одним из простейших способов является вызов find по директории install. В этой директории создается по поддиректории для каждого пакета, и файлы перемещаются сюда именно в порядке соответствия определенному пакету. Следующий пример показывает пакеты и файлы для пакета helloworld:

~/oe%> find tmp/work/helloworld-0.1-r0/install
tmp/work/helloworld-0.1-r0/install
tmp/work/helloworld-0.1-r0/install/helloworld-locale
tmp/work/helloworld-0.1-r0/install/helloworld-dbg
tmp/work/helloworld-0.1-r0/install/helloworld-dev
tmp/work/helloworld-0.1-r0/install/helloworld-doc
tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr
tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share
tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share/doc
tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share/doc/helloworld
tmp/work/helloworld-0.1-r0/install/helloworld-doc/usr/share/doc/helloworld/README.txt
tmp/work/helloworld-0.1-r0/install/helloworld
tmp/work/helloworld-0.1-r0/install/helloworld/usr
tmp/work/helloworld-0.1-r0/install/helloworld/usr/bin
tmp/work/helloworld-0.1-r0/install/helloworld/usr/bin/helloworld
~/oe%>

Как видно из примера, пакеты local, dbg и dev пусты, а doc и базовый содержат по одному файлу каждый. Использование опции "-type f" позволяет увидеть только файлы, что будет удобнее в нашем случае.

В дополнение к директории установки стоит посмотреть image директорию (которая соответствует директории назначения, D), содержащую неупакованные файлы:

~/oe%> find tmp/work/helloworld-0.1-r0/image
tmp/work/helloworld-0.1-r0/image
tmp/work/helloworld-0.1-r0/image/usr
tmp/work/helloworld-0.1-r0/image/usr/bin
tmp/work/helloworld-0.1-r0/image/usr/share
tmp/work/helloworld-0.1-r0/image/usr/share/doc
tmp/work/helloworld-0.1-r0/image/usr/share/doc/helloworld
~/oe%>

В этом случае все файлы были упакованы, и использование find с "-type f" позволяет лучше это увидеть:

~/oe%> find tmp/work/helloworld-0.1-r0/image -type f
~/oe%>

Сообщения об неупакованных файлах также выводятся bitbake в ходе выполнения задачи package:

NOTE: package helloworld-0.1-r0: task do_package: started
NOTE: the following files were installed but not shipped in any package:
NOTE:   /usualdir/README.txt
NOTE: package helloworld-0.1-r0: task do_package: completed

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

Исключение файлов

Нет явной поддержки для исключения файлов из процесса упаковки. Вы можете просто оставить их вне всех пакетов, но тогда будут выводиться предупреждения (или ошибки при запросе полной проверки пакетов) в процессе упаковки, а это нежелательно. Это также не позволит другим людям узнать, что вы намеренно исключили файлы из упаковки.

Для полного исключения файла можно просто не устанавливаться его во время выполнения задачи install.

В некоторых случаях бывает проще установить пакет, а затем явно удалить файл в конце задачи install. Следующий пример из рецепта samba показывает удаление нескольких файлов, устанавливаемых по умолчанию функцией install, генерируемой классом autotools class. Использовав do_install_append, мы делаем эти команды выполняемыми после сгенерированной функции install:

do_install_append() {
    ...
    rm -f ${D}${bindir}/*.old
    rm -f ${D}${sbindir}/*.old
    ...
}

Именование в стиле Debian

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

Debian именование включается добавлением debian класса в local.conf либо в в конфигурацию вашего дистрибутива:

INHERIT += "debian"

Политика работает следующим образом - рассматривается имя разделяемой библиотеки и версия, а затем пакет переименовывается в <libname><lib-major-version>. Например, если имя пакета PN foo и этот пакет содержит только файл libfoo.so.1.2.3, то пакет переименовывается в libfoo1 для соответствия Debian политике.

Если посмотреть на рецепт lzo_1.08.bb, на данный момент 14 релиз, то можно увидеть что он генерирует пакет, содержащий только одну разделяемую библиотеку:

~oe/build/titan-glibc-25%> find tmp/work/lzo-1.08-r14/install/
tmp/work/lzo-1.08-r14/install/lzo
tmp/work/lzo-1.08-r14/install/lzo/usr
tmp/work/lzo-1.08-r14/install/lzo/usr/lib
tmp/work/lzo-1.08-r14/install/lzo/usr/lib/liblzo.so.1
tmp/work/lzo-1.08-r14/install/lzo/usr/lib/liblzo.so.1.0.0

Без использования Debian политики этот пакет имел бы название lzo_1.08-r14_sh4.ipk (и соответствующие dev и dbg пакеты назывались бы lzo-dbg_1.08-r14_sh4.ipk и lzo-dev_1.08-r14_sh4.ipk). Однако при Debian именовании пакет переименовывается на основании имени разделяемой библиотеки, liblzo.so.1.0.0 в нашем случае. Так что lzo заменяется на liblzo1:

~oe/build/titan-glibc-25%> find tmp/deploy/ipk/ -name '*lzo*'  
tmp/deploy/ipk/sh4/liblzo1_1.08-r14_sh4.ipk
tmp/deploy/ipk/sh4/liblzo-dev_1.08-r14_sh4.ipk
tmp/deploy/ipk/sh4/liblzo-dbg_1.08-r14_sh4.ipk

Следующие переменные влияют на работу debian класса:

LEAD_SONAME

Если пакет на самом деле содержит несколько разделяемых библиотек, то одна из них будет выбрана автоматически и соответствующее предупреждение будет выведено. Эта переменная содержит регулярное выражение, используемое для выбора, какую из доступных библиотек использовать для Debian именования.

DEBIAN_NOAUTONAME_<pkgname>

Эта переменная должна быть установлена в 1 если к пакету не должно применяться Debian именование.

AUTO_LIBNAME_PKGS

Если переменная установлена, она определяет префикс пакетов, подлежащих переименованию. Это может использоваться для предотвращения переименования всех пакетов в соответствии с Debian политикой.

Пустые пакеты

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

ALLOW_EMPTY

Управляет тем, будут ли создаваться пустые пакеты. По умолчанию равна "0" и пустые пакеты не создаются. Установка переменной в "1" разрешит создание пустых пакетов.