mirror of https://github.com/sbt/sbt.git
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:
parent
1ff5ff45bd
commit
ae4d3aecb8
101
build.sbt
101
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)
|
||||
|
||||
|
|
@ -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 =>
|
||||
|
|
|
|||
|
|
@ -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._
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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,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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,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)
|
||||
|
|
|
|||
|
|
@ -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 ()
|
||||
|
|
|
|||
|
|
@ -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