Merge pull request #6673 from kxbmap/scripted-java-home

Make javaHome that forks scripted tests configurable
This commit is contained in:
eugene yokota 2021-10-05 11:08:53 -04:00 committed by GitHub
commit adc217000b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 359 additions and 41 deletions

View File

@ -8,7 +8,6 @@
package sbt
import java.io.File
import java.lang.reflect.Method
import sbt.Def._
import sbt.Keys._
@ -47,7 +46,7 @@ object ScriptedPlugin extends AutoPlugin {
val scriptedParallelInstances = settingKey[Int](
"Configures the number of scripted instances for parallel testing, only used in batch mode."
)
val scriptedRun = taskKey[Method]("")
val scriptedRun = taskKey[ScriptedRun]("")
val scriptedLaunchOpts =
settingKey[Seq[String]]("options to pass to jvm launching scripted tasks")
val scriptedDependencies = taskKey[Unit]("")
@ -114,21 +113,8 @@ object ScriptedPlugin extends AutoPlugin {
}
}
private[sbt] def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn {
val fCls = classOf[File]
val bCls = classOf[Boolean]
val asCls = classOf[Array[String]]
val lfCls = classOf[java.util.List[File]]
val iCls = classOf[Int]
val clazz = scriptedTests.value.getClass
val method =
if (scriptedBatchExecution.value)
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)
else
clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)
Def.task(method)
private[sbt] def scriptedRunTask: Initialize[Task[ScriptedRun]] = Def.task {
ScriptedRun.of(scriptedTests.value, scriptedBatchExecution.value)
}
private[sbt] final case class ScriptedTestPage(page: Int, total: Int)
@ -191,21 +177,16 @@ object ScriptedPlugin extends AutoPlugin {
private[sbt] def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask {
val args = scriptedParser(sbtTestDirectory.value).parsed
Def.unit(scriptedDependencies.value)
try {
val method = scriptedRun.value
val scriptedInstance = scriptedTests.value
val dir = sbtTestDirectory.value
val log = Boolean box scriptedBufferLog.value
val launcher = sbtLauncher.value
val opts = scriptedLaunchOpts.value.toArray
val empty = new java.util.ArrayList[File]()
val instances = Int box scriptedParallelInstances.value
if (scriptedBatchExecution.value)
method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances)
else method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty)
()
} catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
scriptedRun.value.run(
sbtTestDirectory.value,
scriptedBufferLog.value,
args,
sbtLauncher.value,
Fork.javaCommand((scripted / javaHome).value, "java").getAbsolutePath,
scriptedLaunchOpts.value,
new java.util.ArrayList[File](),
scriptedParallelInstances.value
)
}
private[this] def getJars(config: Configuration): Initialize[Task[PathFinder]] = Def.task {

View File

@ -0,0 +1,179 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
import java.io.File
import java.lang.reflect.Method
import scala.annotation.unused
sealed trait ScriptedRun {
final def run(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Seq[String],
launcherJar: File,
javaCommand: String,
launchOpts: Seq[String],
prescripted: java.util.List[File],
instances: Int,
): Unit = {
try {
invoke(
resourceBaseDirectory,
bufferLog,
tests.toArray,
launcherJar,
javaCommand,
launchOpts.toArray,
prescripted,
instances,
)
()
} catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
}
protected def invoke(
resourceBaseDirectory: File,
bufferLog: java.lang.Boolean,
tests: Array[String],
launcherJar: File,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
instances: java.lang.Integer,
): AnyRef
}
object ScriptedRun {
def of(scriptedTests: AnyRef, batchExecution: Boolean): ScriptedRun = {
val fCls = classOf[File]
val bCls = classOf[Boolean]
val asCls = classOf[Array[String]]
val sCls = classOf[String]
val lfCls = classOf[java.util.List[File]]
val iCls = classOf[Int]
val clazz = scriptedTests.getClass
if (batchExecution)
try new RunInParallelV2(
scriptedTests,
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, sCls, asCls, lfCls, iCls)
)
catch {
case _: NoSuchMethodException =>
new RunInParallelV1(
scriptedTests,
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)
)
}
else
try new RunV2(
scriptedTests,
clazz.getMethod("run", fCls, bCls, asCls, fCls, sCls, asCls, lfCls)
)
catch {
case _: NoSuchMethodException =>
new RunV1(scriptedTests, clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls))
}
}
private class RunV1(scriptedTests: AnyRef, run: Method) extends ScriptedRun {
override protected def invoke(
resourceBaseDirectory: File,
bufferLog: java.lang.Boolean,
tests: Array[String],
launcherJar: File,
@unused javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
@unused instances: java.lang.Integer,
): AnyRef =
run.invoke(
scriptedTests,
resourceBaseDirectory,
bufferLog,
tests,
launcherJar,
launchOpts,
prescripted,
)
}
private class RunInParallelV1(scriptedTests: AnyRef, runInParallel: Method) extends ScriptedRun {
override protected def invoke(
resourceBaseDirectory: File,
bufferLog: java.lang.Boolean,
tests: Array[String],
launcherJar: File,
@unused javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
instances: Integer,
): AnyRef =
runInParallel.invoke(
scriptedTests,
resourceBaseDirectory,
bufferLog,
tests,
launcherJar,
launchOpts,
prescripted,
instances,
)
}
private class RunV2(scriptedTests: AnyRef, run: Method) extends ScriptedRun {
override protected def invoke(
resourceBaseDirectory: File,
bufferLog: java.lang.Boolean,
tests: Array[String],
launcherJar: File,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
@unused instances: java.lang.Integer,
): AnyRef =
run.invoke(
scriptedTests,
resourceBaseDirectory,
bufferLog,
tests,
launcherJar,
javaCommand,
launchOpts,
prescripted,
)
}
private class RunInParallelV2(scriptedTests: AnyRef, runInParallel: Method) extends ScriptedRun {
override protected def invoke(
resourceBaseDirectory: File,
bufferLog: java.lang.Boolean,
tests: Array[String],
launcherJar: File,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
instances: Integer,
): AnyRef =
runInParallel.invoke(
scriptedTests,
resourceBaseDirectory,
bufferLog,
tests,
launcherJar,
javaCommand,
launchOpts,
prescripted,
instances,
)
}
}

