В «Северстали» внедрены большие корпоративные системы, такие как SAP или QMET, но есть и много разных задач, которые закрывает собственная разработка, и задачи у этой разработки редко бывают простыми. А значит, и требования к инструментам разработки бывают достаточно специфическими. Что делать, если вашим разработчикам потребовался gcc-9 под CentOS, а его нет в общедоступных репозиториях? Конечно, засучить рукава и создать требуемые пакеты. Вот только задача эта выглядит просто только на первый взгляд.
Если вам интересно, какие грабли разложены на пути к замене системного компилятора, и как мы с ними справились, добро пожаловать под кат.
Stage 1. Собственно сборка gcc
Здесь казалось бы всё просто: берём gcc.spec от пакета gcc-8.3.1, меняем 8 на 9, запускаем rpmbuild –bb, долго ждём? Да, но нет. Для начала придётся пересмотреть и поправить все патчи, а заодно ещё и поставить binutils посвежее, благо это несложно. Потом, мы же не просто так компилятор меняем, нам же ещё какие-нибудь nvptx-tools подавай, а это значит, что когда сборка закончится и начнется тестирование, тесты в libgomp, завязанные на выгрузку кода, начнут виснуть и застревать в разных странных позах.
Решения тут может быть два:
Консервативное: сказать разработчикам «извините, не шмогла» и отключить nvptx-tools.
Экспериментальное: предупредить разработчиков, что nvptx они используют на свой страх и риск, после чего запустить rpmbuild, дождаться застревания в тестах и отстреливать их руками. Вы получите массовые tests failed в результатах, но искомые пакеты будут собраны.
Stage 2. Package libgcc.i686 has inferior architecture
Итак, мы складываем все эти замечательные gcc-9.3.1-3.el8.x86_64.rpm, gcc-offload-nvptx-9.3.1-3.el8.x86_64.rpm и т.д. и т.п. в отдельный репозиторий, индексируем его, подключаем в /etc/yum.repos.d, говорим dnf update и… Нет, ну вы правда думали, что он возьмёт и сразу поставится? Как бы не так. Как известно, 64-разрядные дистрибутивы семейств Debian и RedHat для семейств процессоров x86 в глубине души немножечко 32-разрядные (то есть, по умолчанию существуют в режиме multilib), и поэтому вам потребуется или объявить multilib пережитком прошлого и снести 32-разрядные библиотеки системного компилятора, или создать соответствующие пакеты (libgcc.i686, libgfortran.i686, libgomp.i686, libquadmath.i686 и libstdc++.i686) для новой версии. Как ни забавно, но решения тут тоже может быть два:
Рекомендованное производителем: поставить mock и выполнить полную сборку для i686, наступив на все сопутствующие грабли (nvptx, например, лучше сразу выключить).
Ленивое на скорую руку: дело в том, что 32-битные версии библиотек на самом деле собираются вместе с 64-битными, но никуда в итоге не попадают. В стандартной версии gcc.spec они вообще тупо удаляются, но это как раз недолго и закомментировать. А потом скопировать gcc.spec в libgcc-i686.spec, вымарать из него всю секцию %build, а в %install написать примерно следующее:
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}
tar cf - -C %{_buildrootdir}/%{name}-%{version}-%{release}.x86_64 usr | tar xf - -C %{buildroot}
FULLPATH=%{buildroot}%{_prefix}/lib/gcc/%{gcc_target_platform}/%{gcc_major}
FULLEPATH=%{buildroot}%{_prefix}/libexec/gcc/%{gcc_target_platform}/%{gcc_major}
# fix some things
mkdir -p %{buildroot}/%{_lib}
mv -f %{buildroot}%{_prefix}/%{_lib}/libgcc_s.so.1 %{buildroot}/%{_lib}/libgcc_s-%{gcc_major}-%{DATE}.so.1
chmod 755 %{buildroot}/%{_lib}/libgcc_s-%{gcc_major}-%{DATE}.so.1
ln -sf libgcc_s-%{gcc_major}-%{DATE}.so.1 %{buildroot}/%{_lib}/libgcc_s.so.1
mkdir -p %{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}
mv -f %{buildroot}%{_prefix}/%{_lib}/libstdc++*gdb.py* \
%{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/
pushd %{name}-%{version}-%{DATE}/libstdc++-v3/python
for i in `find . -name \*.py`; do
touch -r $i %{buildroot}%{_prefix}/share/gcc-%{gcc_major}/python/$i
done
touch -r hook.in %{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/libstdc++*gdb.py
popd
for f in `find %{buildroot}%{_prefix}/share/gcc-%{gcc_major}/python/ \
%{buildroot}%{_datadir}/gdb/auto-load/%{_prefix}/%{_lib}/ -name \*.py`; do
r=${f/$RPM_BUILD_ROOT/}
%{__python3} -c 'import py_compile; py_compile.compile("'$f'", dfile="'$r'")'
%{__python3} -O -c 'import py_compile; py_compile.compile("'$f'", dfile="'$r'")'
done
rm -rf %{buildroot}%{_prefix}/%{_lib}/%{name}
Теперь достаточно сказать rpmbuild –bb libgcc-i686.spec где-то в соседнем терминале, пока gcc развлекается своим torture, и вуаля, наши 32-битные пакеты у нас в кармане (в смысле, в $RPM_BUILD_ROOT/RPMS/i686). Мы копируем их в наш репозиторий, индексируем его, запускаем dnf makecache –repo gcc-9 && dnf update и… Нет, обновить компилятор всё ещё нельзя.
Stage 3. Annobin и libtool
Те, кто внимательно смотрит на параметры сборки на линуксах линеек RHEL и CentOS, могли заметить, что по умолчанию в gcc подключен плагин annobin. У этого плагина есть неприятная привычка привязываться к версии компилятора, поэтому его придется пересобрать. Детали в принципе изложены в самом annobin.spec в комментариях, поэтому отметим только, что пересобрать его придётся минимум дважды: сперва, используя ещё системный gcc 8.3.1, поправить требование к версии gcc, чтобы gcc < %{gcc_next} превратилось в gcc <= %{gcc_next}, потом, уже заменив gcc, пересобрать заново, вернув требование gcc < %{gcc_next} и раскомментировав строку %undefine _annotated_build – иначе вообще ничего собираться не будет. Ну и для чистоты можно пересобрать в третий раз, вернув _annotated_build на место, а предыдущие две версии пакетов (переходную и без аннотации бинарей) из репозитория удалить.
Остается libtool. Этот джентльмен тоже жёстко привязывается к версии gcc, но к счастью, зависимость эта односторонняя, поэтому libtool можно просто удалить перед заменой gcc, затем собрать его заново новым gcc и добавить соответствующий пакет в наш репозиторий gcc-9.
Как ни странно, на этом всё. У нас есть отдельный репозиторий, подключив который, можно поставить требуемую версию компилятора, а при необходимости вернуть обратно системную версию (командой dnf downgrade gcc), хотя необходимости такой у нас не возникало.
Хабравчане-девопсы, а какие у вас бывали нестандартные запросы от разработчиков?