Сборка пакетов библиотек для rpm-based дистрибутивов Linux

Во многих наших проектах используются open-source библиотеки. Когда разработка ведется под одну конкретную платформу, нет смысла собирать одни и те же библиотеки из исходников каждый раз, когда к проекту подключается новый разработчик. Кроме того, установка библиотек а-ля make && sudo make install считается плохим тоном, поскольку система засоряется «бесхозными» файлами, о которых нет информации в базе данных менеджера пакетов RPM.

В качестве решения предлагается из скомпилированных библиотек собирать RPM-пакеты и хранить их в едином репозитории, доступном для всех разработчиков. Ниже приводится инструкция и некоторые советы по сборке пакетов.

Инструкция будет основываться на примере Red Hat Enterprise Linux 6, но с небольшими изменениями ее можно будет адаптировать и для других дистрибутивов. Для примера будем собирать пакет из библиотеки zeromq.

Перед сборкой пакета


Первое, что нужно сделать перед сборкой — убедиться, что нужный вам пакет не собрал кто-то до вас. Часто на таких ресурсах, как rpmfind.net и rpm.pbone.net можно найти то, что вам нужно. Но если не нашлось необходимой версии библиотеки или нет сборки под вашу платформу, то придется собирать пакет самому.

rpmbuild

Сборка пакетов осуществляется с помощью утилиты rpmbuild. Перед ее использованием необходимо сконфигурировать окружение сборки. Создадим необходимое дерево каталогов, например, в директории ~/rpmbuild:

$ mkdir ~/rpmbuild
$ mkdir ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}

Создаем файл конфигурации утилиты rpmbuild, чтобы она узнала, где находится созданное дерево каталогов:

$ echo '%_topdir %(echo $HOME)/rpmbuild' >~/.rpmmacros

rpmbuild при запуске будет искать файлы пакета в директории ~/rpmbuild/BUILDROOT/<имя_пакета>. Разберемся, как именуются RPM-пакеты, на примере zeromq:

zeromq-3.2.4-1.rhel6.x86_64.rpm

Здесь:
  • zeromq — собственно, имя пакетируемого ПО;
  • 3.2.4 — версия ПО;
  • 1.rhel6 — номер сборки пакета (release number) — сколько раз данная версия ПО собиралась в rpm-пакет. Суффиксом rhel6 или el6 обычно обладают пакеты для Red Hat Enterprise Linux 6;
  • x86_64 — процессорная архитектура, под которую скомпилировано ПО.

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

Итак, создаем директорию для zeromq в BUILDROOT:

$ mkdir ~/rpmbuild/BUILDROOT/zeromq-3.2.4-1.rhel6.x86_64

Сборка библиотеки

Само собой, перед сборкой бинарного пакета, нужно скомпилировать саму библиотеку. Если библиотека использует систему сборки GNU Autotools, то обычно это делается командами:

$ ./configure
$ make

Теперь устанавливаем библиотеку в созданную нами директорию в BUILDROOT:

$ make install DESTDIR="$HOME/rpmbuild/BUILDROOT/zeromq-3.2.4-1.rhel6.x86_64"

Параметр DESTDIR не всегда обрабатывается в мейкфайлах. Например, qmake генерирует мейкфайлы, которые игнорируют этот параметр. Если библиотека использует систему сборки, отличную от GNU Autotools, то прочитайте в соответствующем руководстве, какие параметры нужно передать при сборке, чтобы установить библиотеку в указанную директорию.

spec-файл для сборки пакета


В RPM-пакетах помимо заархивированного дерева файлов хранится метаинформация об этом пакете. При сборке она должна задаваться в spec-файле, который находится в папке ~/rpmbuild/SPECS. Рассмотрим пример файла zmq.spec для библиотеки zeromq:

Name:           zeromq
Version:        3.2.4
Release:        1.rhel6
Summary:        Software library for fast, message-based applications
Packager:       My organization
Group:          System Environment/libraries
License:        LGPLv3+ with exceptions

