Scala插件扩展了Java插件,以添加对Scala项目的支持. 它可以处理Scala代码,Scala和Java混合代码,甚至是纯Java代码(尽管我们不一定建议将其用于后者). 该插件支持联合编译 ,可让您自由混合和匹配Scala和Java代码,并具有双向依赖性. 例如,Scala类可以扩展Java类,而Java类又可以扩展Scala类. 这样就可以为作业使用最佳语言,并在需要时用其他语言重写任何类.

Note that if you want to benefit from the API / implementation separation, you can also apply the java-library plugin to your Scala project.

Usage

要使用Scala插件,请在构建脚本中包含以下内容:

例子1.使用Scala插件
build.gradle
plugins {
    id 'scala'
}
build.gradle.kts
plugins {
    scala
}

Tasks

Scala插件将以下任务添加到项目中. 在此处可以找到有关更改对Java编译任务的依赖性的信息.

compileScalaScalaCompile

取决于: compileJava

编译生产Scala源文件.

compileTestScalaScalaCompile

取决于: compileTestJava

编译测试Scala源文件.

compileSourceSetScalaScalaCompile

Depends on: compileSourceSetJava

编译给定源集的Scala源文件.

scaladocScalaDoc

为生产Scala源文件生成API文档.

Scala插件向Java插件添加的任务添加以下依赖项.

表1. Scala插件-其他任务依赖性
任务名称 取决于

classes

compileScala

testClasses

compileTestScala

sourceSetClasses

compileSourceSetScala

scalaPluginTasks
图1. Scala插件-任务

Project layout

Scala插件采用如下所示的项目布局. 所有的Scala源目录都可以包含Scala Java代码. Java源目录只能包含Java源代码. 这些目录都不需要存在或包含任何内容. Scala插件将简单地编译找到的任何内容.

src/main/java

生产Java源代码.

src/main/resources

生产资源,例如XML和属性文件.

src/main/scala

生产Scala源. 也可能包含Java源文件以进行联合编译.

src/test/java

测试Java源代码.

src/test/resources

测试资源.

src/test/scala

测试Scala源. 也可能包含Java源文件以进行联合编译.

src/sourceSet/java

名为sourceSet的源集的Java源.

src/sourceSet/resources

名为sourceSet的源集的资源.

src/sourceSet/scala

给定源集的Scala源文件. 也可能包含Java源文件以进行联合编译.

Changing the project layout

就像Java插件一样,Scala插件允许您为Scala生产和测试源文件配置自定义位置.

示例2.自定义Scala源布局
build.gradle
sourceSets {
    main {
        scala {
            srcDirs = ['src/scala']
        }
    }
    test {
        scala {
            srcDirs = ['test/scala']
        }
    }
}
build.gradle.kts
sourceSets {
    main {
        withConvention(ScalaSourceSet::class) {
            scala {
                setSrcDirs(listOf("src/scala"))
            }
        }
    }
    test {
        withConvention(ScalaSourceSet::class) {
            scala {
                setSrcDirs(listOf("test/scala"))
            }
        }
    }
}

Dependency management

Scala项目需要声明一个scala-library依赖项. 然后,将在编译和运行时类路径上使用此依赖项. 它还将分别用于获取Scala编译器和Scaladoc工具. [ 1 ]

如果将Scala用于生产代码,则应将scala-library依赖项添加到compile配置中:

例子3.声明生产代码的Scala依赖
build.gradle
repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.scala-lang:scala-library:2.11.12'
    testImplementation 'org.scalatest:scalatest_2.11:3.0.0'
    testImplementation 'junit:junit:4.12'
}
build.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    implementation("org.scala-lang:scala-library:2.11.12")
    testImplementation("org.scalatest:scalatest_2.11:3.0.0")
    testImplementation("junit:junit:4.12")
}

如果Scala仅用于测试代码,则应将scala-library依赖项添加到testCompile配置中:

例子4.声明测试代码的Scala依赖
build.gradle
dependencies {
    testImplementation 'org.scala-lang:scala-library:2.11.1'
}
build.gradle.kts
dependencies {
    testImplementation("org.scala-lang:scala-library:2.11.1")
}

Automatic configuration of scalaClasspath

