Примеры создания пакетов RPM из исходников или со своими файлами

Обновлено Обновлено: Опубликовано Опубликовано:

В данной инструкции мы научимся готовить Linux-среду для работы и рассмотрим примеры по созданию своих пакетов RPM. Мы будем работать в системе CentOS (Red Hat / Fedora).

Подготовка системы

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

1. Установим пакеты:

yum install rpmdevtools rpmlint

* где: 

  • rpmdevtools — позволит нам использовать утилиту rpmdev-setuptree, с помощью которой мы сможем создать рабочую среду в виде каталогов для сборки.
  • rpmlint — позволяет протестировать пакет RPM.

А также ставим:

yum group install «Development Tools»

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

2. Создаем пользователя.

Делать готовые установочные сборки пакетов очень опасно от пользователя root. Если мы допустим ошибку с путями, файлы могут перетереть или удалить важные для работы директории. Стоит создать отдельного пользователя и работать под ним. Однако, если мы работаем в виртуальной среде или контейнере Docker, нам это не страшно. Тогда данный пункт можно пропустить и работать из под root.

Выполняем команду:

useradd builder -m

* в данном примере мы создадим пользователя builder. Опция -m сразу создаст домашний каталог для пользователя.

Теперь заходим под данным пользователем — последующие команды мы будем выполнять от него:

su — builder

3. Создадим структуру каталогов для сборки:

rpmdev-setuptree

В нашей текущем каталоге должна появиться папка rpmbuild — а в ней:

  1. BUILD — содержит все файлы, которые появляются при создании пакета.
  2. RPMS — сюда будут складываться готовые пакеты.
  3. SOURCES — для исходников, из которых и будут собираться RPM-пакеты.
  4. SPECS — для файлов с описанием процесса сборки.
  5. SRPMS — для исходников RPM-файлов.

Мы готовы к сборке.

Сборка из исходников

Рассмотрим пример создания RPM из пакета, который нужно собирать из исходников с помощью команды make. Например, возьмем данную программу: github.com/brettlaforge/pg_redis_pubsub.

Создадим файл spec:

rpmdev-newspec rpmbuild/SPECS/pg_redis_pubsub.spec

Теперь откроем его и приведем к виду:

vi rpmbuild/SPECS/pg_redis_pubsub.spec

Name:           pg_redis_pubsub
Version:        1.0.2
Release:        1%{?dist}
Summary:        Redis Publish from PostgreSQL
License:        X11 License
URL:            https://github.com/brettlaforge/pg_redis_pubsub
Source0:        %{name}-%{version}.tar.gz

BuildRequires:  postgresql-devel postgresql-server-devel
BuildRequires:  hiredis-devel
Requires:       postgresql
%if 0%{?rhel} < 8
Requires:       hiredis-last >= 0.13.3-1
%else
Requires:       hiredis = 0.15
%endif

%define         _build_id_links none

%description
Redis Publish from PostgreSQL

%prep
%{__rm} -rf %{name}-%{version}
%{__mkdir} -p %{name}-%{version}
%{__tar} -xzvf %{SOURCE0} -C %{_builddir}/%{name}-%{version} —strip-components 1

%build
cd %{name}-%{version}
%{__make}

%install
cd %{name}-%{version}
%{__make} install DESTDIR=%{buildroot}

