Ранее мы уже неоднократно обсуждали сборку 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:
    • %cmake -G Ninja – задаёт флаги компиляции и выполняет конфигурацию проекта в автоматическом режиме. Если необходимо, можно добавить опциональные параметры для cmake через пробел;
    • %cmake_build – запускает сборку проекта с использованием Ninja;
  • Qmake:
    • mkdir -p %{_vpath_builddir} – создаёт каталог, в котором будет выполняться процесс сборки (в %prep);
    • pushd %{_vpath_builddir} && %qmake_qt6 PREFIX=%{_prefix} CONFIG+=foo .. && popd – переходит в каталог сборки, задаёт флаги компиляции и выполняет конфигурацию проекта;
    • %make_build -C %{_vpath_builddir} – запускает сборку проекта;
  • Meson:
    • %meson – задаёт флаги компиляции и выполняет конфигурацию проекта;
    • %meson_build – запускает сборку проекта с использованием Ninja.

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

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

  • GNU Automake, Makefile, Qmake:
    • %make_install – выполняет стандартную цель install из сгенерированного Makefile. Опциональные параметры указываются через пробел;
  • Cmake:
    • %cmake_install – автоматическая установка средствами 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}/%{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
 
%build
%cmake -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DFOO=ON \
  -DBAR=OFF
%cmake_build
 
%install
%cmake_install
%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
 
%build
%cmake -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_TESTS=ON
%cmake_build
 
%install
%cmake_install
 
%check
%ctest
 
%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.

Литература

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