ScalaCompileScalaDoc任务以两种方式使用Scala代码:在其classpath和在其scalaClasspath . 前者用于查找源代码引用的类,通常将包含scala-library以及其他库. 后者分别用于加载和执行Scala编译器和Scaladoc工具,并且应仅包含scala-compiler库及其依赖项.

除非明确配置了任务的scalaClasspath否则Scala(基本)插件将尝试从任务的classpath推断出它. 这样做如下:

  • 如果在classpath上找到了一个scala-library jar,并且该项目至少声明了一个存储库,则将相应的scala-compiler存储库依赖项添加到scalaClasspath .

  • 否则,任务的执行将失败,并显示一条消息, scalaClasspath无法推断scalaClasspath .

Configuring the Zinc compiler

Scala插件使用名为zinc的配置来解析Zinc编译器及其依赖项. Gradle将提供Zinc的默认版本,但是如果您需要使用特定的Zinc版本,则可以对其进行更改. Gradle支持Zinc及更高版本的1.2.0.

例子5.声明要使用的Zinc编译器版本
build.gradle
scala {
    zincVersion = "1.2.1"
}
build.gradle.kts
scala {
    zincVersion.set("1.2.1")
}

Zinc编译器本身需要兼容的scala-library版本,该版本可能与您的应用程序所需的版本不同. Gradle会为您指定兼容的scala-library版本. [ 2 ]

您可以通过运行zinc配置的dependencyInsight来诊断所选Zinc编译器版本的问题.

表2.锌兼容性表
摇篮版本 支持的锌版本 锌坐标 所需的Scala版本 支持的Scala编译版本

6.0及更高版本

SBT锌 . 1.2.0及更高版本.

org.scala-sbt:zinc_2.12

运行 Zinc需要2.12.x .

可以编译Scala 2.10.x2.13.x

1.x谷5.x

不推荐使用的 Typesafe Zinc编译器. 版本0.3.0及更高版本,但0.3.2至0.3.5.2除外.

com.typesafe.zinc:zinc

运行 Zinc需要2.10.x .

可以编译Scala 2.9.x2.12.x

Convention properties

Scala插件不会向项目添加任何约定属性.

Source set properties

Scala插件将以下约定属性添加到项目中的每个源集. 您可以在构建脚本中使用这些属性,就像它们是源集对象的属性一样.

scalaSourceDirectorySet (read-only)

此源集的Scala源文件. 包含在Scala源目录中找到的所有.scala.java文件,并且排除所有其他类型的文件. 默认值:非空.

scala.srcDirsSet<File>

包含此源集的Scala源文件的源目录. 也可能包含Java源文件以进行联合编译. 可以使用" 了解对文件集合的隐式转换"中所述的任何内容进行设置. 默认值: [ projectDir /src/ name /scala] .

allScalaFileTree (read-only)

该源集的所有Scala源文件. 仅包含在Scala源目录中找到的.scala文件. 默认值:非空.

这些约定属性由类型为ScalaSourceSet的约定对象提供.

Scala插件还修改了一些源集属性:

表3. Scala插件-源集属性
物业名称 Change

allJava

添加在Scala源目录中找到的所有.java文件.

allSource

添加在Scala源目录中找到的所有源文件.

Compiling in external process

Scala编译在外部过程中进行.

外部进程的内存设置默认为JVM的默认值. 要调整内存设置,请根据需要配置scalaCompileOptions.forkOptions属性:

示例6.调整内存设置
build.gradle
tasks.withType(ScalaCompile) {
    scalaCompileOptions.forkOptions.with {
        memoryMaximumSize = '1g'
        jvmArgs = ['-XX:MaxPermSize=512m']
    }
}
build.gradle.kts
tasks.withType<ScalaCompile>().configureEach {
    scalaCompileOptions.forkOptions.apply {
        memoryMaximumSize = "1g"
        jvmArgs = listOf("-XX:MaxPermSize=512m")
    }
}

Incremental compilation

通过仅编译自上次编译以来其源代码已更改的类以及受这些更改影响的类,增量编译可以显着减少Scala编译时间. 如开发时经常这样做,当频繁编译较小的代码增量时,它特别有效.

通过与Zinc集成,Scala插件默认为增量编译,它是sbt增量Scala编译器的独立版本. 如果要禁用增量编译,请在构建文件中设置force = true

