mirror of https://github.com/sbt/sbt.git
Merge pull request #5367 from eatkins/scripted-run-source-from-main
Run all scripted tests using RunFromSourceMain
This commit is contained in:
commit
996ee5f0d6
109
build.sbt
109
build.sbt
|
|
@ -567,23 +567,11 @@ val sbtProjDepsCompileScopeFilter =
|
|||
)
|
||||
|
||||
lazy val scriptedSbtReduxProj = (project in file("scripted-sbt-redux"))
|
||||
.dependsOn(commandProj, utilLogging, utilScripted)
|
||||
.dependsOn(sbtProj % "compile;test->test", commandProj, utilLogging, utilScripted)
|
||||
.settings(
|
||||
baseSettings,
|
||||
name := "Scripted sbt Redux",
|
||||
libraryDependencies ++= Seq(launcherInterface % "provided"),
|
||||
resourceGenerators in Compile += Def task {
|
||||
val mainClassDir = (classDirectory in Compile in LocalProject("sbtProj")).value
|
||||
val testClassDir = (classDirectory in Test in LocalProject("sbtProj")).value
|
||||
val classDirs = (classDirectory all sbtProjDepsCompileScopeFilter).value
|
||||
val extDepsCp = (externalDependencyClasspath in Compile in LocalProject("sbtProj")).value
|
||||
val cpStrings = (mainClassDir +: testClassDir +: classDirs) ++ extDepsCp.files map (_.toString)
|
||||
val file = (resourceManaged in Compile).value / "RunFromSource.classpath"
|
||||
if (!file.exists || Try(IO.readLines(file)).getOrElse(Nil).toSet != cpStrings.toSet) {
|
||||
IO.writeLines(file, cpStrings)
|
||||
}
|
||||
List(file)
|
||||
},
|
||||
mimaSettings,
|
||||
scriptedSbtReduxMimaSettings,
|
||||
)
|
||||
|
|
@ -777,17 +765,13 @@ lazy val mainSettingsProj = (project in file("main-settings"))
|
|||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "Main Settings",
|
||||
BuildInfoPlugin.buildInfoDefaultSettings,
|
||||
addBuildInfoToConfig(Test),
|
||||
buildInfoObject in Test := "TestBuildInfo",
|
||||
buildInfoKeys in Test := Seq[BuildInfoKey](
|
||||
classDirectory in Compile,
|
||||
classDirectory in Test,
|
||||
// WORKAROUND https://github.com/sbt/sbt-buildinfo/issues/117
|
||||
BuildInfoKey.map((dependencyClasspath in Compile).taskValue) {
|
||||
case (ident, cp) => ident -> cp.files
|
||||
},
|
||||
),
|
||||
testOptions in Test ++= {
|
||||
val cp = (Test / fullClasspathAsJars).value.map(_.data).mkString(java.io.File.pathSeparator)
|
||||
val framework = TestFrameworks.ScalaTest
|
||||
Tests.Argument(framework, s"-Dsbt.server.classpath=$cp") ::
|
||||
Tests.Argument(framework, s"-Dsbt.server.version=${version.value}") ::
|
||||
Tests.Argument(framework, s"-Dsbt.server.scala.version=${scalaVersion.value}") :: Nil
|
||||
},
|
||||
mimaSettings,
|
||||
mimaBinaryIssueFilters ++= Seq(
|
||||
exclude[IncompatibleSignatureProblem]("sbt.Previous#References.getReferences"),
|
||||
|
|
@ -829,15 +813,11 @@ lazy val mainSettingsProj = (project in file("main-settings"))
|
|||
)
|
||||
|
||||
lazy val zincLmIntegrationProj = (project in file("zinc-lm-integration"))
|
||||
.enablePlugins(BuildInfoPlugin)
|
||||
.settings(
|
||||
name := "Zinc LM Integration",
|
||||
testedBaseSettings,
|
||||
buildInfo in Compile := Nil, // Only generate build info for tests
|
||||
BuildInfoPlugin.buildInfoScopedSettings(Test),
|
||||
buildInfoPackage in Test := "sbt.internal.inc",
|
||||
buildInfoObject in Test := "ZincLmIntegrationBuildInfo",
|
||||
buildInfoKeys in Test := List[BuildInfoKey]("zincVersion" -> zincVersion),
|
||||
testOptions in Test +=
|
||||
Tests.Argument(TestFrameworks.ScalaTest, s"-Dsbt.zinc.version=$zincVersion"),
|
||||
mimaSettingsSince(sbt13Plus),
|
||||
libraryDependencies += launcherInterface,
|
||||
)
|
||||
|
|
@ -853,7 +833,6 @@ lazy val mainProj = (project in file("main"))
|
|||
runProj,
|
||||
commandProj,
|
||||
collectionProj,
|
||||
scriptedSbtReduxProj,
|
||||
scriptedPluginProj,
|
||||
zincLmIntegrationProj,
|
||||
utilLogging,
|
||||
|
|
@ -963,7 +942,7 @@ lazy val mainProj = (project in file("main"))
|
|||
// technically, we need a dependency on all of mainProj's dependencies, but we don't do that since this is strictly an integration project
|
||||
// with the sole purpose of providing certain identifiers without qualification (with a package object)
|
||||
lazy val sbtProj = (project in file("sbt"))
|
||||
.dependsOn(mainProj, scriptedSbtReduxProj % "test->test")
|
||||
.dependsOn(mainProj)
|
||||
.settings(
|
||||
testedBaseSettings,
|
||||
name := "sbt",
|
||||
|
|
@ -973,25 +952,16 @@ lazy val sbtProj = (project in file("sbt"))
|
|||
javaOptions ++= Seq("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"),
|
||||
mimaSettings,
|
||||
mimaBinaryIssueFilters ++= sbtIgnoredProblems,
|
||||
BuildInfoPlugin.buildInfoDefaultSettings,
|
||||
addBuildInfoToConfig(Test),
|
||||
BuildInfoPlugin.buildInfoDefaultSettings,
|
||||
buildInfoObject in Test := "TestBuildInfo",
|
||||
buildInfoKeys in Test := Seq[BuildInfoKey](
|
||||
version,
|
||||
// WORKAROUND https://github.com/sbt/sbt-buildinfo/issues/117
|
||||
BuildInfoKey.map((fullClasspath in Compile).taskValue) {
|
||||
case (ident, cp) => ident -> cp.files
|
||||
},
|
||||
BuildInfoKey.map((dependencyClasspath in Compile).taskValue) {
|
||||
case (ident, cp) => ident -> cp.files
|
||||
},
|
||||
classDirectory in Compile,
|
||||
classDirectory in Test,
|
||||
),
|
||||
Test / run / connectInput := true,
|
||||
Test / run / outputStrategy := Some(StdoutOutput),
|
||||
Test / run / fork := true,
|
||||
testOptions in Test ++= {
|
||||
val cp = (Test / fullClasspathAsJars).value.map(_.data).mkString(java.io.File.pathSeparator)
|
||||
val framework = TestFrameworks.ScalaTest
|
||||
Tests.Argument(framework, s"-Dsbt.server.classpath=$cp") ::
|
||||
Tests.Argument(framework, s"-Dsbt.server.version=${version.value}") ::
|
||||
Tests.Argument(framework, s"-Dsbt.server.scala.version=${scalaVersion.value}") :: Nil
|
||||
},
|
||||
)
|
||||
.configure(addSbtIO, addSbtCompilerBridge)
|
||||
|
||||
|
|
@ -1129,33 +1099,19 @@ lazy val vscodePlugin = (project in file("vscode-sbt-scala"))
|
|||
}
|
||||
)
|
||||
|
||||
def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
// publishLocalBinAll.value // TODO: Restore scripted needing only binary jars.
|
||||
publishAll.value
|
||||
(sbtProj / Test / compile).value // make sure sbt.RunFromSourceMain is compiled
|
||||
def scriptedTask(launch: Boolean): Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
publishLocalBinAll.value
|
||||
val launchJar = s"-Dsbt.launch.jar=${(bundledLauncherProj / Compile / packageBin).value}"
|
||||
Scripted.doScripted(
|
||||
(sbtLaunchJar in bundledLauncherProj).value,
|
||||
(fullClasspath in scriptedSbtReduxProj in Test).value,
|
||||
(scalaInstance in scriptedSbtReduxProj).value,
|
||||
scriptedSource.value,
|
||||
scriptedBufferLog.value,
|
||||
Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed,
|
||||
scriptedPrescripted.value,
|
||||
scriptedLaunchOpts.value,
|
||||
streams.value.log
|
||||
)
|
||||
}
|
||||
|
||||
def scriptedUnpublishedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
|
||||
Scripted.doScripted(
|
||||
(sbtLaunchJar in bundledLauncherProj).value,
|
||||
(fullClasspath in scriptedSbtReduxProj in Test).value,
|
||||
(scalaInstance in scriptedSbtReduxProj).value,
|
||||
scriptedSource.value,
|
||||
scriptedBufferLog.value,
|
||||
Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed,
|
||||
scriptedPrescripted.value,
|
||||
scriptedLaunchOpts.value,
|
||||
scriptedLaunchOpts.value ++ (if (launch) Some(launchJar) else None),
|
||||
scalaVersion.value,
|
||||
version.value,
|
||||
(scriptedSbtReduxProj / Test / fullClasspathAsJars).value.map(_.data),
|
||||
streams.value.log
|
||||
)
|
||||
}
|
||||
|
|
@ -1206,18 +1162,18 @@ ThisBuild / scriptedPrescripted := { _ =>
|
|||
|
||||
def otherRootSettings =
|
||||
Seq(
|
||||
scripted := scriptedTask.evaluated,
|
||||
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
|
||||
scripted := scriptedTask(false).evaluated,
|
||||
scriptedUnpublished := scriptedTask(false).evaluated,
|
||||
scriptedSource := (sourceDirectory in sbtProj).value / "sbt-test",
|
||||
watchTriggers in scripted += scriptedSource.value.toGlob / **,
|
||||
watchTriggers in scriptedUnpublished := (watchTriggers in scripted).value,
|
||||
scriptedLaunchOpts := List("-Xmx1500M", "-Xms512M", "-server") :::
|
||||
(sys.props.get("sbt.ivy.home") match {
|
||||
case Some(home) => List(s"-Dsbt.ivy.home=$home")
|
||||
case _ => Nil
|
||||
}),
|
||||
publishAll := { val _ = (publishLocal).all(ScopeFilter(inAnyProject)).value },
|
||||
publishLocalBinAll := { val _ = (publishLocalBin).all(ScopeFilter(inAnyProject)).value },
|
||||
aggregate in bintrayRelease := false
|
||||
publishLocalBinAll := (Compile / publishLocalBin).all(scriptedProjects).value,
|
||||
aggregate in bintrayRelease := false,
|
||||
) ++ inConfig(Scripted.RepoOverrideTest)(
|
||||
Seq(
|
||||
scriptedLaunchOpts := List(
|
||||
|
|
@ -1231,8 +1187,8 @@ def otherRootSettings =
|
|||
case Some(home) => List(s"-Dsbt.ivy.home=$home")
|
||||
case _ => Nil
|
||||
}),
|
||||
scripted := scriptedTask.evaluated,
|
||||
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
|
||||
scripted := scriptedTask(true).evaluated,
|
||||
scriptedUnpublished := scriptedTask(true).evaluated,
|
||||
scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test"
|
||||
)
|
||||
)
|
||||
|
|
@ -1262,6 +1218,7 @@ lazy val otherProjects: ScopeFilter = ScopeFilter(
|
|||
inConfigurations(Test)
|
||||
)
|
||||
lazy val javafmtOnCompile = taskKey[Unit]("Formats java sources before compile")
|
||||
lazy val scriptedProjects = ScopeFilter(inAnyProject -- inProjects(vscodePlugin))
|
||||
|
||||
def customCommands: Seq[Setting[_]] = Seq(
|
||||
commands += Command.command("setupBuildScala212") { state =>
|
||||
|
|
|
|||
|
|
@ -7,19 +7,18 @@
|
|||
|
||||
package sbt.std
|
||||
|
||||
import org.scalatest.FunSuite
|
||||
import org.scalatest.{ TestData, fixture }
|
||||
import sbt.std.TestUtil._
|
||||
|
||||
import scala.tools.reflect.{ FrontEnd, ToolBoxError }
|
||||
|
||||
class TaskConfigSpec extends FunSuite {
|
||||
class TaskConfigSpec extends fixture.FunSuite with fixture.TestDataFixture {
|
||||
private def expectError(
|
||||
errorSnippet: String,
|
||||
compileOptions: String = "",
|
||||
baseCompileOptions: String = s"-cp $toolboxClasspath",
|
||||
)(code: String) = {
|
||||
)(code: String)(implicit td: TestData) = {
|
||||
val errorMessage = intercept[ToolBoxError] {
|
||||
eval(code, s"$compileOptions $baseCompileOptions")
|
||||
eval(code, s"$compileOptions -cp ${toolboxClasspath(td)}")
|
||||
println(s"Test failed -- compilation was successful! Expected:\n$errorSnippet")
|
||||
}.getMessage
|
||||
val userMessage =
|
||||
|
|
@ -29,7 +28,7 @@ class TaskConfigSpec extends FunSuite {
|
|||
""".stripMargin
|
||||
assert(errorMessage.contains(errorSnippet), userMessage)
|
||||
}
|
||||
private class CachingToolbox {
|
||||
private class CachingToolbox(implicit td: TestData) {
|
||||
private[this] val m = scala.reflect.runtime.currentMirror
|
||||
private[this] var _infos: List[FrontEnd#Info] = Nil
|
||||
private[this] val frontEnd = new FrontEnd {
|
||||
|
|
@ -38,7 +37,7 @@ class TaskConfigSpec extends FunSuite {
|
|||
}
|
||||
|
||||
import scala.tools.reflect.ToolBox
|
||||
val toolbox = m.mkToolBox(frontEnd, options = s"-cp $toolboxClasspath")
|
||||
val toolbox = m.mkToolBox(frontEnd, options = s"-cp ${toolboxClasspath(td)}")
|
||||
def eval(code: String): Any = toolbox.eval(toolbox.parse(code))
|
||||
def infos: List[FrontEnd#Info] = _infos
|
||||
}
|
||||
|
|
@ -59,16 +58,17 @@ class TaskConfigSpec extends FunSuite {
|
|||
""".stripMargin
|
||||
private val fooNegError = TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")
|
||||
|
||||
test("Fail on task invocation inside if it is used inside a regular task") {
|
||||
test("Fail on task invocation inside if it is used inside a regular task") { implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")
|
||||
expectError(List(fooNegError).mkString("\n"))(taskDef(Some("import sbt.dsl.LinterLevel.Abort")))
|
||||
val code = taskDef(Some("import sbt.dsl.LinterLevel.Abort"))
|
||||
expectError(List(fooNegError).mkString("\n"))(code)
|
||||
}
|
||||
test("Succeed if the linter level is set to warn") {
|
||||
test("Succeed if the linter level is set to warn") { implicit td =>
|
||||
val toolbox = new CachingToolbox
|
||||
assert(toolbox.eval(taskDef(None)) == ((): Unit))
|
||||
assert(toolbox.infos.exists(i => i.severity.count == 1 && i.msg.contains(fooNegError)))
|
||||
}
|
||||
test("Succeed if the linter level is set to ignore") {
|
||||
test("Succeed if the linter level is set to ignore") { implicit td =>
|
||||
val toolbox = new CachingToolbox
|
||||
assert(toolbox.eval(taskDef(Some("import sbt.dsl.LinterLevel.Ignore"))) == ((): Unit))
|
||||
assert(toolbox.infos.isEmpty)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
package sbt.std
|
||||
|
||||
import org.scalatest.TestData
|
||||
|
||||
import scala.tools.reflect.ToolBox
|
||||
|
||||
object TestUtil {
|
||||
|
|
@ -21,10 +23,9 @@ object TestUtil {
|
|||
m.mkToolBox(options = compileOptions)
|
||||
}
|
||||
|
||||
lazy val toolboxClasspath: String = {
|
||||
val mainClassesDir = buildinfo.TestBuildInfo.classDirectory
|
||||
val testClassesDir = buildinfo.TestBuildInfo.test_classDirectory
|
||||
val depsClasspath = buildinfo.TestBuildInfo.dependencyClasspath
|
||||
mainClassesDir +: testClassesDir +: depsClasspath mkString java.io.File.pathSeparator
|
||||
}
|
||||
def toolboxClasspath(td: TestData): String =
|
||||
td.configMap.get("sbt.server.classpath") match {
|
||||
case Some(s: String) => s
|
||||
case _ => throw new IllegalStateException("No classpath specified.")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,19 +8,17 @@
|
|||
package sbt.std.neg
|
||||
|
||||
import scala.tools.reflect.ToolBoxError
|
||||
|
||||
import org.scalatest.FunSuite
|
||||
|
||||
import sbt.std.TaskLinterDSLFeedback
|
||||
import org.scalatest.{ TestData, fixture }
|
||||
import sbt.std.{ TaskLinterDSLFeedback, TestUtil }
|
||||
import sbt.std.TestUtil._
|
||||
|
||||
class TaskNegSpec extends FunSuite {
|
||||
class TaskNegSpec extends fixture.FunSuite with fixture.TestDataFixture {
|
||||
def expectError(
|
||||
errorSnippet: String,
|
||||
compileOptions: String = "",
|
||||
baseCompileOptions: String = s"-cp $toolboxClasspath",
|
||||
)(code: String) = {
|
||||
)(code: String)(implicit td: TestData) = {
|
||||
val errorMessage = intercept[ToolBoxError] {
|
||||
val baseCompileOptions = s"-cp ${TestUtil.toolboxClasspath(td)}"
|
||||
eval(code, s"$compileOptions $baseCompileOptions")
|
||||
println(s"Test failed -- compilation was successful! Expected:\n$errorSnippet")
|
||||
}.getMessage
|
||||
|
|
@ -32,7 +30,7 @@ class TaskNegSpec extends FunSuite {
|
|||
assert(errorMessage.contains(errorSnippet), userMessage)
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside if it is used inside a regular task") {
|
||||
test("Fail on task invocation inside if it is used inside a regular task") { implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")
|
||||
val barNegError = TaskLinterDSLFeedback.useOfValueInsideIfExpression("barNeg")
|
||||
expectError(List(fooNegError, barNegError).mkString("\n")) {
|
||||
|
|
@ -53,7 +51,7 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside `if` if it is used inside a regular task") {
|
||||
test("Fail on task invocation inside `if` if it is used inside a regular task") { implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")
|
||||
val barNegError = TaskLinterDSLFeedback.useOfValueInsideIfExpression("barNeg")
|
||||
expectError(List(fooNegError, barNegError).mkString("\n")) {
|
||||
|
|
@ -75,7 +73,7 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside `if` of task returned by dynamic task") {
|
||||
test("Fail on task invocation inside `if` of task returned by dynamic task") { implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|
|
@ -100,10 +98,11 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
|
||||
test("Fail on task invocation inside nested `if` of task returned by dynamic task") {
|
||||
val fooNegCatch = TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")
|
||||
val barNegCatch = TaskLinterDSLFeedback.useOfValueInsideIfExpression("barNeg")
|
||||
expectError(List(fooNegCatch, barNegCatch).mkString("\n")) {
|
||||
"""
|
||||
implicit td =>
|
||||
val fooNegCatch = TaskLinterDSLFeedback.useOfValueInsideIfExpression("fooNeg")
|
||||
val barNegCatch = TaskLinterDSLFeedback.useOfValueInsideIfExpression("barNeg")
|
||||
expectError(List(fooNegCatch, barNegCatch).mkString("\n")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|import sbt.Def._
|
||||
|import sbt.dsl.LinterLevel.Abort
|
||||
|
|
@ -128,10 +127,10 @@ class TaskNegSpec extends FunSuite {
|
|||
| } else Def.task("")
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside else of task returned by dynamic task") {
|
||||
test("Fail on task invocation inside else of task returned by dynamic task") { implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.useOfValueInsideIfExpression("barNeg")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|
|
@ -155,9 +154,10 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
|
||||
test("Fail on task invocation inside anonymous function returned by regular task") {
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
expectError(fooNegError) {
|
||||
"""
|
||||
implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
expectError(fooNegError) {
|
||||
"""
|
||||
|import sbt._
|
||||
|import sbt.Def._
|
||||
|import sbt.dsl.LinterLevel.Abort
|
||||
|
|
@ -172,14 +172,15 @@ class TaskNegSpec extends FunSuite {
|
|||
| else anon()
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside nested anonymous function returned by regular task") {
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
val barNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("barNeg")
|
||||
expectError(List(fooNegError, barNegError).mkString("\n")) {
|
||||
"""
|
||||
implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
val barNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("barNeg")
|
||||
expectError(List(fooNegError, barNegError).mkString("\n")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|import sbt.Def._
|
||||
|import sbt.dsl.LinterLevel.Abort
|
||||
|
|
@ -194,13 +195,14 @@ class TaskNegSpec extends FunSuite {
|
|||
| else anon()
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside complex anonymous function returned by regular task") {
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
expectError(fooNegError) {
|
||||
"""
|
||||
implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
expectError(fooNegError) {
|
||||
"""
|
||||
|import sbt._
|
||||
|import sbt.Def._
|
||||
|import sbt.dsl.LinterLevel.Abort
|
||||
|
|
@ -214,13 +216,14 @@ class TaskNegSpec extends FunSuite {
|
|||
| else anon()
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Fail on task invocation inside anonymous function returned by dynamic task") {
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
expectError(fooNegError) {
|
||||
"""
|
||||
implicit td =>
|
||||
val fooNegError = TaskLinterDSLFeedback.useOfValueInsideAnon("fooNeg")
|
||||
expectError(fooNegError) {
|
||||
"""
|
||||
|import sbt._
|
||||
|import sbt.Def._
|
||||
|import sbt.dsl.LinterLevel.Abort
|
||||
|
|
@ -236,10 +239,10 @@ class TaskNegSpec extends FunSuite {
|
|||
| } else Def.task("")
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Detect a missing `.value` inside a task") {
|
||||
test("Detect a missing `.value` inside a task") { implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.missingValueForKey("fooNeg")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|
|
@ -257,7 +260,7 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
}
|
||||
|
||||
test("Detect a missing `.value` inside a val definition of a task") {
|
||||
test("Detect a missing `.value` inside a val definition of a task") { implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.missingValueForKey("fooNeg2")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|
|
@ -276,8 +279,9 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
|
||||
test("Detect a missing `.value` inside a val definition of an inner method of a task") {
|
||||
expectError(TaskLinterDSLFeedback.missingValueForKey("fooNeg2")) {
|
||||
"""
|
||||
implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.missingValueForKey("fooNeg2")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|import sbt.Def._
|
||||
|import sbt.dsl.LinterLevel.Abort
|
||||
|
|
@ -293,10 +297,10 @@ class TaskNegSpec extends FunSuite {
|
|||
| inner
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("Detect a missing `.value` inside an inner method of a task") {
|
||||
test("Detect a missing `.value` inside an inner method of a task") { implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.missingValueForKey("fooNeg3")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|
|
@ -316,7 +320,7 @@ class TaskNegSpec extends FunSuite {
|
|||
}
|
||||
}
|
||||
|
||||
test("Detect a missing `.value` inside a task whose return type is Unit") {
|
||||
test("Detect a missing `.value` inside a task whose return type is Unit") { implicit td =>
|
||||
expectError(TaskLinterDSLFeedback.missingValueForKey("fooNeg4")) {
|
||||
"""
|
||||
|import sbt._
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.internal;
|
||||
|
||||
public class ClassLoaderClose {
|
||||
public static void close(ClassLoader classLoader) throws Exception {
|
||||
if (classLoader instanceof AutoCloseable) ((AutoCloseable) classLoader).close();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import xsbti.AppProvider;
|
||||
import xsbti.ScalaProvider;
|
||||
|
|
@ -61,19 +63,27 @@ public final class MetaBuildLoader extends URLClassLoader {
|
|||
final Pattern pattern = Pattern.compile("test-interface-[0-9.]+\\.jar");
|
||||
final File[] cp = appProvider.mainClasspath();
|
||||
final URL[] interfaceURL = new URL[1];
|
||||
final URL[] rest = new URL[cp.length - 1];
|
||||
final File[] extra =
|
||||
appProvider.id().classpathExtra() == null ? new File[0] : appProvider.id().classpathExtra();
|
||||
final Set<File> bottomClasspath = new LinkedHashSet<>();
|
||||
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0; // index into rest
|
||||
while (i < cp.length) {
|
||||
final File file = cp[i];
|
||||
for (final File file : cp) {
|
||||
if (pattern.matcher(file.getName()).find()) {
|
||||
interfaceURL[0] = file.toURI().toURL();
|
||||
} else {
|
||||
rest[j] = file.toURI().toURL();
|
||||
j += 1;
|
||||
bottomClasspath.add(file);
|
||||
}
|
||||
}
|
||||
for (final File file : extra) {
|
||||
bottomClasspath.add(file);
|
||||
}
|
||||
}
|
||||
final URL[] rest = new URL[bottomClasspath.size()];
|
||||
{
|
||||
int i = 0;
|
||||
for (final File file : bottomClasspath) {
|
||||
rest[i] = file.toURI().toURL();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import java.io.File
|
|||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.URL
|
||||
import java.util.concurrent.{ ExecutorService, Executors }
|
||||
import ClassLoaderClose.close
|
||||
|
||||
import sbt.plugins.{ CorePlugin, IvyPlugin, JvmPlugin }
|
||||
import sbt.util.LogExchange
|
||||
|
|
@ -56,10 +57,6 @@ private[internal] object ClassLoaderWarmup {
|
|||
* in this file.
|
||||
*/
|
||||
private[sbt] class XMainConfiguration {
|
||||
private def close(classLoader: ClassLoader): Unit = classLoader match {
|
||||
case a: AutoCloseable => a.close()
|
||||
case _ =>
|
||||
}
|
||||
def run(moduleName: String, configuration: xsbti.AppConfiguration): xsbti.MainResult = {
|
||||
val updatedConfiguration =
|
||||
if (configuration.provider.scalaProvider.launcher.topLoader.getClass.getCanonicalName
|
||||
|
|
@ -86,9 +83,11 @@ private[sbt] class XMainConfiguration {
|
|||
|
||||
private def makeConfiguration(configuration: xsbti.AppConfiguration): xsbti.AppConfiguration = {
|
||||
val baseLoader = classOf[XMainConfiguration].getClassLoader
|
||||
val url = baseLoader.getResource("sbt/internal/XMainConfiguration.class")
|
||||
val className = "sbt/internal/XMainConfiguration.class"
|
||||
val url = baseLoader.getResource(className)
|
||||
val path = url.toString.replaceAll(s"$className$$", "")
|
||||
val urlArray = new Array[URL](1)
|
||||
urlArray(0) = new URL(url.getPath.replaceAll("[!][^!]*class", ""))
|
||||
urlArray(0) = new URL(path)
|
||||
val topLoader = configuration.provider.scalaProvider.launcher.topLoader
|
||||
// This loader doesn't have the scala library in it so it's critical that none of the code
|
||||
// in this file use the scala library.
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import java.lang.reflect.InvocationTargetException
|
|||
import sbt._
|
||||
import sbt.internal.inc.ScalaInstance
|
||||
import sbt.internal.inc.classpath.{ ClasspathUtilities, FilteredLoader }
|
||||
import sbt.ScriptedPlugin.autoImport._
|
||||
import sbt.util.Level
|
||||
|
||||
object LocalScriptedPlugin extends AutoPlugin {
|
||||
override def requires = plugins.JvmPlugin
|
||||
|
|
@ -15,7 +13,6 @@ object LocalScriptedPlugin extends AutoPlugin {
|
|||
}
|
||||
|
||||
trait ScriptedKeys {
|
||||
val publishAll = taskKey[Unit]("")
|
||||
val publishLocalBinAll = taskKey[Unit]("")
|
||||
val scriptedUnpublished = inputKey[Unit](
|
||||
"Execute scripted without publishing sbt first. " +
|
||||
|
|
@ -93,14 +90,15 @@ object Scripted {
|
|||
}
|
||||
|
||||
def doScripted(
|
||||
launcher: File,
|
||||
scriptedSbtClasspath: Seq[Attributed[File]],
|
||||
scriptedSbtInstance: ScalaInstance,
|
||||
sourcePath: File,
|
||||
bufferLog: Boolean,
|
||||
args: Seq[String],
|
||||
prescripted: File => Unit,
|
||||
launchOpts: Seq[String],
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File],
|
||||
logger: Logger
|
||||
): Unit = {
|
||||
logger.info(s"About to run tests: ${args.mkString("\n * ", "\n * ", "\n")}")
|
||||
|
|
@ -110,7 +108,7 @@ object Scripted {
|
|||
sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true"
|
||||
|
||||
val noJLine = new FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil)
|
||||
val loader = ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine)
|
||||
val loader = ClasspathUtilities.toLoader(classpath, noJLine)
|
||||
val bridgeClass = Class.forName("sbt.scriptedtest.ScriptedRunner", true, loader)
|
||||
|
||||
// Interface to cross class loader
|
||||
|
|
@ -119,9 +117,11 @@ object Scripted {
|
|||
resourceBaseDirectory: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
bootProperties: File,
|
||||
launchOpts: Array[String],
|
||||
prescripted: java.util.List[File],
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File],
|
||||
instances: Int
|
||||
): Unit
|
||||
}
|
||||
|
|
@ -150,9 +150,11 @@ object Scripted {
|
|||
sourcePath,
|
||||
bufferLog,
|
||||
args.toArray,
|
||||
launcher,
|
||||
launchOpts.toArray,
|
||||
callback,
|
||||
scalaVersion,
|
||||
sbtVersion,
|
||||
classpath,
|
||||
instances
|
||||
)
|
||||
} catch { case ite: InvocationTargetException => throw ite.getCause }
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.0")
|
|||
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.0")
|
||||
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.4.4")
|
||||
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0")
|
||||
addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.14")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
|
||||
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.1")
|
||||
|
|
|
|||
|
|
@ -7,17 +7,17 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import org.scalatest.FunSuite
|
||||
import org.scalatest
|
||||
import org.scalatest.{ TestData, fixture }
|
||||
|
||||
import scala.tools.reflect.{ FrontEnd, ToolBoxError }
|
||||
|
||||
class IllegalReferenceSpec extends FunSuite {
|
||||
lazy val toolboxClasspath: String = {
|
||||
val mainClassesDir = buildinfo.TestBuildInfo.classDirectory
|
||||
val testClassesDir = buildinfo.TestBuildInfo.test_classDirectory
|
||||
val depsClasspath = buildinfo.TestBuildInfo.dependencyClasspath
|
||||
mainClassesDir +: testClassesDir +: depsClasspath mkString java.io.File.pathSeparator
|
||||
}
|
||||
class IllegalReferenceSpec extends fixture.FunSuite with fixture.TestDataFixture {
|
||||
private def toolboxClasspath(td: TestData): String =
|
||||
td.configMap.get("sbt.server.classpath") match {
|
||||
case Some(s: String) => s
|
||||
case _ => throw new IllegalStateException("No classpath specified.")
|
||||
}
|
||||
def eval(code: String, compileOptions: String = ""): Any = {
|
||||
val m = scala.reflect.runtime.currentMirror
|
||||
import scala.tools.reflect.ToolBox
|
||||
|
|
@ -26,11 +26,10 @@ class IllegalReferenceSpec extends FunSuite {
|
|||
}
|
||||
private def expectError(
|
||||
errorSnippet: String,
|
||||
compileOptions: String = "",
|
||||
baseCompileOptions: String = s"-cp $toolboxClasspath",
|
||||
)(code: String) = {
|
||||
compileOptions: String = ""
|
||||
)(code: String)(implicit td: TestData): scalatest.Assertion = {
|
||||
val errorMessage = intercept[ToolBoxError] {
|
||||
eval(code, s"$compileOptions $baseCompileOptions")
|
||||
eval(code, s"$compileOptions -cp ${toolboxClasspath(td)}")
|
||||
println(s"Test failed -- compilation was successful! Expected:\n$errorSnippet")
|
||||
}.getMessage
|
||||
val userMessage =
|
||||
|
|
@ -40,7 +39,7 @@ class IllegalReferenceSpec extends FunSuite {
|
|||
""".stripMargin
|
||||
assert(errorMessage.contains(errorSnippet), userMessage)
|
||||
}
|
||||
private class CachingToolbox {
|
||||
private class CachingToolbox(implicit td: TestData) {
|
||||
private[this] val m = scala.reflect.runtime.currentMirror
|
||||
private[this] var _infos: List[FrontEnd#Info] = Nil
|
||||
private[this] val frontEnd = new FrontEnd {
|
||||
|
|
@ -49,12 +48,12 @@ class IllegalReferenceSpec extends FunSuite {
|
|||
}
|
||||
|
||||
import scala.tools.reflect.ToolBox
|
||||
val toolbox = m.mkToolBox(frontEnd, options = s"-cp $toolboxClasspath")
|
||||
val toolbox = m.mkToolBox(frontEnd, options = s"-cp ${toolboxClasspath(td)}")
|
||||
def eval(code: String): Any = toolbox.eval(toolbox.parse(code))
|
||||
def infos: List[FrontEnd#Info] = _infos
|
||||
}
|
||||
|
||||
test("Def.sequential should be legal within Def.taskDyn") {
|
||||
test("Def.sequential should be legal within Def.taskDyn") { implicit td =>
|
||||
val toolbox = new CachingToolbox
|
||||
// This example was taken from @dos65 in https://github.com/sbt/sbt/issues/3110
|
||||
val build =
|
||||
|
|
@ -74,7 +73,7 @@ class IllegalReferenceSpec extends FunSuite {
|
|||
assert(toolbox.eval(build) == Some("baseDirectory"))
|
||||
assert(toolbox.infos.isEmpty)
|
||||
}
|
||||
test("Local task defs should be illegal within Def.task") {
|
||||
test("Local task defs should be illegal within Def.task") { implicit td =>
|
||||
val build =
|
||||
s"""
|
||||
|import sbt._
|
||||
|
|
|
|||
|
|
@ -7,23 +7,33 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import java.io.File.pathSeparator
|
||||
|
||||
import sbt.internal.scriptedtest.ScriptedLauncher
|
||||
import sbt.util.LogExchange
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import buildinfo.TestBuildInfo
|
||||
import xsbti._
|
||||
import scala.sys.process.Process
|
||||
|
||||
object RunFromSourceMain {
|
||||
private val sbtVersion = TestBuildInfo.version
|
||||
private val scalaVersion = "2.12.6"
|
||||
|
||||
def fork(workingDirectory: File): Process = {
|
||||
def fork(
|
||||
workingDirectory: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File]
|
||||
): Process = {
|
||||
val fo = ForkOptions()
|
||||
.withOutputStrategy(OutputStrategy.StdoutOutput)
|
||||
fork(fo, workingDirectory)
|
||||
fork(fo, workingDirectory, scalaVersion, sbtVersion, classpath)
|
||||
}
|
||||
|
||||
def fork(fo0: ForkOptions, workingDirectory: File): Process = {
|
||||
def fork(
|
||||
fo0: ForkOptions,
|
||||
workingDirectory: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
cp: Seq[File]
|
||||
): Process = {
|
||||
val fo = fo0
|
||||
.withWorkingDirectory(workingDirectory)
|
||||
.withRunJVMOptions(sys.props.get("sbt.ivy.home") match {
|
||||
|
|
@ -31,54 +41,84 @@ object RunFromSourceMain {
|
|||
case _ => Vector()
|
||||
})
|
||||
implicit val runner = new ForkRun(fo)
|
||||
val cp = {
|
||||
TestBuildInfo.test_classDirectory +: TestBuildInfo.fullClasspath
|
||||
}
|
||||
val options = Vector(workingDirectory.toString)
|
||||
val options =
|
||||
Vector(workingDirectory.toString, scalaVersion, sbtVersion, cp.mkString(pathSeparator))
|
||||
val log = LogExchange.logger("RunFromSourceMain.fork", None, None)
|
||||
runner.fork("sbt.RunFromSourceMain", cp, options, log)
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = args match {
|
||||
case Array() => sys.error(s"Must specify working directory as the first argument")
|
||||
case Array(wd, args @ _*) => run(file(wd), args)
|
||||
case Array() =>
|
||||
sys.error(
|
||||
s"Must specify working directory, scala version and sbt version and classpath as the first three arguments"
|
||||
)
|
||||
case Array(wd, scalaVersion, sbtVersion, classpath, args @ _*) =>
|
||||
System.setProperty("jna.nosys", "true")
|
||||
run(file(wd), scalaVersion, sbtVersion, classpath, args)
|
||||
}
|
||||
|
||||
// this arrangement is because Scala does not always properly optimize away
|
||||
// the tail recursion in a catch statement
|
||||
@tailrec private[sbt] def run(baseDir: File, args: Seq[String]): Unit =
|
||||
runImpl(baseDir, args) match {
|
||||
case Some((baseDir, args)) => run(baseDir, args)
|
||||
@tailrec private[sbt] def run(
|
||||
baseDir: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: String,
|
||||
args: Seq[String],
|
||||
): Unit =
|
||||
runImpl(baseDir, scalaVersion, sbtVersion, classpath, args) match {
|
||||
case Some((baseDir, args)) => run(baseDir, scalaVersion, sbtVersion, classpath, args)
|
||||
case None => ()
|
||||
}
|
||||
|
||||
private def runImpl(baseDir: File, args: Seq[String]): Option[(File, Seq[String])] =
|
||||
try launch(getConf(baseDir, args)) map exit
|
||||
private def runImpl(
|
||||
baseDir: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: String,
|
||||
args: Seq[String],
|
||||
): Option[(File, Seq[String])] = {
|
||||
try launch(defaultBootDirectory, baseDir, scalaVersion, sbtVersion, classpath, args) map exit
|
||||
catch {
|
||||
case r: xsbti.FullReload => Some((baseDir, r.arguments()))
|
||||
case scala.util.control.NonFatal(e) => e.printStackTrace(); errorAndExit(e.toString)
|
||||
}
|
||||
|
||||
@tailrec private def launch(conf: AppConfiguration): Option[Int] =
|
||||
xMain.run(conf) match {
|
||||
case e: xsbti.Exit => Some(e.code)
|
||||
case _: xsbti.Continue => None
|
||||
case r: xsbti.Reboot => launch(getConf(conf.baseDirectory(), r.arguments()))
|
||||
case x => handleUnknownMainResult(x)
|
||||
}
|
||||
|
||||
private val noGlobalLock = new GlobalLock {
|
||||
def apply[T](lockFile: File, run: java.util.concurrent.Callable[T]) = run.call()
|
||||
}
|
||||
|
||||
private lazy val bootDirectory: File = file(sys.props("user.home")) / ".sbt" / "boot"
|
||||
private lazy val scalaHome: File = {
|
||||
private def launch(
|
||||
bootDirectory: File,
|
||||
baseDirectory: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: String,
|
||||
arguments: Seq[String],
|
||||
): Option[Int] = {
|
||||
ScriptedLauncher
|
||||
.launch(
|
||||
scalaHome(bootDirectory, scalaVersion),
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
bootDirectory,
|
||||
baseDirectory,
|
||||
classpath.split(java.io.File.pathSeparator).map(file),
|
||||
arguments.toArray
|
||||
)
|
||||
.orElse(null) match {
|
||||
case null => None
|
||||
case i if i == Int.MaxValue => None
|
||||
case i => Some(i)
|
||||
}
|
||||
}
|
||||
|
||||
private lazy val defaultBootDirectory: File =
|
||||
file(sys.props("user.home")) / ".sbt" / "scripted" / "boot"
|
||||
private def scalaHome(bootDirectory: File, scalaVersion: String): File = {
|
||||
val log = sbt.util.LogExchange.logger("run-from-source")
|
||||
val scalaHome0 = bootDirectory / s"scala-$scalaVersion"
|
||||
if ((scalaHome0 / "lib").exists) scalaHome0
|
||||
else {
|
||||
log.info(s"""scalaHome ($scalaHome0) wasn't found""")
|
||||
val fakeboot = file(sys.props("user.home")) / ".sbt" / "fakeboot"
|
||||
val fakeboot = bootDirectory / "fakeboot"
|
||||
val scalaHome1 = fakeboot / s"scala-$scalaVersion"
|
||||
val scalaHome1Lib = scalaHome1 / "lib"
|
||||
val scalaHome1Temp = scalaHome1 / "temp"
|
||||
|
|
@ -106,99 +146,6 @@ object RunFromSourceMain {
|
|||
}
|
||||
}
|
||||
|
||||
private def getConf(baseDir: File, args: Seq[String]): AppConfiguration = new AppConfiguration {
|
||||
def baseDirectory = baseDir
|
||||
def arguments = args.toArray
|
||||
|
||||
def provider = new AppProvider { appProvider =>
|
||||
def scalaProvider = new ScalaProvider { scalaProvider =>
|
||||
def scalaOrg = "org.scala-lang"
|
||||
def launcher = new Launcher {
|
||||
def getScala(version: String) = getScala(version, "")
|
||||
def getScala(version: String, reason: String) = getScala(version, reason, scalaOrg)
|
||||
def getScala(version: String, reason: String, scalaOrg: String) = scalaProvider
|
||||
def app(id: xsbti.ApplicationID, version: String) = appProvider
|
||||
def topLoader = new java.net.URLClassLoader(Array(), null)
|
||||
def globalLock = noGlobalLock
|
||||
def bootDirectory = RunFromSourceMain.bootDirectory
|
||||
def ivyHome: File = sys.props.get("sbt.ivy.home") match {
|
||||
case Some(home) => file(home)
|
||||
case _ => file(sys.props("user.home")) / ".ivy2"
|
||||
}
|
||||
case class PredefRepo(id: Predefined) extends PredefinedRepository
|
||||
import Predefined._
|
||||
def ivyRepositories = Array(PredefRepo(Local), PredefRepo(MavenCentral))
|
||||
def appRepositories = Array(PredefRepo(Local), PredefRepo(MavenCentral))
|
||||
def isOverrideRepositories = false
|
||||
def checksums = Array("sha1", "md5")
|
||||
}
|
||||
def version = scalaVersion
|
||||
lazy val libDir: File = RunFromSourceMain.scalaHome / "lib"
|
||||
def jar(name: String): File = libDir / s"$name.jar"
|
||||
lazy val libraryJar = jar("scala-library")
|
||||
lazy val compilerJar = jar("scala-compiler")
|
||||
lazy val jars = {
|
||||
assert(libDir.exists)
|
||||
libDir.listFiles(f => !f.isDirectory && f.getName.endsWith(".jar"))
|
||||
}
|
||||
def loader = new java.net.URLClassLoader(jars map (_.toURI.toURL), null)
|
||||
def app(id: xsbti.ApplicationID) = appProvider
|
||||
}
|
||||
|
||||
def id = ApplicationID(
|
||||
"org.scala-sbt",
|
||||
"sbt",
|
||||
sbtVersion,
|
||||
"sbt.xMain",
|
||||
Seq("xsbti", "extra"),
|
||||
CrossValue.Disabled,
|
||||
Nil
|
||||
)
|
||||
def appHome: File = scalaHome / id.groupID / id.name / id.version
|
||||
|
||||
def mainClasspath = buildinfo.TestBuildInfo.fullClasspath.toArray
|
||||
def loader = new java.net.URLClassLoader(mainClasspath map (_.toURI.toURL), null)
|
||||
def entryPoint = classOf[xMain]
|
||||
def mainClass = classOf[xMain]
|
||||
def newMain = new xMain
|
||||
|
||||
def components = new ComponentProvider {
|
||||
def componentLocation(id: String) = appHome / id
|
||||
def component(id: String) = IO.listFiles(componentLocation(id), _.isFile)
|
||||
|
||||
def defineComponent(id: String, files: Array[File]) = {
|
||||
val location = componentLocation(id)
|
||||
if (location.exists)
|
||||
sys error s"Cannot redefine component. ID: $id, files: ${files mkString ","}"
|
||||
else {
|
||||
copy(files.toList, location)
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
def addToComponent(id: String, files: Array[File]) =
|
||||
copy(files.toList, componentLocation(id))
|
||||
|
||||
def lockFile = appHome / "sbt.components.lock"
|
||||
|
||||
private def copy(files: List[File], toDirectory: File): Boolean =
|
||||
files exists (copy(_, toDirectory))
|
||||
|
||||
private def copy(file: File, toDirectory: File): Boolean = {
|
||||
val to = toDirectory / file.getName
|
||||
val missing = !to.exists
|
||||
IO.copyFile(file, to)
|
||||
missing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleUnknownMainResult(x: MainResult): Nothing = {
|
||||
val clazz = if (x eq null) "" else " (class: " + x.getClass + ")"
|
||||
errorAndExit("Invalid main result: " + x + clazz)
|
||||
}
|
||||
|
||||
private def errorAndExit(msg: String): Nothing = { System.err.println(msg); exit(1) }
|
||||
private def exit(code: Int): Nothing = System.exit(code).asInstanceOf[Nothing]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt.internal.scriptedtest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.FileVisitor;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import xsbti.AppConfiguration;
|
||||
import xsbti.AppMain;
|
||||
import xsbti.AppProvider;
|
||||
import xsbti.ApplicationID;
|
||||
import xsbti.ComponentProvider;
|
||||
import xsbti.CrossValue;
|
||||
import xsbti.GlobalLock;
|
||||
import xsbti.Launcher;
|
||||
import xsbti.Predefined;
|
||||
import xsbti.PredefinedRepository;
|
||||
import xsbti.Repository;
|
||||
import xsbti.ScalaProvider;
|
||||
|
||||
public class ScriptedLauncher {
|
||||
private static URL URLForClass(final Class<?> clazz)
|
||||
throws MalformedURLException, ClassNotFoundException {
|
||||
final String path = clazz.getCanonicalName().replace('.', '/') + ".class";
|
||||
final URL url = clazz.getClassLoader().getResource(path);
|
||||
if (url == null) throw new ClassNotFoundException(clazz.getCanonicalName());
|
||||
return new URL(url.toString().replaceAll(path + "$", ""));
|
||||
}
|
||||
|
||||
public static Optional<Integer> launch(
|
||||
final File scalaHome,
|
||||
final String sbtVersion,
|
||||
final String scalaVersion,
|
||||
final File bootDirectory,
|
||||
final File baseDir,
|
||||
final File[] classpath,
|
||||
String[] arguments)
|
||||
throws InvocationTargetException, ClassNotFoundException, NoSuchMethodException,
|
||||
IllegalAccessException, IOException {
|
||||
String[] args = arguments;
|
||||
Object appID = null;
|
||||
if (System.getProperty("sbt.launch.jar") == null) {
|
||||
final ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
||||
final URL configURL = URLForClass(xsbti.AppConfiguration.class);
|
||||
final URL mainURL = URLForClass(sbt.xMain.class);
|
||||
final URL scriptedURL = URLForClass(ScriptedLauncher.class);
|
||||
final ClassLoader topLoader = new URLClassLoader(new URL[] {configURL}, top());
|
||||
final URLClassLoader loader = new URLClassLoader(new URL[] {mainURL, scriptedURL}, topLoader);
|
||||
try {
|
||||
while (true) {
|
||||
final Class<?> clazz = loader.loadClass("sbt.internal.scriptedtest.ScriptedLauncher");
|
||||
final Class<?> reboot = loader.loadClass("xsbti.Reboot");
|
||||
final Class<?> exit = loader.loadClass("xsbti.Exit");
|
||||
|
||||
Method method =
|
||||
clazz.getDeclaredMethod(
|
||||
"getConf",
|
||||
ClassLoader.class,
|
||||
File.class,
|
||||
String.class,
|
||||
String.class,
|
||||
File.class,
|
||||
File.class,
|
||||
File[].class,
|
||||
String[].class,
|
||||
loader.loadClass("xsbti.ApplicationID"));
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
try {
|
||||
final Object conf =
|
||||
method.invoke(
|
||||
null,
|
||||
topLoader,
|
||||
scalaHome,
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
bootDirectory,
|
||||
baseDir,
|
||||
classpath,
|
||||
args,
|
||||
appID);
|
||||
final Object launchResult =
|
||||
clazz
|
||||
.getDeclaredMethod(
|
||||
"launchImpl", ClassLoader.class, ClassLoader.class, Object.class)
|
||||
.invoke(null, topLoader, loader, conf);
|
||||
if (reboot.isAssignableFrom(launchResult.getClass())) {
|
||||
final Object a = reboot.getDeclaredMethod("arguments").invoke(launchResult);
|
||||
final int length = Array.getLength(a);
|
||||
args = new String[length];
|
||||
for (int j = 0; j < length; ++j) {
|
||||
args[j] = (String) Array.get(a, j);
|
||||
}
|
||||
appID = reboot.getDeclaredMethod("app").invoke(launchResult);
|
||||
} else if (exit.isAssignableFrom(launchResult.getClass())) {
|
||||
return Optional.of((Integer) exit.getDeclaredMethod("code").invoke(launchResult));
|
||||
}
|
||||
} catch (final InvocationTargetException e) {
|
||||
Throwable t = e.getCause();
|
||||
while (t != null && !t.getClass().getCanonicalName().equals("xsbti.FullReload"))
|
||||
t = t.getCause();
|
||||
final RuntimeException reload = t == null ? null : (RuntimeException) t;
|
||||
if (reload != null) {
|
||||
final boolean clean =
|
||||
(boolean) reload.getClass().getDeclaredMethod("clean").invoke(reload);
|
||||
if (clean) deleteRecursive(bootDirectory);
|
||||
final Object reloadArgs =
|
||||
reload.getClass().getDeclaredMethod("arguments").invoke(reload);
|
||||
throw new xsbti.FullReload((String[]) reloadArgs, true);
|
||||
}
|
||||
if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause();
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
swap(loader, previous);
|
||||
}
|
||||
} else {
|
||||
final URL url = new URL("file:" + System.getProperty("sbt.launch.jar"));
|
||||
final URLClassLoader loader = new URLClassLoader(new URL[] {url}, top());
|
||||
final Class<?> boot = loader.loadClass("xsbt.boot.Boot");
|
||||
// If we don't initialize the arguments this way, then the call to invoke on
|
||||
// xsbt.boot.Boot.main fails with an IllegalArgumentException
|
||||
final Object newArgs = Array.newInstance(loader.loadClass("java.lang.String"), args.length);
|
||||
for (int i = 0; i < args.length; ++i) ((String[]) newArgs)[i] = args[i];
|
||||
final ClassLoader previous = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
boot.getDeclaredMethod("main", newArgs.getClass()).invoke(null, newArgs);
|
||||
return Optional.empty();
|
||||
} finally {
|
||||
swap(loader, previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassLoader top() {
|
||||
ClassLoader result = ClassLoader.getSystemClassLoader();
|
||||
while (result.getParent() != null) result = result.getParent();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void swap(final URLClassLoader old, final ClassLoader stashed) {
|
||||
try {
|
||||
old.close();
|
||||
} catch (final Exception e) {
|
||||
}
|
||||
Thread.currentThread().setContextClassLoader(stashed);
|
||||
}
|
||||
|
||||
private static boolean copy(final File[] files, final File toDirectory) throws IOException {
|
||||
boolean result = true;
|
||||
for (final File file : files) {
|
||||
try {
|
||||
Files.createDirectories(toDirectory.toPath());
|
||||
Files.copy(file.toPath(), toDirectory.toPath().resolve(file.getName()));
|
||||
} catch (final FileAlreadyExistsException e) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static Object launchImpl(
|
||||
final ClassLoader topLoader, final ClassLoader loader, final Object conf)
|
||||
throws ClassNotFoundException, InvocationTargetException, IllegalAccessException,
|
||||
NoSuchMethodException, InstantiationException {
|
||||
final Class<?> clazz = loader.loadClass("sbt.xMain");
|
||||
final Object instance = clazz.getConstructor().newInstance();
|
||||
final Method run = clazz.getDeclaredMethod("run", loader.loadClass("xsbti.AppConfiguration"));
|
||||
return run.invoke(instance, conf);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static AppConfiguration getConf(
|
||||
final ClassLoader topLoader,
|
||||
final File scalaHome,
|
||||
final String sbtVersion,
|
||||
final String scalaVersion,
|
||||
final File bootDirectory,
|
||||
final File baseDir,
|
||||
final File[] classpath,
|
||||
String[] args,
|
||||
final ApplicationID appID) {
|
||||
|
||||
final File libDir = new File(scalaHome, "lib");
|
||||
final AtomicReference<File[]> classpathExtra = new AtomicReference<>(new File[0]);
|
||||
final ApplicationID id =
|
||||
appID != null
|
||||
? appID
|
||||
: new ApplicationID() {
|
||||
@Override
|
||||
public String groupID() {
|
||||
return "org.scala-sbt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "sbt";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return sbtVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mainClass() {
|
||||
return "sbt.xMain";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] mainComponents() {
|
||||
return new String[] {"xsbti", "extra"};
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean crossVersioned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrossValue crossVersionedValue() {
|
||||
return CrossValue.Disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] classpathExtra() {
|
||||
return classpathExtra.get();
|
||||
}
|
||||
};
|
||||
final File appHome =
|
||||
scalaHome.toPath().resolve(id.groupID()).resolve(id.name()).resolve(id.version()).toFile();
|
||||
final ComponentProvider provider =
|
||||
new ComponentProvider() {
|
||||
@Override
|
||||
public File componentLocation(String id) {
|
||||
return new File(appHome, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] component(String componentID) {
|
||||
final File dir = componentLocation(componentID);
|
||||
final File[] files = dir.listFiles(File::isFile);
|
||||
return files == null ? new File[0] : files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defineComponent(String componentID, File[] components) {
|
||||
final File dir = componentLocation(componentID);
|
||||
if (dir.exists()) {
|
||||
final StringBuilder files = new StringBuilder();
|
||||
for (final File file : components) {
|
||||
if (files.length() > 0) {
|
||||
files.append(',');
|
||||
}
|
||||
files.append(file.toString());
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"Cannot redefine component. ID: " + id + ", files: " + files);
|
||||
} else {
|
||||
try {
|
||||
copy(components, dir);
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addToComponent(String componentID, File[] components) {
|
||||
try {
|
||||
boolean result = copy(components, componentLocation(componentID));
|
||||
final File[] extra = componentLocation(componentID).listFiles();
|
||||
classpathExtra.set(extra == null ? new File[0] : extra);
|
||||
return result;
|
||||
} catch (final IOException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File lockFile() {
|
||||
return new File(appHome, "sbt.components.lock");
|
||||
}
|
||||
};
|
||||
assert (libDir.exists());
|
||||
final File[] jars = libDir.listFiles(f -> f.isFile() && f.getName().endsWith(".jar"));
|
||||
final URL[] urls = new URL[jars.length];
|
||||
for (int i = 0; i < jars.length; ++i) {
|
||||
try {
|
||||
urls[i] = jars[i].toURI().toURL();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return new AppConfiguration() {
|
||||
@Override
|
||||
public String[] arguments() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File baseDirectory() {
|
||||
return baseDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppProvider provider() {
|
||||
return new AppProvider() {
|
||||
final AppProvider self = this;
|
||||
final ScalaProvider scalaProvider =
|
||||
new ScalaProvider() {
|
||||
private final ScalaProvider sp = this;
|
||||
private final String scalaOrg = "org.scala-lang";
|
||||
private final Repository[] repos =
|
||||
new PredefinedRepository[] {
|
||||
() -> Predefined.Local, () -> Predefined.MavenCentral
|
||||
};
|
||||
private final Launcher launcher =
|
||||
new Launcher() {
|
||||
@Override
|
||||
public ScalaProvider getScala(String version) {
|
||||
return getScala(version, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalaProvider getScala(String version, String reason) {
|
||||
return getScala(version, reason, scalaOrg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalaProvider getScala(
|
||||
String version, String reason, String scalaOrg) {
|
||||
return sp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppProvider app(ApplicationID id, String version) {
|
||||
return self;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader topLoader() {
|
||||
return topLoader;
|
||||
}
|
||||
|
||||
class foo extends Throwable {
|
||||
foo(final Exception e) {
|
||||
super(e.getMessage(), null, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GlobalLock globalLock() {
|
||||
return new GlobalLock() {
|
||||
@Override
|
||||
public <T> T apply(File lockFile, Callable<T> run) {
|
||||
try {
|
||||
return run.call();
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(new foo(e)) {
|
||||
@Override
|
||||
public StackTraceElement[] getStackTrace() {
|
||||
return new StackTraceElement[0];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public File bootDirectory() {
|
||||
return bootDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository[] ivyRepositories() {
|
||||
return repos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repository[] appRepositories() {
|
||||
return repos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverrideRepositories() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File ivyHome() {
|
||||
final String home = System.getProperty("sbt.ivy.home");
|
||||
return home == null
|
||||
? new File(System.getProperty("user.home"), ".ivy2")
|
||||
: new File(home);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] checksums() {
|
||||
return new String[] {"sha1", "md5"};
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public Launcher launcher() {
|
||||
return launcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return scalaVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader loader() {
|
||||
return new URLClassLoader(urls, topLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] jars() {
|
||||
return jars;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public File libraryJar() {
|
||||
return new File(libDir, "scala-library.jar");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public File compilerJar() {
|
||||
return new File(libDir, "scala-compiler.jar");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppProvider app(ApplicationID id) {
|
||||
return self;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public ScalaProvider scalaProvider() {
|
||||
return scalaProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationID id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader loader() {
|
||||
return new URLClassLoader(urls, topLoader);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public Class<? extends AppMain> mainClass() {
|
||||
return AppMain.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> entryPoint() {
|
||||
return AppMain.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppMain newMain() {
|
||||
try {
|
||||
return (AppMain) loader().loadClass("sbt.xMain").getConstructor().newInstance();
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File[] mainClasspath() {
|
||||
return classpath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentProvider components() {
|
||||
return provider;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void deleteRecursive(final File directory) {
|
||||
try {
|
||||
Files.walkFileTree(
|
||||
directory.toPath(),
|
||||
new FileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
if (attrs.isRegularFile()) Files.deleteIfExists(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed(Path file, IOException exc) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
|
||||
throws IOException {
|
||||
Files.deleteIfExists(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
} catch (final IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -159,8 +159,20 @@ object TestServer {
|
|||
def withTestServer(testBuild: String, baseDirectory: File)(
|
||||
f: TestServer => Future[Assertion]
|
||||
)(implicit td: TestData): Future[Assertion] = {
|
||||
val classpath = td.configMap.get("sbt.server.classpath") match {
|
||||
case Some(s: String) => s.split(java.io.File.pathSeparator).map(file)
|
||||
case _ => throw new IllegalStateException("No server classpath was specified.")
|
||||
}
|
||||
val sbtVersion = td.configMap.get("sbt.server.version") match {
|
||||
case Some(v: String) => v
|
||||
case _ => throw new IllegalStateException("No server version was specified.")
|
||||
}
|
||||
val scalaVersion = td.configMap.get("sbt.server.scala.version") match {
|
||||
case Some(v: String) => v
|
||||
case _ => throw new IllegalStateException("No server scala version was specified.")
|
||||
}
|
||||
// Each test server instance will be executed in a Thread pool separated from the tests
|
||||
val testServer = TestServer(baseDirectory)
|
||||
val testServer = TestServer(baseDirectory, scalaVersion, sbtVersion, classpath)
|
||||
// checking last log message after initialization
|
||||
// if something goes wrong here the communication streams are corrupted, restarting
|
||||
val init =
|
||||
|
|
@ -194,7 +206,12 @@ object TestServer {
|
|||
}
|
||||
}
|
||||
|
||||
case class TestServer(baseDirectory: File) {
|
||||
case class TestServer(
|
||||
baseDirectory: File,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File]
|
||||
) {
|
||||
import TestServer.hostLog
|
||||
|
||||
val readBuffer = new Array[Byte](40960)
|
||||
|
|
@ -204,7 +221,7 @@ case class TestServer(baseDirectory: File) {
|
|||
private val RetByte = '\r'.toByte
|
||||
|
||||
hostLog("fork to a new sbt instance")
|
||||
val process = RunFromSourceMain.fork(baseDirectory)
|
||||
val process = RunFromSourceMain.fork(baseDirectory, scalaVersion, sbtVersion, classpath)
|
||||
|
||||
lazy val portfile = baseDirectory / "project" / "target" / "active.json"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,55 +8,28 @@
|
|||
package sbt
|
||||
package scriptedtest
|
||||
|
||||
import java.io.File
|
||||
import xsbt.IPC
|
||||
|
||||
import scala.sys.process.{ BasicIO, Process }
|
||||
|
||||
import sbt.io.IO
|
||||
import sbt.util.Logger
|
||||
|
||||
import xsbt.IPC
|
||||
|
||||
private[sbt] sealed trait RemoteSbtCreatorKind
|
||||
private[sbt] object RemoteSbtCreatorKind {
|
||||
case object LauncherBased extends RemoteSbtCreatorKind
|
||||
case object RunFromSourceBased extends RemoteSbtCreatorKind
|
||||
}
|
||||
|
||||
abstract class RemoteSbtCreator private[sbt] {
|
||||
def newRemote(server: IPC.Server): Process
|
||||
}
|
||||
|
||||
final class LauncherBasedRemoteSbtCreator(
|
||||
directory: File,
|
||||
launcher: File,
|
||||
log: Logger,
|
||||
launchOpts: Seq[String] = Nil,
|
||||
) extends RemoteSbtCreator {
|
||||
def newRemote(server: IPC.Server) = {
|
||||
val launcherJar = launcher.getAbsolutePath
|
||||
val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath
|
||||
val args = List("<" + server.port)
|
||||
val cmd = "java" :: launchOpts.toList ::: globalBase :: "-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() } }
|
||||
thread.start()
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
final class RunFromSourceBasedRemoteSbtCreator(
|
||||
directory: File,
|
||||
log: Logger,
|
||||
launchOpts: Seq[String] = Nil,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File],
|
||||
) extends RemoteSbtCreator {
|
||||
def newRemote(server: IPC.Server) = {
|
||||
val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath
|
||||
val cp = IO readLinesURL (getClass getResource "/RunFromSource.classpath")
|
||||
val cpString = cp mkString File.pathSeparator
|
||||
def newRemote(server: IPC.Server): Process = {
|
||||
val globalBase = "-Dsbt.global.base=" + new File(directory, "global").getAbsolutePath
|
||||
val mainClassName = "sbt.RunFromSourceMain"
|
||||
val args = List(mainClassName, directory.toString, "<" + server.port)
|
||||
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 :: "-cp" :: cpString :: args ::: Nil
|
||||
val io = BasicIO(false, log).withInput(_.close())
|
||||
val p = Process(cmd, directory) run (io)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
package sbt
|
||||
package scriptedtest
|
||||
|
||||
import java.io.{ File, FileNotFoundException, IOException }
|
||||
import java.io.{ FileNotFoundException, IOException }
|
||||
import java.net.SocketException
|
||||
import java.nio.file.Files
|
||||
import java.util.Properties
|
||||
|
|
@ -16,44 +16,33 @@ import java.util.concurrent.ForkJoinPool
|
|||
|
||||
import sbt.internal.io.Resources
|
||||
import sbt.internal.scripted._
|
||||
import sbt.internal.util.{ BufferedLogger, ConsoleOut, FullLogger, Util }
|
||||
import sbt.io.FileFilter._
|
||||
import sbt.io.syntax._
|
||||
import sbt.io.{ DirectoryFilter, HiddenFileFilter, IO }
|
||||
import sbt.nio.file._
|
||||
import sbt.nio.file.syntax._
|
||||
import sbt.util.{ AbstractLogger, Level, Logger }
|
||||
|
||||
import scala.collection.{ GenSeq, mutable }
|
||||
import scala.collection.parallel.ForkJoinTaskSupport
|
||||
import scala.collection.{ GenSeq, mutable }
|
||||
import scala.util.Try
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
final class ScriptedTests(
|
||||
resourceBaseDirectory: File,
|
||||
bufferLog: Boolean,
|
||||
launcher: File,
|
||||
launchOpts: Seq[String],
|
||||
) {
|
||||
import ScriptedTests.{ TestRunner, emptyCallback }
|
||||
import ScriptedTests.TestRunner
|
||||
|
||||
private val testResources = new Resources(resourceBaseDirectory)
|
||||
|
||||
val ScriptFilename = "test"
|
||||
val PendingScriptFilename = "pending"
|
||||
|
||||
def scriptedTest(group: String, name: String, log: xsbti.Logger): Seq[TestRunner] =
|
||||
scriptedTest(group, name, Logger.xlog2Log(log))
|
||||
|
||||
def scriptedTest(group: String, name: String, log: Logger): Seq[TestRunner] =
|
||||
singleScriptedTest(group, name, emptyCallback, log)
|
||||
|
||||
/** Returns a sequence of test runners that have to be applied in the call site. */
|
||||
def singleScriptedTest(
|
||||
group: String,
|
||||
name: String,
|
||||
prescripted: File => Unit,
|
||||
log: Logger,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File]
|
||||
): Seq[TestRunner] = {
|
||||
|
||||
// Test group and names may be file filters (like '*')
|
||||
|
|
@ -71,7 +60,7 @@ final class ScriptedTests(
|
|||
val buffer = new BufferedLogger(new FullLogger(log))
|
||||
val singleTestRunner = () => {
|
||||
val handlers =
|
||||
createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased)
|
||||
createScriptedHandlers(testDirectory, buffer, scalaVersion, sbtVersion, classpath)
|
||||
val runner = new BatchScriptRunner
|
||||
val states = new mutable.HashMap[StatementHandler, StatementHandler#State]()
|
||||
try commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer)
|
||||
|
|
@ -87,15 +76,20 @@ final class ScriptedTests(
|
|||
private def createScriptedHandlers(
|
||||
testDir: File,
|
||||
buffered: Logger,
|
||||
remoteSbtCreatorKind: RemoteSbtCreatorKind,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File]
|
||||
): Map[Char, StatementHandler] = {
|
||||
val fileHandler = new FileCommands(testDir)
|
||||
val remoteSbtCreator = remoteSbtCreatorKind match {
|
||||
case RemoteSbtCreatorKind.LauncherBased =>
|
||||
new LauncherBasedRemoteSbtCreator(testDir, launcher, buffered, launchOpts)
|
||||
case RemoteSbtCreatorKind.RunFromSourceBased =>
|
||||
new RunFromSourceBasedRemoteSbtCreator(testDir, buffered, launchOpts)
|
||||
}
|
||||
val remoteSbtCreator =
|
||||
new RunFromSourceBasedRemoteSbtCreator(
|
||||
testDir,
|
||||
buffered,
|
||||
launchOpts,
|
||||
scalaVersion,
|
||||
sbtVersion,
|
||||
classpath
|
||||
)
|
||||
val sbtHandler = new SbtHandler(remoteSbtCreator)
|
||||
Map('$' -> fileHandler, '>' -> sbtHandler, '#' -> CommentHandler)
|
||||
}
|
||||
|
|
@ -105,7 +99,10 @@ final class ScriptedTests(
|
|||
testGroupAndNames: Seq[(String, String)],
|
||||
prescripted: File => Unit,
|
||||
sbtInstances: Int,
|
||||
log: Logger
|
||||
log: Logger,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File]
|
||||
): Seq[TestRunner] = {
|
||||
// Test group and names may be file filters (like '*')
|
||||
val groupAndNameDirs = {
|
||||
|
|
@ -134,15 +131,7 @@ final class ScriptedTests(
|
|||
case s => s
|
||||
}
|
||||
|
||||
val (launcherBasedTestsUnfiltered, runFromSourceBasedTestsUnfiltered) =
|
||||
labelsAndDirs.partition {
|
||||
case (testName, _) =>
|
||||
determineRemoteSbtCreatorKind(testName) match {
|
||||
case RemoteSbtCreatorKind.LauncherBased => true
|
||||
case RemoteSbtCreatorKind.RunFromSourceBased => false
|
||||
}
|
||||
}
|
||||
val launcherBasedTests = launcherBasedTestsUnfiltered.filterNot(windowsExclude)
|
||||
val runFromSourceBasedTestsUnfiltered = labelsAndDirs
|
||||
val runFromSourceBasedTests = runFromSourceBasedTestsUnfiltered.filterNot(windowsExclude)
|
||||
|
||||
def logTests(size: Int, how: String) =
|
||||
|
|
@ -150,24 +139,19 @@ final class ScriptedTests(
|
|||
f"Running $size / $totalSize (${size * 100d / totalSize}%3.2f%%) scripted tests with $how"
|
||||
)
|
||||
logTests(runFromSourceBasedTests.size, "RunFromSourceMain")
|
||||
logTests(launcherBasedTests.size, "sbt/launcher")
|
||||
|
||||
def createTestRunners(
|
||||
tests: Seq[TestInfo],
|
||||
remoteSbtCreatorKind: RemoteSbtCreatorKind,
|
||||
): Seq[TestRunner] = {
|
||||
def createTestRunners(tests: Seq[TestInfo]): Seq[TestRunner] = {
|
||||
tests
|
||||
.grouped(batchSize)
|
||||
.map { batch => () =>
|
||||
IO.withTemporaryDirectory {
|
||||
runBatchedTests(batch, _, prescripted, remoteSbtCreatorKind, log)
|
||||
runBatchedTests(batch, _, prescripted, log, scalaVersion, sbtVersion, classpath)
|
||||
}
|
||||
}
|
||||
.toList
|
||||
}
|
||||
|
||||
createTestRunners(runFromSourceBasedTests, RemoteSbtCreatorKind.RunFromSourceBased) ++
|
||||
createTestRunners(launcherBasedTests, RemoteSbtCreatorKind.LauncherBased)
|
||||
createTestRunners(runFromSourceBasedTests)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,81 +167,6 @@ final class ScriptedTests(
|
|||
}
|
||||
}
|
||||
else _ => false
|
||||
private def determineRemoteSbtCreatorKind(testName: (String, String)): RemoteSbtCreatorKind = {
|
||||
import RemoteSbtCreatorKind._
|
||||
val (group, name) = testName
|
||||
s"$group/$name" match {
|
||||
case "actions/add-alias" => LauncherBased // sbt/Package$
|
||||
case "actions/cross-incremental" => LauncherBased // tbd
|
||||
case "actions/cross-multiproject" => LauncherBased // tbd
|
||||
case "actions/cross-multi-parser" =>
|
||||
LauncherBased // java.lang.ClassNotFoundException: javax.tools.DiagnosticListener when run with java 11 and an old sbt launcher
|
||||
case "actions/multi-command" =>
|
||||
LauncherBased // java.lang.ClassNotFoundException: javax.tools.DiagnosticListener when run with java 11 and an old sbt launcher
|
||||
case "actions/cross-test-only" => LauncherBased // tbd
|
||||
case "actions/external-doc" => LauncherBased // sbt/Package$
|
||||
case "actions/input-task" => LauncherBased // sbt/Package$
|
||||
case "actions/input-task-dyn" => LauncherBased // sbt/Package$
|
||||
case gn if gn.startsWith("classloader-cache/") =>
|
||||
LauncherBased // This should be tested using launcher
|
||||
case "compiler-project/dotty-compiler-plugin" => LauncherBased // sbt/Package$
|
||||
case "compiler-project/run-test" => LauncherBased // sbt/Package$
|
||||
case "compiler-project/src-dep-plugin" => LauncherBased // sbt/Package$
|
||||
case gn if gn.startsWith("dependency-management/") => LauncherBased // sbt/Package$
|
||||
case gn if gn.startsWith("plugins/") => LauncherBased // sbt/Package$
|
||||
case "java/argfile" => LauncherBased // sbt/Package$
|
||||
case "java/cross" => LauncherBased // sbt/Package$
|
||||
case "java/basic" => LauncherBased // sbt/Package$
|
||||
case "java/varargs-main" => LauncherBased // sbt/Package$
|
||||
case "package/lazy-name" => LauncherBased // sbt/Package$
|
||||
case "package/manifest" => LauncherBased // sbt/Package$
|
||||
case "package/mappings" => LauncherBased // sbt/Package$
|
||||
case "package/resources" => LauncherBased // sbt/Package$
|
||||
case "project/Class.forName" => LauncherBased // sbt/Package$
|
||||
case "project/binary-plugin" => LauncherBased // sbt/Package$
|
||||
case "project/default-settings" => LauncherBased // sbt/Package$
|
||||
case "project/extra" => LauncherBased // tbd
|
||||
case "project/flatten" => LauncherBased // sbt/Package$
|
||||
case "project/generated-root-no-publish" => LauncherBased // tbd
|
||||
case "project/giter8-plugin" => LauncherBased // tbd
|
||||
case "project/lib" => LauncherBased // sbt/Package$
|
||||
case "project/scripted-plugin" => LauncherBased // tbd
|
||||
case "project/scripted-skip-incompatible" => LauncherBased // sbt/Package$
|
||||
case "project/session-update-from-cmd" => LauncherBased // tbd
|
||||
case "project/transitive-plugins" => LauncherBased // tbd
|
||||
case "run/awt" => LauncherBased // sbt/Package$
|
||||
case "run/classpath" => LauncherBased // sbt/Package$
|
||||
case "run/daemon" => LauncherBased // sbt/Package$
|
||||
case "run/daemon-exit" => LauncherBased // sbt/Package$
|
||||
case "run/error" => LauncherBased // sbt/Package$
|
||||
case "run/fork" => LauncherBased // sbt/Package$
|
||||
case "run/fork-loader" => LauncherBased // sbt/Package$
|
||||
case "run/non-local-main" => LauncherBased // sbt/Package$
|
||||
case "run/spawn" => LauncherBased // sbt/Package$
|
||||
case "run/spawn-exit" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/binary" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/export-jars" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/implicit-search" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/java-basic" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/less-inter-inv" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/less-inter-inv-java" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/linearization" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/named" => LauncherBased // sbt/Package$
|
||||
case "source-dependencies/specialized" => LauncherBased // sbt/Package$
|
||||
case gn if gn.startsWith("watch/") && Util.isWindows =>
|
||||
LauncherBased // there is an issue with jansi and coursier
|
||||
case "watch/commands" =>
|
||||
LauncherBased // java.lang.ClassNotFoundException: javax.tools.DiagnosticListener when run with java 11 and an old sbt launcher
|
||||
case "watch/managed" => LauncherBased // sbt/Package$
|
||||
case "tests/scalatest" => LauncherBased
|
||||
case "tests/test-cross" =>
|
||||
LauncherBased // the sbt metabuild classpath leaks into the test interface classloader in older versions of sbt
|
||||
case _ => RunFromSourceBased
|
||||
}
|
||||
// sbt/Package$ means:
|
||||
// java.lang.NoClassDefFoundError: sbt/Package$ (wrong name: sbt/package$)
|
||||
// Typically from Compile / packageBin / packageOptions
|
||||
}
|
||||
|
||||
/** Defines an auto plugin that is injected to sbt between every scripted session.
|
||||
*
|
||||
|
|
@ -314,13 +223,15 @@ final class ScriptedTests(
|
|||
groupedTests: Seq[((String, String), File)],
|
||||
tempTestDir: File,
|
||||
preHook: File => Unit,
|
||||
remoteSbtCreatorKind: RemoteSbtCreatorKind,
|
||||
log: Logger,
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File]
|
||||
): Seq[Option[String]] = {
|
||||
|
||||
val runner = new BatchScriptRunner
|
||||
val buffer = new BufferedLogger(new FullLogger(log))
|
||||
val handlers = createScriptedHandlers(tempTestDir, buffer, remoteSbtCreatorKind)
|
||||
val handlers = createScriptedHandlers(tempTestDir, buffer, scalaVersion, sbtVersion, classpath)
|
||||
val states = new BatchScriptRunner.States
|
||||
val seqHandlers = handlers.values.toList
|
||||
runner.initStates(states, seqHandlers)
|
||||
|
|
@ -362,7 +273,7 @@ final class ScriptedTests(
|
|||
|
||||
// Run the test and delete files (except global that holds local scala jars)
|
||||
val result = runOrHandleDisabled(label, tempTestDir, runTest, buffer)
|
||||
val view = FileTreeView.default
|
||||
val view = sbt.nio.file.FileTreeView.default
|
||||
val base = tempTestDir.getCanonicalFile.toGlob
|
||||
val global = base / "global"
|
||||
val globalLogging = base / ** / "global-logging"
|
||||
|
|
@ -423,7 +334,7 @@ final class ScriptedTests(
|
|||
}
|
||||
}
|
||||
|
||||
val pendingMark = if (pending) PendingLabel else ""
|
||||
val pendingMark: String = if (pending) PendingLabel else ""
|
||||
|
||||
def testFailed(t: Throwable): Option[String] = {
|
||||
if (pending) log.clear() else log.stop()
|
||||
|
|
@ -462,18 +373,28 @@ object ScriptedTests extends ScriptedRunner {
|
|||
/** Represents the function that runs the scripted tests, both in single or batch mode. */
|
||||
type TestRunner = () => Seq[Option[String]]
|
||||
|
||||
val emptyCallback: File => Unit = _ => ()
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val directory = new File(args(0))
|
||||
val buffer = args(1).toBoolean
|
||||
// val sbtVersion = args(2)
|
||||
// val defScalaVersion = args(3)
|
||||
val sbtVersion = args(2)
|
||||
val defScalaVersion = args(3)
|
||||
// val buildScalaVersions = args(4)
|
||||
val bootProperties = new File(args(5))
|
||||
//val bootProperties = new File(args(5))
|
||||
val tests = args.drop(6)
|
||||
val logger = TestConsoleLogger()
|
||||
run(directory, buffer, tests, logger, bootProperties, Array(), emptyCallback)
|
||||
val cp = System.getProperty("java.class.path", "").split(java.io.File.pathSeparator).map(file)
|
||||
runInParallel(
|
||||
directory,
|
||||
buffer,
|
||||
tests,
|
||||
logger,
|
||||
Array(),
|
||||
new java.util.ArrayList[File],
|
||||
defScalaVersion,
|
||||
sbtVersion,
|
||||
cp.toSeq,
|
||||
1
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -482,102 +403,62 @@ object ScriptedTests extends ScriptedRunner {
|
|||
class ScriptedRunner {
|
||||
// This is called by project/Scripted.scala
|
||||
// Using java.util.List[File] to encode File => Unit
|
||||
def run(
|
||||
resourceBaseDirectory: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
bootProperties: File,
|
||||
launchOpts: Array[String],
|
||||
prescripted: java.util.List[File],
|
||||
): Unit = {
|
||||
val logger = new TestConsoleLogger()
|
||||
val addTestFile = (f: File) => { prescripted.add(f); () }
|
||||
run(resourceBaseDirectory, bufferLog, tests, logger, bootProperties, launchOpts, addTestFile)
|
||||
//new FullLogger(Logger.xlog2Log(log)))
|
||||
}
|
||||
|
||||
// This is called by sbt-scripted 0.13.x and 1.x (see https://github.com/sbt/sbt/issues/3245)
|
||||
def run(
|
||||
resourceBaseDirectory: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
bootProperties: File,
|
||||
launchOpts: Array[String],
|
||||
): Unit = {
|
||||
val logger = TestConsoleLogger()
|
||||
val prescripted = ScriptedTests.emptyCallback
|
||||
run(resourceBaseDirectory, bufferLog, tests, logger, bootProperties, launchOpts, prescripted)
|
||||
}
|
||||
|
||||
def run(
|
||||
resourceBaseDirectory: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
logger: AbstractLogger,
|
||||
bootProperties: File,
|
||||
launchOpts: Array[String],
|
||||
prescripted: File => Unit,
|
||||
): Unit = {
|
||||
// Force Log4J to not use a thread context classloader otherwise it throws a CCE
|
||||
sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true"
|
||||
|
||||
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts)
|
||||
val sbtVersion = bootProperties.getName.dropWhile(!_.isDigit).dropRight(".jar".length)
|
||||
val accept = isTestCompatible(resourceBaseDirectory, sbtVersion) _
|
||||
val allTests = get(tests, resourceBaseDirectory, accept, logger) flatMap {
|
||||
case ScriptedTest(group, name) =>
|
||||
runner.singleScriptedTest(group, name, prescripted, logger)
|
||||
}
|
||||
runAll(allTests)
|
||||
}
|
||||
|
||||
def runInParallel(
|
||||
baseDir: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
bootProps: File,
|
||||
launchOpts: Array[String],
|
||||
prescripted: java.util.List[File],
|
||||
): Unit = {
|
||||
runInParallel(baseDir, bufferLog, tests, bootProps, launchOpts, prescripted, 1)
|
||||
}
|
||||
|
||||
// This is used by sbt-scripted sbt 1.x
|
||||
def runInParallel(
|
||||
baseDir: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
bootProps: File,
|
||||
logger: Logger,
|
||||
launchOpts: Array[String],
|
||||
prescripted: java.util.List[File],
|
||||
scalaVersion: String,
|
||||
sbtVersion: String,
|
||||
classpath: Seq[File],
|
||||
instances: Int
|
||||
): Unit = {
|
||||
val logger = TestConsoleLogger()
|
||||
val addTestFile = (f: File) => { prescripted.add(f); () }
|
||||
runInParallel(baseDir, bufferLog, tests, logger, bootProps, launchOpts, addTestFile, instances)
|
||||
}
|
||||
|
||||
def runInParallel(
|
||||
baseDir: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
logger: AbstractLogger,
|
||||
bootProps: File,
|
||||
launchOpts: Array[String],
|
||||
prescripted: File => Unit,
|
||||
instances: Int
|
||||
): Unit = {
|
||||
val runner = new ScriptedTests(baseDir, bufferLog, bootProps, launchOpts)
|
||||
val sbtVersion = bootProps.getName.dropWhile(!_.isDigit).dropRight(".jar".length)
|
||||
val runner = new ScriptedTests(baseDir, bufferLog, launchOpts)
|
||||
val accept = isTestCompatible(baseDir, sbtVersion) _
|
||||
// The scripted tests mapped to the inputs that the user wrote after `scripted`.
|
||||
val scriptedTests =
|
||||
get(tests, baseDir, accept, logger).map(st => (st.group, st.name))
|
||||
val scriptedRunners = runner.batchScriptedRunner(scriptedTests, prescripted, instances, logger)
|
||||
val scriptedRunners =
|
||||
runner.batchScriptedRunner(
|
||||
scriptedTests,
|
||||
addTestFile,
|
||||
instances,
|
||||
logger,
|
||||
scalaVersion,
|
||||
sbtVersion,
|
||||
classpath
|
||||
)
|
||||
val parallelRunners = scriptedRunners.toParArray
|
||||
parallelRunners.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(instances))
|
||||
runAll(parallelRunners)
|
||||
}
|
||||
def runInParallel(
|
||||
baseDir: File,
|
||||
bufferLog: Boolean,
|
||||
tests: Array[String],
|
||||
launchOpts: Array[String],
|
||||
prescripted: java.util.List[File],
|
||||
sbtVersion: String,
|
||||
scalaVersion: String,
|
||||
classpath: Seq[File],
|
||||
instances: Int
|
||||
): Unit =
|
||||
runInParallel(
|
||||
baseDir,
|
||||
bufferLog,
|
||||
tests,
|
||||
TestConsoleLogger(),
|
||||
launchOpts,
|
||||
prescripted,
|
||||
sbtVersion,
|
||||
scalaVersion,
|
||||
classpath,
|
||||
instances
|
||||
)
|
||||
|
||||
private def reportErrors(errors: GenSeq[String]): Unit =
|
||||
if (errors.nonEmpty) sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n")) else ()
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ import org.scalatest._
|
|||
*
|
||||
* This is a very good example on how to instantiate the compiler bridge provider.
|
||||
*/
|
||||
abstract class IvyBridgeProviderSpecification extends FlatSpec with Matchers {
|
||||
abstract class IvyBridgeProviderSpecification
|
||||
extends fixture.FlatSpec
|
||||
with fixture.TestDataFixture
|
||||
with Matchers {
|
||||
def currentBase: File = new File(".")
|
||||
def currentTarget: File = currentBase / "target" / "ivyhome"
|
||||
def currentManaged: File = currentBase / "target" / "lib_managed"
|
||||
|
|
@ -50,13 +53,21 @@ abstract class IvyBridgeProviderSpecification extends FlatSpec with Matchers {
|
|||
ZincComponentCompiler.interfaceProvider(bridge, manager, dependencyResolution, currentManaged)
|
||||
}
|
||||
|
||||
def getCompilerBridge(targetDir: File, log: Logger, scalaVersion: String): File = {
|
||||
def getCompilerBridge(
|
||||
targetDir: File,
|
||||
log: Logger,
|
||||
scalaVersion: String,
|
||||
)(implicit td: TestData): File = {
|
||||
val zincVersion = td.configMap.get("sbt.zinc.version") match {
|
||||
case Some(v: String) => v
|
||||
case _ => throw new IllegalStateException("No zinc version specified")
|
||||
}
|
||||
val bridge0 = ZincComponentCompiler.getDefaultBridgeModule(scalaVersion)
|
||||
// redefine the compiler bridge version
|
||||
// using the version of zinc used during testing
|
||||
// this way when building with zinc as a source dependency
|
||||
// these specs don't go looking for some SHA-suffixed compiler bridge
|
||||
val bridge1 = bridge0.withRevision(ZincLmIntegrationBuildInfo.zincVersion)
|
||||
val bridge1 = bridge0.withRevision(zincVersion)
|
||||
val provider = getZincProvider(bridge1, targetDir, log)
|
||||
val scalaInstance = provider.fetchScalaInstance(scalaVersion, log)
|
||||
val bridge = provider.fetchCompiledBridge(scalaInstance, log)
|
||||
|
|
|
|||
|
|
@ -25,31 +25,31 @@ class ZincComponentCompilerSpec extends IvyBridgeProviderSpecification {
|
|||
|
||||
val logger = ConsoleLogger()
|
||||
|
||||
it should "compile the bridge for Scala 2.10.5 and 2.10.6" in {
|
||||
it should "compile the bridge for Scala 2.10.5 and 2.10.6" in { implicit td =>
|
||||
if (isJava8) {
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2105) should exist)
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2106) should exist)
|
||||
} else ()
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.11.8 and 2.11.11" in {
|
||||
it should "compile the bridge for Scala 2.11.8 and 2.11.11" in { implicit td =>
|
||||
if (isJava8) {
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2118) should exist)
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala21111) should exist)
|
||||
} else ()
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.12.2" in {
|
||||
it should "compile the bridge for Scala 2.12.2" in { implicit td =>
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2121) should exist)
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2122) should exist)
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2123) should exist)
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.13.0-M2" in {
|
||||
it should "compile the bridge for Scala 2.13.0-M2" in { implicit td =>
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2130M2) should exist)
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.13.0-RC1" in {
|
||||
it should "compile the bridge for Scala 2.13.0-RC1" in { implicit td =>
|
||||
IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2130RC1) should exist)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue