如何在库中支持多个Scala版本

我有一个相当普通的Scala项目,目前正在使用Maven构build。 我想要支持Scala 2.9.x和即将推出的2.10,它不是二进制或源代码兼容的。 如果有必要的话,我愿意考虑转换到SBT,但是我遇到了一些挑战。

我对这个项目的要求是:

  • 单一来源树(不分支)。 我相信试图支持每个Scala版本的多个并发“主”分支将是错过分支之间错误修正的最快方法。

  • 版本特定的源目录。 由于Scala版本不是源兼容的,我需要能够为版本特定的源指定辅助源目录。

  • 特定于版本的源码jar。 最终用户应该能够下载正确的源代码jar,以及正确的版本特定的源代码,用于他们的Scala for IDE版本集成。

  • 综合部署。 我目前使用Maven发行版插件将新版本部署到Sonatype OSS存储库,并希望为发行版提供类似的简单工作stream程。

  • 最终用户Maven支持。 我的最终用户通常是Maven用户,所以准确反映依赖关系的function性POM至关重要。

  • 阴影jar支持。 我需要能够生成包含我的依赖项的子集的JAR, 并从已发布的POM中去除阴影依赖项。

我已经尝试过的事情:

  • Mavenconfiguration文件。 我创build了一组Mavenconfiguration文件来控制使用哪个版本的Scala来构build,使用Maven build-helper插件来select版本特定的源代码树。 在发布之前,这一切都运行良好。

    • 使用分类器来限定版本并不能正常工作,因为源代码的jar也需要自定义分类器('source-2.9.2'等),大多数IDE工具都不知道如何定位它们。

    • 我尝试使用Maven属性将SBT风格的_ $ {scala.version}后缀添加到工件名称,但Maven不喜欢工件名称中的属性。

  • SBT。 一旦你可以使用它,就可以很好地工作(尽pipe有大量的文档,这不是一件小事)。 不利的一面是,似乎没有与Maven shade插件相当的function。 我看过:

    • Proguard的。 该插件没有针对SBT 0.12.x进行更新,并且不会从源代码生成,因为它取决于另一个更改了groupIds的SBT插件,并且旧名称下没有0.12.x版本。 我还没有能够解决如何指示SBT忽略/replace插件的依赖关系。

    • OneJar。 这使用自定义类加载来运行embedded式jar的主要类,这不是所需的结果; 我希望我的项目的类文件在我的阴影依赖关系的jar文件中(可能是重命名的)类文件。

    • SBT大会插件。 这可以在一定程度上工作,但POM文件似乎包括我试图遮蔽的依赖关系,这对我的最终用户没有帮助。

我接受,可能没有一个解决scheme可以满足我对Scala的需求,或者我可能需要编写自己的Maven或Scala插件来完成目标。 但是,如果我可以,我想find一个现有的解决scheme。

更新

我接受@ Jon-Ander的出色答案 ,但对我来说,还是有一个杰出的作品,那就是一个统一的发布stream程。 我的build.sbt的当前状态在GitHub上 。 (我将在后面为后代重现它)。

sbt-release插件不支持多版本构build(也就是说, + release行为并不像人们可能想象的那样),这是有意义的,因为发布标签的过程并不是真的需要在版本之间进行。 但是我希望这个过程的两个部分是多版本的:testing和发布。

我想要发生的事情类似于两阶段maven-release-plugin过程。 第一阶段将执行更新Git和运行testing的pipe理工作,在这种情况下,这将意味着运行+ test以便所有版本都经过testing,标记,更新到快照,然后将结果推送到上游。

第二阶段将签出标签版本和+ publish ,这将重新运行testing,并将标签版本推到Sonatype存储库。

我怀疑我可以编写releaseProcess值来完成其中的每一个,但我不确定我是否可以在build.sbt支持多个releaseProcess值。 它可能可以使用一些额外的范围,但SBT的那部分仍然是奇怪的对我来说。

我目前所做的是更改releaseProcess不发布。 然后,我必须手动签出标签版本,然后在事实之后运行+ publish ,这与我想要的非常接近,但却做了妥协,特别是因为testing只在发布过程中的当前scala版本上运行。 我可以用一个不像Maven插件那样的两阶段进程,但是实现了多版本testing和发布。

任何额外的反馈,可以让我穿越最后一英里,将不胜感激。

大部分这一点在单一源码树的sbt中得到很好的支持

特定版本的源代码目录通常是不需要的。 斯卡拉计划往往是源兼容的 – 通常事实上,跨build设( http://www.scala-sbt.org/release/docs/Detailed-Topics/Cross-Build )有一stream的支持在sbt。

如果您确实需要特定于版本的代码,则可以添加额外的源文件夹。 在build.sbt文件中join“src / main / scala- [scalaVersion]”作为跨越常规“src / main / scala”的跨版本的源代码目录。 (也有一个插件可用于在版本之间生成垫片,但我没有尝试过 – https://github.com/sbt/sbt-scalashim

 unmanagedSourceDirectories in Compile <+= (sourceDirectory in Compile, scalaVersion){ (s,v) => s / ("scala-"+v) } 

特定于版本的源码jar – 请参阅crossbuilding,开箱即用

集成部署 – https://github.com/sbt/sbt-release (也有真棒的git集成)

Maven最终用户 – http://www.scala-sbt.org/release/docs/Detailed-Topics/Publishing.html

着色 – 我已经使用这一个https://github.com/sbt/sbt-assembly哪些工作正常我的需要。; 您的程序集插件的问题可以通过重写生成的pom来解决。 这是一个排除乔达时间的例子。

 pomPostProcess := { import xml.transform._ new RuleTransformer(new RewriteRule{ override def transform(node:xml.Node) = { if((node \ "groupId").text == "joda-time") xml.NodeSeq.Empty else node } }) } 

完成build.sbt供参考

 scalaVersion := "2.9.2" crossScalaVersions := Seq("2.9.2", "2.10.0-RC5") unmanagedSourceDirectories in Compile <+= (sourceDirectory in Compile, scalaVersion){ (s,v) => s / ("scala-"+v) } libraryDependencies += "joda-time" % "joda-time" % "1.6.2" libraryDependencies += "org.mindrot" % "jbcrypt" % "0.3m" pomPostProcess := { import xml.transform._ new RuleTransformer(new RewriteRule{ override def transform(node:xml.Node) = { if((node \ "groupId").text == "joda-time") xml.NodeSeq.Empty else node } }) } 

我用SBT做了类似的事情: https : //github.com/seanparsons/scalaz/commit/21298eb4af80f107181bfd09eaaa51c9b56bdc28

SBT使得所有的设置都可以基于其他设置来确定,这意味着大多数其他的东西都应该“正常工作”。

至于pom.xml方面,我只能推荐在SBT邮件列表中提出这个问题,但是如果你不能这样做,我会感到惊讶。

我的博客文章http://www.day-to-day-stuff.blogspot.nl/2013/04/fixing-code-and-binary.html包含一个稍微更精细的解决scheme,用于附加不同的源目录的示例;; 每个主要的一个S.另外它解释了如何创build可用于非特定代码的scala版本特定的代码。

更新2016年11月8日 :Sbt现在支持开箱即用: http : //www.scala-sbt.org/0.13/docs/sbt-0.13-Tech-Previews.html#Cross-version+support+for+斯卡拉+源