Привет, Хабр!
Сегодня мы рассмотрим, как написать один RPM .spec-файл так, чтобы он одинаково успешно собирался на Fedora, RHEL и даже на openSUSE.
Но для начала: зачем вообще поддерживать несколько дистрибутивов одной спецификацией?
Есть несколько кейсов:
-
CI/CD на всех платформах сразу.
Вы ведёте опенсорс. У вас.gitlab-ci.ymlи в нём matrix build: Fedora 39, EPEL9, openSUSE Leap 15.6. Если.specодин, можно тривиально написатьrpmbuild -baна каждой ноде и забыть. -
Патчи upstream прилетают разом.
Если у вас три разные.spec: на Fedora нужна версия 3.x библиотеки, на SUSE — 1.1, а на RHEL ещё и со старым автотулзом. И в каждой специи — свои костыли. В единой специи ты хотя бы видишь, чем они отличаются, и можешь аккуратно поднятьifилиbcond. -
Снижение числа ошибок.
Три спеки — это три точки для багов. Особенно когда люди начинают забывать их синхронизировать. Кто-то забыл обновить релиз на SUSE — и всё, пользователи пишут тикеты.
Так что мысль простая: один файл — один источник истины.
Conditional-макросы: %if 0%{?fedora}, %if 0%{?rhel}, %bcond_with tests
Вот тут большинство задумываютися. Ты либо напишешь свои if-ы топорно — и через полгода сам их не прочитаешь, либо аккуратно заведёшь глобалы и обернёшь в bcond.
0%{?…} нужен именно для того, чтобы %if не свалился в синтаксическую ошибку, если макроса нет. SUSE не определяет ни %fedora, ни %rhel, а только %suse_version. Поэтому всегда начинаем спецификацию примерно так:
%global distro fedora %if 0%{?rhel} %global distro rhel %endif %if 0%{?suse_version} %global distro suse %endif
Почему не %distname? В Fedora %distname в будущем может конфликтовать, а %distro — наш, локальный.
Дальше — правило хорошего тона: не пишем большие вложенные if, лучше разносить логические блоки и добавлять комментарии:
# У Fedora >=39 нужен OpenSSL3 %if 0%{?fedora} >= 39 BuildRequires: openssl-devel >= 3 %endif # SUSE уже давно на OpenSSL3 %if 0%{?suse_version} >= 1550 BuildRequires: libopenssl-devel >= 3 %endif
С bcond_with история тоже не всегда очевидная: он позволяет гибко отключать и включать опциональные блоки, не правя спецификацию. Самое классное — в OBS они отображаются как чекбоксы прямо в веб-интерфейсе. Вот так можно описать опциональные тесты:
%bcond_with tests %prep %autosetup %build %configure make %{?_smp_mflags} %check %if %{with tests} make check %endif
На проде обычно tests выключают, а в CI включают.
Раздел Source/BuildRequires: как выбирать разные URL и патчи
На этом этапе уже важно помнить, что Fedora любит bleeding-edge, SUSE — более консервативна, а RHEL — это всегда «не спешим».
Например, в Fedora пакет собирается на GCC 13, а на RHEL всё ещё живёт GCC 8. Значит — не стесняемся брать патчи из апстрима под более старые версии и заворачивать их условно:
%if 0%{?rhel} == 8 Patch0: rhel8-gcc8-fix.patch %endif %if 0%{?suse_version} <= 1500 Patch1: suse15.5-backport.patch %endif
С URL аналогично — иногда приходится брать другие зеркала или форки для SUSE:
%if "%{distro}" == "suse" Source0: https://download.opensuse.org/repositories/home:/myproject/my-awesome-tool-%{version}.tar.gz %else Source0: https://github.com/myorg/my-awesome-tool/archive/v%{version}.tar.gz %endif
На уровне BuildRequires не боимся явно указывать разные пакеты — Fedora и SUSE часто расходятся по неймингу:
%if "%{distro}" == "fedora" BuildRequires: pkgconfig(libfoo) %endif %if "%{distro}" == "suse" BuildRequires: libfoo-devel %endif
На Fedora принято писать pkgconfig(...), на SUSE — старомодно -devel.
Секция %package и %files: split-subpackages и различия в /usr/lib64 vs /usr/lib
Это частый косяк: «у меня билд прошёл, а файлы не поставились». Потому что в Fedora всё строго /usr/lib64/, а в SUSE часто /usr/lib/.
Проверяем это заранее:
%files %if "%{distro}" == "suse" /usr/lib/my-awesome-tool %else /usr/lib64/my-awesome-tool %endif
Или ещё лучше — определем глобальный макрос libdir и используем везде его:
%global libdir %{_libdir} %files %{libdir}/my-awesome-tool
Так читать проще, менять тоже.
Для split-пакетов, вроде -devel, -doc, -tests, заводим отдельные секции %package и %files, как положено:
%package doc Summary: Documentation for %{name} BuildArch: noarch %files doc %doc README.md docs/
Так пакеты выглядят аккуратнее и позволяют пользователям не тащить лишнее.
Обработка системных юнитов, tmpfiles и скриплетов: systemd vs legacy init
Пакет, который ставит сервис — почти всегда таит в себе проблему: systemd у всех свой, пути разные, да и tmpfiles.d, sysusers.d и прочие штуки далеко не одинаково реализованы. Особенно в openSUSE, где любят systemd-rpm-macros, но всё равно что-нибудь отличается от Fedora.
Описываем установку системного юнита:
Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %post %systemd_post myservice.service %preun %systemd_preun myservice.service %postun %systemd_postun_with_restart myservice.service
Но в openSUSE свои макросы: %service_* вместо %systemd_*, и если бездумно вставлять Fedora-шные макросы — на SUSE всё взорвётся с ошибкой Unknown %systemd_post.
Правильный кросс-подход:
%if "%{distro}" == "suse" %define systemd_post() /usr/lib/rpm/postun-systemd %{*} %define systemd_preun() /usr/lib/rpm/preun-systemd %{*} %define systemd_postun_with_restart() /usr/lib/rpm/postun-systemd %{*} %endif
Или ещё лучше — использовать %{?systemd_post:...} как условную конструкцию. Для tmpfiles:
%post %tmpfiles_create %{_tmpfilesdir}/myservice.conf
Не забываем:
Requires: systemd Requires(post): systemd Requires: systemd-tmpfiles
Всё это — чтобы сервис заработал, каталоги создались, а юнит был корректно включён и переинициализирован.
Ещё нюанс: SUSE кладёт юниты в /usr/lib/systemd/system/, Fedora — туда же, но RHEL 7 любит /lib/systemd/system/. На это тоже можно писать if или использовать %{_unitdir}.
Заключение
В итоге, единый .spec — это меньше дублирования, проще поддержка, чище CI и предсказуемое поведение пакета на Fedora, RHEL и SUSE. Используйте макросы, bcond_with, аккуратные if, заведите libdir и тестируйте всё в COPR и OBS.
Приглашаем вас ознакомиться со специализацией «Administrator Linux», в рамках которой рассматриваются современные методы и инструменты администрирования Linux‑систем.
Для выбора других образовательных программ рекомендуем посетить каталог курсов.
Кроме того, в календаре открытых уроков вы найдёте мероприятия, посвящённые различным аспектам работы с Linux. Это хорошая возможность получить практические знания и ознакомиться с материалами курса.
Чтобы оставаться в курсе самых актуальных технологий и трендов, подписывайтесь на Telegram-канал OTUS.
ссылка на оригинал статьи https://habr.com/ru/articles/928414/
Добавить комментарий