Хабрахабр

[Из песочницы] Кросс-компиляция Scala в Gradle проекте

Как правило для целей создания нескольких версий одного артефакта в сообществе принято использовать SBT, где эта возможность есть прямо из коробки и настраивается в пару строк. Для Scala проектов довольно распространённым является предоставление бинарных артефактов скомпилированных под несколько версий Scala компилятора. Но что если мы хотим заморочится и создать билд для кросс компиляции не используя SBT?

Исторически весь проект собирается с помощью Gradle, и фасад было решено добавить в этот же самый проект в качестве сабмодуля. Для одного из своих Java проектов я решил создать Scala фасад. Есть открытый тикет 2017 года и пара плагинов (1, 2), которые обещают добавить эту возможность в ваш проект, но с ними есть проблемы, как правило связанные с публикацией артефактов. Gradle в целом может компилировать Scala модули с той лишь оговоркой что никакой кросс компиляции в поддержке не заявлено. Я решил проверить, как сложно на самом деле сконфирурировать билд для кросс компиляции без специальных плагинов и СМС. И больше в целом ничего нет.

Хотелось бы чтобы один и тот же набор исходников был скомпилирован тремя версиями Scala компилятора: 2. Для начала опишем желаемый результат. 12 и 2. 11, 2. 13. 13 (на этот момент самый актуальный 2. И так как в Scala 2. 0-RC2). Опять же, в SBT это все в добавляется в пару строчек конфигурации. 13 есть куча всяких назад несовместимых изменений в коллекциях, хотелось бы иметь возможность добавить дополнительные source сеты для кода, специфичного для каждого из компиляторов. Давайте смотреть что можно сделать в Gradle.

Структура проекта

Плюс, все зависимости, имеющие префикс версии Scala компилятора, тоже нужно менять. Первая трудность с которой приходиться столкнуться это то, что версия компилятора вычисляется из версии задекларированной зависимости на scala-library. для каждой версии компилятора лист зависимостей должен быть свой. Т.е. Некоторые флаги были переименованы между версиями, а какие-то просто помечены как устаревшие или убраны совсем. В добавок, набор флагов для разных версий компилятора на самом деле разный. Поэтому решил поисследовать возможные другие способы решения этой задачи. Я решил, что пытаться уловить все ньюансы разных компиляторов в одном билд файле кажется уж больно затруднительной задачей и ещё более затруднительной её дальнейшая поддержка. А что если мы создадим несколько билд конфигураций для одной и той же структуры директорий проекта?

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

settings.gradle

rootProject.name = 'test'
include 'java-library' include 'scala-facade_2.11'
project(':scala-facade_2.11').with { projectDir = file('scala-facade') buildFileName = 'build-2.11.gradle'
} include 'scala-facade_2.12'
project(':scala-facade_2.12').with { projectDir = file('scala-facade') buildFileName = 'build-2.12.gradle'
} include 'scala-facade_2.13'
project(':scala-facade_2.13').with { projectDir = file('scala-facade') buildFileName = 'build-2.13.gradle'
}

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

build-2.12.gradle

plugins { id 'scala'
} buildDir = 'build-2.12' clean { delete 'build-2.12'
} // ...

С одной лишь проблемой, что такой билд сведет с ума вашу любимую IDE и скорее всего дальнейшее редактирование вашего проекта придется вести по приборам. Теперь совсем красиво. всегда можно просто закоментировать лишние импорты сабмодулей и превратить кросс билд в обычный билд, с которым ваша IDE скорее всего умеет работать. Я подумал, что это не большая беда, т.к.

Опять же, с раздельными файлами это оказалось довольно просто, создаем новую директорию и конфигурируем ее как source set. А что насчёт дополнительных source сетов?

build-2.12.gradle

// ...
sourceSets } main { scala { compileClasspath += compat.output } } test { scala { compileClasspath += compat.output runtimeClasspath += compat.output } }
}
// ...

build-2.13.gradle

// ...
sourceSets { compat { scala { srcDir 'src/main/scala-2.13+' } } main { scala { compileClasspath += compat.output } } test { scala { compileClasspath += compat.output runtimeClasspath += compat.output } }
}
// ...

Финальная структура проекта выглядит так:

Финальный проект

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

Надеюсь кому-то этот пост будет полезен. Итого, проблема была решена, гибкости Gradle хватило для того чтобы довольно изящно выразить весьма нетривияльных сетап, а кросс билд Scala возможен не только с использованием SBT и, если по той или иной причине вы используете Gradle для сборки Scala проекта, кросс компиляция как возможность вам так же доступна. Спасибо за внимание.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть