Scala 插件扩展了 Java 插件,以添加对 Scala 项目的支持。该插件还支持联合编译,它允许您自由混合和匹配 Scala 和 Java 代码,并双向依赖。例如,Scala 类可以扩展 Java 类,而 Java 类又可以扩展 Scala 类。这使得能够为这项工作使用最好的语言,并且在需要时可以使用另一种语言重写任何类。

请注意,如果您希望受益于 API/实现分离,您还可以将 java-library 插件应用到您的 Scala 项目。

用法

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

build.gradle.kts
plugins {
    scala
}
build.gradle
plugins {
    id 'scala'
}

任务

Scala 插件向项目添加了以下任务。有关更改对 Java 编译任务的依赖的信息,请参见 此处

compileScalaScalaCompile

依赖于compileJava

编译生成环境 Scala 源文件。

compileTestScalaScalaCompile

依赖于compileTestJava

编译测试 Scala 源文件。

compileSourceSetScalaScalaCompile

依赖于compileSourceSetJava

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

scaladocScalaDoc

为生成环境 Scala 源文件生成 API 文档。

ScalaCompileScalaDoc 任务开箱即用地支持 Java 工具链

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

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

classes

compileScala

testClasses

compileTestScala

sourceSetClasses

compileSourceSetScala

scalaPluginTasks
图 1. Scala 插件 - 任务

项目布局

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 源文件。

更改项目布局

与 Java 插件一样,Scala 插件允许您为 Scala 生成环境和测试源文件配置自定义位置。

build.gradle.kts
sourceSets {
    main {
        scala {
            setSrcDirs(listOf("src/scala"))
        }
    }
    test {
        scala {
            setSrcDirs(listOf("test/scala"))
        }
    }
}
build.gradle
sourceSets {
    main {
        scala {
            srcDirs = ['src/scala']
        }
    }
    test {
        scala {
            srcDirs = ['test/scala']
        }
    }
}

依赖项管理

Scala 项目需要声明 scala-library 依赖项。此依赖项随后将用于编译和运行时类路径。它还将用于分别获取 Scala 编译器和 Scaladoc 工具。[1]

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

build.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    implementation("org.scala-lang:scala-library:2.13.12")
    testImplementation("junit:junit:4.13")
}
build.gradle
repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.scala-lang:scala-library:2.13.12'
    testImplementation 'junit:junit:4.13'
}

如果您想使用 Scala 3 而不是 scala-library 依赖项,则应添加 scala3-library_3 依赖项

build.gradle.kts
plugins {
    scala
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.scala-lang:scala3-library_3:3.0.1")
    testImplementation("org.scalatest:scalatest_3:3.2.9")
    testImplementation("junit:junit:4.13")
}

dependencies {
    implementation("commons-collections:commons-collections:3.2.2")
}
build.gradle
plugins {
    id 'scala'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.scala-lang:scala3-library_3:3.0.1'
    implementation 'commons-collections:commons-collections:3.2.2'
    testImplementation 'org.scalatest:scalatest_3:3.2.9'
    testImplementation 'junit:junit:4.13'
}

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

build.gradle.kts
dependencies {
    testImplementation("org.scala-lang:scala-library:2.13.12")
}
build.gradle
dependencies {
    testImplementation 'org.scala-lang:scala-library:2.13.12'
}

scalaClasspath 的自动配置

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

除非显式配置任务的 scalaClasspath,否则 Scala(基本)插件将尝试从任务的 classpath 推断它。这是按照如下方式完成的

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

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

配置 Zinc 编译器

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

build.gradle.kts
scala {
    zincVersion = "1.9.3"
}
build.gradle
scala {
    zincVersion = "1.9.3"
}

Zinc 编译器本身需要一个兼容的 scala-library 版本,该版本可能与应用程序所需的版本不同。Gradle 会负责为您指定一个兼容的 scala-library 版本。

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

表 2. Zinc 兼容性表
Gradle 版本 支持的 Zinc 版本 Zinc 坐标 所需的 Scala 版本 支持的 Scala 编译版本

7.5 及更高版本

SBT Zinc。1.6.0 及更高版本。

org.scala-sbt:zinc_2.13

运行 Zinc 需要 Scala 2.13.x

可以编译 Scala 2.10.x3.x

6.0 到 7.5

SBT Zinc。1.2.0 及更高版本。

org.scala-sbt:zinc_2.12

运行 Zinc 需要 Scala 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 需要 Scala 2.10.x

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

向 Scala 编译器添加插件

Scala 插件添加了一个名为 scalaCompilerPlugins 的配置,用于声明和解析可选编译器插件。

build.gradle.kts
dependencies {
    implementation("org.scala-lang:scala-library:2.13.12")
    scalaCompilerPlugins("org.typelevel:kind-projector_2.13.12:0.13.2")
}
build.gradle
dependencies {
    implementation "org.scala-lang:scala-library:2.13.12"
    scalaCompilerPlugins "org.typelevel:kind-projector_2.13.12:0.13.2"
}

约定属性

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

源集属性

Scala 插件向项目中的每个源集添加以下扩展。您可以在构建脚本中使用它们,就好像它们是源集对象的属性一样。

scalaSourceDirectorySet(只读)

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

scala.srcDirsSet<File>

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

allScalaFileTree(只读)

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

这些扩展由类型为 ScalaSourceSet 的对象支持。

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

表 3. Scala 插件 - 源集属性
属性名称 更改

allJava

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

allSource

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

目标字节码级别和 Java API 版本

在运行 Scala 编译任务时,Gradle 总是会添加一个参数来配置 Scala 编译器的 Java 目标,该目标是从 Gradle 配置派生的

  • 在使用工具链时,会选择 -release 选项或旧版 Scala 版本的 target,其版本与已配置工具链的 Java 语言级别匹配。

  • 在不使用工具链时,Gradle 总是会传递一个 target 标志(其确切值取决于 Scala 版本)以编译到 Java 8 字节码。

这意味着使用具有较新 Java 版本和较旧 Scala 版本的工具链可能会导致失败,因为 Scala 一段时间内只支持 Java 8 字节码。然后,解决方案是使用工具链中的正确 Java 版本或在需要时显式降级目标。

下表说明了 Gradle 计算的值

表 4. 基于项目配置的 Scala 目标参数
Scala 版本 正在使用的工具链 参数值

版本 < 2.13.1

-target:jvm-1.<java_version>

-target:jvm-1.8

2.13.1 <= 版本 < 2.13.9

-target:<java_version>

-target:8

2.13.9 <= 版本 < 3.0

-release:<java_version>

-target:8

3.0 <= 版本

-release:<java_version>

-Xtarget:8

ScalaCompile.scalaCompileOptions.additionalParameters 中明确设置其中任何标志,或使用包含 java-output-version 的标志,将禁用该逻辑,转而使用明确的标志。

在外部进程中编译

Scala 编译在外部进程中进行。

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

build.gradle.kts
tasks.withType<ScalaCompile>().configureEach {
    scalaCompileOptions.forkOptions.apply {
        memoryMaximumSize = "1g"
        jvmArgs = listOf("-XX:MaxMetaspaceSize=512m")
    }
}
build.gradle
tasks.withType(ScalaCompile) {
    scalaCompileOptions.forkOptions.with {
        memoryMaximumSize = '1g'
        jvmArgs = ['-XX:MaxMetaspaceSize=512m']
    }
}

增量编译

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

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

build.gradle.kts
tasks.withType<ScalaCompile>().configureEach {
    scalaCompileOptions.apply {
        isForce = true
    }
}
build.gradle
tasks.withType(ScalaCompile) {
    scalaCompileOptions.with {
        force = 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 配置为指向类文件夹或 Jar 存档,通过该文件夹或存档将代码传递给下游 ScalaCompile 任务的编译类路径。请注意,如果未正确设置 publishedCode,下游任务可能不会重新编译受上游更改影响的代码,从而导致不正确的编译结果。

请注意,不支持 Zinc 的 Nailgun 守护程序模式。相反,我们计划增强 Gradle 自己的编译器守护程序,使其在 Gradle 调用之间保持活动状态,并重复使用相同的 Scala 编译器。预计这将为 Scala 编译带来另一个显著的加速。

Eclipse 集成

当 Eclipse 插件遇到 Scala 项目时,它会添加其他配置,以便项目开箱即用地与 Scala IDE 配合使用。具体来说,该插件会添加 Scala 特性和依赖项容器。

IntelliJ IDEA 集成

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

build.gradle.kts
idea {
    targetVersion = "13"
}
build.gradle
idea {
    targetVersion = '13'
}