Explicitly set scripted and server test classpath

This commit makes it so that the scalaVersion, sbtVersion and classpath
are always passed in as parameters to any method that creates an sbt
server -- either for scripted or for the sbt server tests. By making
that change, I was able to change the implementation of scripted in the
sbt project to use publishLocalBin instead of publishLocal. This makes
the scripted tests start much faster (doc alone can easily take 30
second) with messing with the build to exclude slow tasks from
publishLocal.

As part of this change, I removed the test dependency on scriptedSbtRedux for
sbtProj and instead had scriptedSbtRedux depend on sbtProj. This allowed
me to remove some messy LocalProject logic in the resourceGenerators for
scriptedSbtReduxProj. I also had to remove a number of imports in the
scriptedSbtReduxProj because the definitions available in the sbt
package object became available.

I also removed the dependency on sbt-buildinfo and instead pass the
values from the build into test classes using scalatest properties. I
ran into a number of minor issues with the build info plugin, namely
that I couldn't get fullClasspathAsJars to reliably run as a BuildInfo
key. It also is somewhat more clear to me to just rely on the built in
scalatest functionality. The big drawback is that the scalatest
properties can only be strings, but that restriction isn't really a
problem here (strangely the TestData structure has a field configMap
which is effectively Map[String, Any] but Any is actually always String
given how the TestData is created as part of framework initialization.

Since scripted no longer publishes, scriptedUnpublished is now
effectively an alias for scripted.

To get publishLocalBin working, I had to copy private code from
IvyXml.scala into PublishBinPlugin. Once we publish a new version of
sbt, we can remove the copied code and invoke IvyXml.makeIvyXmlBefore
directly.
This commit is contained in:
Ethan Atkins 2020-01-11 19:52:36 -08:00
parent 1ff5ff45bd
commit ae4d3aecb8
14 changed files with 316 additions and 316 deletions

101
build.sbt
View File

@ -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)
@ -1130,32 +1100,17 @@ 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
publishLocalBinAll.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,
scalaVersion.value,
version.value,
(scriptedSbtReduxProj / Test / fullClasspathAsJars).value.map(_.data),
streams.value.log
)
}
@ -1207,18 +1162,17 @@ ThisBuild / scriptedPrescripted := { _ =>
def otherRootSettings =
Seq(
scripted := scriptedTask.evaluated,
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
scriptedUnpublished := scriptedTask.evaluated,
scriptedSource := (sourceDirectory in sbtProj).value / "sbt-test",
watchTriggers in scripted += scriptedSource.value.toGlob / **,
watchTriggers in scriptedUnpublished += 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(
@ -1233,7 +1187,7 @@ def otherRootSettings =
case _ => Nil
}),
scripted := scriptedTask.evaluated,
scriptedUnpublished := scriptedUnpublishedTask.evaluated,
scriptedUnpublished := scriptedTask.evaluated,
scriptedSource := (sourceDirectory in sbtProj).value / "repo-override-test"
)
)
@ -1263,6 +1217,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 =>

View File

@ -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)

View File

@ -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.")
}
}

View File

@ -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._

View File

@ -13,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. " +
@ -91,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")}")
@ -108,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
@ -117,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
}
@ -148,9 +150,11 @@ object Scripted {
sourcePath,
bufferLog,
args.toArray,
launcher,
launchOpts.toArray,
callback,
scalaVersion,
sbtVersion,
classpath,
instances
)
} catch { case ite: InvocationTargetException => throw ite.getCause }

View File

@ -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")

View File

@ -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._

View File

