Начиная с версии 6.0.1-3 пакета llvm/clang в дистрибутиве ROSA Fresh есть макрос %clang_gcc_wrapper
.
Код макроса
Для тех, кому проще посмотреть его код, чем читать объяснение:
clang-gcc-wrapper.macros
clang-gcc-wrapper.sh
Как при сборке пакета задается компилятор
Существует 2 основных компилятора: GCC и Clang (LLVM). Дистрибутив Роса собирается с помощью GCC, однако для некоторых пакетов используется clang. Обычно это либо очень увесистые проекты (LibreOffice), либо те, разработчики которых сами перешли на clang и используют много функций, специфичных для него (Chromium, Firefox).
Строго говоря, стандарта, как задать компилятор, нет. Но GNU Make издавна используются переменные:
CC
для задания компилятора языка Си, принимающая значенияgcc
илиclang
CXX
для задания компилятора языка Си++, принимающая значенияg++
илиclang++
Другие системы сборки обычно также понимают эти переменные.
Есть 2 способа задать переменные для make
:
-
В аргументах вызова, например:
make CC=clang CXX=clang++
-
Переменными окружения:
- Установив их глобально (документация:
export CC=clang export CXX=clang make
- Или задав переменные окружения только для одной команды, чтобы они не влияли на все последующие:
env CC=clang CXX=clang++ make
- Установив их глобально (документация:
Так вот, в простых случаях для сборки пакета с помощью clang вместо gcc достаточно в RPM-спеке сделать так:
... %build export CC=clang export CXX=clang ...
Дальше может идти, например, %make
.
Флаги компилятора
Для флагов (параметров) компилятора обычно используются переменные:
CFLAGS
для параметров вызова компилятора Си (gcc, clang)CXXLAGS
илиCPPFLAGS
для параметров вызова компилятора Си++ (g++, clang++)
При сборке RPM устанавливается стандартный набор флагов компилятора, за который отвечает макрос %optflags
.
На Ubuntu он очень прост (да, RPM работает на Ubuntu, sudo apt install rpm
):
$ rpm --eval '%optflags' -O2 -g
На Росе сложнее:
$ rpm --eval '%optflags' -O2 -Wa,--compress-debug-sections -gdwarf-4 -fvar-tracking-assignments -frecord-gcc-switches -Wstrict-aliasing=2 -pipe -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -ffat-lto-objects -fno-delete-null-pointer-checks -fstack-protector --param=ssp-buffer-size=4 -fPIC
Clang версии 6.0 падает в ошибку «Неизвестный флаг» на флагах -frecord-gcc-switches
и -fvar-tracking-assignments
. Это главная проблема, из-за которой появилась эта статья.
Вообще рекомендую прочитать статью «Оптимизация GCC» на Gentoo Wiki.
Переменным окружения CFLAGS
и CPPFLAGS
до начала сборки в качестве значения присваивается вот этот набор. Чтобы не присваивался, в начале спека пишут:
%global %{optflags} %{nil}
Далее можно стандартными для шелла методами манипулировать значениями этих переменных.
Подводные камни
Смотрите баг 9567. Суть в том, что макросы %qmake_qt4
и %qmake_qt5
в Росе сделаны так, что в них жестко прописывается набор флагов компилятора из %optflags
, которые переопределяют то, что будет задано переменными окружениями и параметрами сборки.
Макрос %clang_gcc_wrapper
Изначально этого макроса не было. Была задача собрать QtWebEngine с помощью clang, т.к. на ABF не хватало ресурсов на сборку с помощью gcc.
Здесь можно посмотреть дерево исходников пакета qt5-qtwebengine на момент, когда он был собран с помощью костылей, в дальнейшем переродившихся в макрос %clang_gcc_wrapper
.
Имевшийся код в минимальными изменениями был переделан в макрос, чтобы вместо копирования одного и того же кода из множества строк между спеками просто использовать макрос.
Не все понимают одну важную вещь: при сборке RPM для каждой секции (%prep
, %build
и др.) создается свой шелл-скрипт, который просто тупо выполняется с параметром -e
, что означает фатальный сбой всего скрипта при сбое любой промежуточной команды. Раскрытие макроса означает, что макрос преобразуется в набор шелл-кода, который просто записывается во временный файл со скриптом, который затем выполнится. Всё.
Напомню ссылки на исходники макроса:
сам макрос: clang-gcc-wrapper.macros
вспомогательный скрипт: clang-gcc-wrapper.sh
Как вы уже догадались, текст "%clang_gcc_wrapper"
в RPM-спеке просто заменяется на код clang-gcc-wrapper.macros, который потом выполняется в скрипте сборки.
Пример использования этого макроса:
%build %clang_gcc_wrapper
Живой пример — спек qtiplot (но это несколько сложный случай с нюансами, там потом вручную задаются CFLAGS, %optflgs отключены, но обычно этого не нужно делать).
Код clang-gcc-wrapper.macros делает следующие хаки:
- В папке с распакованными исходниками собираемого пакета создает папку
local_bin
1 - Скрипт clang-gcc-wrapper.sh, находящийся в составе пакета clang по адресу
/usr/share/clang/clang-gcc-wrapper.sh
, копирует вlocal_bin/clang
иlocal_bin/clang++
- В файле
local_bin/clang
«_CLANG_» заменяется на вывод командаwhich clang
, то есть/usr/bin/clang
- В файле
local_bin/clang++
«_CLANG_» заменяется на вывод командаwhich clang++
, то есть/usr/bin/clang++
- Переменная PATH задается таким образом, что
local_bin/
находится слева, благодаря чему по командеclang
будет вызываться скриптlocal_bin/clang
, а не/usr/bin/clang
, аналогично дляclang++
- Задаются переменные окружения CC, CXX, CPP, CLANG2, CLANGXX2 так, чтобы они имели абсолютное знаничение пути к файлам local_bin/clang или local_bin/clang++ начиная от корня системы
- Проверяется, что эти значения действительно указывают в нужное место
Скрипт clang-gcc-wrapper.sh, который теперь стал local_bin/clang
и local_bin/clang++
, принимает все вызовы clang или clang++, смотрит, есть ли в них пробелмные флаги компилятора, то есть -fvar-tracking-assignments или -frecord-gcc-switches, вырезает их и вызывает настоящий clang или clang++, то есть /usr/bin/clang
или /usr/bin/clang++
, сохраняя все имевшиеся параметры, только лишь вырезав проблемные флаги. В лог сборки при этом записывается, какие флаги были и стали.
Все просто и костыльно, как видите 🙂
Если идет речь о системе сборки qmake, то потребуется еще немного телодвижений, пример по аналогии с qtiplot:
%build %clang_gcc_wrapper %qmake_qt5 \ QMAKE_CC="$CC" \ QMAKE_CXX="$CXX"
То есть лучше дополнительно задать параметры qmake QMAKE_CC
и QMAKE_CXX
, в результате чего QMAKE_CC будет указывать в local_bin/clang, а QMAKE_CXX в local_bin/clang++. Смотрите документацию Qt.
В целом макрос %clang_gcc_wrapper
можно использовать и там, где было бы достаточно
%global optflags %{nil} ... %build export CC=clang export CXX=clang++
Благодаря использованию макроса можно обойтись без %global optflags %{nil}
, то есть сохранить дополнительные флаги безопасности, которые clang понимает.
Примечания
1) Если папка local_bin уже есть в исходниках, то лучше придумать свой любое другое название, например, bin_sdlnflskkhkl
и задать его:
%build export BIN_DIR="bin_sdlnflskkhkl" %clang_gcc_wrapper
2) Переменные CLANG и CLANGXX я придумал сам, чтобы их можно было использовать в дальнейшем, но, кажется, нигде не использовал.
Отправить ответ