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
而且,在文件系统上,我有:
- shared/ — JS/JVM builds
之间共享的跨平台代码
- jvm/ — JVM 特定代码
- js/ — 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
访问这些文件的内容是不可能的,而这正是我想要访问的地方。
到目前为止,我已经有了一个非常草率的解决方法:
- shared
中单例对象中声明了一个var
- 在 JVM 和 JS 主入口点中,我做的第一件事就是分配该变量以匹配
Version.scala
中定义的常量
另外,我用 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 ...
使用 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
而且,在文件系统上,我有:
- shared/ — JS/JVM builds 之间共享的跨平台代码
- jvm/ — JVM 特定代码
- js/ — 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
访问这些文件的内容是不可能的,而这正是我想要访问的地方。
到目前为止,我已经有了一个非常草率的解决方法:
- shared 中单例对象中声明了一个
- 在 JVM 和 JS 主入口点中,我做的第一件事就是分配该变量以匹配
Version.scala
中定义的常量
var
另外,我用 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 ...