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

请注意,如果您想利用 API / 实现分离 的优势,您也可以将 java-library 插件应用到您的 Scala 项目中。

用法

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

build.gradle.kts
plugins {
    id("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 版本可以直接在 scala 扩展上声明。依赖项会根据声明的 Scala 版本自动添加到 scalaToolchain 配置中。scalaToolchainRuntimeClasspath 配置会解析在 scalaToolchain 配置中声明的依赖项,并包含运行 Scala 编译器所需的文件。

设置 scalaVersion 属性后,无需声明对 Scala SDK 的直接依赖项。
build.gradle.kts
repositories {
    mavenCentral()
}

scala {
    scalaVersion = "2.13.12"
}
build.gradle
repositories {
    mavenCentral()
}

scala {
    scalaVersion = "2.13.12"
}

配置 Gradle 使用 Scala 3 与使用 Scala 2 没有区别。

build.gradle.kts
repositories {
    mavenCentral()
}

scala {
    scalaVersion = "3.6.3"
}
build.gradle
repositories {
    mavenCentral()
}

scala {
    scalaVersion = "3.6.3"
}
scalaVersion 属性处于孵化阶段。

声明 Scala 依赖项

当不使用 scalaVersion 属性时,必须在 implementation 配置上手动声明 Scala SDK 依赖项。不推荐使用这种模式,因为它依赖于从生产运行时类路径 推断 Scala 类路径。在未来的 Gradle 版本中,从 scala 扩展中省略 Scala 版本将被弃用。

Scala 2 项目需要声明 scala-library 依赖项。

build.gradle.kts
repositories {
    mavenCentral()
}

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

dependencies {
    implementation("org.scala-lang:scala-library:2.13.12")
}

Scala 3 项目需要声明 scala3-library_3 依赖项

build.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    implementation("org.scala-lang:scala3-library_3:3.6.3")
}
build.gradle
repositories {
    mavenCentral()
}

dependencies {
    implementation("org.scala-lang:scala3-library_3:3.6.3")
}

如果 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 代码:通过它们的 classpathscalaClasspath。前者用于查找源代码引用的类,通常包含 scala-library 以及其他库。后者分别用于加载和执行 Scala 编译器和 Scaladoc 工具,应仅包含 scala-compiler 库及其依赖项。

如果 scala 扩展的 scalaVersion 属性未设置,并且任务的 scalaClasspath 未明确配置,Scala(基础)插件将尝试从任务的 classpath 推断类路径。推断过程如下:

  • 如果在 classpath 上找到 scala-library 的 jar 包,并且项目至少声明了一个仓库,则会将对应的 scala-compiler 仓库依赖项添加到 scalaClasspath 中。

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

配置 Zinc 编译器

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

build.gradle.kts
scala {
    scalaVersion = "2.13.12"
    zincVersion = "1.10.4"
}
build.gradle
scala {
    scalaVersion = "2.13.12"
    zincVersion = "1.10.4"
}

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 {
    scalaCompilerPlugins("org.typelevel:kind-projector_2.13.12:0.13.2")
}
build.gradle
dependencies {
    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(一个独立版本的 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 nature 和依赖容器。

IntelliJ IDEA 集成

当 IDEA 插件遇到 Scala 项目时,它会添加额外的配置,使项目能够直接与 IDEA 一起工作。具体来说,该插件会添加一个 Scala SDK (IntelliJ IDEA 14+) 和一个与项目类路径上的 Scala 版本匹配的 Scala 编译器库。Scala 插件向后兼容 IntelliJ IDEA 的早期版本,并且可以通过在 IdeaModel 上配置 targetVersion 来添加 Scala facet,而不是默认的 Scala SDK。

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