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 属性时,Scala SDK 依赖项必须在 implementation 配置上手动声明。不推荐这种模式,因为它依赖于从生产运行时类路径推断 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]

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 版本 正在使用的工具链 参数值

version < 2.13.1

-target:jvm-1.<java_version>

-target:jvm-1.8

2.13.1 <= version < 2.13.9

-target:<java_version>

-target:8

2.13.9 <= version < 3.0

-release:<java_version>

-target:8

3.0 <= version

-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 性质和依赖项容器。

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'
}