Skip to content

Commit 59f178a

Browse files
committed
Make javaHome that forks scripted tests configurable
Normally scripted tests are forked using the JVM that is running sbt. If set `scripted / javaHome`, forked using it. ``` scripted / javaHome := Some(file("/path/to/jdk-x.y.z")) ``` Or use `java++` command before scripted. ``` sbt> java++ 11! sbt> scripted ```
1 parent 5f56fce commit 59f178a

File tree

10 files changed

+359
-41
lines changed

10 files changed

+359
-41
lines changed

main/src/main/scala/sbt/ScriptedPlugin.scala

+13-32
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
package sbt
99

1010
import java.io.File
11-
import java.lang.reflect.Method
1211

1312
import sbt.Def._
1413
import sbt.Keys._
@@ -47,7 +46,7 @@ object ScriptedPlugin extends AutoPlugin {
4746
val scriptedParallelInstances = settingKey[Int](
4847
"Configures the number of scripted instances for parallel testing, only used in batch mode."
4948
)
50-
val scriptedRun = taskKey[Method]("")
49+
val scriptedRun = taskKey[ScriptedRun]("")
5150
val scriptedLaunchOpts =
5251
settingKey[Seq[String]]("options to pass to jvm launching scripted tasks")
5352
val scriptedDependencies = taskKey[Unit]("")
@@ -114,21 +113,8 @@ object ScriptedPlugin extends AutoPlugin {
114113
}
115114
}
116115

117-
private[sbt] def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn {
118-
val fCls = classOf[File]
119-
val bCls = classOf[Boolean]
120-
val asCls = classOf[Array[String]]
121-
val lfCls = classOf[java.util.List[File]]
122-
val iCls = classOf[Int]
123-
124-
val clazz = scriptedTests.value.getClass
125-
val method =
126-
if (scriptedBatchExecution.value)
127-
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)
128-
else
129-
clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)
130-
131-
Def.task(method)
116+
private[sbt] def scriptedRunTask: Initialize[Task[ScriptedRun]] = Def.task {
117+
ScriptedRun.of(scriptedTests.value, scriptedBatchExecution.value)
132118
}
133119

134120
private[sbt] final case class ScriptedTestPage(page: Int, total: Int)
@@ -191,21 +177,16 @@ object ScriptedPlugin extends AutoPlugin {
191177
private[sbt] def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask {
192178
val args = scriptedParser(sbtTestDirectory.value).parsed
193179
Def.unit(scriptedDependencies.value)
194-
try {
195-
val method = scriptedRun.value
196-
val scriptedInstance = scriptedTests.value
197-
val dir = sbtTestDirectory.value
198-
val log = Boolean box scriptedBufferLog.value
199-
val launcher = sbtLauncher.value
200-
val opts = scriptedLaunchOpts.value.toArray
201-
val empty = new java.util.ArrayList[File]()
202-
val instances = Int box scriptedParallelInstances.value
203-
204-
if (scriptedBatchExecution.value)
205-
method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances)
206-
else method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty)
207-
()
208-
} catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
180+
scriptedRun.value.run(
181+
sbtTestDirectory.value,
182+
scriptedBufferLog.value,
183+
args,
184+
sbtLauncher.value,
185+
Fork.javaCommand((scripted / javaHome).value, "java").getAbsolutePath,
186+
scriptedLaunchOpts.value,
187+
new java.util.ArrayList[File](),
188+
scriptedParallelInstances.value
189+
)
209190
}
210191

