Глава 57. Плагин Scala.

Плагин Scala расширяет плагин Java, добавляя поддержку проектов Scala. Он может работать с кодов Scala, смесью Scala и Java и даже чистым Java-кодом (хотя его и необязательно использовать для последнего). Плагин поддерживает совместную компиляцию, которая позволяет свободно смешивать код Scala и Java, с зависимостями в обе стороны. Например, класс Scala может наследоваться от Java класса, который в свою очередь наследуется от Scala класса. Это позволяет использовать наилучший язык для работы и переписать любой класс на другой язык, если потребуется.

57.1. Использование.

Чтобы использовать плагин Scala, включите следующую строчку в вашу сборку:

Пример 57.1. Использование плагина Scala

build.gradle

apply plugin: 'scala'
	  

57.2. Задачи.

Плагин Scala добавляет следующие задачи к проекту.

Таблица 57.1. Плагин Scala - задачи
Имя задачиЗависит отТипОписание
compileScalacompileJavaScalaCompileКомпилирует исходные файлы Scala.
compileTestScalacompileTestJavaScalaCompileКомпилирует исходные файлы тестов Scala.
compileНаборИсходниковScalacompileНаборИсходниковJavaScalaCompileКомпилирует исходные файлы Scala данного набора исходников.
scaladoc-ScalaDocГенерирует документацию API для исходные файлов Scala.

Плагин Scala добавляет следующие зависимости к задачам, добавленным плагином Java.

Таблица 57.2. Плагин Scala - дополнительные зависимости задач
Имя задачиЗависит от
classescompileScala
testClassescompileTestScala
наборИсходниковClassescompileНаборИсходниковScala
Рисунок 57.1. Плагин Scala - задачи
Плагин Scala - задачи

57.3. Разметка проекта.

Плагин Scala предполагает, что у проекта разметка такая, как показано ниже. Все исходные папки Scala могут содержать код Scala и Java. Исходные папки Java могут содержать только исходные коды Java. Ни одна из этих папок не обязана существовать или содержать что-либо; плагин Scala просто скомпилирует все, что найдет.

Таблица 57.3. Плагин Scala - разметка проекта
ПапкаЗначение
src/main/javaИсходники Java.
src/main/resourcesРесурсы.
src/main/scalaИсходники Scala. Может также содержать исходники Java для совместной компиляции.
src/test/javaИсходники тестов Java
src/test/resourcesРесурсы тестов.
src/test/scalaИсходники тестов Scala. Может также содержать исходники Java для совместной компиляции.
src/наборИсходников/javaИсходники Java данного набора исходников.
src/наборИсходников/resourcesРесурсы данного набора исходников.
src/наборИсходников/scalaИсходники Scala данного набора исходников. Может также содержать исходники Java для совместной компиляции.

57.3.1. Изменение разметки проекта.

Так же как плагин Java, плагин Scala позволяет вам настроить местоположение исходников и тестов Scala.

Пример 57.2. Пользовательская разметка исходников Scala

build.gradle

sourceSets {
    main {
        scala {
            srcDirs = ['src/scala']
        }
    }
    test {
        scala {
            srcDirs = ['test/scala']
        }
    }
}
	  

57.4. Управление зависимостями.

Проектам Scala необходимо объявить зависимость scala-library. Эта зависимость потом будет использована в путях к классам компиляции и выполнения. Также она будет использована для получения компилятора Scala и инструмента Scaladoc.

Если Scala используется для 'боевого' кода, зависимость scala-library должна быть добавлена в конфигурацию compile:

Пример 57.3. Объявление зависимости от Scala

build.gradle

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.scala-lang:scala-library:2.11.8'
    testCompile 'org.scalatest:scalatest_2.11:3.0.0'
    testCompile 'junit:junit:4.12'
}
	  

Если Scala используется только для кода тестов, тогда зависимость scala-library должна быть добавлена в конфигурацию testCompile:

Пример 57.4. Объявление зависимости кодов тестов от Scala

build.gradle

dependencies {
    testCompile "org.scala-lang:scala-library:2.11.1"
}
	  

57.5. Автоматическая настройка scalaClasspath.

Задачи ScalaCompile и ScalaDoc получают код Scala двумя способами: в их пути к классам и в их scalaClasspath. Первый используется для нахождения классов, на которые ссылается исходный код и обычно содержит scala-library вместе с другими библиотеками. Последний используется для загрузки и выполнения компилятора Scala и инструмента Scaladoc и должен содержать только библиотеку scala-compiler и ее зависимости.

Если scalaClasspath задачи не настроен явно, плагин Scala (базовый) попытается вывести его из пути к классам задачи. Делается это так:

  • Если jar-файл scala-library найден в пути к классам и у проекта есть хотя бы одно объявленное хранилище, соответствующая зависимость хранилища scala-compiler будет добавлена в scalaClasspath.
  • В противном случае, выполнение задачи прервется с ошибкой, говорящей, что scalaClasspath не может быть выведен.

57.6. Настройка компилятора Zinc.

Плагин Scala использует конфигурацию с именем zinc для разрешения компилятора Zinc и его зависимостей. Gradle по умолчанию предоставляет версию Zinc, но если вам требуется особая версия, вы можете добавить явную зависимость наподобие 'com.typesafe.zinc:zinc:0.3.6' в конфигурацию zinc. Gradle поддерживает версию Zinc 0.3.0 и выше; хотя, вследствие регрессии в компиляторе Zinc, версии с 0.3.2 по 0.3.5.2 не могут быть использованы.

Пример 57.5. Объявление используемой версии компилятора Zinc

build.gradle

dependencies {
    zinc 'com.typesafe.zinc:zinc:0.3.9'
}
	  

Важно быть осторожным при объявлении вашей зависимости scala-library. Компилятору Zinc требется совместимая версия scala-library, которая может отличаться от требуемой вашим приложением. Gradle заботится о добавлении совместимой версии scala-library для вас, но расширенные правила разрешения зависимостей могут вынудить использовать несовместимую версию вместо нее.

Например, использование configurations.all перезапишет версию, используемую компилятором Zinc, определенной версией scala-library:

Пример 57.6. Принудительная установка зависимости scala-library для всех конфигураций

build.gradle

configurations.all {
    resolutionStrategy.force "org.scala-lang:scala-library:2.11.7"
}
	    

Наилучший способ избежать этой проблемы, быть более избирательным при настройки зависимости scala-library (например, не использовать правило configuration.all или использовать условие, препятствующее применению этого правила к конфигурации zinc). Иногда, это правило может прийти из плагина или другого кода, который вы не можете контролировать. В таком случае, вы можете принудительно использовать корректную версию библиотеки только в конфигурации zinc:

Пример 57.7. Принудительная установка зависимости scala-library для конфигурации zinc

build.gradle

configurations.zinc {
    resolutionStrategy.force "org.scala-lang:scala-library:2.10.5"
}
	    

Вы можете диагностировать проблему с выбором версии компилятора Zinc, запустив dependencyInsight для конфигурации zinc.

57.7. Условные свойства.

Плагин Scala не добавляет никаких условных свойств к проекту.

57.8. Свойства наборов исходников.

Плагин Scala добавляет следующие условные свойства к каждому набору исходников в проекте. Вы можете использовать эти свойства в вашем сборочном скрипте, как будто они являются свойствами объекта набора исходников.

Таблица 57.4. Плагин Scala - свойства наборов исходников
Имя свойстваТипЗначение по умолчаниюОписание
scalaSourceDirectorySet (только для чтения)Не nullИсходные файлы Scala этого набора исходников. Содержит все файлы .scala и .java, найденные в исходных папках Scala и исключает все остальные типы файлов.
scala.srcDirsSet<File>. Может быть установлено с использованием любого способа, описанного в Секции 20.5 Указание набора входных файлов.[папкаПроекта/src/имя/scala]Исходные папки, содержащие исходные файлы Scala, этого набора исходников.
allScalaFileTree (только для чтения)Не nullВсе исходные файлы Scala этого набора исходников. Содержит только файлы .scala, найденные в исходных папках Scala.

Эти условные свойства предоставляются условным объектом типа ScalaSourceSet.

Также плагин Scala изменяет некоторые свойства наборов исходников:

Таблица 57.5. Плагин Scala - свойства наборов исходников
Имя свойстваИзменение
allJavaДобавляет все файлы .java, найденные в исходных папках Scala.
allSourceДобавляет все исходные файлы, найденные в исходных папках Scala.

57.9. Компиляция во внешнем процессе.

Компиляция Scala происходит во внешнем процессе.

По умолчанию, в качестве настроек памяти для внешнего процесса используются умолчания для JVM. Чтобы подогнать настройки памяти, настройте свойство scalaCompileOptions.forkOptions как требуется:

Пример 57.8. Подгонка настроек памяти

build.gradle

tasks.withType(ScalaCompile) {
    configure(scalaCompileOptions.forkOptions) {
        memoryMaximumSize = '1g'
        jvmArgs = ['-XX:MaxPermSize=512m']
    }
}
	  

57.10. Инкрементная компиляция.

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

Инкрементная компиляция плагина Scala по умолчанию интегрирована с Zinc, автономной версией инкрементного Scala-компилятора sbt. Если вы хотите отключить инкрементную компиляцию, установите force = true в вашем сборочном файле:

Пример 57.9. Принудительная компиляция всего кода

build.gradle

tasks.withType(ScalaCompile) {
    scalaCompileOptions.with {
        force = true
    }
}
	  

Примечание: все классы будут перекомпилированы только, если хотя бы один входной исходный файл был изменен. Если изменений исходных файлов не было, задача compileScala будет считаться актуальной.

Компилятор Scala, основанный на Zinc, поддерживает совместную компиляцию кода Java и Scala. По умолчанию, весь код Java и Scala в папке src/main/scala будет участвовать в совместной компиляции. Даже код Java будет компилироваться инкрементно.

Для инкрементной компиляции требуется анализ зависимостей исходного кода. Результаты анализа сохраняются в файл указанный в scalaCompileOptions.incrementalOptions.analysisFile (по умолчанию уже установлено разумное значение). В многопроектной сборке анализируемые файлы передаются в находящиеся ниже задачи ScalaCompile, чтобы сделать возможным инкрементную компиляцию за пределами границ проекта. Для задач ScalaCompile, добавленных плагином Scala, не требуется никакой настройки, чтобы это работало. Для других задачи ScalaCompile, которые вы можете добавить, необходимо настроить свойство scalaCompileOptions.incrementalOptions.publishedCode, чтобы оно указывало на папку с классами или архив jar, которым код будет передаваться в путь к классам компиляции нижележащих задач ScalaCompile. Обратите внимание, что если свойство publishedCode установлено неправильно, нижележащие задачи могут не перекомпилировать код, который затрагивается вышележащими изменениями, приводя к некорректным результам компиляции.

Обратите внимание, что режим демона, основанный на Zinc Nailgun, не поддерживается. Вместо этого, планируется расширение демона компиляции Gradle, чтобы он действовал между вызовами, повторно используя тот же компилятор Scala. Ожидается, что это даст значительное ускорение компиляции Scala.

57.11. Компиляция и тестирование с Java 6.

Компилятор Scala игнорирует настройки Gradle targetCompatibility и sourceCompatibility. В Scala 2.11 компилятор всегда компилирует в байткод, совместимый с Java 6. В Scala 2.12 компилятор всегда компилирует в байткод, совместимый с Java 8. Если у вас есть исходники Java, вы можете следовать тем же шагам, что и в главе про плагин Java, чтобы гарантировать использование корректной версии компилятора Java.

Пример 57.10. Настройка сборки Java 6 для Scala

gradle.properties

# in $HOME/.gradle/gradle.properties
java6Home=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
	  

build.gradle

sourceCompatibility = 1.6

assert hasProperty('java6Home') : "Set the property 'java6Home' in your your gradle.properties pointing to a Java 6 installation"
def javaExecutablesPath = new File(java6Home, 'bin')
def javaExecutables = [:].withDefault { execName ->
    def executable = new File(javaExecutablesPath, execName)
    assert executable.exists() : "There is no ${execName} executable in ${javaExecutablesPath}"
    executable
}

tasks.withType(AbstractCompile) {
    options.with {
        fork = true
        forkOptions.javaHome = file(java6Home)
    }
}
tasks.withType(Test) {
    executable = javaExecutables.java
}
tasks.withType(JavaExec) {
    executable = javaExecutables.java
}
tasks.withType(Javadoc) {
    executable = javaExecutables.javadoc
}
	  

57.12. Интеграция с Eclipse.

Когда плагин Eclipse встречает проект Scala, он добавляет дополнительную конфигурацию, чтобы проект работал со Scala IDE 'из коробки'. В частности, плагин добавляет основной контейнер и контейнер зависимостей.

57.13. Интеграция с IntelliJ IDEA.

Когда плагин IDEA встречает проект Scala, он добавляет дополнительную конфигурацию, чтобы проект работал с IDEA 'из коробки'. В частности, плагин добавляет Scala SDK (IntelliJ IDEA 14+) и библиотеку компилятора Scala, которые совпадают с версией Scala, находящейся в пути к классам. Плагин Scala обратно совместим с ранними версиями IntelliJ IDEA и есть возможность добавить Scala Facet вместо, используемого по умолчанию, Scala SDK, настроив targetVersion у IdeaModel.

Пример 57.11. Явное указание целевой версии IntelliJ IDEA

build.gradle

idea {
    targetVersion = "13"
}