在Gradle项目中交叉编译Scala

Scala项目提供为多个版本的Scala编译器编译的二进制工件是很常见的。 通常,为了在社区中创建一个工件的多个版本,习惯上使用SBT,此功能是开箱即用的,并且配置成两行。 但是,如果我们不使用SBT感到困惑并创建用于交叉编译的构建,该怎么办?


对于我的一个Java项目,我决定创建一个Scala门面。 从历史上看,整个项目都是使用Gradle组装的,因此决定将外观作为子模块添加到同一项目中。 作为一个整体,Gradle可以编译Scala模块,但需要注意的是,尚未声明任何交叉编译支持。 2017年有一张公开票,还有几个插件( 1,2 ),可以将这个功能添加到您的项目中,但是它们存在问题,通常与工件的发布有关。 总的来说,这里没有别的了。 我决定检查在没有特殊插件和SMS的情况下为交叉编译实际配置构建的难度。


首先,我们描述期望的结果。 我希望通过三个版本的Scala编译器来编译同一套源代码:2.11、2.12和2.13(目前是最新的2.13.0-RC2)。 而且由于Scala 2.13在集合中有许多向后不兼容的更改,因此我希望能够为每个编译器特有的代码添加其他源集。 同样,在SBT中,所有这些都添加到几行配置中。 让我们看看在Gradle中可以做什么。


项目结构


您必须面对的第一个困难是,编译器版本是根据scala库上已声明依赖项的版本计算得出的。 另外,还必须更改所有具有Scala版本的编译器前缀的依赖项。 即 对于编译器的每个版本,依赖性列表应该不同。 此外,不同版本的编译器的标志集实际上是不同的。 一些标志在版本之间被重命名,而另一些标志则被简单地标记为已过时或完全删除。 我认为,试图在一个构建文件中捕获不同编译器的所有细微差别似乎是一件困难的事情,而对其进一步的支持则更加困难。 因此,我决定探索其他可能的方法来解决此问题。 但是,如果我们为同一项目目录结构创建多个配置版本,该怎么办?


在Gradle项目中包括子模块的声明中,可以指定子模块根目录和负责其配置的文件的名称所在的目录。 让我们为多个导入指定相同的目录,并为每个版本的编译器创建构建脚本的多个副本。


settings.gradle
rootProject.name = 'test' include 'java-library' include 'scala-facade_2.11' project(':scala-facade_2.11').with { projectDir = file('scala-facade') buildFileName = 'build-2.11.gradle' } include 'scala-facade_2.12' project(':scala-facade_2.12').with { projectDir = file('scala-facade') buildFileName = 'build-2.12.gradle' } include 'scala-facade_2.13' project(':scala-facade_2.13').with { projectDir = file('scala-facade') buildFileName = 'build-2.13.gradle' } 

不错,但是有时我们会得到奇怪的编译错误,这与所有三个构建脚本都使用相同的构建目录有关。 我们可以通过为每个版本设置它们来解决此问题:


内部版本2.12.gradle
 plugins { id 'scala' } buildDir = 'build-2.12' clean { delete 'build-2.12' } // ... 

现在很漂亮。 仅存在一个问题,这样的构建会使您最喜欢的IDE疯狂,并且很可能必须使用仪器对项目进行进一步的编辑。 我以为这不是什么大麻烦,因为 您总是可以简单地注释掉多余的子模块导入,并将交叉构建转换为常规构建,您的IDE很有可能可以使用该常规构建。


那么其他来源集呢? 同样,使用单独的文件,结果非常简单,创建一个新目录并将其配置为源集。


内部版本2.12.gradle
 // ... sourceSets { compat { scala { srcDir 'src/main/scala-2.12-' } } main { scala { compileClasspath += compat.output } } test { scala { compileClasspath += compat.output runtimeClasspath += compat.output } } } // ... 

内部版本2.13.gradle
 // ... sourceSets { compat { scala { srcDir 'src/main/scala-2.13+' } } main { scala { compileClasspath += compat.output } } test { scala { compileClasspath += compat.output runtimeClasspath += compat.output } } } // ... 

该项目的最终结构如下所示:


最终项目


在这里,您还可以将各个通用部分分离到外部配置文件中,并将它们导入到内部版本中,以减少重复次数。 但是对我来说,声明性地证明了它与所有可能的Gradle插件是隔离的并且兼容的。


总体而言,问题得到了解决,Gradle的灵活性足以优雅地表达非常简单的设置,而且不仅可以使用SBT进行Scala交叉构建,而且如果出于某种原因而您使用Gradle来构建Scala项目,则可以通过交叉编译为您提供机会也可以。 我希望这篇文章对某人有用。 谢谢您的关注。

Source: https://habr.com/ru/post/zh-CN452552/


All Articles