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

时间:2012-12-14 03:28:18

标签: scala maven maven-plugin sbt

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

我对这个项目的要求是:

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

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

  • 特定于版本的源jar。最终用户应该能够为他们的Scala版本集成下载具有正确版本特定源的正确源jar。

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

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

  • 阴影罐支撑。我需要能够生成一个包含我的依赖项的子集的JAR,并从已发布的POM中删除着色的依赖项。

我尝试过的事情:

  • Maven个人资料。我创建了一组Maven配置文件来控制用于构建Scala的版本,使用Maven build-helper插件来选择特定于版本的源树。这一点很有效,直到发布时间为止;

    • 使用分类器来限定版本并不能很好地工作,因为源jar也需要自定义分类器(' source-2.9.2'等),以及大多数IDE工具我不知道如何找到它们。

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

  • SBT。一旦你能够理解它,它就能很好地工作(尽管有大量文档,但这不是一项小任务)缺点是似乎没有Maven阴影插件的等价物。我看过了:

    • Proguard的。该插件未针对SBT 0.12.x进行更新,并且不会从源代码构建,因为它依赖于另一个已更改groupIds的SBT插件,并且在旧名称下没有0.12.x版本。我还没有弄清楚如何指示SBT忽略/替换插件依赖。

    • OneJar。这使用自定义类加载来从嵌入式jar中运行Main类,这不是期望的结果;我希望我的项目的类文件与我的着色依赖项中的(可能已重命名的)类文件一起放在jar中。

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

我接受可能没有解决方案能够满足我对Scala的需求,并且/或者我可能需要编写自己的Maven或Scala插件来实现目标。但如果我能找到现有的解决方案。

更新

我接近@ Jon-Ander的优秀answer,但我仍然有一个优秀的作品,这是一个统一的发布过程。我build.sbt is on GitHub的当前状态。 (我将在稍后的回答中为后代再现它。)

sbt-release插件不支持多版本构建(即,+ release不像人们想象的那样),这有点意义,因为发布标记的过程并不真实需要跨版本发生。但我希望这个过程的两个部分是多版本的:测试和发布。

我希望发生的事情类似于两阶段maven-release-plugin流程。第一阶段将执行更新Git和运行测试的管理工作,在这种情况下,这将意味着运行+ test,以便对所有版本进行测试,标记,更新到快照,然后将结果推送到上游。 / p>

第二阶段将签出标记版本和+ publish,它将重新运行测试并将标记版本推送到Sonatype存储库。

我怀疑我可以写releaseProcess个值来执行其中每个操作,但我不确定我是否可以支持releaseProcess中的多个build.sbt值。它可能适用于一些额外的范围,但SBT的那部分对我来说仍然很奇怪。

我目前所做的是将releaseProcess更改为不发布。然后,我必须手动签出标记版本并在事实之后运行+ publish,这接近我想要但确实妥协,特别是因为测试仅在发布过程中的当前scala版本上运行。我可以使用像maven插件那样不是两个阶段的流程,但是实现了多版本测试和发布。

任何可以让我跨越最后一英里的其他反馈都将受到赞赏。

3 个答案:

答案 0 :(得分:12)

在单个源树中的sbt中,大部分都得到了很好的支持

通常不需要特定于版本的源目录。 Scala程序往往与源代码兼容 - 事实上往往是这样 crossbuilding(http://www.scala-sbt.org/release/docs/Detailed-Topics/Cross-Build)在sbt。

中有一流的支持

如果您确实需要特定于版本的代码,则可以添加额外的源文件夹。 将它放在你的build.sbt文件中会添加" 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) }

版本特定的来源罐 - 参见交叉构建,开箱即用

集成部署 - 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来解决组装插件的问题。 这是一个剥夺joda-time的例子。

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

答案 1 :(得分:2)

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

SBT允许根据其他设置确定所有设置,这意味着大多数其他设置应该“正常工作”。

就pom.xml方面而言,我只能建议在SBT邮件列表中提出问题,但如果你不能这样做,我会感到惊讶。

答案 2 :(得分:0)

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

更新2016-11-08 :Sbt现在支持开箱即用:http://www.scala-sbt.org/0.13/docs/sbt-0.13-Tech-Previews.html#Cross-version+support+for+Scala+sources