【Gradle jvm插件系列】 scala插件权威详解
文章目录
Scala插件扩展了Java插件,以支持Scala项目。该插件还支持联合编译,允许您自由混合和匹配具有双向依赖关系的Scala和Java代码。例如,一个Scala类可以扩展一个Java类,而该Java类又扩展了一个Scala类。这使得您可以根据需要使用最适合的语言,并在需要时重新编写任何语言的类。
请注意,如果您想从API/实现分离中获益,也可以将java-library插件应用于您的Scala项目。
使用方法
要使用Scala插件,请在构建脚本中包含以下内容:
示例1. 使用Scala插件
plugins {
id 'scala'
}
任务
Scala插件向项目添加了以下任务。有关更改Java编译任务的依赖项的信息,请参阅此处。
-
compileScala:ScalaCompile
- 依赖于:compileJava
- 编译生产环境的Scala源文件。
-
compileTestScala:ScalaCompile
- 依赖于:compileTestJava
- 编译测试环境的Scala源文件。
-
compileSourceSetScala:ScalaCompile
- 依赖于:compileSourceSetJava
- 编译给定源集的Scala源文件。
-
scaladoc:ScalaDoc
- 为生产环境的Scala源文件生成API文档。
ScalaCompile和ScalaDoc任务默认支持Java工具链。
Scala插件还向Java插件添加了以下任务的依赖关系。
表1. Scala插件-额外任务依赖关系
任务名称 | 依赖于 |
---|---|
classes | compileScala |
testClasses | compileTestScala |
sourceSetClasses | compileSourceSetScala |
项目布局
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生产和测试源文件配置自定义位置。
示例2. 自定义Scala源布局
sourceSets {
main {
scala {
srcDirs = ['src/scala']
}
}
test {
scala {
srcDirs = ['test/scala']
}
}
}
依赖管理
Scala项目需要声明一个scala-library依赖项。此依赖项将用于编译和运行时类路径。它还将用于获取Scala编译器和Scaladoc工具。
如果Scala用于生产代码,则应将scala-library依赖项添加到implementation配置中:
示例3. 声明生产代码的Scala依赖项
repositories {
mavenCentral()
}
dependencies {
implementation 'org.scala-lang:scala-library:2.13.12'
testImplementation 'junit:junit:4.13'
}
如果您想使用Scala 3而不是scala-library依赖项,您应该添加scala3-library_3依赖项:
示例4. 声明生产代码的Scala 3依赖项
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配置中:
示例5. 声明测试代码的Scala依赖项
dependencies {
testImplementation 'org.scala-lang:scala-library:2.13.12'
}
scalaClasspath的自动配置
ScalaCompile和ScalaDoc任务以两种方式使用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支持1.6.0及以上版本的Zinc。
示例6. 声明要使用的Zinc编译器的版本
scala {
zincVersion = "1.9.3"
}
Zinc编译器本身需要与您的应用程序所需版本不同的scala-library兼容版本。Gradle会为您指定兼容的scala-library版本。
您可以通过运行dependencyInsight for zinc配置来诊断所选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.x至3.x。 |
6.0到7.5 | SBT Zinc. 版本1.2.0及以上 | org.scala-sbt:zinc_2.12 | 运行Zinc所需的Scala 2.12.x | 可以编译Scala 2.10.x至2.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.x至2.12.x。 |
向Scala编译器添加插件
Scala插件添加了一个名为scalaCompilerPlugins的配置,用于声明和解析可选的编译器插件。
示例7. 添加对Scala编译器插件的依赖
dependencies {
implementation "org.scala-lang:scala-library:2.13.12"
scalaCompilerPlugins "org.typelevel:kind-projector_2.13.12:0.13.2"
}
约定属性
Scala插件不向项目添加任何约定属性。
源集属性
Scala插件为项目中的每个源集添加了以下扩展。您可以在构建脚本中使用这些扩展,就像它们是源集对象的属性一样。
-
scala:SourceDirectorySet(只读)
- 该源集的Scala源文件。包含在Scala源目录中找到的所有.scala和.java文件,并排除所有其他类型的文件。默认值:非空。
-
scala.srcDirs:Set
- 包含该源集的Scala源文件的源目录。也可以包含Java源文件进行联合编译。可以使用任何在理解隐式转换为文件集合中描述的内容进行设置。默认值:[projectDir/src/name/scala]。
-
allScala:FileTree(只读)
- 该源集的所有Scala源文件。仅包含在Scala源目录中找到的.scala文件。默认值:非空。
这些扩展由ScalaSourceSet类型的对象支持。
Scala插件还修改了一些源集属性:
表3. Scala插件 - 源集属性
属性名称 | 变更 |
---|---|
allJava | 添加在Scala源目录中找到的所有.java文件。 |
allSource | 添加在Scala源目录中找到的所有源文件。 |
目标字节码级别和Java API版本
运行Scala编译任务时,Gradle将始终添加一个参数来配置Scala编译器的Java目标,该参数派生自Gradle配置:
- 使用工具链、-release选项或旧版Scala版本的目标,选择与配置的工具链的Java语言级别匹配的版本。
- 如果不使用工具链,Gradle将始终传递一个目标标志,具体的值取决于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 |
显式设置这些标志,或使用包含java-output-version的标志,将禁用显式标志的逻辑,而使用指定的标志。
在外部进程中编译
Scala编译发生在外部进程中。
外部进程的内存设置默认为JVM的默认值。要调整内存设置,请根据需要配置scalaCompileOptions.forkOptions属性:
示例8. 调整内存设置
tasks.withType(ScalaCompile) {
scalaCompileOptions.forkOptions.with {
memoryMaximumSize = '1g'
jvmArgs = ['-XX:MaxMetaspaceSize=512m']
}
}
增量编译
通过仅编译自上次编译以来已更改的源代码以及受这些更改影响的类,增量编译可以显著减少Scala编译时间。当频繁编译小的代码增量时,它尤其有效,这在开发时经常发生。
Scala插件默认使用增量编译,通过与Zinc集成实现,Zinc是sbt增量Scala编译器的一个独立版本。如果要禁用增量编译,请在构建文件中设置force = true:
示例9. 强制重新编译所有代码
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属性,以指向传递给下游ScalaCompile任务的类文件夹或Jar归档文件。请注意,如果未正确设置publishedCode,下游任务可能不会重新编译受上游更改影响的代码,从而导致编译结果不正确。
请注意,不支持基于Zinc的Nailgun守护进程模式。相反,我们计划增强Gradle自己的编译器守护进程,在Gradle调用之间保持活动状态,重用相同的Scala编译器。这预计将为Scala编译带来另一个显著的加速。
Eclipse集成
当Eclipse插件遇到Scala项目时,它会添加额外的配置,以使项目与Scala IDE开箱即用。具体而言,该插件会添加Scala nature和dependency container。
IntelliJ IDEA集成
当IDEA插件遇到Scala项目时,它会添加额外的配置,以使项目与IDEA开箱即用。具体而言,该插件会添加一个Scala SDK(IntelliJ IDEA 14+)和与项目类路径上的Scala版本匹配的Scala编译器库。Scala插件与早期版本的IntelliJ IDEA向后兼容,可以通过在IdeaModel上配置targetVersion来添加Scala facet而不是默认的Scala SDK。
示例10. 显式指定目标IntelliJ IDEA版本
idea {
targetVersion = '13'
}
参考文档
小军李:【Gradle jvm插件系列1】 Java Application插件权威详解
小军李:【Gradle jvm插件系列2】 Java Library插件用法示例权威详解
小军李:【Gradle jvm插件系列3】 Java platform平台插件权威详解
小军李:【Gradle jvm插件系列4】 scala插件权威详解
小军李:【gradle多模块系列1】多项目构建和子项目的添加管理
小军李:【Gradle多模块系列2】在子项目之间声明依赖关系和共享构建逻辑示例详解