%clean
%{__rm} -rf $RPM_BUILD_ROOT
%{__rm} -rf $RPM_BUILD_DIR/*

%files
%defattr(-,root,root)
%{_libdir}/pgsql/redis.so
%{_datadir}/pgsql/extension/redis.control
%{_datadir}/pgsql/extension/redis—0.0.1.sql
%doc %{_datadir}/doc/extension/redis.mmd

%changelog
* Fri Jul  9 2021 root

* чтобы понять, как заполнить spec-файл, рекомендуется для начала собрать и установить приложение вручную с помощью make и make install. Также необходимо изучить документацию устанавливаемого пакета или (при наличие возможности) поговорить с разработчиками программного обеспечения.

Установим зависимости, которые необходимы для сборки (BuildRequires):

yum-builddep rpmbuild/SPECS/pg_redis_pubsub.spec

* утилита yum-builddep сама читает зависимости, необходимые для сборки и устанавливает недостающие пакеты.

Можно это сделать и вручную. В данном примере это: 

yum install epel-release

yum install postgresql-devel postgresql-server-devel hiredis-devel

* конкретно, в моем примере для установки hiredis-devel необходимо поставить репозиторий epel-release. Список пакетов, необходимый для сборки конкретного пакета необходимо уточнить в документации.

Теперь копируем исходник на свой компьютер. В моем примере клонируем репозиторий:

git clone https://github.com/brettlaforge/pg_redis_pubsub.git

Готовим архив и помещаем его в каталог rpmbuild/SOURCES:

tar -czvf rpmbuild/SOURCES/pg_redis_pubsub-1.0.2.tar.gz pg_redis_pubsub

Если бы в качестве Source мы указали внешний URL, можно было бы предварительно загрузить исходники командой:

spectool -g -R rpmbuild/SPECS/pg_redis_pubsub.spec

Данная команда разместит загруженные файлы в каталоге rpmbuild/SOURCES/.

Проверяем корректность SPEC-файла:

rpmlint rpmbuild/SPECS/pg_redis_pubsub.spec

В моем примере команда вернула ответ:

rpmbuild/SPECS/pg_redis_pubsub.spec: W: invalid-url Source0: pg_redis_pubsub-1.0.2.tar.gz
0 packages and 1 specfiles checked; 0 errors, 1 warnings.

Данное предупреждение можно проигнорировать.

Выполняем сборку:

rpmbuild -bb rpmbuild/SPECS/pg_redis_pubsub.spec

Если она пройдет без ошибок, мы должны найти RPM-пакет в каталоге rpmbuild/RPMS/x86_64, где x86_64 — архитектура пакета.

Описание файла SPEC

Данный файл является инструкцией по сборке пакета. В нем мы описываем сам пакет, задаем метаданные и указываем, как извлекать файлы и куда их копировать при установке пакета. Синтаксис файла включает такие элементы, как разделы, макросы, операторы, опции. Рассмотрим их отдельно.

Опции заголовка

Определяют описание пакета, а также некоторые важные для сборки параметры.

ОпцияОписаниеПример значения
NameНазвание для пакета RPMpg_redis_pubsub
VersionВерсия собираемого пакета1.0.2
ReleaseРелиз или версия программы1%{?dist}
?dist обозначение версии доработки
SummaryКраткое описание пакетаRedis Publish from PostgreSQL
LicenseСпособ лицензированияX11 License
URLАдрес источника пакетаhttps://github.com/brettlaforge/pg_redis_pubsub
Source0Источник данных, из которых должен собираться пакет.
Можно указать несколько источников: Source1, Source2 … SourceN
%{name}-%{version}.tar.gz
Данная запись означает, что сборщик будет искать архив 
pg_redis_pubsub-1.0.2.tar.gz в каталоге rpmbuild/SOURCES/
BuildRequiresТребования к пакетам, которые нужны для сборки пакета.
Можно написать большим списком, а можно добавить несколько отдельных строк BuildRequires.
Также мы можем задать требование к версии пакета.
postgresql-devel postgresql-server-devel hiredis-devel
RequiresТребования к пакетам, которые нужны для установки собранного пакета.
Можно написать большим списком, а можно добавить несколько отдельных строк Requires.
Также мы можем задать требование к версии пакета.
hiredis >= 0.13.3-1
ObsoletesОбъявляет пакеты устаревшими. Установщик выполнит их удаление при установке собранного пакета.hiredis < 0.13.3-1

Теги для определения переменных

С помощью тега %define можно определять переменные. Предоставлены разные возможности это сделать:

Пример определенияОписание
%define debug_package 1Простое создание переменной debug_package со значением 1. Обращение к данной перемнной возможно с помощью написания %{debug_package}.
%{!?osname: %define osname «redos»}Данная переменная osname будет определена со значением redos, если она не определена ранее.

Основные рабочие разделы

В каждом разделе описывается своя часть логики процесса сборки пакета.

РазделОписание
%descriptionОписание пакета. Может состоять из нескольких строк, но каждая строка должна содержать до 73 символов.
%prepПредварительная обработка. Используется для подготовки исходников. Как правило, в данном разделе происходит их распаковка. Также на данном этапе могут применяться патчи.
%buildЭтап сборки пакета. Как правило, это make.
%installУстановка пакета. Это может быть make install или копирование конкретных файлов в конкретные директории или запуск произвольного скрипта.
%cleanРаздел содержит инструкции по удалению устаревших файлов, которые больше не нужны.
%filesПеречисляем файлы, которые должны попасть в конечную систему при установке пакета.
%changelogСписок изменений в работе программного обеспечения.

Макросы разделов

У каждого из разделов могут быть свои макросы:

РазделМакросОписание
%files%docУказываем, что конкретные файлы относятся к документации. Такие файлы будут установлены в раздел /usr/doc/
%configПомечаем файлы как конфигурационные. Это задает действие при удалении пакета — если файлы были изменены, они будут переименованы с добавлением .rpmsave.
%dirОбозначает директорию, которой владеет пакет. При удалении пакета, также будет удаляться данная директория.
%files -fПозволяет перечислить файлы во внешнем файле и передать его как аргумент.
%defattr(<mode>,<user>,<group>)Задаем права для файлов и каталогов, которые будут назначены при установке пакета. Данный макрос применяется глобально ко всем файлам. Например: %defattr(644,root,root).
%attr(<mode>,<user>,<group>)Задаем права, которые должны быть назначены при установке пакета. Данный макрос применяется к конкретному перечню файлов. Например: %attr(644,postgres,postgres) %{_libdir}/pgsql/redis.so.

Сценарии

Мы можем описать команды, которые будут выполняться на конечном компьютере при установке или удалении пакета:

СценарийОписание
%pre Выполняется перед установкой пакета в систему
%postВыполняется после установки пакета в систему
%preunВыполняется перед удалением пакета из системы
%postunВыполняется после удаления пакета из системы

Макросы для сценариев

Внутри сценариев могут быть запущены свои макросы:

СценарийМакросОписание
%post%systemd_postЗапускается процедура установки и регистрации сервиса.
%preun %systemd_preunЗапрещается автозапуск сервиса (systemctl —no-reload disable …)
%postun%systemd_postun_with_restartОтмечает сервис для перезагрузки

Макросы для команд

Некоторые системные команды лучше писать не напрямую, а через макросы. Это позволит добиться большей стабильности при сборке на различных системах. Приведем в пример данные команды:

МакросКомандаОписание
%{__rm} rmУдаление файлов
%{__mv}mvПеренос файлов
%{__tar}tarРаспаковка или создание архивов формата gz
%{__unzip}unzipРаспаковка архивов формата zip
%{__sed}sedПоиск по шаблону текста и его замена
%{__ln_s}ln -sСоздание симлинка
%{__mkdir}mkdirСоздание каталога
%{__mkdir_p}mkdir -pРекурсивное создание каталога (создает папки по пути)

* полный список макросов можно получить командой rpm —showrc.

Макросы для каталогов

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

МакросПуть
%{_prefix}/usr
%{_usr}/usr
%{_libdir} %{_prefix}/lib64
%{_datarootdir}%{_prefix}/share
%{_datadir}%{_datarootdir}
%{_sysconfdir}/etc
%{_var}/var
%{_localstatedir}/var
%{_sharedstatedir}/var/lib

* обратите внимание, что некоторые макросы ведут не на конкретные пути, а на другие макросы.
* полный список макросов можно получить командой rpm —showrc.

Операторы сравнения

SPEC файл позволяет задавать логику с помощью операторов сравнения. Приведем примеры их использования:

ПримерОписание
%if 0%{?rhel} < 8
Requires:       hiredis-last >= 0.13.3-1
%else
Requires:       hiredis
%endif
В данном примере мы проверяем версию системы, на которой идет сборка. Если rhel (релиз системы) меньше 8, то мы указываем в качестве требования hiredis-last. В данном примере это имеет смысл, так как в CentOS 8 пакет hiredis-last переименовали в hiredis.
%if 0%{?rhel} == 8

%endif
В данном условии мы проверяем, является ли версия релиза 8.
%if %{?osname} != «el»

%endif
Проверяем значение переменной osname. Если она не равна «el», выполняем действие.

Возможные ошибки

Рассмотрим примеры ошибко, с которыми мы можем столкнуться.

Installed (but unpackaged) file(s) found

Ошибка появляется в конце процесса сборки пакета.

Причина: обнаружены файлы, которые были установлены с помощью make install, но которые не были перечислены в %files. Таким образом, сборщик пакета не знает, что с ними делать.

Решение: секция %files должна содержать все файлы, необходимые для работы приложения. Их нужно перечислить.

Но если у нас есть полная уверенность, что мы перечислили все необходимое, а оставшиеся файлы нам ни к чему, то добавляем в файл spec:

%define _unpackaged_files_terminate_build 0

* в верхнюю часть.