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