gpt4 book ai didi

scala - 如何覆盖任务 `` run `` and ` `runMain `` in SBT to use my own ` `ForkOptions``?

转载 作者:行者123 更新时间:2023-12-01 10:04:19 25 4
gpt4 key购买 nike

问题

在多模块构建中,每个模块都有自己的 baseDirectory 但我想启动模块中定义的应用程序,使用根项目的 baseDirectory 而不是 baseDirectory 相对于所涉及的模块。

这样,应用程序总是会从根文件夹中获取相对文件名,这是一种非常常见的模式。

问题是 ForkOptions 从模块强制执行 baseDirectory 并且显然没有简单的方法来更改它,因为 forkOptions 是私有(private)的。我想传递一个 forkOptions 来代替根项目中的 baseDirectory

此外,还有包含两个或多个应用程序的模块。因此,我想为包含两个或更多应用程序的给定模块中的每个应用程序进行单独配置。

一个例子说明了 1000 多个单词:

构建.sbt

import sbt._
import Keys._

lazy val buildSettings: Seq[Setting[_]] = Defaults.defaultSettings
lazy val forkRunOptions: Seq[Setting[_]] = Seq(fork := true)

addCommandAlias("r1", "ModuleA/RunnerR1:run")
addCommandAlias("r2", "ModuleA/RunnerR2:run")

lazy val RunnerR1 = sbt.config("RunnerR1").extend(Compile)
lazy val RunnerR2 = sbt.config("RunnerR2").extend(Compile)

lazy val root =
project
.in(file("."))
.settings(buildSettings:_*)
.aggregate(ModuleA)

lazy val ModuleA =
project
.in(file("ModuleA"))
.settings(buildSettings:_*)
.configs(RunnerR1,RunnerR2)
.settings(inConfig(RunnerR1)(
forkRunOptions ++
Seq(
mainClass in Compile := Option("sbt.tests.issueX.Application1"))):_*)
.settings(inConfig(RunnerR2)(
forkRunOptions ++
Seq(
mainClass in Compile := Option("sbt.tests.issueX.Application2"))):_*)

在 SBT 控制台中,我希望这样:

> r1
This is Application1
> r2
This is Application2

但是我看到了这个:

> r1
This is Application2
> r2
This is Application2

有什么收获?

不仅如此... SBT 正在运行进程中的应用程序。这不是 fork 他们。为什么 fork := true 没有任何效果?

最佳答案

解释

参见:https://github.com/frgomes/sbt-issue-2247

事实证明,配置并不像人们想象的那样起作用。

问题是,在下面的代码片段中,配置 RunnerR1 没有像您预期的那样从模块 ModuleA 继承任务。因此,当您键入 r1r2(即:ModuleA/RunnerR1:runModuleA/RunnerR2:run ), SBT 将采用委托(delegate)算法来查找任务和设置,根据这些任务和设置的定义方式,它最终将运行您不期望的范围内的任务,或者从您不期望的范围内查找设置。

lazy val ModuleA =
project
.in(file("ModuleA"))
.settings(buildSettings:_*)
.configs(RunnerR1,RunnerR2)
.settings(inConfig(RunnerR1)(
forkRunOptions ++
Seq(
mainClass in Compile := Option("sbt.tests.issueX.Application1"))):_*)

此问题与可用性有关,因为 SBT 提供的 API 具有误导性。最终可以改进或更好地记录此模式,但它更像是一个可用性问题,而不是其他任何问题。

规避困难

请在下方找到如何规避此问题的方法。

由于 ForkOptions 是私有(private)的,我们必须尽可能提供我们自己的基于 SBT 代码的应用程序运行方式。

简而言之,我们必须保证在所有配置中重新定义runrunMainrunner

import sbt._
import Keys._


//-------------------------------------------------------------
// This file contains a solution for the problem presented by
// https://github.com/sbt/sbt/issues/2247
//-------------------------------------------------------------


lazy val buildSettings: Seq[Setting[_]] = Defaults.defaultSettings ++ runSettings

lazy val runSettings: Seq[Setting[_]] =
Seq(
fork in (Compile, run) := true)


def forkRunOptions(s: Scope): Seq[Setting[_]] =
Seq(
// see: https://github.com/sbt/sbt/issues/2247
// see: https://github.com/sbt/sbt/issues/2244
runner in run in s := {
val forkOptions: ForkOptions =
ForkOptions(
workingDirectory = Some((baseDirectory in ThisBuild).value),
bootJars = Nil,
javaHome = (javaHome in s).value,
connectInput = (connectInput in s).value,
outputStrategy = (outputStrategy in s).value,
runJVMOptions = (javaOptions in s).value,
envVars = (envVars in s).value)
new {
val fork_ = (fork in run).value
val config: ForkOptions = forkOptions
} with ScalaRun {
override def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String] =
javaRunner(
Option(mainClass), Option(classpath), options,
Some("java"), Option(log), fork_,
config.runJVMOptions, config.javaHome, config.workingDirectory, config.envVars, config.connectInput, config.outputStrategy)
}
},
runner in runMain in (s) := (runner in run in (s)).value,
run in (s) <<= Defaults.runTask (fullClasspath in s, mainClass in run in s, runner in run in s),
runMain in (s) <<= Defaults.runMainTask(fullClasspath in s, runner in runMain in s)
)


def javaRunner(mainClass: Option[String] = None,
classpath: Option[Seq[File]] = None,
options: Seq[String],
javaTool: Option[String] = None,
log: Option[Logger] = None,
fork: Boolean = false,
jvmOptions: Seq[String] = Nil,
javaHome: Option[File] = None,
cwd: Option[File] = None,
envVars: Map[String, String] = Map.empty,
connectInput: Boolean = false,
outputStrategy: Option[OutputStrategy] = Some(StdoutOutput)): Option[String] = {

def runner(app: String,
args: Seq[String],
cwd: Option[File] = None,
env: Map[String, String] = Map.empty): Int = {
import scala.collection.JavaConverters._

val cmd: Seq[String] = app +: args
val pb = new java.lang.ProcessBuilder(cmd.asJava)
if (cwd.isDefined) pb.directory(cwd.get)
pb.inheritIO
//FIXME: set environment
val process = pb.start()
if (fork) 0
else {
def cancel() = {
if(log.isDefined) log.get.warn("Background process cancelled.")
process.destroy()
15
}
try process.waitFor catch {
case e: InterruptedException => cancel()
}
}
}

val app: String = javaHome.fold("") { p => p.absolutePath + "/bin/" } + javaTool.getOrElse("java")
val jvm: Seq[String] = jvmOptions.map(p => p.toString)
val cp: Seq[String] =
classpath
.fold(Seq.empty[String]) { paths =>
Seq(
"-cp",
paths
.map(p => p.absolutePath)
.mkString(java.io.File.pathSeparator))
}
val klass = mainClass.fold(Seq.empty[String]) { name => Seq(name) }
val xargs: Seq[String] = jvm ++ cp ++ klass ++ options

if(log.isDefined)
if(fork) {
log.get.info(s"Forking: ${app} " + xargs.mkString(" "))
} else {
log.get.info(s"Running: ${app} " + xargs.mkString(" "))
}

if (cwd.isDefined) IO.createDirectory(cwd.get)
val exitCode = runner(app, xargs, cwd, envVars)
if (exitCode == 0)
None
else
Some("Nonzero exit code returned from " + app + ": " + exitCode)
}


addCommandAlias("r1", "ModuleA/RunnerR1:run")
addCommandAlias("r2", "ModuleA/RunnerR2:run")


lazy val RunnerR1 = sbt.config("RunnerR1").extend(Compile)
lazy val RunnerR2 = sbt.config("RunnerR2").extend(Compile)


lazy val root =
project
.in(file("."))
.settings(buildSettings:_*)
.aggregate(ModuleA)

lazy val ModuleA =
project
.in(file("ModuleA"))
.settings(buildSettings:_*)
.configs(RunnerR1,RunnerR2)
.settings(inConfig(RunnerR1)(
forkRunOptions(ThisScope) ++
Seq(
mainClass := Option("sbt.tests.issueX.Application1"))):_*)
.settings(inConfig(RunnerR2)(
forkRunOptions(ThisScope) ++
Seq(
mainClass := Option("sbt.tests.issueX.Application2"))):_*)

关于scala - 如何覆盖任务 `` run `` and ` `runMain `` in SBT to use my own ` `ForkOptions``?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33243981/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com