Начиная с rosa2021.1, дистрибутив ROSA Fresh переходит с пакетных менеджеров RPM 5 и urpmi на RPM 4 и DNF. Эта статья описывает основные отличия для пользователей и сборщиков пакетов.

Содержание

Откуда куда переход

Более восьми лет в дистрибутивах ROSA Desktop использовался пакетный менеджер RPM5 - форк RPM4, созданный Джеффом Джонсоном. Долгое время RPM5 развивался гораздо активнее своего родителя, что и обсуловило его выбор для РОСЫ. Однако постепенно активность по разработке RPM5 угасла, а RPM4 наоборот - возродился и постепенно не только вобрал большинство интересных свойств RPM5, но и получил множество новых. В настоящее время сайт http://rpm5.org уже недоступен, а ROSA Fresh переходит обратно на RPM 4.

Было:

Стало:

Затронутые платформы: rosa2021.1 (в будущем релизы ROSA >= 12) и новее, в старых (rosa2012.1, rosa2012lts, rosa2014.1, rosa2016.1, rosa2019.0) пакетная система не меняется.

Про этот переход было рассказано на конференции OSDAY-2020, ознакомиться с выступлением и слайдами презентации можно здесь: https://nixtux.ru/1052

Причины для перехода

Общий план перехода

(возможны изменения, то, что еще не сделано, является приблизительным видением дальнейшей работы)

Изменения во флагах компилятора (CFLAGS, %optflags) по умолчанию

Части флагов не оказалось после перехода на rpm4, здесь описаны размышления, какие вернуть, а какие нет. Можно переделать.

- убран -fPIC: флаг был в cpu-os-macros.tar.gz, импортированном из Mandriva и не менявшимся, зачем он нужен в %optflags, я не понял, поэтому обратно в RPM4 не добавляю; пришлось в qt4 и qt5-qtbase добавить -fPIC вручную;

-fPIC возвращен; чтобы не было проблем с линковкой разделяемых библиотек, собранных с -fPIC, со статичными библиотеками, собранными без -fPIC, -fPIC возвращен в %optflags по умолчанию

Если необходимо отключить -fPIC для конкретного пакета, то можно сделать так:

 %global _fpic_cflags %{nil}

- убран -ffat-lto-objects: он был в rpm5/macros/mandriva.in, но не ясно, зачем тратить машинное время на компиляцию одновременно вариантов для LTO и нет, когда как LTO не используется в дистрибутиве, не понятно;

- убран -fno-delete-null-pointer-checks: он был тоже в rpm5/macros/mandriva.in, но зачем нужно изменять поведение gcc по умолчанию таким образом, не очень ясно; можно будет вернуть

