Кросс-компиляция Scala в Gradle проекте

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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


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


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


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


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


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


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 сетов? Опять же, с раздельными файлами это оказалось довольно просто, создаем новую директорию и конфигурируем ее как source set.


build-2.12.gradle
// ...
sourceSets {
    compat {
        scala {
            srcDir 'src/main/scala-2.12-'
        }
    }
    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 проекта, кросс компиляция как возможность вам так же доступна. Надеюсь кому-то этот пост будет полезен. Спасибо за внимание.

Источник: https://habr.com/ru/post/452552/


Интересные статьи

Интересные статьи

В прошлом месяце вышла стабильная LTS-версия многоязычной среды выполнения GraalVM 20.3.0 от корпорации Oracle и мне захотелось испробовать её для решения какой-нибудь интересной практиче...
Часть 1 ‣ Часть 2 ‣ Часть 3 ‣ Часть 4 ‣ Часть 5 ‣ Часть 6 ‣ Часть 7 ‣ Часть 8 ‣ Часть 9 ‣ Часть 10 Starlink и Астрономы Начиная с первого «поезда Илона Маска» — цепочки из 60 спутни...
Всем привет! Не так давно на работе в рамках тестирования нового бизнес-процесса мне понадобилась возможность авторизации под разными пользователями. Переход в соответствующий р...
Если в вашей компании хотя бы два сотрудника, отвечающих за работу со сделками в Битрикс24, рано или поздно возникает вопрос распределения лидов между ними.
Получить трафик для интернет-магазина сегодня не проблема. Есть много каналов его привлечения: органическая выдача, контекстная реклама, контент-маркетинг, RTB-сети и т. д. Вопрос в том, как вы распор...