%description
The 0MQ lightweight messaging kernel is a library which extends the
standard socket interfaces with features traditionally provided by
specialized messaging middle-ware products. 0MQ sockets provide an
abstraction of asynchronous message queues, multiple messaging
patterns, message filtering (subscriptions), seamless access to
multiple transport protocols and more.

This package contains the ZeroMQ shared library for versions 3.x.

%post
/sbin/ldconfig

%postun
/sbin/ldconfig

%files
%defattr(-,root,root)
/usr/lib64/libzmq.so.3
/usr/lib64/libzmq.so.3.0.0

%package devel
Summary:    Development files for zeromq3
Group:        Development/Libraries
Requires:    %{name} = %{version}-%{release}

%description devel
The zeromq3-devel package contains libraries and header files for
developing applications that use zeromq3 3.x.

%files        devel
%defattr(-,root,root)
/usr/include
/usr/share
/usr/lib64/pkgconfig
/usr/lib64/libzmq.so
/usr/lib64/libzmq.a
/usr/lib64/libzmq.la

В начале файла указывается минимальный набор полей с информацией о пакете. Из значений первых трех полей (Name, Version, Release) формируется имя пакета, поэтому важно, чтобы там были указаны правильные значения. Если значения не будут соответствовать имени каталога с деревом файлов собираемого пакета, rpmbuild выдаст ошибку. Поле License также является обязательным — без него rpmbuild откажется собирать пакет.

Назначение секции %description очевидно. Секции %post и %postun содержат скрипты, выполняющиеся после установки файлов пакета в систему и после удаления файлов пакета из системы, соответственно. Это полезно, если пакет устанавливает динамические библиотеки (.so) в нестандартные директории (т. е. не в /lib, /usr/lib, /lib64 или /usr/lib64). В этом случае пакет должен предоставлять файл конфигурации для ldconfig и устанавливать его в /etc/ld.so.conf.d. Команда ldconfig обновляет кэш динамического загрузчика, добавляя в него все библиотеки, найденные в директориях, указанных в конфигурационных файлах.

В секции %files указывается список файлов, которые пакуются в rpm. Директива %defattr указывает атрибуты файлов по умолчанию в формате:

%defattr(<file mode>, <user>, <group>, <dir mode>)

<file mode> указывается в восьмеричном виде, например, «644» для rw-r--r--. Атрибут <dir mode> может быть опущен. Вместо атрибутов, которые не должны меняться для устанавливаемых файлов, можно указать дефис. Директории, указанные в секции %files, будут внесены в пакет вместе со всем их содержимым.

Дальше самое интересное. Фактически существует два типа RPM-пакетов библиотек. В одних находятся собственно сами файлы динамических библиотек, необходимые для работы программ, которые скомпонованы с этими библиотеками. Например, пакет zeromq-3.2.4-1.rhel6.x86_64.rpm предоставляет только два файла: /usr/lib64/libzmq.so.3.0.0 и символьную ссылку на него, /usr/lib64/libzmq.so.3. Другой тип пакетов содержит файлы, необходимые для разработки приложений с использованием предоставляемой библиотеки. К имени таких пакетов добавляется суффикс "-devel", например, zeromq-devel-3.2.4-1.el6.x86_64.rpm. В таких пакетах обычно содержатся заголовочные файлы C/C++, документация, статические библиотеки (.a), и эти пакеты являются зависимыми от пакетов первого типа.

В приведенном выше spec-файле директива %package позволяет собрать «дочерний» пакет одним запуском rpmbuild. Значения полей заголовков дочернего пакета наследуются от родительского, но их можно переопределить. Поле Requires задает дополнительную зависимость от родительского пакета. Заметьте, что секция %files пакета zeromq-devel содержит файл /usr/lib64/libzmq.so. Это символьная ссылка на настоящий файл с динамической библиотекой. Он необходим компоновщику ld на этапе сборки приложения с использованием библиотеки, поскольку он ищет файлы динамических библиотек, начинающиеся на «lib» и заканчивающиеся на ".so".

Сборка


Перед сборкой нужно иметь в виду две вещи.
Первое: при успешной сборке пакета rpmbuild очистит директорию BUILDROOT. Так что на всякий случай сделайте резервную копию пакетируемых файлов.
Второе: никогда не собирайте пакеты с правами root. Здесь объясняется, почему так нельзя делать.

Теперь все готово для сборки пакета. Запускаем rpmbuild:

$ cd ~/rpmbuild/SPECS
$ rpmbuild -bb zmq.spec

Параметр -bb означает «build binary», то есть, собрать бинарный пакет. Помимо бинарных пакетов есть еще пакеты исходных кодов, но они здесь не обсуждаются.

В случае успеха полученный rpm-пакет будет сохранен в папке RPMS.

Пара советов


Если не знаете, что писать в полях заголовка spec-файла для пакетируемой библиотеки, можно взять RPM-пакет для другого дистрибутива c указанных выше ресурсов и посмотреть, что пишут там:

$ rpm -qip package.rpm

Здесь «q» означает «режим запросов (query)», «i» — получение информации о пакете, «p» — получение информации об указанном файле пакета (без этой опции будет получена информация о пакете, установленном в системе, если он установлен).

Если не знаете, какие файлы принадлежат пакету devel, а какие — пакету с библиотеками, но у вас есть оба пакета для другого дистрибутива, можно распаковать дерево файлов, предоставляемых данным пакетом, в текущую директорию:

$ rpm2cpio package.rpm | cpio -di

Утилита rpm2cpio пишет в стандартный вывод cpio-архив, хранящийся в rpm-пакете; утилита cpio распаковывает архив, принятый из стандартного ввода. Параметр «i» включает режим распаковки, а «d» создает нужные директории.

Посмотреть, какие файлы предоставляет пакет, можно и не распаковывая его, с помощью опции «l»:

$ rpm -qlp package.rpm

Итого