UPD: этот флаг был вручную восстановлен в пакете webkit в соответствии с рекомендацией от 1С — проприетарной программы, использующей системный libwebkitgtk-3.0 (ALT#36998#c28). Возможно, его стоит вернуть глобально?

Полезные обсуждения по теме: https://news.ycombinator.com/item?id=17360316, https://github.com/RIOT-OS/RIOT/issues/12039

- убран -fvar-tracking-assignments: надобность сомнительная, кому нужно дебажить, может сам собрать ПО, в т.ч. с -O0 вместо -O2, а еще не все компиляторы понимают этот флаг (clang вообще не понимает этот флаг, при его наличии приходится изобретать костыли вроде %clang_gcc_wrapper)

- убран -frecord-gcc-switches: надобность не очень понятна, а clang-у мешает

- было -fstack-protector в rpm5, стало -fstack-protector-strong в rpm4

В rpm5:

 rosa-2016 ~ # rpm --eval %optflags | sed -e 's, ,\n,g' | sed -e 's,^\-,,g' -e 's,^\-,,g' | sort -u
 ffat-lto-objects
 fno-delete-null-pointer-checks
 fPIC
 frecord-gcc-switches
 fstack-protector
 fvar-tracking-assignments
 gdwarf-4
 O2
 param=ssp-buffer-size=4
 pipe
 Wa,--compress-debug-sections
 Werror=format-security
 Wformat
 Wp,-D_FORTIFY_SOURCE=2
 Wstrict-aliasing=2

В rpm4:

 bash-4.4# rpm --eval %optflags | sed -e 's, ,\n,g' | sed -e 's,^\-,,g' -e 's,^\-,,g' | sort -u
 D_FORTIFY_SOURCE=2
 O2
 Werror=format-security
 Wformat
 Wstrict-aliasing=2
 fomit-frame-pointer
 fPIC
 fstack-protector-strong
 gdwarf-4
 m64
 mtune=generic
 param=ssp-buffer-size=4
 pipe
 RPM5				RPM4
 				
 		Есть в RPM5	
 ffat-lto-objects		-
 fno-delete-null-pointer-checks	-
 frecord-gcc-switches		-
 fvar-tracking-assignments	-
 Wa,--compress-debug-sections	-
 				
 		Есть в RPM4	
 -				fomit-frame-pointer
 -				m64
 -				mtune=generic
 				
 		Общее		
 fstack-protector		fstack-protector-strong
 fPIC				fPIC
 gdwarf-4			gdwarf-4
 O2				O2
 param=ssp-buffer-size=4	param=ssp-buffer-size=4
 pipe				pipe
 Werror=format-security	Werror=format-security
 Wformat			Wformat
 Wp,-D_FORTIFY_SOURCE=2	D_FORTIFY_SOURCE=2
 Wstrict-aliasing=2		Wstrict-aliasing=2


Особенности перевода спеков на RPM 4

Скрипт rpm5-to-rpm4.sh, который автоматически вносит правки в спеки, здесь: https://gitlab.com/abf-mirror/abf-mirror-scripts

Если вы видите коммиты от "NixTux Commit Bot" с текстом: "bot: rpm5 -> rpm4 (N)", — где N — номер итерации прохода скрипта по всем пакетам в abf.io/import/, то это коммиты, сделанные этим скриптом. Каковы были изменения в скрипте между итерациями, можно посмотреть в git по ссылке выше.

 Макрос     Значение в rpm4     Значение в rpm5
 %rpm4      1                   0
 %rpm5      0                   1
 %_rpm      4                   5
 %if %rpm4
 < вариант для rpm4 >
 %else
 < вариант для rpm5 >
 %endif

Если хочется заморочиться и сделать так, чтобы была ошибка сборки, если ни один из вариантов %if не сработал, то можно написать так:

 %if 0%{?rpm4}
 < вариант для rpm4 >
 %else
 %if 0%{?rpm5}
 < вариант для rpm5 >
 %else
 %{error:Error!}
 %endif
 %endif

Эта конструкция применит "< вариант для rpm4 >", если макрос %rpm4 задан и равен 1, "< вариант для rpm5 >", если макрос %rpm5 задан и равен 1, а если ни один из двух вариантов не подошел, то сборка упадет с ошибкой с текстом "Error!". Лучше избегать таких сложных конструкций!

По причине отсутствия пакетов python / python3 в базовой системе в BuildRequires не работают макросы %python* из этих пакетов. Однако пакет python3 подтягивается через cmake-rpm-generators.

Больше нет krb5-devel, поэтому в некоторых пакетах нужно прописать: "BuildRequires: pkgconfig(krb5" или "BuildRequires: pkgconfig(krb5-gssapi)", в некоторых пакетах с дурацкими сборочными скриптами, которые полагаются на автоматику и в которых нельзя в явном виде включить или выключить GSSAPI, например, в curl, рекомендуется добавить проверку по образцу:

 %check
 readelf -a %{buildroot}%{_libdir}/libcurl.so | grep NEEDED | grep -q libgssapi
 BUILDSTDERR: sh: /usr/bin/python: No such file or directory

В настоящий момент /usr/bin/python — это python2.

 %__requires_exclude -> %__noautoreq
 %__provides_exclude -> %__noautoprov
 %__requires_exclude_from -> %__noautoreqfiles
 %__provides_exclude_from -> %__noautoprovfiles

Нет возможности легко научить RPM 4 понимать %__noautoreq, %__noautoprov, %__noautoreqfiles и %__noautoprovfiles, но есть возможность легко научить RPM 5 понимать оба варианта, что мы и сделали. Теперь, если в спеке указаны одновременно старый макрос и эквивалентный ему новый макрос, например:

 %define __noautoreq 'libGL.*'
 %global __requires_exclude 'libGL.*'

...то rpm5 будет брать только значение старого (__noautoreq) и игнорировать новый (__requires_exclude). Если же указан только новый (__requires_exclude), то будет браться его значение. В большинстве случаев достаточно использовать только новый вариант от RPM 4, т.е. только

 %global __requires_exclude libGL.*

...но, если вдруг понадобится задать разные правила для RPM 5 и RPM 4, то укажите оба варианта, тогда RPM 5 возьмет только свой прежний вариант, а RPM 4 только свой. Обратите внимание, что RPM 4 не понимает старые варианты от RPM 5, поэтому для RPM 4 обязательно указать __requires_exclude, а не __noautoreq. %global и %define здесь примерно одно и то же и для RPM 4, и для RPM 5, но документация от разработчиков RPM 4 рекомендует использовать %global, когда как в rpm5 более предпочтительно было %define.

rpm4 не понимает значения %__requires_exclude(_from)/%__provides_exclude(_from) в кавычках. Кавычки нужно убрать.

Про варианты от RPM 4 читайте здесь.

Мы тщательно не изучали особенности раскрытия регулярных выражений в этих макросах-фильтрах, возможны нюансы, новые варианты сделаны эквивалентными старым для большинства типовых случаев.

Типовые замены макросов:

 %configure2_5x -> %configure
 %_sys_macros_dir -> %_rpmmacrodir
 %_rpmhome -> %_rpmconfigdir

Возможные, но нерекомендуемые замены:

 %make -> %make_build
 %makeinstall_std -> %make_install
 %setup_compile_flags -> %set_build_flags
 %ldflags -> %build_ldflags

Также появились %build_cflags (CFLAGS, флаги компилятора Си), %build_cxxflags (CXXFLAGS, флаги компилятора Cи++), %build_fflags (FFLAGS, флаги компилятора Фортран); в rpm5 они сделаны эквивалентными %optflags; %optflags продолжает существовать, как и раньше.

Макросы %make_* стали унифицированы с %ninja_*, %meson_* и др., т.е. build означает сборку, install — установку. Возможно изменение политики в отношение макросов.

 #%define xxx yyy

не будет работать, нужно заменить "#%define" на "#define"

 %__pkgconfig_path    ^((%{_libdir}|%{_datadir})/pkgconfig/.*\.pc|%{_bindir}/pkg-config)$

Если значение %_libdir переназначено, то файлы *.pc для создания Requires и Provides скриптом scripts/pkgconfigdeps.sh будут искаться, возможно, не там, где вы хотите

Пример решения (переназначим этот макрос, указав нужные пути):

 %global __pkgconfig_path ^(%{_olibdir}/pkgconfig/.*\\.pc|%{_obindir}/pkg-config)$

В RPM 5 такого макроса нет, поэтому этот %global/%define ему безразличен.

 Explicit %attr() mode not applicable to symlink: /builddir/build/BUILDROOT/log4cpp-1.0-6.i386/usr/lib/liblog4cpp.so

Исправление — убрать лишний %attr

 BUILDSTDERR: error: Bad source: /builddir/build/SOURCES/mds-2.4.2.2.tar.gz: No such file or directory

В таких случаях нужно вручную или с помощью "abf put" закачать исходники пакета на ABF. Раз такие пакеты собирались ранее, значит в них достаточно автоматизированно сделать "abf put" и закоммитить, но для этого нужно составить список таких пакетов

 DEBUG: BUILDSTDERR: error: Installed (but unpackaged) file(s) found:
 DEBUG: BUILDSTDERR:    /usr/share/doc/miau/examples/miaurc

При этом в пакете было:

 %doc AUTHORS ChangeLog README TODO misc/miaurc

Файл examples/miaurc не прописан ни в %files, ни в %doc, а rpm5 игнорировал такую недоработку спека.

Другой пример:

 # (cg) Copy the whole contrib dir as docs. It contains useful scripts.
 mkdir -p %{buildroot}%{_datadir}/doc/git-core
 cp -ar contrib %{buildroot}%{_datadir}/doc/git-core

Это рассчитано на поведение rpm5, для rpm4 так делать не нужно.

 BUILDSTDERR: error: line 133: %package -n ipa-client47: package ipa-client47 already exists

Это логично, потому что %package, грубо говоря, используется для подпакетов, а пакет с именем %name уже и так автоматически задан. Пример исправления.

 find . -name config.guess -o -name config.sub | while read i ; do 
          [ -f /usr/share/libtool/config/"$(basename "$i")" ] && /bin/rm -f "$i" && /bin/cp -fv /usr/share/libtool/config/"$(basename "$i")" "$i" ; 
 done ; 
 if [ -e configure.ac -a -e Makefile.am ]; then 
   find . -name configure.ac |xargs dirname |while read D; do 
     pushd $D; 
     if grep -qE '(LT_INIT|LIBTOOL)' configure.ac >/dev/null; then 
       libtoolize --force ; 
     fi ; 
     aclocal $((find . -name "*.m4" 2>/dev/null |grep -vE 'ac(local|include).m4' | xargs dirname) | grep -v '^.$' |sort |uniq |cut -d/ -f2- |while read R; do [ -e $R/configure.ac ] || echo -n "-I$R "; done) ; 
     automake -a --foreign ; 
     autoconf ; 
     popd ; 
   done ; 
 fi ;

Этого не было и нет в %configure(2_5x) в RPM 5, но представляется полезным действием для актуализации сборочных скриптов тарболлов, что особенно важно при сборке на не-x86. Это может потребовать добавления новых BuildRequires, например, gettext-devel.

Если это пересоздание configure нужно отключить (например, при сборке пакетов libtool и autoconf, где такое действие вызывает как бы циклическую зависимость от самих себя), то:

 %define _disable_rebuild_configure 1
 %files
 file.*
 %exclude file.php

Тогда в пакет попадут file.c и file.sh, а file.php не попадет. Но, если вы его вручную не удалите и не положите ни в один пакет, rpm5 выдаст ошибку о неупакованном файле, а rpm4 не выдаст и сам удалит этот файл. Изменение поведения rpm4 обсуждается здесь.

 # todo - use native %systemd_post

Была выдана ошибка:

 $ rpmspec --parse --trace kdebase4-workspace.spec
 <...>
   1>   %post^-n kdm
   1>   %systemd_post^
   2>     %{expand:%%{?__systemd_someargs_%#}}^
   3>       %#^
   3>       %{?__systemd_someargs_0}^
   4>         %{error:This macro requires some arguments}^
   1>   %{?_color_output}^{!?_color_output:auto}
   1>   %{!?_color_output:auto}^
 error: This macro requires some arguments

Решение: превратить "%systemd_post" в "%%systemd_post"

 require "find.pl";

была выставлена зависимость:

 Requires: perl(find.pl)

Но ни один пакет такое не провайдит. RPM 5 такую зависимость не выставлял. Решилось патчем, который заменил "require "find.pl";" на "use File::Find qw(find);".

 %define _python_bytecompile_build 0

В RPM 4 и 5 используются разные места для размещения дополнительных макросов, но, что самое главное, отличаются имена файлов с макросами: в RPM 5 это *.macros, а в RPM 4 — macros.*. В связис этим был придуман макрос %install_macro, который создает универсальный метод установки макросов и в rpm4, и в rpm5. Макрос был добавлен в rpm4 и в rpm5. Пример:

 %install_macro ninja %{SOURCE2}

устанавливает файл %{SOURCE2} в папку с макросами, на rpm4 называя файл macros.ninja, а на rpm5 — ninja.macros. В список файлов пакета (%files) пишем:

 %{_rpmmacrodir}/*%{name}*

Под "*%{name}*" попадают и "%{name}.macros", и "macros.%{name}", что позволяет использовать один и тот же спек для сборки и на rpm4, и на rpm5, не делая if/else.

Примеры перевода пакетов на новую схему установки макросов: meson, ninja, python2, python3, qt5-macros, waf, firefox

 %if 0%{?rpm4}
 # rpm4 filetriggers
 %include %{SOURCE23}
 %else
 %if 0%{?rpm5}
 # rpm5 filetriggers
 %include %{SOURCE24}
 %else
 %{error:No file triggers have been included!}
 %endif
 %endif

где %{SOURCE23} и %{SOURCE24} — файлы, которые будут включены в спек, как если бы были его частью.

Генератор: https://abf.io/import/order-rpm-generators

Патч RPM, добавляющий нужный для его работа функционал: https://github.com/rpm-software-management/rpm/pull/1257

Во все пакеты добавляет: "OrderWithRequires: setup filesystem basesystem-minimal" + systemd при наличии файлов

 '/(usr/lib|lib|etc)/(systemd|sysusers.d|tmpfiles.d)/.*$'

и chkconfig при наличии файлов

'/etc/(rc\..*|init\.d)/.*$'.

OrderWithRequires делает что-то приблизительно эквивалентное Requires(pre) (или без pre?), но только если указанный пакет существует в транзакции иными путями.

Этот генератор позволяет выправить порядок установки пакетов в больших транзакциях типа сборки образа с 3,5к пакетов, где иначе часть пакетов с сервисами или конфигами systemd, systemd-sysusers, systemd-tmpfiles ставятся, когда сам systemd еще не существует, из-за чего образ или чрут получается корявым.