Создаём RPM пакеты для Fedora

Ранее мы уже неоднократно обсуждали сборку RPM пакетов, однако делали это по готовым SPEC файлам. Теперь пришло время подробно рассмотреть их устройство и научиться создавать пакеты, соответствующие современным гайдлайнам на примере дистрибутива Fedora.

Устройство SPEC файлов

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

Именование

Имя SPEC файла должно строго соответствовать названию проекта и заканчиваться расширением .spec, например: foo-bar.spec. Если в названии упаковываемого проекта присутствуют пробелы, они должны быть заменены дефисами. Использование символа подчёркивания для этой цели не допускается.

Директивы

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

Обязательные директивы:

  • Name — название проекта;
  • Version — версия проекта;
  • Release — версия сборки пакета:
    • нумерация всегда начинается с 1 (для предрелизных версий допускается использовать значения меньше единицы, т.е. 0.1, 0.2 и т.д.);
    • увеличивается мейнтейнером на единицу при каждой сборке пакета в Koji;
    • при увеличении значения Version сбрасывается на 1;
    • всегда завершается макросом %{?dist};
  • Summary — короткое описание упаковываемого проекта:
    • строго на английском языке;
    • не должно быть длиннее 70 символов;
    • в конце точка не ставится;
  • License — лицензия проекта и всех его ресурсов:
    • если проект лицензирован под несколькими лицензиями, необходимо их указать последовательно, в качестве разделителя применяя оператор and;
    • необходимо указать все лицензии контента, используемого в проекте;
    • хорошим тоном считается указание соответствия каждой лицензии конкретному модулю проекта в виде комментария внутри спека;
    • мейнтейнеры должны самостоятельно следить за списком допустимых к использованию лицензий;
    • тексты всех использованных в проекте лицензий должны быть упакованы вместе с пакетом посредством модификатора %license секции %files;
  • URL — адрес в сети Интернет официального сайта проекта или его репозитория;
  • Source — имя архива с исходными текстами проекта:
    • должен включать полный URL для загрузки;
    • при невозможности указания прямой ссылки необходимо добавить инструкцию по его созданию в виде комментария;
    • проект может включать несколько архивов. В таком случае директива должна завершаться числом от 0 до MAXINT, например Source0, Source1, …, Source999.

Опциональные директивы:

  • Patch — позволяет включить патч, который будет накладываться на исходные тексты перед их сборкой:
    • все патчи должны быть загружены в VCS вместе со спеком;
    • аналогично Source, можно включать несколько патчей;
    • по общему правилу, патчи накладываются в порядке их следования;
  • BuildRequires — указывает имена пакетов, которые должны быть установлены в обособленном mock окружении перед началом сборки:
    • каждый пакет указывается отдельной директивой;
    • нумерация не применяется;
    • допускается указать номер версии. Поддерживаются операторы сравнения;
  • Requires — жёстко задаёт зависимость от конкретного пакета (включая виртуальные), либо файла. Должен применяться только при необходимости. Обычные зависимости от библиотек в пакет добавляются автоматически. Допускается номер версии;
  • Provides — указывает информацию о дополнительных компонентах, предоставляемых пакетом. Чаще всего применяется в связке с Obsoletes;
  • Obsoletes — сообщает о том, что данный пакет заменяет собой другой;
  • BuildArch — устанавливает архитектуру. В большинстве случаев применяется в архитектурно-независимых (noarch) пакетах;
  • ExclusiveArch — позволяет жёстко задать список архитектур, для которых будет собран данный пакет. Любые не указанные здесь будут проигнорированы сборочной платформой;
  • ExcludeArch — исключает указанные здесь архитектуры из сборки;
  • Recommends — указывает т.н. «слабые зависимости», т.е. опциональные пакеты, без установки которых пакет может исправно функционировать;
  • Epoch — задаёт эпоху пакета. Применяется если необходимо заменить пакет более высокой версии.

Секции

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

Обязательные секции:

  • %description — описание проекта:
    • строго на английском языке;
    • длина не ограничена;
    • должно заканчиваться точкой;
  • %prep — подготовка к сборке:
    • распаковывает архивы с исходными текстами;
    • применяет патчи;
  • %build — сборка пакета:
    • задаёт правильные флаги сборки;
    • выполняет последовательность команд для запуска сборки;
    • поддерживает готовые макросы для большинства сборочных систем;
  • %install — установка результата сборки:
    • выполняется автоматически посредством макросов для популярных сборочных систем;
    • при отсутствии готовой цели установка осуществляется посредством команды install;
  • %files — описывает все файлы внутри пакета:
    • допускается использовать символы подстановки (wildcards);
    • для текстов лицензионных соглашений применяется модификатор %license;
    • для файлов с документацией применяется модификатор %doc;
    • для различных файлов конфигурации — %config(noreplace);
    • может быть пустой если пакет не устанавливает никаких файлов (мета-пакет);
  • %changelog — список изменений в SPEC:
    • при каждом изменении значения директивы Release пакета должна быть добавлена соответствующая строка;
    • не допускается удаление предыдущих строк даже если они занимают очень много места;
    • должен содержать лишь список изменений внутри спека, поэтому не следует здесь указывать отличие между версиями проекта;
    • дата должна быть указана в правильном формате.

