[recovery mode] Gradle + LLVM

Этот небольшой пост может оказаться полезен тем, кто хотел бы быстро начать работать с LLVM, не заморачиваясь с закачкой исходников и просторойкой фреймворка. Кто не хотел бы ковыряться в малопонятных скриптах CMake-а чтобы добиться ожидаемого результата, ну и наконец, просто для ленивых :)

Я расскажу, как сделать это изящно, буквально парой строк в билд-скрипте Gradle-а.
Столкнувшись с необходимостью писать код под LLVM, я понял, что «попал». :) «Попал» уже на стадии конфигурации билда для проекта. Мне было необходимо чтобы проект единообразно строился как под Linux так и под Windows. Само собой, каждый раз вместе с проектом простраивать еще и весь LLVM совсем не хотелось. И, по началу, я понадеялся на предпростроенные версии библиотеки с их официального сайта, но как оказалось, по факту, там есть только линуксовые бинарники построенные без RTTI и исключений. Под Windows есть инсталятор, но, несмотря на то, что он называется LLVM-x.x.x-win64.exe внутри только clang.

Ну и в довесок билд система там CMake у которой для простройки в релизном варианте под Linux нужно на стадии конфигурирования задать:

cmake -DCMAKE_BUILD_TYPE=Release


А под виндой этого уже, почему-то, недостаточно и еще дополнительно приходится менять конфигурацию в самой билдовой команде:

cmake --build . --config Release


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

Собственно речь дальше пойдет исключительно о нем: cpp-llvm.
Вот минимальная конфигурация, необходимая для интеграции с LLVM:

plugins {
    id 'org.bitbucket.akornilov.cpp-llvm' version '0.1'
}

llvm {
    version = '9.0.0' // Required version.
}


Требования для работы:

  • Установленная Java (8 и выше).
  • Установленный Gradle (я использовал последнюю на момент написания статьи версию 5.6.2 и не проверял работу плагина на более старых версиях)
  • Gcc/CLang (версии с поддержкой как минимум C++11) на Linux.
  • MSVC 2019 (например Community Edition) на Windows.
  • Плагин должен использоваться совместно с «cpp-application» или «cpp-library».
  • Доступ в сеть на момент первой постройки для загрузки бинарных файлов LLVM (либо режим автономной работы, см. ниже).


Поддерживаемые версии LLVM:

  • 9.0.0
  • 8.0.0


Неофициальные сборки под x86_64 под Windows (msvc 2019) и Linux (Debian 10.0 + CLang 10.0). Простроены с разрешенными RTTI и исключениями в двух вариантах Release и Debug.

  • 7.1.0
  • 7.0.1
  • 7.0.0
  • 6.0.1
  • 6.0.0
  • 5.0.2
  • 5.0.1
  • 5.0.0
  • 4.0.1
  • 4.0.0
  • 3.9.1
  • 3.9.0
  • 3.8.1
  • 3.8.0
  • 3.7.1
  • 3.7.0
  • 3.6.2
  • 3.6.1
  • 3.6.0
  • 3.5.2
  • 3.5.1
  • 3.5.0
  • 3.4.2
  • 3.4.1
  • 3.4
  • 3.3
  • 3.2
  • 3.1
  • 3.0


Официальные сборки с серверов LLVM, только x86_64 под Linux с выключенным RTTI и исключениями.

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

gradle llvmVersions
> Task :llvm-app:llvmVersions
9.0.0
8.0.0

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed


Все необходимые при билде бинарники LLVM загружаются в локальный кэш (в папке gradle-а в пользовательском каталоге) и переиспользуются для других билдов.

При первом запуске подготовка к построению может занять существенное время, т.к. из сети будет скачено около 300Мб для отладочной версии или 30Мб для релизной (официальные версии весят тоже около 300Мб).

Используется только статическая линковка т.к. по каким-то непонятным причинам билд обвязка LLVM не умеет создавать динамические библиотеки под MSVC.

Прочая конфигурация плагина


Можно задать конкретный адрес для загрузки архива с бинарниками (.tar.xz или .tar.gz).

llvm {
    version = '7.0.1'
    serverUrl = 'http://releases.llvm.org/7.0.1/clang%2bllvm-7.0.1-x86_64-linux-gnu-ubuntu-18.04.tar.xz'
}


Главное чтобы указанная версия (version = xxx) совпадала с той, что реально в архиве, выбранном для закачки, иначе конфигурация билда может оказаться неправильной.

Можно работать с готовыми бинарями LLVM (скаченными или простроенными самостоятельно) автономно

llvm {
    version = '7.0.1'
    localPath = ''
}


localPath должен указывать на существующую папку на локальной файловой системой внутри которой плагин ожидает увидеть подкаталоги 'include' и «lib», т.е. структура каталогов, которая используется в официальных билдах. Если после самостоятельной простройки у вас структура папок отличается, ее нужно будет «причесать». :)

Версии так же должны совпадать, как уже отмечалось, в предыдущем пункте.
В этом случае из сети ничего, свзяанного с LLVM грузиться не будет.

Работа с отдельными компонентами:

llvm {
    version = '9.0.0'
    components = ['Engine', 'OrcJIT']
}


Если вы работаете только с какой-то определенной частью LLVM, то вам, несомненно, нужно обратить внимание на этот параметр. Если, к примеру, вам нужен только OrcJIT зачем линковать все 145 статические библиотеки? Выбор конкретных компонентов существенно облегчит жизнь линковщику, а вам сэкономит время.

Со списком всех доступных компонент можно ознакомиться вот так:

gradle llvmComponents


На Линуксе можно ощутимо уменьшить время линковки в отладочном варианте, если использовать вот такую опцию

llvm {
    version = '9.0.0'
    forceReleaseLinux = true
}


В этом случае принудительно используется релизный вариант LLVM при отладочном варианте вашего билда. В большинстве случаев я бы рекомендовал пользоваться им, если только вам не нужно отлаживать сам LLVM. На сборку на Windows это параметр не влияет, потому как, к сожалению, MSVC 2019 не дает миксовать релизные и дебажные библиотеки в одном билде.

Ну и в заключение я, конечно, рекомендовал бы использовать этот плагин в связке с моими другими плагинами :), о которых можно почитать в этой статье.

cpp-build-tuner поможет оптимизировать время компиляции и размер итогового бинарника.

cpp-ide-generator даст вам легкую интеграцию с несколькими IDE, в конфигурации которых уже будут пути к хидерам LLVM и IDE сможет корректно заиндексировать их.

kbm8he9ajb1xkt8wnieerb1wrv8.png

Возможная проблема при использовании cpp-ide-generator:

FAILURE: Build failed with an exception.

* What went wrong:

A problem occurred configuring project ':llvm-app'.

> Cannot change dependencies of configuration ':llvm-app:cppCompileDebug' after it has been resolved.


Если вы увидели такое, то это всего лишь означает, что cpp-ide-generator должен быть добавлен после cpp-llvm, как вот здесь:

plugins {
    id 'cpp-application'
    id 'org.bitbucket.akornilov.cpp-build-tuner' version '0.7'
    id 'org.bitbucket.akornilov.cpp-llvm' version '0.1'
    id 'org.bitbucket.akornilov.cpp-ide-generator' version '0.5'
}


Полный пример использования плагина можно посмотреть вот здесь.

© Habrahabr.ru