View File

@ -0,0 +1,14 @@
[@kxbmap]: https://github.com/kxbmap
[#6673]: https://github.com/sbt/sbt/pull/6673
### Improvements
- Make javaHome that forks scripted tests configurable. [#6673][] by [@kxbmap][]
- Normally scripted tests are forked using the JVM that is running sbt. If set `scripted / javaHome`, forked using it.
- Or use `java++` command before scripted.
### Fixes with compatibility implications
- Change type of `scriptedRun` task key from `TaskKey[java.lang.reflect.Method]` to `TaskKey[sbt.ScriptedRun]`
- `sbt.ScriptedRun` is a new interface for hiding substance of scripted invocation.

View File

@ -114,7 +114,7 @@ object Fork {
(classpathOption, newOptions)
}
private def javaCommand(javaHome: Option[File], name: String): File = {
private[sbt] def javaCommand(javaHome: Option[File], name: String): File = {
val home = javaHome.getOrElse(new File(System.getProperty("java.home")))
new File(new File(home, "bin"), name)
}

View File

@ -0,0 +1,23 @@
lazy val scriptedJavaVersion = settingKey[Long]("")
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)
.settings(
scriptedJavaVersion := {
val versions = discoveredJavaHomes.value
.map { case (jv, _) => JavaVersion(jv).numbers }
.collect {
case Vector(1L, ver, _*) => ver
case Vector(ver, _*) => ver
}
if (versions.isEmpty) sys.error("No Java versions discovered")
else versions.max
},
commands += Command.command("setJavaVersion") { state =>
val extracted = Project.extract(state)
import extracted._
val jv = (currentRef / scriptedJavaVersion).get(structure.data).get
s"java++ $jv!" :: state
},
scriptedLaunchOpts += s"-Dscripted.java.version=${scriptedJavaVersion.value}"
)

View File

@ -0,0 +1,13 @@
lazy val check = taskKey[Unit]("check")
lazy val root = (project in file("."))
.settings(
check := {
val version = sys.props("java.version").stripPrefix("1.").takeWhile(_.isDigit)
val expected = sys.props("scripted.java.version")
assert(
version == expected,
s"Expected Java version is '$expected', but actual is '$version'"
)
}
)

View File

@ -0,0 +1 @@
> check

View File

@ -0,0 +1,5 @@
$ copy-file changes/build.sbt src/sbt-test/group/name/build.sbt
$ copy-file changes/test src/sbt-test/group/name/test
> setJavaVersion
> scripted

View File

@ -27,14 +27,22 @@ final class LauncherBasedRemoteSbtCreator(
directory: File,
launcher: File,
log: Logger,
launchOpts: Seq[String] = Nil,
javaCommand: String,
launchOpts: Seq[String],
) extends RemoteSbtCreator {
def newRemote(server: IPC.Server) = {
def this(
directory: File,
launcher: File,
log: Logger,
launchOpts: Seq[String] = Nil,
) = this(directory, launcher, log, "java", launchOpts)
def newRemote(server: IPC.Server): Process = {
val launcherJar = launcher.getAbsolutePath
val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath
val scripted = "-Dsbt.scripted=true"
val args = List("<" + server.port)
val cmd = "java" :: launchOpts.toList ::: globalBase :: scripted :: "-jar" :: launcherJar :: args ::: Nil
val cmd = javaCommand :: launchOpts.toList ::: globalBase :: scripted :: "-jar" :: launcherJar :: args ::: Nil
val io = BasicIO(false, log).withInput(_.close())
val p = Process(cmd, directory) run (io)
val thread = new Thread() { override def run() = { p.exitValue(); server.close() } }
@ -46,11 +54,21 @@ final class LauncherBasedRemoteSbtCreator(
final class RunFromSourceBasedRemoteSbtCreator(
directory: File,
log: Logger,
launchOpts: Seq[String] = Nil,
javaCommand: String,
launchOpts: Seq[String],
scalaVersion: String,
sbtVersion: String,
classpath: Seq[File],
) extends RemoteSbtCreator {
def this(
directory: File,
log: Logger,
launchOpts: Seq[String] = Nil,
scalaVersion: String,
sbtVersion: String,
classpath: Seq[File],
) = this(directory, log, "java", launchOpts, scalaVersion, sbtVersion, classpath)
def newRemote(server: IPC.Server): Process = {
val globalBase = "-Dsbt.global.base=" + new File(directory, "global").getAbsolutePath
val scripted = "-Dsbt.scripted=true"
@ -58,7 +76,7 @@ final class RunFromSourceBasedRemoteSbtCreator(
val cpString = classpath.mkString(java.io.File.pathSeparator)
val args =
List(mainClassName, directory.toString, scalaVersion, sbtVersion, cpString, "<" + server.port)
val cmd = "java" :: launchOpts.toList ::: globalBase :: scripted :: "-cp" :: cpString :: args ::: Nil
val cmd = javaCommand :: launchOpts.toList ::: globalBase :: scripted :: "-cp" :: cpString :: args ::: Nil
val io = BasicIO(false, log).withInput(_.close())
val p = Process(cmd, directory) run (io)
val thread = new Thread() { override def run() = { p.exitValue(); server.close() } }

View File

@ -26,8 +26,15 @@ import scala.util.control.NonFatal
final class ScriptedTests(
resourceBaseDirectory: File,
bufferLog: Boolean,
javaCommand: String,
launchOpts: Seq[String],
) {
def this(
resourceBaseDirectory: File,
bufferLog: Boolean,
launchOpts: Seq[String],
) = this(resourceBaseDirectory, bufferLog, "java", launchOpts)
import ScriptedTests.TestRunner
private val testResources = new Resources(resourceBaseDirectory)
@ -80,11 +87,12 @@ final class ScriptedTests(
val remoteSbtCreator =
prop match {
case LauncherBased(launcherJar) =>
new LauncherBasedRemoteSbtCreator(testDir, launcherJar, buffered, launchOpts)
new LauncherBasedRemoteSbtCreator(testDir, launcherJar, buffered, javaCommand, launchOpts)
case RunFromSourceBased(scalaVersion, sbtVersion, classpath) =>
new RunFromSourceBasedRemoteSbtCreator(
testDir,
buffered,
javaCommand,
launchOpts,
scalaVersion,
sbtVersion,
@ -383,6 +391,7 @@ class ScriptedRunner {
bufferLog,
tests,
logger,
javaCommand = "java",
launchOpts,
prescripted = new java.util.ArrayList[File],
LauncherBased(launcherJar),
@ -411,6 +420,36 @@ class ScriptedRunner {
bufferLog,
tests,
logger,
javaCommand = "java",
launchOpts,
prescripted,
LauncherBased(launcherJar),
Int.MaxValue,
parallelExecution = false,
)
}
/**
* This is the entry point used by SbtPlugin in sbt 2.0.x etc.
* Removing this method will break scripted and sbt plugin cross building.
* See https://github.com/sbt/sbt/issues/3245
*/
def run(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
launcherJar: File,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
): Unit = {
val logger = TestConsoleLogger()
run(
resourceBaseDirectory,
bufferLog,
tests,
logger,
javaCommand,
launchOpts,
prescripted,
LauncherBased(launcherJar),
@ -440,6 +479,36 @@ class ScriptedRunner {
bufferLog,
tests,
logger,
javaCommand = "java",
launchOpts,
prescripted,
LauncherBased(launcherJar),
instance,
)
}
/**
* This is the entry point used by SbtPlugin in sbt 2.0.x etc.
* Removing this method will break scripted and sbt plugin cross building.
* See https://github.com/sbt/sbt/issues/3245
*/
def runInParallel(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
launcherJar: File,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
instance: Int,
): Unit = {
val logger = TestConsoleLogger()
runInParallel(
resourceBaseDirectory,
bufferLog,
tests,
logger,
javaCommand,
launchOpts,
prescripted,
LauncherBased(launcherJar),
@ -466,6 +535,7 @@ class ScriptedRunner {
bufferLog,
tests,
logger,
javaCommand = "java",
launchOpts,
prescripted,
RunFromSourceBased(scalaVersion, sbtVersion, classpath),
@ -477,11 +547,24 @@ class ScriptedRunner {
bufferLog: Boolean,
tests: Array[String],
logger: Logger,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
prop: RemoteSbtCreatorProp,
instances: Int
) = run(baseDir, bufferLog, tests, logger, launchOpts, prescripted, prop, instances, true)
): Unit =
run(
baseDir,
bufferLog,
tests,
logger,
javaCommand,
launchOpts,
prescripted,
prop,
instances,
parallelExecution = true
)
@nowarn
private[this] def run(
@ -489,6 +572,7 @@ class ScriptedRunner {
bufferLog: Boolean,
tests: Array[String],
logger: Logger,
javaCommand: String,
launchOpts: Array[String],
prescripted: java.util.List[File],
prop: RemoteSbtCreatorProp,
@ -496,7 +580,7 @@ class ScriptedRunner {
parallelExecution: Boolean,
): Unit = {
val addTestFile = (f: File) => { prescripted.add(f); () }
val runner = new ScriptedTests(baseDir, bufferLog, launchOpts)
val runner = new ScriptedTests(baseDir, bufferLog, javaCommand, launchOpts)
val sbtVersion =
prop match {
case LauncherBased(launcherJar) =>