Опциональные секции:

  • %post — содержит скриптлет, который будет выполняться по окончании установки пакета;
  • %preun — содержит скриптлет, который будет выполняться перед удалением пакета;
  • %postun — содержит скриптлет, который будет выполняться по окончании удаления пакета;
  • %posttrans — содержит скриптлет, который будет выполняться по завершении транзакции;
  • %check — выполняет различные проверки, запускает тесты и т.д.

Автоматическая проверка

Для проверки валидности SPEC файлов рекомендуется применять утилиту rpmlint:

rpmlint foo-bar.spec

По результатам будет выдано заключение в виде ошибок (E), предупреждений (W) и сообщений (N). Ошибки обязательно следует исправить, предупреждения и сообщения можно игнорировать.

Подготовительная секция

Как уже говорилось выше, секция %prep предназначена для подготовки проекта к сборке. Для автоматической распаковки архивов с исходными текстами (тарболов) в большинстве случаев применяется макрос %autosetup, автоматизирующий это действие.

Autosetup выполняет автоматическую распаковку первого указанного Source, а также накладывает все патчи последовательно, по порядку их следования в директивах Patch.

Опциональные параметры:

  • -p — аналогичен соответствующему параметру GNU Patch. Позволяет указать сколько вложенных каталогов следует отбросить при его применении;
  • -n — позволяет задать название корневого каталога внутри тарбола. Без его указания autosetup ожидает %{name}-%{version}, что часто не соответствует действительности.

Прочие тарболы (при их наличии) следует распаковать самостоятельно:

tar -xf %{SOURCE1}

Секция сборки

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

Рассмотрим самые популярные системы сборки:

  • GNU Automake:
    • %configure — задаёт флаги компиляции и автоматически вызывает ./configure скрипт. Если необходимо, можно добавить опциональные параметры через пробел;
    • %make_build — запускает процесс сборки в несколько потоков (в зависимости от количества доступных вычислительных ядер CPU);
  • Makefile:
    • %set_build_flags — задаёт флаги компиляции;
    • %make_build — запускает процесс сборки;
  • Cmake:
    • mkdir -p %{_target_platform} — создаёт каталог, в котором будет выполняться процесс сборки (в %prep);
    • pushd %{_target_platform} %cmake -G Ninja .. popd — переходит в каталог сборки, задаёт флаги компиляции и выполняет конфигурацию проекта. Если необходимо, можно добавить опциональные параметры для cmake через пробел;
    • %ninja_build -C %{_target_platform} — запускает сборку проекта с использованием Ninja;
  • Qmake:
    • mkdir -p %{_target_platform} — создаёт каталог, в котором будет выполняться процесс сборки (в %prep);
    • pushd %{_target_platform} %qmake_qt5 PREFIX=%{_prefix} CONFIG+=foo .. popd — переходит в каталог сборки, задаёт флаги компиляции и выполняет конфигурацию проекта;
    • %make_build -C %{_target_platform} — запускает сборку проекта;
  • Meson:
    • %meson — задаёт флаги компиляции и выполняет конфигурацию проекта;
    • %meson_build — запускает сборку проекта с использованием Ninja.

Секция установки

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

  • GNU Automake, Makefile, Qmake:
    • %make_install — выполняет стандартную цель install из сгенерированного Makefile. Опциональные параметры указываются через пробел;
  • Cmake:
    • %ninja_install -C %{_target_platform} — установка средствами Ninja;
    • %make_install -C %{_target_platform} — установка средствами Makefile если генеатор Ninja не применялся;
  • Meson:
    • %meson_install — установка средствами Ninja.

Если секция install в проекте не используется, то мейнтейнер должен выполнять установку самостоятельно посредством ручного запуска команды install:

# Создаём каталог для размещения бинарника...
mkdir -p %{buildroot}%{_bindir}
# Устанавливаем бинарник...
install -m 0755 -p out/foo-bar "%{buildroot}%{_bindir}/%{name}"

Стандартные chmod для файлов и каталогов:

  • файлы:
    • 0755 — исполняемые;
    • 0644 — все остальные;
  • каталоги:
    • 0755 (можно не указывать при их создании средствами mkdir).

