Ранее мы уже неоднократно обсуждали сборку 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.
Литература
При написании статьи использовалась литература из следующих источников: