Структура spec-файла: Основные разделы и базовый синтаксис
Структура spec-файла: Основные разделы и базовый синтаксис
В предыдущем модуле мы разобрали архитектуру RPM и поняли, как пакетный менеджер взаимодействует с системой. Теперь пришло время спуститься на уровень разработчика и научиться создавать пакеты. Сердцем любого RPM-пакета является spec-файл (spec file от слова specification). Это текстовый документ, который содержит пошаговую инструкцию: откуда взять исходный код, как его подготовить, как скомпилировать и какие файлы в итоге нужно упаковать в готовый архив.
Если RPM-пакет — это готовое блюдо, то исходный код — это ингредиенты, а spec-файл — это подробный кулинарный рецепт. В этой статье мы разберём анатомию этого «рецепта», изучим базовый синтаксис и научимся писать спеки, соответствующие стандартам Rosa Linux.
Общая архитектура spec-файла
Spec-файл имеет строгую, исторически сложившуюся структуру. Он читается сверху вниз и делится на две логические части:
- Преамбула (Preamble) — метаданные пакета (название, версия, лицензия, зависимости). Эта часть описывает что мы собираем.
- Секции сборки (Build sections) — набор скриптов и директив, описывающих как мы это собираем. Каждая секция начинается со знака процента (например,
%prep,%build).
Давайте рассмотрим минимальный рабочий пример spec-файла для вымышленной утилиты hello-world, а затем разберём каждую его строку под микроскопом.
Name: hello-world
Version: 1.0
Release: 1
Summary: Простая утилита для вывода приветствия
License: GPLv3+
URL: https://example.com/hello-world
Source0: %{name}-%{version}.tar.gz
BuildRequires: gcc
BuildRequires: make
%description
Утилита hello-world выводит приветственное сообщение в терминал.
Она служит отличным примером для изучения сборки RPM-пакетов.
%prep
%autosetup
%build
%configure
%make_build
%install
%make_install
%files
%license LICENSE
%doc README.md
%{_bindir}/hello-world
%changelog
* Mon Oct 23 2023 Ivan Ivanov <ivan@example.com> - 1.0-1
- Инициализация пакета
На первый взгляд код может показаться набором заклинаний, но за каждым тегом стоит чёткая логика.
Преамбула: Паспорт вашего пакета
Преамбула не имеет специального открывающего тега. Она начинается с первой строки файла и содержит пары «Ключ: Значение». Это паспортные данные пакета, которые пользователь увидит при выполнении команды rpm -qi имя_пакета.
Основные теги метаданных
Name— базовое имя пакета. Оно должно состоять из строчных букв, цифр и дефисов. Избегайте нижних подчеркиваний.Version— версия программы (апстрим-версия), точно совпадающая с версией разработчика исходного кода. В версии запрещено использовать дефисы, так как RPM использует дефис для отделения версии от релиза.Release— номер сборки (релиза) пакета для текущей версии. Если вы изменили spec-файл, но версия программы осталась прежней (например, добавили патч или исправили зависимость), вы увеличиваетеRelease(1, 2, 3...). При обновленииVersionзначениеReleaseсбрасывается обратно в 1.Summary— краткое описание пакета. Строго в одну строку, без точки на конце. Это то, что пользователи видят при поиске пакетов.License— лицензия, под которой распространяется код. В Rosa Linux принято использовать короткие идентификаторы стандарта SPDX (например,GPL-2.0-or-later,MIT,Apache-2.0).URL— ссылка на домашнюю страницу проекта.
Исходники и патчи
Source0— имя архива с исходным кодом. Обычно это tar-архив (.tar.gz,.tar.xz). Цифра 0 означает, что это первый (и часто единственный) исходник. Если программе нужны дополнительные файлы (иконки, конфигурации по умолчанию), их добавляют какSource1,Source2и так далее.Patch0— файл с изменениями (патч), который нужно применить к исходному коду перед сборкой. Патчи используются для исправления ошибок или адаптации программы под специфику Rosa Linux без изменения оригинального архиваSource0.
Зависимости: BuildRequires против Requires
Это один из самых важных аспектов, в котором новички часто допускают ошибки.
BuildRequires— пакеты, необходимые только для сборки программы. Например: компиляторы (gcc,clang), системы сборки (cmake,meson), заголовочные файлы библиотек (pkgconfig(gtk+-3.0),openssl-devel). Эти пакеты устанавливаются в сборочной среде (на серверах ABF) и не тянутся на компьютер конечного пользователя.Requires— пакеты, необходимые для работы уже установленной программы. Например, если ваша утилита написана на Python, ей нужен интерпретатор Python для запуска.
Важное правило: Современный RPM очень умён. Он автоматически анализирует скомпилированные бинарные файлы и сам прописывает зависимости от системных библиотек (например,
libc.so.6). Поэтому вRequiresнужно указывать только неочевидные зависимости (скрипты, внешние утилиты, специфические шрифты), которые RPM не может определить автоматически.
Макросы: Переменные мира RPM
Вы наверняка заметили в примере конструкции вида %{name} или %{_bindir}. Это макросы — встроенные переменные RPM.
Макросы решают три критически важные задачи:
- Избавление от дублирования. Если вы назвали пакет
hello-worldв тегеName, вам не нужно писатьhello-world-1.0.tar.gzвSource0. Вы пишете%{name}-%{version}.tar.gz. При изменении версии вам нужно будет обновить только одну строку в преамбуле. - Кросс-платформенность и стандартизация. В разных дистрибутивах пути к системным папкам могут отличаться. Например, 64-битные библиотеки в одних системах лежат в
/usr/lib, а в Rosa Linux — в/usr/lib64. - Управление флагами компилятора. Макросы передают правильные параметры оптимизации и безопасности при сборке.
Базовые макросы путей
Никогда не используйте жестко заданные пути (хардкод) в spec-файлах. Всегда используйте макросы:
| Жесткий путь (Неправильно) | Макрос (Правильно) | Назначение |
|---|---|---|
/usr/bin |
%{_bindir} |
Исполняемые файлы (бинарники) |
/usr/lib64 или /usr/lib |
%{_libdir} |
Системные библиотеки |
/etc |
%{_sysconfdir} |
Конфигурационные файлы |
/usr/share/doc |
%{_docdir} |
Документация |
/usr/share/man |
%{_mandir} |
Руководства (man pages) |
Секция %prep: Подготовка плацдарма
Секция %prep (от preparation) — это первый этап реальной работы. Здесь исходный код распаковывается из архива Source0 и подготавливается к компиляции. На этом этапе применяются патчи.
Исторически для распаковки использовался макрос %setup -q. Однако в современных спеках Rosa Linux стандартом де-факто стал макрос %autosetup.
%prep
%autosetup -p1
Что делает %autosetup -p1 под капотом?
- Удаляет старую директорию с исходниками (если она осталась от предыдущей неудачной сборки).
- Распаковывает архив
Source0. - Переходит в созданную директорию.
- Автоматически находит все файлы
Patch0,Patch1и т.д. в преамбуле и применяет их к исходному коду с параметром-p1(отбрасывая первый уровень вложенности путей в патче, что является стандартом для git-патчей).
Секция %build: Превращение кода в программу
В секции %build происходит магия компиляции. Здесь исходный код превращается в исполняемые бинарные файлы. Содержимое этой секции напрямую зависит от того, какую систему сборки использует разработчик программы (Autotools, CMake, Meson, Ninja, чистый Makefile).
Для классических программ на C/C++, использующих Autotools (скрипт configure), секция выглядит так:
%build
%configure
%make_build
Почему мы используем макрос %configure, а не пишем ./configure напрямую?
Макрос %configure автоматически подставляет десятки параметров, специфичных для Rosa Linux. Он указывает правильные пути (--prefix=/usr, --libdir=/usr/lib64), а также передает системные флаги компилятора (CFLAGS, CXXFLAGS, LDFLAGS), которые обеспечивают оптимизацию под архитектуру процессора и включают защиту от уязвимостей (например, ASLR и Stack Smashing Protection).
Макрос %make_build заменяет обычный вызов make. Его главное преимущество — он автоматически определяет количество ядер процессора на сборочном сервере и запускает параллельную сборку (эквивалент make -j8), что ускоряет процесс в разы.
Секция %install: Сборочный корень (Buildroot)
Секция %install — самая сложная для понимания концепция среди новичков.
Когда программа скомпилирована, её нужно «установить». Но если мы выполним стандартную команду make install, программа попытается установиться прямо в системные директории сборочного сервера (в /usr/bin, /etc и т.д.). Это приведет к хаосу: файлы пакета смешаются с файлами системы, и мы не сможем собрать их в RPM-архив. Более того, у процесса сборки обычно нет root-прав для записи в /usr.
Для решения этой проблемы используется сборочный корень (buildroot). Это временная, изолированная директория (песочница), которая эмулирует корневую файловую систему /.
В секции %install мы устанавливаем программу не в реальную систему, а в этот сборочный корень.
%install
%make_install
Макрос %make_install эквивалентен команде make install DESTDIR=%{buildroot}. Программа думает, что устанавливается в /usr/bin/hello-world, но физически файл копируется во временную папку, например: /home/builder/rpmbuild/BUILDROOT/hello-world-1.0-1.x86_64/usr/bin/hello-world.
Важно: Секция
%installне устанавливает пакет на ваш компьютер! Она лишь формирует структуру папок и файлов внутри временной директории, из которой RPM-утилита затем запакует архив.
Секция %files: Опись имущества
После того как файлы помещены в buildroot, RPM должен знать, какие именно файлы нужно включить в итоговый пакет. Этим занимается секция %files.
Если файл есть в buildroot, но не указан в %files, сборка завершится с ошибкой (ошибка unpackaged files found). Это механизм защиты: вы не должны случайно забыть упаковать нужный файл или упаковать лишний мусор.
%files
%license LICENSE
%doc README.md
%{_bindir}/hello-world
%{_sysconfdir}/hello-world.conf
Специальные директивы в %files
%license— помечает файл как лицензионное соглашение. RPM установит его в специальную системную директорию для лицензий.%doc— помечает файл как документацию (README, CHANGELOG).%config(noreplace)— критически важный макрос для конфигурационных файлов (обычно в/etc). Флагnoreplaceговорит пакетному менеджеру: «Если пользователь изменил этот файл на своем компьютере, при обновлении пакета не перезаписывай его новым дефолтным файлом». Вместо этого новый файл будет сохранен с расширением.rpmnew.
Управление директориями
Существует огромная разница между указанием директории и указанием файлов внутри неё.
Если вы напишете:
%{_datadir}/hello-world/
RPM упакует саму директорию hello-world и абсолютно все файлы и поддиректории внутри неё. Это удобно для папок, которые целиком принадлежат вашей программе.
Но если вы напишете:
%{_bindir}
Это катастрофическая ошибка! Вы прикажете RPM стать владельцем всей системной папки /usr/bin и всех программ в ней. Пакет будет конфликтовать с базовой системой.
Если вам нужно создать пустую директорию, принадлежащую пакету, используйте директиву %dir:
%dir %{_sysconfdir}/hello-world
Секция %changelog: История изменений
В самом конце spec-файла находится секция %changelog. Это журнал изменений пакета (не путать с changelog самой программы). Здесь мейнтейнеры записывают, кто, когда и зачем изменил spec-файл.
Формат записи строго регламентирован:
* День_недели Месяц День Год Имя Фамилия <email> - Версия-Релиз
- Описание того, что было сделано
Например:
* Mon Oct 23 2023 Ivan Ivanov <ivan@example.com> - 1.0-2
- Добавлен патч для исправления падения при запуске
Типичные ошибки новичков
- Сборка от root. Никогда не собирайте пакеты от имени суперпользователя. Ошибка в spec-файле (например, случайный
rm -rf /в секции%installвместо%install_root) может уничтожить вашу систему. Сборка всегда должна происходить от обычного пользователя. - Загрузка файлов из сети во время сборки. Сборочные серверы (включая ABF от Rosa Linux) изолированы от интернета в целях безопасности и воспроизводимости. Команды вроде
wgetилиnpm install(без предварительно подготовленного кэша) в секции%buildзавершатся ошибкой. Все исходники должны быть скачаны заранее и указаны вSource. - Игнорирование rpmlint. После сборки пакета всегда проверяйте его утилитой
rpmlint. Она укажет на хардкод путей, неправильные права доступа и отсутствующие макросы.
В следующей статье мы подробно погрузимся в управление зависимостями: научимся разрешать конфликты библиотек, использовать виртуальные пакеты и правильно связывать компоненты системы между собой.