@ -7,24 +7,32 @@
package sbt
import buildinfo.TestBuildInfo
import sbt.internal.scriptedtest.ScriptedLauncher
import sbt.util.LogExchange
import scala.annotation.tailrec
import scala.sys.process.Process
import java.io.File.pathSeparator
object RunFromSourceMain {
private val sbtVersion = TestBuildInfo.version
private val scalaVersion = "2.12.10"
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 {
@ -32,43 +40,63 @@ 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 @ _*) =>
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(baseDir, args) map exit
private def runImpl(
baseDir: File,
scalaVersion: String,
sbtVersion: String,
classpath: String,
args: Seq[String],
): Option[(File, Seq[String])] =
try launch(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)
}
private def launch(baseDirectory: File, arguments: Seq[String]): Option[Int] = {
private def launch(
baseDirectory: File,
scalaVersion: String,
sbtVersion: String,
classpath: String,
arguments: Seq[String],
): Option[Int] = {
ScriptedLauncher
.launch(
scalaHome,
scalaHome(scalaVersion),
sbtVersion,
scalaVersion,
bootDirectory,
baseDirectory,
buildinfo.TestBuildInfo.fullClasspath.toArray,
classpath.split(java.io.File.pathSeparator).map(file),
arguments.toArray
)
.orElse(null) match {
@ -79,7 +107,7 @@ object RunFromSourceMain {
}
private lazy val bootDirectory: File = file(sys.props("user.home")) / ".sbt" / "boot"
private lazy val scalaHome: File = {
private def scalaHome(scalaVersion: String): File = {
val log = sbt.util.LogExchange.logger("run-from-source")
val scalaHome0 = bootDirectory / s"scala-$scalaVersion"
if ((scalaHome0 / "lib").exists) scalaHome0

View File

@ -53,7 +53,7 @@ public class ScriptedLauncher {
final File[] classpath,
String[] args)
throws MalformedURLException, InvocationTargetException, ClassNotFoundException,
IllegalAccessException {
NoSuchMethodException, IllegalAccessException {
while (true) {
final URL configURL = URLForClass(xsbti.AppConfiguration.class);
final URL mainURL = URLForClass(sbt.xMain.class);
@ -67,10 +67,20 @@ public class ScriptedLauncher {
final AtomicInteger result = new AtomicInteger(-1);
final AtomicReference<String[]> newArguments = new AtomicReference<>();
final Class<?> clazz = loader.loadClass("sbt.internal.scriptedtest.ScriptedLauncher");
Method method = null;
for (final Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals("launchImpl")) method = m;
}
Method method =
clazz.getDeclaredMethod(
"launchImpl",
ClassLoader.class,
ClassLoader.class,
File.class,
String.class,
String.class,
File.class,
File.class,
File[].class,
String[].class,
AtomicInteger.class,
AtomicReference.class);
method.invoke(
null,
topLoader,
@ -122,7 +132,7 @@ public class ScriptedLauncher {
final AtomicInteger result,
final AtomicReference<String[]> newArguments)
throws ClassNotFoundException, InvocationTargetException, IllegalAccessException,
InstantiationException {
NoSuchMethodException, InstantiationException {
final AppConfiguration conf =
getConf(
topLoader,
@ -134,11 +144,8 @@ public class ScriptedLauncher {
classpath,
args);
final Class<?> clazz = loader.loadClass("sbt.xMain");
final Object instance = clazz.newInstance();
Method run = null;
for (final Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals("run")) run = m;
}
final Object instance = clazz.getConstructor().newInstance();
final Method run = clazz.getDeclaredMethod("run", loader.loadClass("xsbti.AppConfiguration"));
final Object runResult = run.invoke(instance, conf);
if (runResult instanceof xsbti.Reboot) newArguments.set(((Reboot) runResult).arguments());
else {
@ -401,7 +408,7 @@ public class ScriptedLauncher {
@Override
public AppMain newMain() {
try {
return (AppMain) loader().loadClass("sbt.xMain").newInstance();
return (AppMain) loader().loadClass("sbt.xMain").getConstructor().newInstance();
} catch (final Exception e) {
throw new RuntimeException(e);
}

View File

@ -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"

View File

@ -8,15 +8,10 @@
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
abstract class RemoteSbtCreator private[sbt] {
def newRemote(server: IPC.Server): Process
}
@ -25,13 +20,16 @@ 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)

View File

@ -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,13 +16,6 @@ import java.util.concurrent.ForkJoinPool
import sbt.internal.io.Resources
import sbt.internal.scripted._
import sbt.internal.util.{ BufferedLogger, ConsoleOut, FullLogger }
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.parallel.ForkJoinTaskSupport
import scala.collection.{ GenSeq, mutable }
@ -32,28 +25,24 @@ 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 '*')
@ -70,7 +59,8 @@ final class ScriptedTests(
val result = testResources.readWriteResourceDirectory(g, n) { testDirectory =>
val buffer = new BufferedLogger(new FullLogger(log))
val singleTestRunner = () => {
val handlers = createScriptedHandlers(testDirectory, buffer)
val handlers =
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)
@ -86,9 +76,20 @@ final class ScriptedTests(
private def createScriptedHandlers(
testDir: File,
buffered: Logger,
scalaVersion: String,
sbtVersion: String,
classpath: Seq[File]
): Map[Char, StatementHandler] = {
val fileHandler = new FileCommands(testDir)
val remoteSbtCreator = new RunFromSourceBasedRemoteSbtCreator(testDir, buffered, launchOpts)
val remoteSbtCreator =
new RunFromSourceBasedRemoteSbtCreator(
testDir,
buffered,
launchOpts,
scalaVersion,
sbtVersion,
classpath
)
val sbtHandler = new SbtHandler(remoteSbtCreator)
Map('$' -> fileHandler, '>' -> sbtHandler, '#' -> CommentHandler)
}
@ -98,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 = {
@ -141,7 +145,7 @@ final class ScriptedTests(
.grouped(batchSize)
.map { batch => () =>
IO.withTemporaryDirectory {
runBatchedTests(batch, _, prescripted, log)
runBatchedTests(batch, _, prescripted, log, scalaVersion, sbtVersion, classpath)
}
}
.toList
@ -220,11 +224,14 @@ final class ScriptedTests(
tempTestDir: File,
preHook: File => Unit,
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)
val handlers = createScriptedHandlers(tempTestDir, buffer, scalaVersion, sbtVersion, classpath)
val states = new BatchScriptRunner.States
val seqHandlers = handlers.values.toList
runner.initStates(states, seqHandlers)
@ -266,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"
@ -327,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()
@ -366,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
)
}
}
@ -386,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 ()

View File

@ -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)

View File

@ -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)
}
}