Если в проекте имеются локализации, то необходимо использовать макрос %find_lang для их поиска и добавления в секцию %files.

Если производилась установка AppStream или Desktop файлов, то в SPEC файле должна присутствовать секция %check с функциями их проверки:

appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/%{name}.appdata.xml
desktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop

Примеры пакетов

Листинг простейшего проекта 1 с использованием GNU Automake:

Name: foo-bar
Version: 1.0
Release: 1%{?dist}

License: GPLv3+
Summary: Example application

URL: https://github.com/example/%{name}
Source0: %{url}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz

BuildRequires: desktop-file-utils
BuildRequires: libappstream-glib
BuildRequires: gcc-c++
BuildRequires: gcc

%description
Full description of our example application.

%prep
%autosetup -p1

%build
%configure
%make_build

%install
%make_install PREFIX=%{_prefix}

%check
appstream-util validate-relax --nonet "%{buildroot}%{_datadir}/metainfo/%{name}.appdata.xml"
desktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop

%files
%doc README.md
%license LICENSE
%{_bindir}/%{name}
%{_mandir}/man1/%{name}.*
%{_datadir}/applications/%{name}.desktop
%{_datadir}/metainfo/%{name}.appdata.xml
%config(noreplace) %{_sysconfdir}/default/%{name}

%changelog
* Mon Jan 28 2019 Your Name <[email protected]> - 1.0-1
- Initial SPEC release.

Листинг проекта 2 с использованием фреймворка Qt, использующего для сборки cmake и лицензированного под множественной лицензией:

Name: foo-bar
Version: 1.0
Release: 1%{?dist}

# Main code - GPLv3+;
# Logger module - MIT
# Icon - CC-BY-SA
License: GPLv3+ and MIT and CC-BY-SA
Summary: Example application

URL: https://github.com/example/%{name}
Source0: %{url}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
Patch0: %{name}-fix-gcc90.patch

BuildRequires: cmake(Qt5LinguistTools)
BuildRequires: cmake(Qt5Multimedia)
BuildRequires: cmake(Qt5X11Extras)
BuildRequires: cmake(Qt5Network)
BuildRequires: cmake(Qt5Core)
BuildRequires: cmake(Qt5DBus)
BuildRequires: cmake(Qt5Gui)

BuildRequires: desktop-file-utils
BuildRequires: libappstream-glib
BuildRequires: gcc-c++
BuildRequires: cmake
BuildRequires: ninja
BuildRequires: gcc

%description
Full description of our example application.

%prep
%autosetup -p1
mkdir -p %{_target_platform}

%build
pushd %{_target_platform}
    %cmake -G Ninja \
    -DFOO=ON \
    -DBAR=OFF \
    ..
popd
%ninja_build -C %{_target_platform}

%install
%ninja_install -C %{_target_platform}
%find_lang %{name} --with-qt

%check
appstream-util validate-relax --nonet "%{buildroot}%{_datadir}/metainfo/%{name}.appdata.xml"
desktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop

%files -f %{name}.lang
%doc README.md
%license LICENSE
%{_bindir}/%{name}
%{_mandir}/man1/%{name}.*
%{_datadir}/applications/%{name}.desktop
%{_datadir}/metainfo/%{name}.appdata.xml

%changelog
* Mon Jan 28 2019 Your Name <[email protected]> - 1.0-1
- Initial SPEC release.

Листинг проекта 3 — динамической библиотеки с использованием Cmake и созданием дополнительных пакетов для заголовочных файлов и запуском юнит-тестов:

Name: foo-bar
Version: 1.0
Release: 1%{?dist}

License: GPLv3+
Summary: Example shared library

URL: https://github.com/example/%{name}
Source0: %{url}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz

BuildRequires: gcc-c++
BuildRequires: cmake
BuildRequires: ninja
BuildRequires: gcc

%description
Full description of our shared library.

%package devel
Summary: Development files for %{name}
Requires: %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}

%description devel
%{summary}.

%prep
%autosetup -p1
mkdir -p %{_target_platform}

%build
pushd %{_target_platform}
    %cmake -G Ninja \
    -DBUILD_TESTS=ON \
    ..
popd
%ninja_build -C %{_target_platform}

%install
%ninja_install -C %{_target_platform}

%check
pushd %{_target_platform}
    ctest --output-on-failure
popd

%files
%doc README.md
%license LICENSE
%{_libdir}/lib%{name}.so.0*

%files devel
%{_includedir}/%{name}
%{_libdir}/lib%{name}.so
%{_libdir}/cmake/%{name}
%{_libdir}/pkgconfig/%{name}.pc

%changelog
* Mon Jan 28 2019 Your Name <[email protected]> - 1.0-1
- Initial SPEC release.

Литература

При написании статьи использовалась литература из следующих источников: