mirror of https://github.com/sbt/sbt.git
Make full set of ForkOptions configurable for both run and test. Fixes #665.
Environment variables may be passed by configuring the envVars task.
This commit is contained in:
parent
5119ea2574
commit
98e2662bc4
|
|
@ -11,7 +11,7 @@ import Tests.{Output => TestOutput, _}
|
|||
import ForkMain._
|
||||
|
||||
private[sbt] object ForkTests {
|
||||
def apply(frameworks: Seq[TestFramework], tests: List[TestDefinition], config: Execution, classpath: Seq[File], javaHome: Option[File], javaOpts: Seq[String], log: Logger): Task[TestOutput] = {
|
||||
def apply(frameworks: Seq[TestFramework], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = {
|
||||
val opts = config.options.toList
|
||||
val listeners = opts flatMap {
|
||||
case Listeners(ls) => ls
|
||||
|
|
@ -75,8 +75,8 @@ private[sbt] object ForkTests {
|
|||
new Thread(Acceptor).start()
|
||||
|
||||
val fullCp = classpath ++: Seq(IO.classLocationFile[ForkMain], IO.classLocationFile[Framework])
|
||||
val options = javaOpts ++: Seq("-classpath", fullCp mkString File.pathSeparator, classOf[ForkMain].getCanonicalName, server.getLocalPort.toString)
|
||||
val ec = Fork.java(javaHome, options, StdoutOutput)
|
||||
val options = Seq("-classpath", fullCp mkString File.pathSeparator, classOf[ForkMain].getCanonicalName, server.getLocalPort.toString)
|
||||
val ec = Fork.java(fork, options)
|
||||
val result =
|
||||
if (ec != 0)
|
||||
(TestResult.Error, Map("Running java with options " + options.mkString(" ") + " failed with exit code " + ec -> TestResult.Error))
|
||||
|
|
|
|||
|
|
@ -200,7 +200,11 @@ object Tests
|
|||
|
||||
sealed trait TestRunPolicy
|
||||
case object InProcess extends TestRunPolicy
|
||||
final case class SubProcess(javaOptions: Seq[String]) extends TestRunPolicy
|
||||
final case class SubProcess(config: ForkOptions) extends TestRunPolicy
|
||||
object SubProcess {
|
||||
@deprecated("Construct SubProcess with a ForkOptions argument.", "0.13.0")
|
||||
def apply(javaOptions: Seq[String]): SubProcess = SubProcess(ForkOptions(runJVMOptions = javaOptions))
|
||||
}
|
||||
|
||||
final case class Group(name: String, tests: Seq[TestDefinition], runPolicy: TestRunPolicy)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ object Defaults extends BuildCommon
|
|||
logBuffered :== false,
|
||||
connectInput :== false,
|
||||
cancelable :== false,
|
||||
envVars :== Map.empty,
|
||||
sourcesInBase :== true,
|
||||
autoAPIMappings := false,
|
||||
apiMappings := Map.empty,
|
||||
|
|
@ -378,10 +379,17 @@ object Defaults extends BuildCommon
|
|||
val mod = tdef.fingerprint match { case f: SubclassFingerprint => f.isModule; case f: AnnotatedFingerprint => f.isModule; case _ => false }
|
||||
extra.put(name.key, tdef.name).put(isModule, mod)
|
||||
}
|
||||
def singleTestGroup(key: Scoped): Initialize[Task[Seq[Tests.Group]]] =
|
||||
((definedTests in key, fork in key, javaOptions in key) map {
|
||||
(tests, fork, javaOpts) => Seq(new Tests.Group("<default>", tests, if (fork) Tests.SubProcess(javaOpts) else Tests.InProcess))
|
||||
})
|
||||
def singleTestGroup(key: Scoped): Initialize[Task[Seq[Tests.Group]]] = Def.task {
|
||||
val tests = (definedTests in key).value
|
||||
val fk = (fork in key).value
|
||||
val opts = inTask(key, forkOptions).value
|
||||
Seq(new Tests.Group("<default>", tests, if(fk) Tests.SubProcess(opts) else Tests.InProcess))
|
||||
}
|
||||
private[this] def forkOptions: Initialize[Task[ForkOptions]] =
|
||||
(baseDirectory, scalaInstance, javaOptions, outputStrategy, envVars, javaHome, connectInput) map {
|
||||
(base, si, options, strategy, env, javaHomeDir, connectIn) =>
|
||||
ForkOptions(scalaJars = si.jars, javaHome = javaHomeDir, connectInput = connectIn, outputStrategy = strategy, runJVMOptions = options, workingDirectory = Some(base), envVars = env)
|
||||
}
|
||||
|
||||
def testExecutionTask(task: Scoped): Initialize[Task[Tests.Execution]] =
|
||||
(testOptions in task, parallelExecution in task, tags in task) map {
|
||||
|
|
@ -445,8 +453,8 @@ object Defaults extends BuildCommon
|
|||
val groupTasks = groups map {
|
||||
case Tests.Group(name, tests, runPolicy) =>
|
||||
runPolicy match {
|
||||
case Tests.SubProcess(javaOpts) =>
|
||||
ForkTests(frameworks.keys.toSeq, tests.toList, config, cp.files, javaHome, javaOpts, s.log) tag Tags.ForkedTestGroup
|
||||
case Tests.SubProcess(opts) =>
|
||||
ForkTests(frameworks.keys.toSeq, tests.toList, config, cp.files, opts, s.log) tag Tags.ForkedTestGroup
|
||||
case Tests.InProcess =>
|
||||
Tests(frameworks, loader, tests, config, s.log)
|
||||
}
|
||||
|
|
@ -598,14 +606,11 @@ object Defaults extends BuildCommon
|
|||
}
|
||||
|
||||
def runnerTask = runner <<= runnerInit
|
||||
def runnerInit: Initialize[Task[ScalaRun]] =
|
||||
(taskTemporaryDirectory, scalaInstance, baseDirectory, javaOptions, outputStrategy, fork, javaHome, trapExit, connectInput) map {
|
||||
(tmp, si, base, options, strategy, forkRun, javaHomeDir, trap, connectIn) =>
|
||||
if(forkRun) {
|
||||
new ForkRun( ForkOptions(scalaJars = si.jars, javaHome = javaHomeDir, connectInput = connectIn, outputStrategy = strategy, runJVMOptions = options, workingDirectory = Some(base)) )
|
||||
} else
|
||||
new Run(si, trap, tmp)
|
||||
}
|
||||
def runnerInit: Initialize[Task[ScalaRun]] = Def.task {
|
||||
val tmp = taskTemporaryDirectory.value
|
||||
val si = scalaInstance.value
|
||||
if(fork.value) new ForkRun(forkOptions.value) else new Run(si, trapExit.value, tmp)
|
||||
}
|
||||
|
||||
@deprecated("Use `docTaskSettings` instead", "0.12.0")
|
||||
def docSetting(key: TaskKey[File]) = docTaskSettings(key)
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ object Keys
|
|||
val connectInput = SettingKey[Boolean]("connect-input", "If true, connects standard input when running a main class forked.", CSetting)
|
||||
val javaHome = SettingKey[Option[File]]("java-home", "Selects the Java installation used for compiling and forking. If None, uses the Java installation running the build.", ASetting)
|
||||
val javaOptions = TaskKey[Seq[String]]("java-options", "Options passed to a new JVM when forking.", BPlusTask)
|
||||
val envVars = TaskKey[Map[String,String]]("envVars", "Environment variables used when forking a new JVM", BTask)
|
||||
|
||||
// Test Keys
|
||||
val testLoader = TaskKey[ClassLoader]("test-loader", "Provides the class loader used for testing.", DTask)
|
||||
|
|
|
|||
|
|
@ -435,6 +435,13 @@ trait ProjectExtra
|
|||
def inScope(scope: Scope)(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||
Project.transform(Scope.replaceThis(scope), ss)
|
||||
|
||||
private[sbt] def inConfig[T](conf: Configuration, i: Initialize[T]): Initialize[T] =
|
||||
inScope(ThisScope.copy(config = Select(conf)), i)
|
||||
private[sbt] def inTask[T](t: Scoped, i: Initialize[T]): Initialize[T] =
|
||||
inScope(ThisScope.copy(task = Select(t.key)), i)
|
||||
private[sbt] def inScope[T](scope: Scope, i: Initialize[T]): Initialize[T] =
|
||||
i mapReferenced Project.mapScope(Scope.replaceThis(scope))
|
||||
|
||||
/** Creates a new Project. This is a macro that expects to be assigned directly to a val.
|
||||
* The name of the val is used as the project ID and the name of the base directory of the project. */
|
||||
def project: Project = macro Project.projectMacroImpl
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ trait ForkScalaRun extends ForkScala
|
|||
def workingDirectory: Option[File]
|
||||
def runJVMOptions: Seq[String]
|
||||
}
|
||||
final case class ForkOptions(javaHome: Option[File] = None, outputStrategy: Option[OutputStrategy] = None, scalaJars: Iterable[File] = Nil, workingDirectory: Option[File] = None, runJVMOptions: Seq[String] = Nil, connectInput: Boolean = false) extends ForkScalaRun
|
||||
final case class ForkOptions(javaHome: Option[File] = None, outputStrategy: Option[OutputStrategy] = None, scalaJars: Iterable[File] = Nil, workingDirectory: Option[File] = None, runJVMOptions: Seq[String] = Nil, connectInput: Boolean = false, envVars: Map[String,String] = Map.empty) extends ForkScalaRun
|
||||
|
||||
sealed abstract class OutputStrategy extends NotNull
|
||||
case object StdoutOutput extends OutputStrategy
|
||||
|
|
@ -45,16 +45,28 @@ object Fork
|
|||
new File(new File(home, "bin"), name)
|
||||
}
|
||||
|
||||
final class ForkJava(commandName: String) extends NotNull
|
||||
final class ForkJava(commandName: String)
|
||||
{
|
||||
def apply(config: ForkOptions, arguments: Seq[String]): Int = fork(config, arguments).exitValue()
|
||||
def fork(config: ForkOptions, arguments: Seq[String]): Process =
|
||||
fork(config.javaHome, config.runJVMOptions ++ arguments, config.workingDirectory, config.envVars, config.connectInput, config.outputStrategy getOrElse StdoutOutput)
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], options: Seq[String], log: Logger): Int =
|
||||
apply(javaHome, options, BufferedOutput(log))
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], options: Seq[String], outputStrategy: OutputStrategy): Int =
|
||||
apply(javaHome, options, None, outputStrategy)
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], options: Seq[String], workingDirectory: Option[File], log: Logger): Int =
|
||||
apply(javaHome, options, workingDirectory, BufferedOutput(log))
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], options: Seq[String], workingDirectory: Option[File], outputStrategy: OutputStrategy): Int =
|
||||
apply(javaHome, options, workingDirectory, Map.empty, outputStrategy)
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], options: Seq[String], workingDirectory: Option[File], env: Map[String, String], outputStrategy: OutputStrategy): Int =
|
||||
fork(javaHome, options, workingDirectory, env, false, outputStrategy).exitValue
|
||||
def fork(javaHome: Option[File], options: Seq[String], workingDirectory: Option[File], env: Map[String, String], connectInput: Boolean, outputStrategy: OutputStrategy): Process =
|
||||
|
|
@ -75,21 +87,35 @@ object Fork
|
|||
}
|
||||
}
|
||||
|
||||
final class ForkScala(mainClassName: String) extends NotNull
|
||||
final class ForkScala(mainClassName: String)
|
||||
{
|
||||
def apply(options: ForkOptions, arguments: Seq[String]): Int = fork(options, arguments).exitValue()
|
||||
def fork(options: ForkOptions, arguments: Seq[String]): Process =
|
||||
fork(options.javaHome, options.runJVMOptions, options.scalaJars, arguments, options.workingDirectory, options.envVars, options.connectInput, options.outputStrategy getOrElse StdoutOutput)
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], jvmOptions: Seq[String], scalaJars: Iterable[File], arguments: Seq[String], log: Logger): Int =
|
||||
apply(javaHome, jvmOptions, scalaJars, arguments, None, BufferedOutput(log))
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], jvmOptions: Seq[String], scalaJars: Iterable[File], arguments: Seq[String], workingDirectory: Option[File], log: Logger): Int =
|
||||
apply(javaHome, jvmOptions, scalaJars, arguments, workingDirectory, BufferedOutput(log))
|
||||
|
||||
@deprecated("Use apply(ForkOptions, Seq[String])", "0.13.0")
|
||||
def apply(javaHome: Option[File], jvmOptions: Seq[String], scalaJars: Iterable[File], arguments: Seq[String], workingDirectory: Option[File], outputStrategy: OutputStrategy): Int =
|
||||
fork(javaHome, jvmOptions, scalaJars, arguments, workingDirectory, false, outputStrategy).exitValue()
|
||||
|
||||
@deprecated("Use fork(ForkOptions, Seq[String])", "0.13.0")
|
||||
def fork(javaHome: Option[File], jvmOptions: Seq[String], scalaJars: Iterable[File], arguments: Seq[String], workingDirectory: Option[File], connectInput: Boolean, outputStrategy: OutputStrategy): Process =
|
||||
fork(javaHome, jvmOptions, scalaJars, arguments, workingDirectory, connectInput, outputStrategy)
|
||||
|
||||
def fork(javaHome: Option[File], jvmOptions: Seq[String], scalaJars: Iterable[File], arguments: Seq[String], workingDirectory: Option[File], env: Map[String,String], connectInput: Boolean, outputStrategy: OutputStrategy): Process =
|
||||
{
|
||||
if(scalaJars.isEmpty) error("Scala jars not specified")
|
||||
val scalaClasspathString = "-Xbootclasspath/a:" + scalaJars.map(_.getAbsolutePath).mkString(File.pathSeparator)
|
||||
val mainClass = if(mainClassName.isEmpty) Nil else mainClassName :: Nil
|
||||
val options = jvmOptions ++ (scalaClasspathString :: mainClass ::: arguments.toList)
|
||||
Fork.java.fork(javaHome, options, workingDirectory, Map.empty, connectInput, outputStrategy)
|
||||
Fork.java.fork(javaHome, options, workingDirectory, env, connectInput, outputStrategy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,15 +13,18 @@ trait ScalaRun
|
|||
{
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String]
|
||||
}
|
||||
class ForkRun(config: ForkScalaRun) extends ScalaRun
|
||||
class ForkRun(config: ForkOptions) extends ScalaRun
|
||||
{
|
||||
@deprecated("Use the `ForkRun(ForkOptions) constructor`", "0.13.0")
|
||||
def this(options: ForkScalaRun) = this(ForkOptions(options.javaHome, options.outputStrategy, options.scalaJars, options.workingDirectory, options.runJVMOptions, options.connectInput))
|
||||
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String] =
|
||||
{
|
||||
log.info("Running " + mainClass + " " + options.mkString(" "))
|
||||
|
||||
val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList
|
||||
val strategy = config.outputStrategy getOrElse LoggedOutput(log)
|
||||
val process = Fork.scala.fork(config.javaHome, config.runJVMOptions, config.scalaJars, scalaOptions, config.workingDirectory, config.connectInput, strategy)
|
||||
val configLogged = if(config.outputStrategy.isDefined) config else config.copy(outputStrategy = Some(LoggedOutput(log)))
|
||||
val process = Fork.scala.fork(configLogged, scalaOptions)
|
||||
def cancel() = {
|
||||
log.warn("Run canceled.")
|
||||
process.destroy()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
object ForkTest {
|
||||
def main(args:Array[String]) {
|
||||
val cwd = (new java.io.File("flag")).getAbsoluteFile
|
||||
val name = Option(System.getenv("flag.name")) getOrElse("flag")
|
||||
println("Name: " + name)
|
||||
val cwd = (new java.io.File(name)).getAbsoluteFile
|
||||
cwd.getParentFile.mkdirs()
|
||||
cwd.createNewFile()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,11 @@ $ mkdir "forked"
|
|||
|
||||
> run "forked"
|
||||
$ exists forked/flag
|
||||
$ absent flag
|
||||
$ absent flag
|
||||
$ delete forked/flag
|
||||
|
||||
> 'set envVars += ("flag.name" -> "env.flag")'
|
||||
> run "forked"
|
||||
$ exists forked/env.flag
|
||||
$ absent flag
|
||||
$ absent forked/flag
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import org.scalatest.FlatSpec
|
|||
import org.scalatest.matchers.MustMatchers
|
||||
|
||||
class Test extends FlatSpec with MustMatchers {
|
||||
val v = Option(System.getenv("tests.max.value")) getOrElse Int.MaxValue
|
||||
"A simple equation" must "hold" in {
|
||||
Int.MaxValue must equal (Int.MaxValue)
|
||||
Int.MaxValue must equal (v)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,8 @@ $ copy-file changes/Test.scala src/test/scala/Test.scala
|
|||
> test
|
||||
|
||||
> 'set javaOptions += "-Xno-opt"'
|
||||
-> test
|
||||
-> test
|
||||
|
||||
> session clear
|
||||
> 'set envVars += ("tests.max.value" -> "0")'
|
||||
-> test
|
||||
|
|
|
|||
Loading…
Reference in New Issue