Пакуя используемые библиотеки в RPM, можно избавить ваших коллег от необходимости каждый раз скачивать исходники нужной версии библиотеки, избавить их от проблем при сборке (если, к примеру, вам к скачанным исходникам библиотеки нужно применить какой-нибудь патч) и вообще унифицировать процесс добавления библиотеки в проект. Статья не описывает все тонкости сборки пакетов и написания spec-файлов (как, например, разделение файлов конфигурации, документации и пр.), но для сборки пакетов библиотек это, по большому счету, и не нужно.
Метки:
Поделиться публикацией
Похожие публикации
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 17
  • +7
    Oh god.
    У пакета должны быть Source* и Requires/BuildRequires теги, и секция %prep, тогда не надо ничего распаковывать и собирать в билдруте руками.
    Собирать лучше через mock (и ваш спек там не соберется), особенно если нужно покрыть несколько архитектур.
    В секции %files лучше использовать макросы для указания целевых директорий.
    В теге Release стоит использовать макрос {?dist} вместо захардкоженного.
    Прогоните ваш спек через rpmlint хотя-бы.
    А пока я оставлю здесь fedoraproject.org/wiki/How_to_create_an_RPM_package
    Хорошая стартовая точка для нового спек-файла — rpmdev-newspec (хотя там тоже есть пара устаревших вещей)
    • 0
      Спасибо. А что нужно писать в поле Requires для библиотеки? Ведь зависимости будут автоматически прописаны в пакете.
      • 0
        Насколько я помню, автоматически будут прописаны только очевидные прямые зависимости (например, найденные через ldd). Соответственно все остальное (либы, которые грузятся через dlopen, например) надо указывать вручную.
        Или же у вас какое-нибудь веб-приложение (ownclowd, например), которому нужен веб-сервер (там чуть сложнее, правда, с подпакетами).
        • 0
          Когда будете собирать через mock — будут сыпаться постоянно ошибки из-за неустановленных зависимостей. Их и нужно будет прописать в BuildRequires. В Requires нужно прописывать пакеты, которые нужны для работы. Но в вашем случае уже есть готовый пакет со спеком: mirror.yandex.ru/fedora/linux/development/rawhide/source/SRPMS/z/ Да и вообще в большинстве случаев в котле федоры есть уже опакетированные программы.
        • 0
          посмотрел по Вашей ссылке и нашел упоминание mock в этой команде:
          usermod -a -G mock makerpm

          Вы это имели ввиду написав «Собирать лучше через mock»?

          (а тема и обсуждение очень как вовремя, спасибо)
          • 0
            Нет-нет. mock это такая штука, которая создает почти виртуалку, ставит туда базовый набор пакетов и зависимости, прописанные в спеке и пытается все это собрать.
            fedoraproject.org/wiki/Using_Mock_to_test_package_builds
        • +1
          на примере Red Hat Enterprise Linux 6

          В RHEL6 не надо прописывать ничего в .rpmmacros чтобы собиралось в ~/rpmbuild
          Создавать директории вручную тоже совершенно необязательно, они созаются при установке первого src.rpm в систему, плюс у rpmbuild есть специаьный ключ для создания дерева каталогов
          Лучше использовать mock/koji
          Есть такая замечательная штука как rpmlint, которая будет сильно ругаться на Ваш пакет.
          В Release лучше использовать макрсо, вместо хардкордного rhel6
          /usr/lib64/libzmq.so.3
          — ваш пакет только для x86_64? Почему не использовать %{_libdir} для универсальности?
          /usr/include
          /usr/share

          Никому этого не показывайте :) Почему zeromq должен быть владельцем этих директорий? Вы хотите чтобы директории были удалены при удалении пакета?

          Почитайте RPM Guide и Fedora Packaging Guidelines, правда, откроете для себя много нового и полезного.
          • 0
            А как же сборка пакетов из исходников от других версий ОС?
            Чаще возникает ситуация когда пакет есть, но устарел и его свежая версия есть для следующей версии дистрибутива.
            тогда приходит на помощь rpmbuild --rebuild somepackage.src.rpm
            Да и, говоря сборке пакетов, следует упомянуть Fascist build policy с которым можно столкнуться.

            • 0
              Посмотреть, какие файлы предоставляет пакет, можно и не распаковывая его, с помощью опции «f»:

              $ rpm -qfp package.rpm

              С помощью опции -l (маленькая латинская эль). С помощью -f можно найти пакет владеющий указанным файлом.
              • 0
                Точно, спасибо!
              • 0
                Есть хороший пример spec фаила с объяснениями объяснениями
                • 0
                  А нет варианта, как в debian-based, вообще не возиться ни с какими спец. файлами а просто сказать «make checkinstall» вместо «make install»?
                  Вот последний чем и радует, что минимально рабочий пакет, который будет нормально ставиться и удаляться можно вот так просто и сделать.
                  Отложим разные преимущества «тонкого тюнинга», речь не о них. Именно максимально прямолинейный и «влобный» способ — есть такой для rpm?

                  (а, ну да, если билд управляется cmake, то вроде очевидно. А если просто автотулзы?)
                  • 0
                    checkinstall работает и для rpm, но, насколько я знаю, он сразу устанавливает пакет. А речь о том, чтобы а) собрать пакет один раз, который потом будет использоваться всей командой, и б) вынести заголовочные файлы в devel-пакет, т.к. они не нужны будут там, где будет запускаться приложение. Хотя, может, я недостаточно разобрался в checkinstall.
                  • 0
                    У rpm есть все средства собрать пакет самостоятельно. Зачем закатывать солнце в ручную не понятно. Это упрощает задачу до сделать spec файл и положить исходники и патчи.
                    • +1
                      Зачем это всё, если есть fpm?
                      • 0
                        Напишите пост, как им пользоваться ;)
                        • 0
                          Он простой, как три копейки. Например собрать собственный rpm-пакет из папки можно так(пример из оф. вики):
                          fpm -s dir -t rpm -n «slashbin» -v 1.0 /bin /sbin
                          Эта команда пакетирует все файлы из директорий /bin и /sbin в rpm-пакет slashbin версии 1.0.
                          В fpm нет огромных манов, это удобный инструмент от того, кто устал генерить валидные spec-файлы и всё, что там еще надо, для таких же уставших.
                          Если вы не мейнтейнер каких-то пакетов — то вся это возня вам практически наверняка не нужна.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.