使用 SBT 驱动包含 SWIG 和 C++ 编译的构建 Java+Scala 构建

Using SBT to drive a build Java+Scala build that includes SWIG and C++ compilation

我正准备为一个(主要是)SWIG 包装的 C++ 库编写构建配置,以便从 Scala 调用。由于 Scala 端存在第 3 方依赖项以及广泛的社区支持,我想使用 sbt 来自动化构建。

但是,对于自动化 swig 和 c++ 编译步骤,我有点不知所措。有没有人有 sbt 驱动的混合语言构建配置的经验并且可以提供这方面的建议?

到目前为止,我已经找到了 sbt-sh 插件,但如果可能的话,我宁愿避免直接退出。我还考虑过使用 cmake 作为驱动程序(因为 cmake 确实有一个 swig-driver 插件),并且在完成 swig 和 c++ 构建步骤后,java/scala-only 构建步骤触发了 cmake。

不幸的是,由于 swig 是一个命令行工具,我认为您无法绕过 shell 编写脚本。 (我没有找到任何 java 包装器。)

SBT 有源发生器的概念。源代码生成器只不过是 function/task 写入(并返回)一些文件。如果您这样做,那么 SBT 将在开始编译(其余)代码之前首先执行所有源代码生成器。

只需确保让 swig 将其文件输出到 sourceManaged in Compile,然后生成的源文件在编译期间可在类路径中使用。

def runSwig: Seq[File] = {
  import sys.process._
  Seq("swig" , "param1", "param2", "..." ).!
  // Somehow determine which files have been written by swig.
  // I would make swig output its files to a temp directory and copy them to the source managed directory manually.
}

sourceGenerators in Compile += Def.task {
  runSwig
} 

虽然我不确定 C++ 代码。在我自己的项目中,我使用 docker 交叉编译到各种平台。如果您的本机代码不经常更改,我不建议每次 SBT 编译您的 java/scala 代码时都重新编译本机代码。

SBT 有自己的执行外部进程的语法。您可以利用此 + 路径查找 + 源代码生成。

一个粗略的例子:

lazy val invokeSwig = taskKey[Seq[File]]("invoke swig")
invokeSwig := {
  s"swig -java example.i -outdir ${target.value}" !
  val pathFinder = target.value ** "*.java"
  pathFinder.get
}
target in invokeSwig := target.value / "swig"
sourceGenerators in Compile <+= invokeSwig

注意:我使用的是 sbt v0.13.9,但这应该也适用于其他版本。

您必须为本机组件做一些额外的工作。这包括编译它们以及确保它们在 运行 期间可用。