使用 SBT 进行跨平台构建

Posted

技术标签:

【中文标题】使用 SBT 进行跨平台构建【英文标题】:Cross platform build with SBT 【发布时间】:2016-01-09 08:53:49 【问题描述】:

我目前正在测试 SBT Native Packager,我想要的结果是为每个受支持的平台提供一个本地安装程序。显然,要做到这一点,平台特定的 SBT 任务需要在该平台上运行。

将使用 Atlassian 的 Bamboo 或 JetBrains 的 Team City 完成构建。

理想情况下,我将只进行一次编译和测试,并为打包任务重用相同的工件。

用 SBT 解决这个问题的好方法是什么?

我能想到的一种方法是在任何平台上进行编译和测试,然后将它们发布到本地存储库。然后包会以某种方式使用这些。但是,这也需要更改包装,使其不依赖于编译任务等。

【问题讨论】:

“显然要做到这一点,平台特定的 SBT 任务需要在该平台上运行。”这绝对不明显,这是众多打包程序的典型限制,包括 SBT Native Packager 在后台使用的“javapackager”,但我自己的基于 Java 的打包程序可以在 Windows 下创建本机 OS X 包,在 Mac OS X 下创建 GNU Linux DEB 和 RPM 和GNU Linux 下的 Windows 可执行安装程序:tuer.sourceforge.net/en/documentation/#jndt 例如,可以在 GNU Linux 下进行一些持续集成,同时针对 Windows 和 OS X。您可以使用 ant4sbt 调用 JNDT。 【参考方案1】:

TL;DR 版本:使用单独的 sbt 项目。

您可能已经注意到,JDKPackager 插件在各种平台上创建本地包。构建和测试主要工件一次的技巧是将它们发布到工件服务器,然后有一个单独的项目来创建安装程序。

如果您的项目每个平台只需要一个安装程序,那么您只需添加依赖项,设置mainClass 键,然后添加enablePlugins(JDKPackagerPlugin)

enablePlugins(JDKPackagerPlugin)

name := "JDKPackagerPlugin Example"

version := "0.1.0"

organization := "com.foo.bar"

libraryDependencies += "com.foo.bar" %% "myProject" % "0.1.0"

mainClass in Compile := Some("com.foo.bar.ExampleApp")

// Optional: provide application file associations
jdkPackagerAssociations := Seq(
    FileAssociation("foobar", "application/foobar", "Foobar file type"),
    FileAssociation("barbaz", "application/barbaz", "Barbaz file type", jdkAppIcon.value)
)

如果您有每个平台需要多个安装程序的场景(例如,命令行工具与 GUI),我通常会构建一个项目,使其具有一个名为“packaging”的子目录,其中包含一个独立的 build.xml 文件,聚合定义每个安装程序配置的单独子项目:

// Settings common across subprojects. Could also do this with a 
// project-specific `AutoPlugin`
val baseSettings = Seq(
    libraryDependencies += "com.foo.bar" %% "myProject" % "0.1.0"
)

// The packaging aggregation project
lazy val packaging = project
   .in(file("."))
   .aggregate(a, b)

// Project with CLI configuration
lazy val a = Project(id = "my-project-cli", base = file("my-project-cli"))
  .settings(baseSettings: _*)

// Project with GUI configuration    
lazy val b = Project(id = "my-project-gui", base = file("my-project-gui"))
  .settings(baseSettings: _*)

// Create a task for invoking the sub-projects as needed
val packageSubs = taskKey[Seq[File]]("Build packages in subprojects")
(packageSubs in packaging) := 
  Seq(
    (packageBin.in(a, Universal)).value,
    (packageBin.in(b, JDKPackager)).value
  ) 

我发现像这样分解安装程序配置有助于保持依赖关系以及特定自定义的效果。

【讨论】:

在我开始使用 IntelliJ 之前,这是一个非常棒的解决方案。现在它总是无法加载打包,除非我在每次提交后发布。 指定.dependsOn(...) 是否有问题?如a.dependsOn(b)。我也用 IntelliJ,没遇到过问题。 其实我所拥有的有点不同。我有ab 生成我的第一个构建计划将生成和发布的JAR。然后,我有单独的平台特定构建,这些构建调用 packaging/rpm:packageBin 等,这些构建具有来自 ab 的工件作为依赖项。所以packaging/update 会失败,除非ab 已经发布。 我受够了这个并深入研究了 IntelliJ SBT 源代码并最终找到了这个sbt-ide-settings。在打包项目中添加ideSkipProject := true 效果很好。

以上是关于使用 SBT 进行跨平台构建的主要内容,如果未能解决你的问题,请参考以下文章

使用 sbt 进行多项目构建

使用外部常春藤进行 sbt 构建

使用SBT构建Scala应用转载

项目构建工具CMake、GYP、GN

在 sbt 0.13 中使用 .sbt 文件编写多项目构建的惯用方式

dotnet 使用 XWT 构建跨平台客户端 入门篇