sbt:在跨平台项目中生成共享源

sbt: generating shared sources in cross-platform project

使用 sbt 在 Scala 上构建我的项目,我希望有一个任务将 运行 在实际 Scala 编译之前生成一个包含项目版本信息的 Version.scala 文件。这是我想出的任务:

lazy val generateVersionTask = Def.task {
  // Generate contents of Version.scala
  val contents = s"""package io.kaitai.struct
                    |
                    |object Version {
                    |  val name = "${name.value}"
                    |  val version = "${version.value}"
                    |}
                    |""".stripMargin

  // Update Version.scala file, if needed
  val file = (sourceManaged in Compile).value / "version" / "Version.scala"
  println(s"Version file generated: $file")
  IO.write(file, contents)
  Seq(file)
}

这个任务似乎可行,但问题是如何插入它,因为它是一个跨项目,目标是 Scala/JVM、Scala/JS 等

这是 build.sbt 在我开始触摸它之前的样子:

lazy val root = project.in(file(".")).
  aggregate(fooJS, fooJVM).
  settings(
    publish := {},
    publishLocal := {}
  )

lazy val foo = crossProject.in(file(".")).
  settings(
    name := "foo",
    version := sys.env.getOrElse("CI_VERSION", "0.1"),
    // ...
  ).
  jvmSettings(/* JVM-specific settings */).
  jsSettings(/* JS-specific settings */)

lazy val fooJVM = foo.jvm
lazy val fooJS = foo.js

而且,在文件系统上,我有:

到目前为止我想到的最好的方法是将此任务添加到 foo crossProject:

lazy val foo = crossProject.in(file(".")).
  settings(
    name := "foo",
    version := sys.env.getOrElse("CI_VERSION", "0.1"),
    sourceGenerators in Compile += generateVersionTask.taskValue, // <== !
    // ...
  ).
  jvmSettings(/* JVM-specific settings */).
  jsSettings(/* JS-specific settings */)

这有效,但以一种非常笨拙的方式,与 "shared" 代码库并不真正兼容。它为 JS 和 JVM 生成 2 个不同的 Version.scala 文件:

sbt:root> compile
Version file generated: /foo/js/target/scala-2.12/src_managed/main/version/Version.scala
Version file generated: /foo/jvm/target/scala-2.12/src_managed/main/version/Version.scala

当然,从shared访问这些文件的内容是不可能的,而这正是我想要访问的地方。

到目前为止,我已经有了一个非常草率的解决方法:

另外,我用 sbt-buildinfo 插件尝试了同样的技巧——结果完全一样,它是按平台生成的 BuildInfo.scala,我不能直接从共享源使用它.

有没有更好的解决方案?

考虑将 sourceManaged 指向 shared/src/main/scala/src_managed 目录,并将 generateVersionTask 的范围限定为 root 项目

val sharedSourceManaged = Def.setting(
  baseDirectory.value / "shared" / "src" / "main" / "scala" / "src_managed"
)

lazy val root = project.in(file(".")).
  aggregate(fooJS, fooJVM).
  settings(
    publish := {},
    publishLocal := {},
    sourceManaged := sharedSourceManaged.value,
    sourceGenerators in Compile += generateVersionTask.taskValue,
    cleanFiles += sharedSourceManaged.value
  )

现在 sbt compile 应该输出类似

的内容
Version file generated: /Users/mario/IdeaProjects/scalajs-cross-compile-example/shared/src/main/scala/src_managed/version/Version.scala
...
[info] Compiling 3 Scala sources to /Users/mario/IdeaProjects/scalajs-cross-compile-example/js/target/scala-2.12/classes ...
[info] Compiling 1 Scala source to /Users/mario/IdeaProjects/scalajs-cross-compile-example/target/scala-2.12/classes ...
[info] Compiling 3 Scala sources to /Users/mario/IdeaProjects/scalajs-cross-compile-example/jvm/target/scala-2.12/classes ...