隔离插件类路径
目标
我想隔离插件类路径,例如以便使用 Scala 2.12 应用程序。
步骤
-
以发布到 Maven Central 的命令行应用程序为前提,例如 Coursier CLI 或您自己的应用程序。
-
定义一个封装该命令行应用程序的合成子项目:
package example import sbt.{ *, given } import Keys.* object BootstrapPlugin extends AutoPlugin: override lazy val requires = sbt.plugins.JvmPlugin lazy val bootstrapCs = project .settings( scalaVersion := "2.12.21", libraryDependencies += "io.get-coursier" %% "coursier-cli" % "2.1.14", Compile / run / mainClass := Some("coursier.cli.Coursier"), // 仅适用于 sbt 2.x clientSide := false, ) override lazy val extraProjects = Vector(bootstrapCs) .... end BootstrapPlugin -
定义一个表示命令行参数的设置,以及一个在合成子项目上调用
run任务的动态任务。
package example
import sbt.{ *, given }
import Keys.*
object BootstrapPlugin extends AutoPlugin:
override lazy val requires = sbt.plugins.JvmPlugin
object autoImport:
val packageBootstrap = taskKey[HashedVirtualFileRef]("packageBootstrap")
val packageBootstrapArgs = settingKey[Seq[String]]("packageBootstrapArgs")
val packageBootstrapOutput = settingKey[File]("packageBootstrapOutput")
end autoImport
import autoImport.*
lazy val bootstrapCs = project
.settings(
scalaVersion := "2.12.21",
libraryDependencies += "io.get-coursier" %% "coursier-cli" % "2.1.14",
Compile / run / mainClass := Some("coursier.cli.Coursier"),
// applicable to sbt 2.x only
clientSide := false,
)
override lazy val extraProjects = Vector(bootstrapCs)
override lazy val projectSettings = Vector(
packageBootstrapOutput := target.value / "bootstrap" / s"${moduleName.value}.jar",
packageBootstrapArgs := {
val coord =
s"${organization.value}:${name.value}_${scalaBinaryVersion.value}:${version.value}"
val sv = scalaVersion.value
Vector("bootstrap", "--verbose", "--bat=true",
"--scala-version", sv,
"-f", coord,
"-o", packageBootstrapOutput.value.toString)
},
packageBootstrap := Def.uncached {
// to process args before toTask, we need to use dynamic tasks
(Def.taskDyn {
// phase 1
val c = fileConverter.value
val args = packageBootstrapArgs.value
val out = packageBootstrapOutput.value
// phase 2
val outVf: HashedVirtualFileRef = c.toVirtualFile(out.toPath())
IO.createDirectory(out.getParentFile())
// phase 3
(bootstrapCs / Compile / run)
.toTask(args.mkString(" ", " ", ""))
.map(_ => outVf)
}).value
},
)
end BootstrapPlugin
如果命令行应用程序调用了 sys.exit(1),则必须 fork run:
// sbtn 的默认值
clientSide := true,
// 用于 sbt --server
Compile / run / fork := true,
测试
$ sbt app/publishLocal
$ sbt app/packageBootstrap
[info] running coursier.cli.Coursier bootstrap --verbose --bat=true
--scala-version 3.8.4 -f com.example:hello_3:0.1.0-SNAPSHOT
-o /.../isolation/target/out/jvm/scala-3.8.4/hello/bootstrap/hello.jar
Dependencies:
com.example:hello_3:0.1.0-SNAPSHOT:
Wrote target/out/jvm/scala-3.8.4/hello/bootstrap/hello.jar
Wrote target/out/jvm/scala-3.8.4/hello/bootstrap/hello.jar.bat
[success] elapsed time: 3 s, cache 100%, 17 disk cache hits
$ target/out/jvm/scala-3.8.4/hello/bootstrap/hello.jar world
hi world