例子7.强制所有代码被编译
build.gradle
tasks.withType(ScalaCompile) {
    scalaCompileOptions.with {
        force = true
    }
}
build.gradle.kts
tasks.withType<ScalaCompile>().configureEach {
    scalaCompileOptions.apply {
        isForce = true
    }
}

注意:仅在更改了至少一个输入源文件的情况下,这才会导致重新编译所有类. 如果源文件没有任何更改,则compileScala任务将照常视为UP-TO-DATE .

基于Zinc的Scala编译器支持Java和Scala代码的联合编译. 默认情况下, src/main/scala下的所有Java和Scala代码都将参与联合编译. 甚至Java代码也将以增量方式编译.

增量编译需要对源代码进行依赖性分析. 此分析的结果存储在scalaCompileOptions.incrementalOptions.analysisFile指定的文件中(该文件具有明智的默认设置). 在多项目构建中,分析文件将传递到下游的ScalaCompile任务,以实现跨项目边界的增量编译. 对于由Scala插件添加的ScalaCompile任务,无需进行配置即可完成此工作. 对于您可能添加的其他ScalaCompile任务,需要将属性scalaCompileOptions.incrementalOptions.publishedCode配置为指向将代码传递到的class文件夹或Jar存档,以编译下游ScalaCompile任务的类路径. 请注意,如果未正确设置publishedCode ,则下游任务可能无法重新编译受上游更改影响的代码,从而导致错误的编译结果.

请注意,不支持Zinc基于Nailgun的守护程序模式. 取而代之的是,我们计划增强Gradle自己的编译器守护进程,以在Gradle调用中保持活动状态,并重用相同的Scala编译器. 预计这将为Scala编译带来另一个显着的加速.

Compiling and testing for Java 6 or Java 7

Scala编译器忽略Gradle的targetCompatibilitysourceCompatibility设置. 在Scala 2.11中,Scala编译器始终将其编译为Java 6兼容的字节码. 在Scala 2.12中,Scala编译器始终将其编译为Java 8兼容的字节码. 如果您也有Java源代码,则可以按照与Java插件相同的步骤操作,以确保使用正确的Java编译器.

gradle.properties
# in $HOME/.gradle/gradle.properties
java6Home=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
build.gradle
java {
    sourceCompatibility = JavaVersion.VERSION_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
}
build.gradle.kts
java {
    sourceCompatibility = JavaVersion.VERSION_1_6
}

require(hasProperty("java6Home")) { "Set the property 'java6Home' in your your gradle.properties pointing to a Java 6 installation" }
val java6Home: String by project
val javaExecutablesPath = File(java6Home, "bin")
fun javaExecutable(execName: String): String {
    val executable = File(javaExecutablesPath, execName)
    require(executable.exists()) { "There is no ${execName} executable in ${javaExecutablesPath}" }
    return executable.toString()
}

tasks.withType<ScalaCompile>().configureEach {
    options.apply {
        isFork = true
        forkOptions.javaHome = file(java6Home)
    }
}
tasks.withType<Test>().configureEach {
    executable = javaExecutable("java")
}
tasks.withType<JavaExec>().configureEach {
    executable = javaExecutable("java")
}
tasks.withType<Javadoc>().configureEach {
    executable = javaExecutable("javadoc")
}

Eclipse Integration

当Eclipse插件遇到Scala项目时,它会添加其他配置,以使该项目可以立即使用Scala IDE. 具体来说,该插件添加了Scala性质和依赖项容器.

IntelliJ IDEA Integration

当IDEA插件遇到Scala项目时,它会添加其他配置,以使该项目开箱即可使用IDEA. 具体来说,该插件添加了一个Scala SDK(IntelliJ IDEA 14+)和一个Scala编译器库,该库与项目的类路径上的Scala版本匹配. 斯卡拉插件与早期版本的IntelliJ IDEA的向后兼容,并且可以通过配置添加斯卡拉方面,而不是默认的Scala SDK的targetVersionIdeaModel .

示例8.明确指定目标IntelliJ IDEA版本
build.gradle
idea {
    targetVersion = '13'
}
build.gradle.kts
idea {
    targetVersion = "13"
}

2 . Gradle不支持在Scala 2.11中运行Zinc编译器v1.2.0.