211192
private[this] def getJars(config: Configuration): Initialize[Task[PathFinder]] = Def.task {
+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* sbt
3+
* Copyright 2011 - 2018, Lightbend, Inc.
4+
* Copyright 2008 - 2010, Mark Harrah
5+
* Licensed under Apache License 2.0 (see LICENSE)
6+
*/
7+
8+
package sbt
9+
10+
import java.io.File
11+
import java.lang.reflect.Method
12+
import scala.annotation.unused
13+
14+
sealed trait ScriptedRun {
15+
final def run(
16+
resourceBaseDirectory: File,
17+
bufferLog: Boolean,
18+
tests: Seq[String],
19+
launcherJar: File,
20+
javaCommand: String,
21+
launchOpts: Seq[String],
22+
prescripted: java.util.List[File],
23+
instances: Int,
24+
): Unit = {
25+
try {
26+
invoke(
27+
resourceBaseDirectory,
28+
bufferLog,
29+
tests.toArray,
30+
launcherJar,
31+
javaCommand,
32+
launchOpts.toArray,
33+
prescripted,
34+
instances,
35+
)
36+
()
37+
} catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
38+
}
39+
40+
protected def invoke(
41+
resourceBaseDirectory: File,
42+
bufferLog: java.lang.Boolean,
43+
tests: Array[String],
44+
launcherJar: File,
45+
javaCommand: String,
46+
launchOpts: Array[String],
47+
prescripted: java.util.List[File],
48+
instances: java.lang.Integer,
49+
): AnyRef
50+
51+
}
52+
53+
object ScriptedRun {
54+
55+
def of(scriptedTests: AnyRef, batchExecution: Boolean): ScriptedRun = {
56+
val fCls = classOf[File]
57+
val bCls = classOf[Boolean]
58+
val asCls = classOf[Array[String]]
59+
val sCls = classOf[String]
60+
val lfCls = classOf[java.util.List[File]]
61+
val iCls = classOf[Int]
62+
63+
val clazz = scriptedTests.getClass
64+
if (batchExecution)
65+
try new RunInParallelV2(
66+
scriptedTests,
67+
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, sCls, asCls, lfCls, iCls)
68+
)
69+
catch {
70+
case _: NoSuchMethodException =>
71+
new RunInParallelV1(
72+
scriptedTests,
73+
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)
74+
)
75+
}
76+
else
77+
try new RunV2(
78+
scriptedTests,
79+
clazz.getMethod("run", fCls, bCls, asCls, fCls, sCls, asCls, lfCls)
80+
)
81+
catch {
82+
case _: NoSuchMethodException =>
83+
new RunV1(scriptedTests, clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls))
84+
}
85+
}
86+
87+
private class RunV1(scriptedTests: AnyRef, run: Method) extends ScriptedRun {
88+
override protected def invoke(
89+
resourceBaseDirectory: File,
90+
bufferLog: java.lang.Boolean,
91+
tests: Array[String],
92+
launcherJar: File,
93+
@unused javaCommand: String,
94+
launchOpts: Array[String],
95+
prescripted: java.util.List[File],
96+
@unused instances: java.lang.Integer,
97+
): AnyRef =
98+
run.invoke(
99+
scriptedTests,
100+
resourceBaseDirectory,
101+
bufferLog,
102+
tests,
103+
launcherJar,
104+
launchOpts,
105+
prescripted,
106+
)
107+
}
108+
109+
private class RunInParallelV1(scriptedTests: AnyRef, runInParallel: Method) extends ScriptedRun {
110+
override protected def invoke(
111+
resourceBaseDirectory: File,
112+
bufferLog: java.lang.Boolean,
113+
tests: Array[String],
114+
launcherJar: File,
115+
@unused javaCommand: String,
116+
launchOpts: Array[String],
117+
prescripted: java.util.List[File],
118+
instances: Integer,
119+
): AnyRef =
120+
runInParallel.invoke(
121+
scriptedTests,
122+
resourceBaseDirectory,
123+
bufferLog,
124+
tests,
125+
launcherJar,
126+
launchOpts,
127+
prescripted,
128+
instances,
129+
)
130+
}
131+
132+
private class RunV2(scriptedTests: AnyRef, run: Method) extends ScriptedRun {
133+
override protected def invoke(
134+
resourceBaseDirectory: File,
135+
bufferLog: java.lang.Boolean,
136+
tests: Array[String],
137+
launcherJar: File,
138+
javaCommand: String,
139+
launchOpts: Array[String],
140+
prescripted: java.util.List[File],
141+
@unused instances: java.lang.Integer,
142+
): AnyRef =
143+
run.invoke(
144+
scriptedTests,
145+
resourceBaseDirectory,
146+
bufferLog,
147+
tests,
148+
launcherJar,
149+
javaCommand,
150+
launchOpts,
151+
prescripted,
152+
)
153+
}
154+
155+
private class RunInParallelV2(scriptedTests: AnyRef, runInParallel: Method) extends ScriptedRun {
156+
override protected def invoke(
157+
resourceBaseDirectory: File,
158+
bufferLog: java.lang.Boolean,
159+
tests: Array[String],
160+
launcherJar: File,
161+
javaCommand: String,
162+
launchOpts: Array[String],
163+
prescripted: java.util.List[File],
164+
instances: Integer,
165+
): AnyRef =
166+
runInParallel.invoke(
167+
scriptedTests,
168+
resourceBaseDirectory,
169+
bufferLog,
170+
tests,
171+
launcherJar,
172+
javaCommand,
173+
launchOpts,
174+
prescripted,
175+
instances,
176+
)
177+
}
178+
179+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[@kxbmap]: https://github.com/kxbmap
2+
3+
[#6673]: https://github.com/sbt/sbt/pull/6673
4+
5+
### Improvements
6+
7+
- Make javaHome that forks scripted tests configurable. [#6673][] by [@kxbmap][]
8+
- Normally scripted tests are forked using the JVM that is running sbt. If set `scripted / javaHome`, forked using it.
9+
- Or use `java++` command before scripted.
10+
11+
### Fixes with compatibility implications
12+
13+
- Change type of `scriptedRun` task key from `TaskKey[java.lang.reflect.Method]` to `TaskKey[sbt.ScriptedRun]`
14+
- `sbt.ScriptedRun` is a new interface for hiding substance of scripted invocation.

run/src/main/scala/sbt/Fork.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ object Fork {
114114
(classpathOption, newOptions)
115115
}
116116

117-
private def javaCommand(javaHome: Option[File], name: String): File = {
117+
private[sbt] def javaCommand(javaHome: Option[File], name: String): File = {
118118
val home = javaHome.getOrElse(new File(System.getProperty("java.home")))
119119
new File(new File(home, "bin"), name)
120120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
lazy val scriptedJavaVersion = settingKey[Long]("")
2+
3+
lazy val root = (project in file("."))
4+
.enablePlugins(SbtPlugin)
5+
.settings(
6+
scriptedJavaVersion := {
7+
val versions = discoveredJavaHomes.value
8+
.map { case (jv, _) => JavaVersion(jv).numbers }
9+
.collect {
10+
case Vector(1L, ver, _*) => ver
11+
case Vector(ver, _*) => ver
12+
}
13+
if (versions.isEmpty) sys.error("No Java versions discovered")
14+
else versions.max
15+
},
16+
commands += Command.command("setJavaVersion") { state =>
17+
val extracted = Project.extract(state)
18+
import extracted._
19+
val jv = (currentRef / scriptedJavaVersion).get(structure.data).get
20+
s"java++ $jv!" :: state
21+
},
22+
scriptedLaunchOpts += s"-Dscripted.java.version=${scriptedJavaVersion.value}"
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
lazy val check = taskKey[Unit]("check")
2+
3+
lazy val root = (project in file("."))
4+
.settings(
5+
check := {
6+
val version = sys.props("java.version").stripPrefix("1.").takeWhile(_.isDigit)
7+
val expected = sys.props("scripted.java.version")
8+
assert(
9+
version == expected,
10+
s"Expected Java version is '$expected', but actual is '$version'"
11+
)
12+
}
13+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
> check
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$ copy-file changes/build.sbt src/sbt-test/group/name/build.sbt
2+
$ copy-file changes/test src/sbt-test/group/name/test
3+
4+
> setJavaVersion
5+
> scripted

scripted-sbt-redux/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala

+23-5
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,22 @@ final class LauncherBasedRemoteSbtCreator(
2727
directory: File,
2828
launcher: File,
2929
log: Logger,
30-
launchOpts: Seq[String] = Nil,
30+
javaCommand: String,
31+
launchOpts: Seq[String],
3132
) extends RemoteSbtCreator {
32-
def newRemote(server: IPC.Server) = {
33+
def this(
34+
directory: File,
35+
launcher: File,
36+
log: Logger,
37+
launchOpts: Seq[String] = Nil,
38+
) = this(directory, launcher, log, "java", launchOpts)
39+
40+
def newRemote(server: IPC.Server): Process = {
3341
val launcherJar = launcher.getAbsolutePath
3442
val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath
3543
val scripted = "-Dsbt.scripted=true"
3644
val args = List("<" + server.port)
37-
val cmd = "java" :: launchOpts.toList ::: globalBase :: scripted :: "-jar" :: launcherJar :: args ::: Nil
45+
val cmd = javaCommand :: launchOpts.toList ::: globalBase :: scripted :: "-jar" :: launcherJar :: args ::: Nil
3846
val io = BasicIO(false, log).withInput(_.close())
3947
val p = Process(cmd, directory) run (io)
4048
val thread = new Thread() { override def run() = { p.exitValue(); server.close() } }
@@ -46,19 +54,29 @@ final class LauncherBasedRemoteSbtCreator(
4654
final class RunFromSourceBasedRemoteSbtCreator(
4755
directory: File,
4856
log: Logger,
49-
launchOpts: Seq[String] = Nil,
57+
javaCommand: String,
58+
launchOpts: Seq[String],
5059
scalaVersion: String,
5160
sbtVersion: String,
5261
classpath: Seq[File],
5362
) extends RemoteSbtCreator {
63+
def this(
64+
directory: File,
65+
log: Logger,
66+
launchOpts: Seq[String] = Nil,
67+
scalaVersion: String,
68+
sbtVersion: String,
69+
classpath: Seq[File],
70+
) = this(directory, log, "java", launchOpts, scalaVersion, sbtVersion, classpath)
71+
5472
def newRemote(server: IPC.Server): Process = {
5573
val globalBase = "-Dsbt.global.base=" + new File(directory, "global").getAbsolutePath
5674
val scripted = "-Dsbt.scripted=true"
5775
val mainClassName = "sbt.RunFromSourceMain"
5876
val cpString = classpath.mkString(java.io.File.pathSeparator)
5977
val args =
6078
List(mainClassName, directory.toString, scalaVersion, sbtVersion, cpString, "<" + server.port)
61-
val cmd = "java" :: launchOpts.toList ::: globalBase :: scripted :: "-cp" :: cpString :: args ::: Nil
79+
val cmd = javaCommand :: launchOpts.toList ::: globalBase :: scripted :: "-cp" :: cpString :: args ::: Nil
6280
val io = BasicIO(false, log).withInput(_.close())
6381
val p = Process(cmd, directory) run (io)
6482
val thread = new Thread() { override def run() = { p.exitValue(); server.close() } }

0 commit comments

Comments
 (0)