View on GitHub

Methodics of GNU/Linux application software development

Practical materials for learning application software development on Linux

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

Работа системы с пакетами

Как мы помним из прошлого раздела, пакет представляет собой архив данных, предназначенный для распространения в составе дистрибутива - какого-то рабочего набора установочных программ из репозитория пакетов, оформленного в виде комплекта для распространения и установки ОС (дистрибутивом иногда называют саму ветку репозитория, с которой делается этот “срез”). Помимо самих файлов и правил раскладывания их по системе при установке в пакет входит информация о типах файлов. В зависимости от того, какую роль исполняет тот или иной элемент пакета, с ним необходимо поступать по-разному. В случае с бинарниками нужно производить замену старой версии на новую явно. Если же файл является конфигурационным, то правильнее будет сохранить предыдущую его версию при установке новой, или же вовсе просто отредактировать старую по добавленным изменениям.

Для того чтобы сохранять целостность системы при работе с пакетами каждый устанавливаемый пакет регистрируется в системе с указанием всех своих зависимостей, файлов, мест установки и т.д. Соответственно при удалении пакета система сама очищает все данные, возвращает системные данные (например, те самые старые сохранённые конфигурационные файлы) и дерегистрирует пакет. Также в пакетах передаются сценарии пред- и постустановки, выполняющие какие-либо системные функции. Среди них есть автоматически запускающиеся (например, добавление в систему информации об иконке устанавливаемого приложения, сохранение и установление доступа к новой страницы man), а также специализированные разработческие сценарии, требующие явного указания к исполнению при установке.

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

hasher и сборка пакета

Как мы помним, сборку пакета делает сборочница. Но перед отправкой нам для локальной проверки нам необходимо сделать себе какое-то сборочное окружение. Для создания изолированного сборочного окружения воспользуемся разработанным командой ALT Linux Team инструментом hasher. hasher при вызове обращается к репозиторию, собирает необходимый минимум данных для сборки, распаковывает их и собирает в изолированной среде.

Обсудим особенности данной утилиты:

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

TODO: Здесь нужны, наверное, примеры, но я сделать их не могу, hasher-а под OpenSUSE я не нашёл. Вернее как, нашёл в виде репозитория, который надо собрать, но пока мне это не поддалось.

  1. Для начала сделаем git clone проекта и развернём его в hasher-е. Мы специально выбрали проект со знакомым нам autotools (configure.ac и Makefile.am нам об этом явно подсказывают), соответственно, первое, что мы будем делать, это пробовать собирать проект и проверять его работоспособность.
  2. Важное правило при проверке будущего пакета - искать в логах работы configure информацию на непроверенные данные (строки вида check <...> ...no). Это поможет найти неучтённые автором репозитория зависимости или найти проблемы сборки. В нашей системе указана неучтённая зависимость sqlite и нескольких других библиотек. Проводим установку их devel-библиотек через hsh-install
  3. В общем случае мы не можем гарантировать себе безошибочную сборку проекта из репозитория, поскольку какие-то её особенности могли зависеть от конкретной системы разработчика (например, те самые отсутствующие в указанных зависимостях библиотеки, которые мы установили шагом ранее). В таком случае нам необходимо будет провести патчирование исходного кода для будущего пакета. В нашем случае всё собралось.
  4. Теперь небходимо проверить работоспособность собранного проекта. Правильным было бы собрать пакет, попробовать его поставить и проверить, заработает ли он. Однако так как проект небольшой (всего два бинарника), просто попробуем явно их запустить и протестировать. Здесь открывается особенность программы: в коде есть фиксированная константа, которая записывает сообщения в память только при накоплении 10922 сообщений. Для проверки работы перебьём константу на меньшее значение, но не просто поменяем значение, а сделаем ключ для уменьшения значения. К слову, чтобы оставить данное изменение в итоговом пакете, необходимо будет их добавить в рецепт пакета при его создании.

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

Name:        nfcollect
Version:     0.2
Release:     alt1
Summary:     Collect Netfilter NFLOG log entries and commit them to stable storage
Source:      %name-%version.tar.gz
Patch0:      <...>
Patch1:      <...>
URL:         https://github.com/yunchih/nfcollect
Group:       Networking/Other
License:     MIT

BuildRequires: <...>

%description
Collect packets from _Netfilter_ netlink kernel interface. Packets are aggregated onto a memory region (we call it _a trunk_), until the _trunk_ is full. A full _trunk_ will be committed to disk by configurable means (currently `zstd` compression and no compression is implemented). Trunks will be stored in a specific directory, which will be scanned by `nfextract` to extract all trunks.

%prep # Секция подготовки
%setup
%patch0 -p1
%patch1 -p1

%build # секция сборки
%autoreconf
%configure
%make_build

%install # секция установки
%makeinstall

Обсудим основные составляющие файла. Первые позиции - это паспорт пакета. В него входят имя, версия сборки, номер и индекс релиза, краткое описание пакета; источник данных (архив исходников) и наши патчи, которые мы хотим применить к ним; ссылка на репозиторий, принадлежность к группе пакетов (у них своя старая классификация, уже давно не отвечающая (или почти не отвечающая) на вопрос “А где искать тот или иной пакет?”), а также лицензия.

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

Далее описываются секции и макросы для установки данных. В секции %description описывается более подробно функционал пакета, секция %prep задаёт подготовительные действия над пакетом (у нас - разархивирование и применение патчей), в секциях %build и %install описываются этапы сборки и установки соответственно. Обработчики .spec-файлов пакетных менеджеров умеют работать с различными системами автосборки и имеют в запасе огромное количество макросов для упрощения записи рецептов. Вот, например, некоторые из них:

user@localhost:~> rpm --eval %autoreconf
autoreconf -fisv
user@localhost:~> rpm --eval %configure

  
  CONFIG_SHELL="${CONFIG_SHELL:-/usr/bin/bash}" ; export CONFIG_SHELL ; 
  CFLAGS="${CFLAGS:--O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables}" ; export CFLAGS ; 
  CXXFLAGS="${CXXFLAGS:--O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables}" ; export CXXFLAGS ; 
  FFLAGS="${FFLAGS:--O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables }" ; export FFLAGS ; 
  FCFLAGS="${FCFLAGS:--O2 -g -m64 -fmessage-length=0 -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables }" ; export FCFLAGS ; 
  LDFLAGS="${LDFLAGS:-}" ; export LDFLAGS; 
  ./configure --host=x86_64-suse-linux --build=x86_64-suse-linux \
	--program-prefix= \
	--disable-dependency-tracking \
	--prefix=/usr \
	--exec-prefix=/usr \
	--bindir=/usr/bin \
	--sbindir=/usr/sbin \
	--sysconfdir=/etc \
	--datadir=/usr/share \
	--includedir=/usr/include \
	--libdir=/usr/lib64 \
	--libexecdir=/usr/libexec \
	--localstatedir=/var \
	--sharedstatedir=/var/lib \
	--mandir=/usr/share/man \
	--infodir=/usr/share/info
user@localhost:~> 
user@localhost:~> rpm --eval %makeinstall
make DESTDIR="/home/user/rpmbuild/BUILDROOT/%{NAME}-%{VERSION}-%{RELEASE}.x86_64" install
user@localhost:~> 

Сборка пакета проводится с помощью пакетного менеджера с указанием .spec-файла командой

rpmbuild -ba RPM/SPECS/nfcollect.spec