Merge branch '1.x' into global-eviction-warning-options

This commit is contained in:
TATSUNO Yasuhiro 2018-03-08 09:18:53 +09:00 committed by GitHub
commit 68f58aba87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 475 additions and 373 deletions

View File

@ -246,3 +246,12 @@ cd vscode-sbt-scala/client
$ vsce package
$ vsce publish
```
## Signing the CLA
Contributing to sbt requires you or your employer to sign the
[Lightbend Contributor License Agreement](https://www.lightbend.com/contribute/cla).
To make it easier to respect our license agreements, we have added an sbt task
that takes care of adding the LICENSE headers to new files. Run `headerCreate`
and sbt will put a copyright notice into it.

View File

@ -24,10 +24,12 @@ def buildLevelSettings: Seq[Setting[_]] =
Developer("eed3si9n", "Eugene Yokota", "@eed3si9n", url("https://github.com/eed3si9n")),
Developer("jsuereth", "Josh Suereth", "@jsuereth", url("https://github.com/jsuereth")),
Developer("dwijnand", "Dale Wijnand", "@dwijnand", url("https://github.com/dwijnand")),
Developer("gkossakowski",
"Grzegorz Kossakowski",
"@gkossakowski",
url("https://github.com/gkossakowski")),
Developer(
"gkossakowski",
"Grzegorz Kossakowski",
"@gkossakowski",
url("https://github.com/gkossakowski")
),
Developer("Duhemm", "Martin Duhem", "@Duhemm", url("https://github.com/Duhemm"))
),
homepage := Some(url("https://github.com/sbt/sbt")),
@ -38,32 +40,31 @@ def buildLevelSettings: Seq[Setting[_]] =
scalafmtVersion := "1.3.0",
))
def commonSettings: Seq[Setting[_]] =
Seq[SettingsDefinition](
headerLicense := Some(HeaderLicense.Custom(
"""|sbt
|Copyright 2011 - 2017, Lightbend, Inc.
|Copyright 2008 - 2010, Mark Harrah
|Licensed under BSD-3-Clause license (see LICENSE)
|""".stripMargin
)),
scalaVersion := baseScalaVersion,
componentID := None,
resolvers += Resolver.typesafeIvyRepo("releases"),
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.4" cross CrossVersion.binary),
concurrentRestrictions in Global += Util.testExclusiveRestriction,
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"),
javacOptions in compile ++= Seq("-Xlint", "-Xlint:-serial"),
crossScalaVersions := Seq(baseScalaVersion),
bintrayPackage := (bintrayPackage in ThisBuild).value,
bintrayRepository := (bintrayRepository in ThisBuild).value,
publishArtifact in Test := false,
fork in compile := true,
fork in run := true
) flatMap (_.settings)
def commonSettings: Seq[Setting[_]] = Def.settings(
headerLicense := Some(HeaderLicense.Custom(
"""|sbt
|Copyright 2011 - 2017, Lightbend, Inc.
|Copyright 2008 - 2010, Mark Harrah
|Licensed under BSD-3-Clause license (see LICENSE)
|""".stripMargin
)),
scalaVersion := baseScalaVersion,
componentID := None,
resolvers += Resolver.typesafeIvyRepo("releases"),
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += "bintray-sbt-maven-releases" at "https://dl.bintray.com/sbt/maven-releases/",
addCompilerPlugin("org.spire-math" % "kind-projector" % "0.9.4" cross CrossVersion.binary),
concurrentRestrictions in Global += Util.testExclusiveRestriction,
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"),
testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"),
javacOptions in compile ++= Seq("-Xlint", "-Xlint:-serial"),
crossScalaVersions := Seq(baseScalaVersion),
bintrayPackage := (bintrayPackage in ThisBuild).value,
bintrayRepository := (bintrayRepository in ThisBuild).value,
publishArtifact in Test := false,
fork in compile := true,
fork in run := true
)
def minimalSettings: Seq[Setting[_]] =
commonSettings ++ customCommands ++
@ -213,6 +214,11 @@ lazy val testingProj = (project in file("testing"))
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala",
contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats,
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// private[sbt]
ProblemFilters.exclude[IncompatibleMethTypeProblem]("sbt.TestStatus.write"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("sbt.TestStatus.read"),
),
)
.configure(addSbtIO, addSbtCompilerClasspath, addSbtUtilLogging)
@ -557,7 +563,6 @@ lazy val vscodePlugin = (project in file("vscode-sbt-scala"))
)
def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed
// publishLocalBinAll.value // TODO: Restore scripted needing only binary jars.
publishAll.value
(sbtProj / Test / compile).value // make sure sbt.RunFromSourceMain is compiled
@ -567,21 +572,20 @@ def scriptedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
(scalaInstance in scriptedSbtProj).value,
scriptedSource.value,
scriptedBufferLog.value,
result,
Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed,
scriptedPrescripted.value,
scriptedLaunchOpts.value
)
}
def scriptedUnpublishedTask: Def.Initialize[InputTask[Unit]] = Def.inputTask {
val result = scriptedSource(dir => (s: State) => Scripted.scriptedParser(dir)).parsed
Scripted.doScripted(
(sbtLaunchJar in bundledLauncherProj).value,
(fullClasspath in scriptedSbtProj in Test).value,
(scalaInstance in scriptedSbtProj).value,
scriptedSource.value,
scriptedBufferLog.value,
result,
Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed,
scriptedPrescripted.value,
scriptedLaunchOpts.value
)
@ -624,7 +628,6 @@ def otherRootSettings =
aggregate in bintrayRelease := false
) ++ inConfig(Scripted.RepoOverrideTest)(
Seq(
scriptedPrescripted := (_ => ()),
scriptedLaunchOpts := List(
"-Xmx1500M",
"-Xms512M",

View File

@ -31,7 +31,8 @@ sealed trait AttributeKey[T] {
def description: Option[String]
/**
* In environments that support delegation, looking up this key when it has no associated value will delegate to the values associated with these keys.
* In environments that support delegation, looking up this key when it has no associated value
* will delegate to the values associated with these keys.
* The delegation proceeds in order the keys are returned here.
*/
def extend: Seq[AttributeKey[_]]

View File

@ -9,6 +9,7 @@ package sbt.internal.util
package complete
import java.io.File
import org.scalatest.Assertion
import sbt.io.IO
class FileExamplesTest extends UnitSpec {
@ -57,7 +58,8 @@ class FileExamplesTest extends UnitSpec {
}
def withDirectoryStructure[A](withCompletionPrefix: String = "")(
thunk: DirectoryStructure => A): Unit = {
thunk: DirectoryStructure => Assertion
): Assertion = {
IO.withTemporaryDirectory { tempDir =>
val ds = new DirectoryStructure(withCompletionPrefix)
ds.createSampleDirStructure(tempDir)

View File

@ -238,14 +238,16 @@ object State {
def process(f: (Exec, State) => State): State = {
def runCmd(cmd: Exec, remainingCommands: List[Exec]) = {
log.debug(s"> $cmd")
f(cmd,
s.copy(remainingCommands = remainingCommands,
currentCommand = Some(cmd),
history = cmd :: s.history))
val s1 = s.copy(
remainingCommands = remainingCommands,
currentCommand = Some(cmd),
history = cmd :: s.history,
)
f(cmd, s1)
}
s.remainingCommands match {
case List() => exit(true)
case List(x, xs @ _*) => runCmd(x, xs.toList)
case Nil => exit(true)
case x :: xs => runCmd(x, xs)
}
}
def :::(newCommands: List[String]): State = ++:(newCommands map { Exec(_, s.source) })

View File

@ -63,7 +63,8 @@ private[sbt] object Server {
val maxSocketLength = new UnixDomainSocketLibrary.SockaddrUn().sunPath.length - 1
val path = socketfile.getAbsolutePath
if (path.length > maxSocketLength)
sys.error("socket file absolute path too long; " +
sys.error(
"socket file absolute path too long; " +
"either switch to another connection type " +
"or define a short \"SBT_GLOBAL_SERVER_DIR\" value. " +
s"Current path: ${path}")

View File

@ -10,47 +10,57 @@ package xsbt
import java.io.{ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter }
import java.net.{ InetAddress, ServerSocket, Socket }
import scala.annotation.tailrec
import scala.util.control.NonFatal
object IPC {
private val portMin = 1025
private val portMax = 65536
private val loopback = InetAddress.getByName(null) // loopback
private val loopback = InetAddress.getByName(null)
def client[T](port: Int)(f: IPC => T): T =
ipc(new Socket(loopback, port))(f)
def client[T](port: Int)(f: IPC => T): T = ipc(new Socket(loopback, port))(f)
def pullServer[T](f: Server => T): T = {
val server = makeServer
try { f(new Server(server)) } finally { server.close() }
try f(new Server(server))
finally server.close()
}
def unmanagedServer: Server = new Server(makeServer)
def makeServer: ServerSocket = {
val random = new java.util.Random
def nextPort = random.nextInt(portMax - portMin + 1) + portMin
def createServer(attempts: Int): ServerSocket =
if (attempts > 0)
try { new ServerSocket(nextPort, 1, loopback) } catch {
case NonFatal(_) => createServer(attempts - 1)
} else
sys.error("Could not connect to socket: maximum attempts exceeded")
if (attempts > 0) {
try new ServerSocket(nextPort, 1, loopback)
catch { case NonFatal(_) => createServer(attempts - 1) }
} else sys.error("Could not connect to socket: maximum attempts exceeded")
createServer(10)
}
def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f)
def server[T](port: Int)(f: IPC => Option[T]): T =
serverImpl(new ServerSocket(port, 1, loopback), f)
private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = {
def listen(): T = {
@tailrec def listen(): T = {
ipc(server.accept())(f) match {
case Some(done) => done
case None => listen()
}
}
try { listen() } finally { server.close() }
try listen()
finally server.close()
}
private def ipc[T](s: Socket)(f: IPC => T): T =
try { f(new IPC(s)) } finally { s.close() }
try f(new IPC(s))
finally s.close()
final class Server private[IPC] (s: ServerSocket) {
def port = s.getLocalPort
@ -59,6 +69,7 @@ object IPC {
def connection[T](f: IPC => T): T = IPC.ipc(s.accept())(f)
}
}
final class IPC private (s: Socket) {
def port = s.getLocalPort
private val in = new BufferedReader(new InputStreamReader(s.getInputStream))

View File

@ -338,6 +338,11 @@ object Scoped {
(this.? zipWith i)((x, y) => (x, y) map { case (a, b) => a getOrElse b })
}
/** Enriches `Initialize[Task[S]]` types.
*
* @param i the original `Initialize[Task[S]]` value to enrich
* @tparam S the type of the underlying value
*/
final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] {
protected def onTask[T](f: Task[S] => Task[T]): Initialize[Task[T]] = i apply f
@ -367,8 +372,14 @@ object Scoped {
}
}
/** Enriches `Initialize[InputTask[S]]` types.
*
* @param i the original `Initialize[InputTask[S]]` value to enrich
* @tparam S the type of the underlying value
*/
final class RichInitializeInputTask[S](i: Initialize[InputTask[S]])
extends RichInitTaskBase[S, InputTask] {
protected def onTask[T](f: Task[S] => Task[T]): Initialize[InputTask[T]] = i(_ mapTask f)
def dependsOn(tasks: AnyInitTask*): Initialize[InputTask[S]] = {
@ -378,6 +389,11 @@ object Scoped {
}
}
/** Enriches `Initialize[R[S]]` types. Abstracts over the specific task-like type constructor.
*
* @tparam S the type of the underlying vault
* @tparam R the task-like type constructor (either Task or InputTask)
*/
sealed abstract class RichInitTaskBase[S, R[_]] {
protected def onTask[T](f: Task[S] => Task[T]): Initialize[R[T]]

View File

@ -8,11 +8,11 @@
package sbt
package std
import reflect.macros._
import scala.reflect.macros._
import Def.Initialize
import sbt.internal.util.complete.Parser
import sbt.internal.util.appmacro.{ Convert, Converted }
import Def.Initialize
object InputInitConvert extends Convert {
def apply[T: c.WeakTypeTag](c: blackbox.Context)(nme: String, in: c.Tree): Converted[c.type] =
@ -46,14 +46,13 @@ object TaskConvert extends Convert {
/** Converts an input `Tree` of type `Initialize[T]`, `Initialize[Task[T]]`, or `Task[T]` into a `Tree` of type `Initialize[Task[T]]`.*/
object FullConvert extends Convert {
import InputWrapper._
def apply[T: c.WeakTypeTag](c: blackbox.Context)(nme: String, in: c.Tree): Converted[c.type] =
nme match {
case WrapInitTaskName => Converted.Success[c.type](in)
case WrapPreviousName => Converted.Success[c.type](in)
case WrapInitName => wrapInit[T](c)(in)
case WrapTaskName => wrapTask[T](c)(in)
case _ => Converted.NotApplicable[c.type]
case InputWrapper.WrapInitTaskName => Converted.Success[c.type](in)
case InputWrapper.WrapPreviousName => Converted.Success[c.type](in)
case InputWrapper.WrapInitName => wrapInit[T](c)(in)
case InputWrapper.WrapTaskName => wrapTask[T](c)(in)
case _ => Converted.NotApplicable[c.type]
}
private def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree): Converted[c.type] = {

View File

@ -8,9 +8,10 @@
package sbt
package std
import language.experimental.macros
import reflect.macros._
import reflect.internal.annotations.compileTimeOnly
import scala.language.experimental.macros
import scala.annotation.compileTimeOnly
import scala.reflect.macros._
import Def.Initialize
import sbt.internal.util.appmacro.ContextUtil

View File

@ -7,15 +7,19 @@
package sbt.std.neg
import scala.tools.reflect.ToolBoxError
import org.scalatest.FunSuite
import sbt.std.TaskLinterDSLFeedback
import sbt.std.TestUtil._
class TaskNegSpec extends FunSuite {
import tools.reflect.ToolBoxError
def expectError(errorSnippet: String,
compileOptions: String = "",
baseCompileOptions: String = s"-cp $toolboxClasspath")(code: String) = {
def expectError(
errorSnippet: String,
compileOptions: String = "",
baseCompileOptions: String = s"-cp $toolboxClasspath",
)(code: String) = {
val errorMessage = intercept[ToolBoxError] {
eval(code, s"$compileOptions $baseCompileOptions")
println(s"Test failed -- compilation was successful! Expected:\n$errorSnippet")

View File

@ -8,7 +8,6 @@
package sbt
import sbt.internal.{ Load, BuildStructure, Act, Aggregation, SessionSettings }
import Project._
import Scope.GlobalScope
import Def.{ ScopedKey, Setting }
import sbt.internal.util.complete.Parser
@ -43,7 +42,7 @@ final case class Extracted(structure: BuildStructure,
structure.data.get(inCurrent(key.scope), key.key)
private[this] def inCurrent[T](scope: Scope): Scope =
if (scope.project == This) scope.copy(project = Select(currentRef)) else scope
if (scope.project == This) scope in currentRef else scope
/**
* Runs the task specified by `key` and returns the transformed State and the resulting value of the task.

View File

@ -52,7 +52,6 @@ import xsbti.compile.CompilerCache
import scala.annotation.tailrec
import sbt.io.IO
import sbt.io.syntax._
import StandardMain._
import java.io.{ File, IOException }
import java.net.URI
@ -69,34 +68,35 @@ final class xMain extends xsbti.AppMain {
import BasicCommandStrings.runEarly
import BuiltinCommands.defaults
import sbt.internal.CommandStrings.{ BootCommand, DefaultsCommand, InitCommand }
val state = initialState(
val state = StandardMain.initialState(
configuration,
Seq(defaults, early),
runEarly(DefaultsCommand) :: runEarly(InitCommand) :: BootCommand :: Nil)
runManaged(state)
StandardMain.runManaged(state)
}
}
final class ScriptMain extends xsbti.AppMain {
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = {
import BasicCommandStrings.runEarly
runManaged(
initialState(
configuration,
BuiltinCommands.ScriptCommands,
runEarly(Level.Error.toString) :: Script.Name :: Nil
))
val state = StandardMain.initialState(
configuration,
BuiltinCommands.ScriptCommands,
runEarly(Level.Error.toString) :: Script.Name :: Nil
)
StandardMain.runManaged(state)
}
}
final class ConsoleMain extends xsbti.AppMain {
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
runManaged(
initialState(
configuration,
BuiltinCommands.ConsoleCommands,
IvyConsole.Name :: Nil
))
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult = {
val state = StandardMain.initialState(
configuration,
BuiltinCommands.ConsoleCommands,
IvyConsole.Name :: Nil
)
StandardMain.runManaged(state)
}
}
object StandardMain {

View File

@ -8,44 +8,44 @@
package sbt
import java.io.File
import Def.Initialize
import Keys._
import sbt.internal.util.complete.{ Parser, DefaultParsers }
import sbt.internal.inc.classpath.ClasspathUtilities
import sbt.internal.inc.ModuleUtilities
import java.lang.reflect.Method
import sbt.librarymanagement._
import sbt.librarymanagement.syntax._
import sbt.io._
import sbt.io.syntax._
import Project._
import sbt.internal.util.complete.{ Parser, DefaultParsers }
import sbt.librarymanagement._
import sbt.librarymanagement.syntax._
import sbt.internal.inc.classpath.ClasspathUtilities
import sbt.internal.inc.ModuleUtilities
import Def._
import Keys._
import Project._
object ScriptedPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
object autoImport {
val ScriptedConf = Configurations.config("scripted-sbt") hide
val ScriptedLaunchConf = Configurations.config("scripted-sbt-launch") hide
val scriptedSbt = SettingKey[String]("scripted-sbt")
val sbtLauncher = TaskKey[File]("sbt-launcher")
val sbtTestDirectory = SettingKey[File]("sbt-test-directory")
val scriptedBufferLog = SettingKey[Boolean]("scripted-buffer-log")
val scriptedClasspath = TaskKey[PathFinder]("scripted-classpath")
val scriptedTests = TaskKey[AnyRef]("scripted-tests")
val scriptedSbt = settingKey[String]("")
val sbtLauncher = taskKey[File]("")
val sbtTestDirectory = settingKey[File]("")
val scriptedBufferLog = settingKey[Boolean]("")
val scriptedClasspath = taskKey[PathFinder]("")
val scriptedTests = taskKey[AnyRef]("")
val scriptedBatchExecution =
settingKey[Boolean]("Enables or disables batch execution for scripted.")
val scriptedParallelInstances =
settingKey[Int](
"Configures the number of scripted instances for parallel testing, only used in batch mode.")
val scriptedRun = TaskKey[Method]("scripted-run")
val scriptedLaunchOpts = SettingKey[Seq[String]](
"scripted-launch-opts",
"options to pass to jvm launching scripted tasks")
val scriptedDependencies = TaskKey[Unit]("scripted-dependencies")
val scripted = InputKey[Unit]("scripted")
val scriptedParallelInstances = settingKey[Int](
"Configures the number of scripted instances for parallel testing, only used in batch mode.")
val scriptedRun = taskKey[Method]("")
val scriptedLaunchOpts =
settingKey[Seq[String]]("options to pass to jvm launching scripted tasks")
val scriptedDependencies = taskKey[Unit]("")
val scripted = inputKey[Unit]("")
}
import autoImport._
override lazy val globalSettings = Seq(
@ -114,10 +114,10 @@ object ScriptedPlugin extends AutoPlugin {
Def.task(method)
}
import DefaultParsers._
private[sbt] case class ScriptedTestPage(page: Int, total: Int)
private[sbt] final case class ScriptedTestPage(page: Int, total: Int)
private[sbt] def scriptedParser(scriptedBase: File): Parser[Seq[String]] = {
import DefaultParsers._
val scriptedFiles: NameFilter = ("test": NameFilter) | "pending"
val pairs = (scriptedBase * AllPassFilter * AllPassFilter * scriptedFiles).get map {
@ -125,15 +125,16 @@ object ScriptedPlugin extends AutoPlugin {
val p = f.getParentFile
(p.getParentFile.getName, p.getName)
}
val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet);
val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet)
val id = charClass(c => !c.isWhitespace && c != '/').+.string
val groupP = token(id.examples(pairMap.keySet.toSet)) <~ token('/')
val groupP = token(id.examples(pairMap.keySet)) <~ token('/')
// A parser for page definitions
val pageP: Parser[ScriptedTestPage] = ("*" ~ NatBasic ~ "of" ~ NatBasic) map {
case _ ~ page ~ _ ~ total => ScriptedTestPage(page, total)
}
// Grabs the filenames from a given test group in the current page definition.
def pagedFilenames(group: String, page: ScriptedTestPage): Seq[String] = {
val files = pairMap(group).toSeq.sortBy(_.toLowerCase)
@ -143,9 +144,11 @@ object ScriptedPlugin extends AutoPlugin {
if (page.page == page.total) dropped
else dropped.take(pageSize)
}
def nameP(group: String) = {
token("*".id | id.examples(pairMap.getOrElse(group, Set.empty[String])))
}
val PagedIds: Parser[Seq[String]] =
for {
group <- groupP
@ -153,10 +156,11 @@ object ScriptedPlugin extends AutoPlugin {
files = pagedFilenames(group, page)
// TODO - Fail the parser if we don't have enough files for the given page size
//if !files.isEmpty
} yield files map (f => group + '/' + f)
} yield files map (f => s"$group/$f")
val testID = (for (group <- groupP; name <- nameP(group)) yield (group, name))
val testIdAsGroup = matched(testID) map (test => Seq(test))
//(token(Space) ~> matched(testID)).*
(token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten)
}
@ -168,15 +172,16 @@ object ScriptedPlugin extends AutoPlugin {
val method = scriptedRun.value
val scriptedInstance = scriptedTests.value
val dir = sbtTestDirectory.value
val log: java.lang.Boolean = scriptedBufferLog.value
val log = Boolean box scriptedBufferLog.value
val launcher = sbtLauncher.value
val opts = scriptedLaunchOpts.value.toArray
val empty = new java.util.ArrayList[File]()
val instances: java.lang.Integer = scriptedParallelInstances.value
val instances = Int box scriptedParallelInstances.value
if (scriptedBatchExecution.value)
method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances)
else method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty)
()
} catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause }
}

View File

@ -154,7 +154,8 @@ case class DetectedAutoPlugin(name: String, value: AutoPlugin, hasAutoImport: Bo
* Auto-discovered modules for the build definition project. These include modules defined in build definition sources
* as well as modules in binary dependencies.
*
* @param builds The [[Build]]s detected in the build definition. This does not include the default [[Build]] that sbt creates if none is defined.
* @param builds The [[BuildDef]]s detected in the build definition.
* This does not include the default [[BuildDef]] that sbt creates if none is defined.
*/
final class DetectedPlugins(val autoPlugins: Seq[DetectedAutoPlugin],
val builds: DetectedModules[BuildDef]) {

View File

@ -5,14 +5,12 @@ import sbt.librarymanagement.ConfigRef
/** This local plugin provides ways of publishing just the binary jar. */
object PublishBinPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
override def trigger = allRequirements
object autoImport {
val publishLocalBin = taskKey[Unit]("")
val publishLocalBinConfig = taskKey[PublishConfiguration]("")
}
import autoImport._
override def globalSettings = Seq(publishLocalBin := (()))
@ -20,22 +18,17 @@ object PublishBinPlugin extends AutoPlugin {
override def projectSettings = Def settings (
publishLocalBin := Classpaths.publishTask(publishLocalBinConfig, deliverLocal).value,
publishLocalBinConfig := {
val _ = deliverLocal.value
Classpaths.publishConfig(
publishMavenStyle.value,
deliverPattern(crossTarget.value),
false, // publishMavenStyle.value,
Classpaths.deliverPattern(crossTarget.value),
if (isSnapshot.value) "integration" else "release",
ivyConfigurations.value.map(c => ConfigRef(c.name)).toVector,
(packagedArtifacts in publishLocalBin).value.toVector,
(checksums in publishLocalBin).value.toVector,
resolverName = "local",
logging = ivyLoggingLevel.value,
overwrite = isSnapshot.value
)
},
packagedArtifacts in publishLocalBin := Classpaths.packaged(Seq(packageBin in Compile)).value
)
def deliverPattern(outputPath: File): String =
(outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath
}

View File

@ -1,39 +1,34 @@
import java.lang.reflect.InvocationTargetException
import sbt._
import Keys._
import Def.Initialize
import sbt.internal.inc.ScalaInstance
import sbt.internal.inc.classpath
import sbt.internal.inc.classpath.{ ClasspathUtilities, FilteredLoader }
import scala.language.reflectiveCalls
object ScriptedPlugin extends sbt.AutoPlugin {
object ScriptedPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
object autoImport extends ScriptedKeys {
def scriptedPath = file("scripted")
}
import autoImport._
import Scripted._
override def projectSettings = Seq(
override def globalSettings = super.globalSettings ++ Seq(
scriptedBufferLog := true,
scriptedPrescripted := { _ =>
}
scriptedPrescripted := { _ => },
)
}
trait ScriptedKeys {
lazy val publishAll = TaskKey[Unit]("publish-all")
lazy val publishLocalBinAll = taskKey[Unit]("")
lazy val scripted = InputKey[Unit]("scripted")
lazy val scriptedUnpublished = InputKey[Unit](
"scripted-unpublished",
"Execute scripted without publishing SBT first. Saves you some time when only your test has changed.")
lazy val scriptedSource = SettingKey[File]("scripted-source")
lazy val scriptedPrescripted = TaskKey[File => Unit]("scripted-prescripted")
lazy val scriptedBufferLog = SettingKey[Boolean]("scripted-buffer-log")
lazy val scriptedLaunchOpts = SettingKey[Seq[String]](
"scripted-launch-opts",
"options to pass to jvm launching scripted tasks")
val publishAll = taskKey[Unit]("")
val publishLocalBinAll = taskKey[Unit]("")
val scripted = inputKey[Unit]("")
val scriptedUnpublished = inputKey[Unit]("Execute scripted without publishing sbt first. " +
"Saves you some time when only your test has changed")
val scriptedSource = settingKey[File]("")
val scriptedPrescripted = taskKey[File => Unit]("")
val scriptedBufferLog = settingKey[Boolean]("")
val scriptedLaunchOpts = settingKey[Seq[String]]("options to pass to jvm launching scripted tasks")
}
object Scripted {
@ -43,27 +38,31 @@ object Scripted {
val RepoOverrideTest = config("repoOverrideTest") extend Compile
import sbt.complete._
import DefaultParsers._
// Paging, 1-index based.
case class ScriptedTestPage(page: Int, total: Int)
final case class ScriptedTestPage(page: Int, total: Int)
// FIXME: Duplicated with ScriptedPlugin.scriptedParser, this can be
// avoided once we upgrade build.properties to 0.13.14
def scriptedParser(scriptedBase: File): Parser[Seq[String]] = {
import DefaultParsers._
val scriptedFiles: NameFilter = ("test": NameFilter) | "pending"
val pairs = (scriptedBase * AllPassFilter * AllPassFilter * scriptedFiles).get map {
(f: File) =>
val p = f.getParentFile
(p.getParentFile.getName, p.getName)
}
val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet);
val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet)
val id = charClass(c => !c.isWhitespace && c != '/').+.string
val groupP = token(id.examples(pairMap.keySet.toSet)) <~ token('/')
val groupP = token(id.examples(pairMap.keySet)) <~ token('/')
// A parser for page definitions
val pageP: Parser[ScriptedTestPage] = ("*" ~ NatBasic ~ "of" ~ NatBasic) map {
case _ ~ page ~ _ ~ total => ScriptedTestPage(page, total)
}
// Grabs the filenames from a given test group in the current page definition.
def pagedFilenames(group: String, page: ScriptedTestPage): Seq[String] = {
val files = pairMap(group).toSeq.sortBy(_.toLowerCase)
@ -73,9 +72,11 @@ object Scripted {
if (page.page == page.total) dropped
else dropped.take(pageSize)
}
def nameP(group: String) = {
token("*".id | id.examples(pairMap.getOrElse(group, Set.empty[String])))
}
val PagedIds: Parser[Seq[String]] =
for {
group <- groupP
@ -83,55 +84,64 @@ object Scripted {
files = pagedFilenames(group, page)
// TODO - Fail the parser if we don't have enough files for the given page size
//if !files.isEmpty
} yield files map (f => group + '/' + f)
} yield files map (f => s"$group/$f")
val testID = (for (group <- groupP; name <- nameP(group)) yield (group, name))
val testIdAsGroup = matched(testID) map (test => Seq(test))
//(token(Space) ~> matched(testID)).*
(token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten)
}
// Interface to cross class loader
type SbtScriptedRunner = {
def runInParallel(resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
prescripted: java.util.List[File]): Unit
}
def doScripted(launcher: File,
scriptedSbtClasspath: Seq[Attributed[File]],
scriptedSbtInstance: ScalaInstance,
sourcePath: File,
bufferLog: Boolean,
args: Seq[String],
prescripted: File => Unit,
launchOpts: Seq[String]): Unit = {
def doScripted(
launcher: File,
scriptedSbtClasspath: Seq[Attributed[File]],
scriptedSbtInstance: ScalaInstance,
sourcePath: File,
bufferLog: Boolean,
args: Seq[String],
prescripted: File => Unit,
launchOpts: Seq[String],
): Unit = {
System.err.println(s"About to run tests: ${args.mkString("\n * ", "\n * ", "\n")}")
// 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 noJLine = new classpath.FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil)
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine)
val noJLine = new FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil)
val loader = ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine)
val bridgeClass = Class.forName("sbt.scriptedtest.ScriptedRunner", true, loader)
// Interface to cross class loader
type SbtScriptedRunner = {
def runInParallel(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
prescripted: java.util.List[File],
): Unit
}
val bridge = bridgeClass.getDeclaredConstructor().newInstance().asInstanceOf[SbtScriptedRunner]
try {
// Using java.util.List to encode File => Unit.
val callback = new java.util.AbstractList[File] {
override def add(x: File): Boolean = {
prescripted(x)
false
}
override def add(x: File): Boolean = { prescripted(x); false }
def get(x: Int): sbt.File = ???
def size(): Int = 0
}
bridge.runInParallel(sourcePath,
bufferLog,
args.toArray,
launcher,
launchOpts.toArray,
callback)
} catch { case ite: java.lang.reflect.InvocationTargetException => throw ite.getCause }
import scala.language.reflectiveCalls
bridge.runInParallel(
sourcePath,
bufferLog,
args.toArray,
launcher,
launchOpts.toArray,
callback,
)
} catch { case ite: InvocationTargetException => throw ite.getCause }
}
}

View File

@ -1,3 +1,5 @@
object A extends App {
if(args(0).toBoolean) () else sys.error("Fail")
object A {
def main(args: Array[String]): Unit = {
if (args(0).toBoolean) () else sys.error("Fail")
}
}

View File

@ -1,2 +1,2 @@
addCommandAlias("demo-success", "run true") ++
addCommandAlias("demo-success", "run true")
addCommandAlias("demo-failure", "run false")

View File

@ -1,8 +1,8 @@
lazy val a = project.settings(
scalaVersion := "2.12.2",
scalaInstance in (Compile,doc) := (scalaInstance in b).value,
scalaInstance in (Compile, doc) := (scalaInstance in b).value,
// 2.10.1-only, so this will only succeed if `doc` recognizes the more specific scalaInstance scoped to `doc`
scalacOptions in (Compile,doc) += "-implicits"
scalacOptions in (Compile, doc) += "-implicits"
)
lazy val b = project.settings(

View File

@ -1,13 +1,11 @@
lazy val buildInfo = taskKey[Seq[File]]("The task that generates the build info.")
scalaVersion := "2.11.8"
lazy val root = (project in file("."))
.settings(
scalaVersion := "2.11.8",
buildInfo := {
val x = sourceManaged.value / "BuildInfo.scala"
IO.write(x, """object BuildInfo""")
x :: Nil
},
sourceGenerators in Compile += buildInfo,
sourceGenerators in Compile += Def.task { Nil }
)
val buildInfo = taskKey[Seq[File]]("generates the build info")
buildInfo := {
val file = sourceManaged.value / "BuildInfo.scala"
IO.write(file, "object BuildInfo")
file :: Nil
}
sourceGenerators in Compile += buildInfo
sourceGenerators in Compile += Def.task { Nil }

View File

@ -1,13 +1,7 @@
lazy val root = (project in file(".")).
settings(
myRun,
fork in demo := true,
javaOptions in demo := "-Dsbt.check.forked=true" :: Nil,
myIn
)
val demo = taskKey[Unit]("Demo run task")
fullRunTask(demo, Compile, "A", "1", "1")
fork in demo := true
javaOptions in demo := "-Dsbt.check.forked=true" :: Nil
lazy val demoIn = InputKey[Unit]("demoIn", "Demo run input task", demo)
lazy val demo = taskKey[Unit]("Demo run task")
def myRun = fullRunTask(demo, Compile, "A", "1", "1")
def myIn = fullRunInputTask(demoIn, Compile, "A", "1")
val demoIn = InputKey[Unit]("demoIn", "Demo run input task", demo)
fullRunInputTask(demoIn, Compile, "A", "1")

View File

@ -0,0 +1,8 @@
val commonSettings = Seq(
crossScalaVersions := (0 to 6).map(i => s"2.10.$i") ++ (0 to 11).map(i => s"2.11.$i") ++ (0 to 2).map(i => s"2.12.$i")
)
val p1 = project.in(file("p1")).settings(commonSettings)
val p2 = project.in(file("p2")).settings(commonSettings)
val p3 = project.in(file("p3")).settings(commonSettings)
val p4 = project.in(file("p4")).settings(commonSettings)

View File

@ -0,0 +1,3 @@
object B {
def show(what: String): String = s"String interpolation is ${what.toUpperCase}!"
}

View File

@ -0,0 +1,3 @@
class A {
def show(what: String): Unit = println(what)
}

View File

@ -0,0 +1,3 @@
object B {
def show(what: String): String = s"String interpolation is ${what.toUpperCase}!"
}

View File

@ -0,0 +1,3 @@
class A {
def show(what: String): Unit = println(what)
}

View File

@ -0,0 +1,3 @@
object B {
def show(what: String): String = s"String interpolation is ${what.toUpperCase}!"
}

View File

@ -0,0 +1,3 @@
class A {
def show(what: String): Unit = println(what)
}

View File

@ -0,0 +1,3 @@
object B {
def show(what: String): String = s"String interpolation is ${what.toUpperCase}!"
}

View File

@ -0,0 +1,3 @@
class A {
def show(what: String): Unit = println(what)
}

View File

@ -0,0 +1,3 @@
# https://github.com/sbt/sbt/issues/3143
> crossScalaVersions
> +version

View File

@ -8,11 +8,17 @@
package sbt
package scriptedtest
import scala.collection.mutable
import sbt.internal.scripted._
import sbt.scriptedtest.BatchScriptRunner.States
private[sbt] object BatchScriptRunner {
type States = mutable.HashMap[StatementHandler, StatementHandler#State]
}
/** Defines an alternative script runner that allows batch execution. */
private[sbt] class BatchScriptRunner extends ScriptRunner {
import BatchScriptRunner.States
/** Defines a method to run batched execution.
*
@ -37,9 +43,8 @@ private[sbt] class BatchScriptRunner extends ScriptRunner {
def processStatement(handler: StatementHandler, statement: Statement, states: States): Unit = {
val state = states(handler).asInstanceOf[handler.State]
val nextState =
try { Right(handler(statement.command, statement.arguments, state)) } catch {
case e: Exception => Left(e)
}
try Right(handler(statement.command, statement.arguments, state))
catch { case e: Exception => Left(e) }
nextState match {
case Left(err) =>
if (statement.successExpected) {
@ -58,8 +63,3 @@ private[sbt] class BatchScriptRunner extends ScriptRunner {
}
}
}
private[sbt] object BatchScriptRunner {
import scala.collection.mutable
type States = mutable.HashMap[StatementHandler, Any]
}

View File

@ -22,41 +22,40 @@ final case class SbtInstance(process: Process, server: IPC.Server)
final class SbtHandler(remoteSbtCreator: RemoteSbtCreator) extends StatementHandler {
type State = Option[SbtInstance]
def initialState = None
def apply(command: String, arguments: List[String], i: Option[SbtInstance]): Option[SbtInstance] =
onSbtInstance(i) { (process, server) =>
onSbtInstance(i) { (_, server) =>
send((command :: arguments.map(escape)).mkString(" "), server)
receive(command + " failed", server)
receive(s"$command failed", server)
}
def onSbtInstance(i: Option[SbtInstance])(f: (Process, IPC.Server) => Unit): Option[SbtInstance] =
i match {
case Some(SbtInstance(_, server)) if server.isClosed =>
finish(i)
onNewSbtInstance(f)
case Some(SbtInstance(process, server)) =>
f(process, server)
i
case None =>
onNewSbtInstance(f)
case Some(SbtInstance(_, server)) if server.isClosed => finish(i); onNewSbtInstance(f)
case Some(SbtInstance(process, server)) => f(process, server); i
case None => onNewSbtInstance(f)
}
private[this] def onNewSbtInstance(f: (Process, IPC.Server) => Unit): Option[SbtInstance] = {
val server = IPC.unmanagedServer
val p = try newRemote(server)
catch { case e: Throwable => server.close(); throw e }
val ai = Some(SbtInstance(p, server))
val p =
try newRemote(server)
catch { case e: Throwable => server.close(); throw e }
val i = Some(SbtInstance(p, server))
try f(p, server)
catch {
case e: Throwable =>
// TODO: closing is necessary only because StatementHandler uses exceptions for signaling errors
finish(ai); throw e
finish(i)
throw e
}
ai
i
}
def finish(state: Option[SbtInstance]) = state match {
def finish(state: State) = state match {
case None =>
case Some(SbtInstance(process, server)) =>
try {
send("exit", server)
@ -65,24 +64,28 @@ final class SbtHandler(remoteSbtCreator: RemoteSbtCreator) extends StatementHand
} catch {
case _: IOException => process.destroy()
}
case None =>
}
def send(message: String, server: IPC.Server) = server.connection { _.send(message) }
def send(message: String, server: IPC.Server) = server.connection(_.send(message))
def receive(errorMessage: String, server: IPC.Server) =
server.connection { ipc =>
val resultMessage = ipc.receive
if (!resultMessage.toBoolean) throw new TestFailed(errorMessage)
}
def newRemote(server: IPC.Server): Process = {
val p = remoteSbtCreator.newRemote(server)
try receive("Remote sbt initialization failed", server)
catch { case _: SocketException => throw new TestFailed("Remote sbt initialization failed") }
p
}
import java.util.regex.Pattern.{ quote => q }
// if the argument contains spaces, enclose it in quotes, quoting backslashes and quotes
def escape(argument: String) =
def escape(argument: String) = {
import java.util.regex.Pattern.{ quote => q }
if (argument.contains(" "))
"\"" + argument.replaceAll(q("""\"""), """\\""").replaceAll(q("\""), "\\\"") + "\""
else argument
}
}

View File

@ -9,27 +9,31 @@ package sbt
package scriptedtest
import java.io.File
import java.net.SocketException
import java.util.Properties
import java.util.concurrent.ForkJoinPool
import scala.util.control.NonFatal
import sbt.internal.scripted._
import sbt.io.{ DirectoryFilter, HiddenFileFilter, IO }
import sbt.io.IO.wrapNull
import sbt.io.FileFilter._
import sbt.internal.io.Resources
import sbt.internal.util.{ BufferedLogger, ConsoleLogger, FullLogger }
import sbt.util.{ AbstractLogger, Logger }
import scala.collection.GenSeq
import scala.collection.mutable
import scala.collection.parallel.ForkJoinTaskSupport
import scala.collection.parallel.mutable.ParSeq
import scala.util.control.NonFatal
import sbt.internal.scripted._
import sbt.internal.io.Resources
import sbt.internal.util.{ BufferedLogger, ConsoleLogger, FullLogger }
import sbt.io.syntax._
import sbt.io.{ DirectoryFilter, HiddenFileFilter, IO }
import sbt.io.FileFilter._
import sbt.util.{ AbstractLogger, Logger }
final class ScriptedTests(
resourceBaseDirectory: File,
bufferLog: Boolean,
launcher: File,
launchOpts: Seq[String],
) {
import ScriptedTests.{ TestRunner, emptyCallback }
final class ScriptedTests(resourceBaseDirectory: File,
bufferLog: Boolean,
launcher: File,
launchOpts: Seq[String]) {
import sbt.io.syntax._
import ScriptedTests._
private val testResources = new Resources(resourceBaseDirectory)
val ScriptFilename = "test"
@ -37,14 +41,17 @@ final class ScriptedTests(resourceBaseDirectory: File,
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): Seq[TestRunner] = {
def singleScriptedTest(
group: String,
name: String,
prescripted: File => Unit,
log: Logger,
): Seq[TestRunner] = {
// Test group and names may be file filters (like '*')
for (groupDir <- (resourceBaseDirectory * group).get; nme <- (groupDir * name).get) yield {
@ -60,7 +67,7 @@ final class ScriptedTests(resourceBaseDirectory: File,
val handlers =
createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased)
val runner = new BatchScriptRunner
val states = new mutable.HashMap[StatementHandler, Any]()
val states = new mutable.HashMap[StatementHandler, StatementHandler#State]()
commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer)
}
runOrHandleDisabled(label, testDirectory, singleTestRunner, buffer)
@ -97,8 +104,8 @@ final class ScriptedTests(resourceBaseDirectory: File,
val groupAndNameDirs = {
for {
(group, name) <- testGroupAndNames
groupDir <- resourceBaseDirectory.*(group).get
testDir <- groupDir.*(name).get
groupDir <- (resourceBaseDirectory * group).get
testDir <- (groupDir * name).get
} yield (groupDir, testDir)
}
@ -309,7 +316,7 @@ final class ScriptedTests(resourceBaseDirectory: File,
def runBatchTests = {
groupedTests.map {
case ((group, name), originalDir) =>
val label = s"$group / $name"
val label = s"$group/$name"
println(s"Running $label")
// Copy test's contents and reload the sbt instance to pick them up
IO.copyDirectory(originalDir, tempTestDir)
@ -318,8 +325,8 @@ final class ScriptedTests(resourceBaseDirectory: File,
// Reload and initialize (to reload contents of .sbtrc files)
val pluginImplementation = createAutoPlugin(name)
IO.write(tempTestDir / "project" / "InstrumentScripted.scala", pluginImplementation)
val sbtHandlerError = "Missing sbt handler. Scripted is misconfigured."
val sbtHandler = handlers.getOrElse('>', sbtHandlerError).asInstanceOf[SbtHandler]
def sbtHandlerError = sys error "Missing sbt handler. Scripted is misconfigured."
val sbtHandler = handlers.getOrElse('>', sbtHandlerError)
val commandsToRun = ";reload;setUpScripted"
val statement = Statement(commandsToRun, Nil, successExpected = true, line = -1)
@ -368,7 +375,7 @@ final class ScriptedTests(resourceBaseDirectory: File,
label: String,
testDirectory: File,
preScriptedHook: File => Unit,
createHandlers: Map[Char, StatementHandler],
handlers: Map[Char, StatementHandler],
runner: BatchScriptRunner,
states: BatchScriptRunner.States,
log: BufferedLogger
@ -382,15 +389,15 @@ final class ScriptedTests(resourceBaseDirectory: File,
}
val pendingMark = if (pending) PendingLabel else ""
def testFailed(t: Throwable): Option[String] = {
if (pending) log.clear() else log.stop()
log.error(s"x $label $pendingMark")
if (!NonFatal(t)) throw t // We make sure fatal errors are rethrown
if (t.isInstanceOf[TestException]) {
t.getCause match {
case null | _: java.net.SocketException =>
log.error(" Cause of test exception: " + t.getMessage)
case _ => t.printStackTrace()
case null | _: SocketException => log.error(s" Cause of test exception: ${t.getMessage}")
case _ => t.printStackTrace()
}
}
if (pending) None else Some(label)
@ -399,7 +406,6 @@ final class ScriptedTests(resourceBaseDirectory: File,
import scala.util.control.Exception.catching
catching(classOf[TestException]).withApply(testFailed).andFinally(log.clear).apply {
preScriptedHook(testDirectory)
val handlers = createHandlers
val parser = new TestScriptParser(handlers)
val handlersAndStatements = parser.parse(file)
runner.apply(handlersAndStatements, states)
@ -421,6 +427,7 @@ object ScriptedTests extends ScriptedRunner {
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
@ -432,48 +439,52 @@ object ScriptedTests extends ScriptedRunner {
val logger = ConsoleLogger()
run(directory, buffer, tests, logger, bootProperties, Array(), emptyCallback)
}
}
/** Runner for `scripted`. Not be confused with ScriptRunner. */
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 = {
// 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"
run(resourceBaseDirectory, bufferLog, tests, ConsoleLogger(), bootProperties, launchOpts, {
f: File =>
prescripted.add(f); ()
}) //new FullLogger(Logger.xlog2Log(log)))
def run(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
prescripted: java.util.List[File],
): Unit = {
val logger = ConsoleLogger()
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 =
run(resourceBaseDirectory,
bufferLog,
tests,
ConsoleLogger(),
bootProperties,
launchOpts,
ScriptedTests.emptyCallback)
def run(
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
): Unit = {
val logger = ConsoleLogger()
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"
def run(resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
logger: AbstractLogger,
bootProperties: File,
launchOpts: Array[String],
prescripted: File => Unit): Unit = {
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts)
val sbtVersion = bootProperties.getName.dropWhile(!_.isDigit).dropRight(".jar".length)
val accept = isTestCompatible(resourceBaseDirectory, sbtVersion) _
@ -484,22 +495,15 @@ class ScriptedRunner {
runAll(allTests)
}
def runInParallel(resourceBaseDirectory: File,
bufferLog: Boolean,
tests: Array[String],
bootProperties: File,
launchOpts: Array[String],
prescripted: java.util.List[File]): Unit = {
val logger = ConsoleLogger()
val addTestFile = (f: File) => { prescripted.add(f); () }
runInParallel(resourceBaseDirectory,
bufferLog,
tests,
logger,
bootProperties,
launchOpts,
addTestFile,
1)
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
@ -518,54 +522,54 @@ class ScriptedRunner {
}
def runInParallel(
resourceBaseDirectory: File,
baseDir: File,
bufferLog: Boolean,
tests: Array[String],
logger: AbstractLogger,
bootProperties: File,
bootProps: File,
launchOpts: Array[String],
prescripted: File => Unit,
instances: Int
): Unit = {
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, bootProperties, launchOpts)
val sbtVersion = bootProperties.getName.dropWhile(!_.isDigit).dropRight(".jar".length)
val accept = isTestCompatible(resourceBaseDirectory, sbtVersion) _
val runner = new ScriptedTests(baseDir, bufferLog, bootProps, launchOpts)
val sbtVersion = bootProps.getName.dropWhile(!_.isDigit).dropRight(".jar".length)
val accept = isTestCompatible(baseDir, sbtVersion) _
// The scripted tests mapped to the inputs that the user wrote after `scripted`.
val scriptedTests =
get(tests, resourceBaseDirectory, accept, logger).map(st => (st.group, st.name))
get(tests, baseDir, accept, logger).map(st => (st.group, st.name))
val scriptedRunners = runner.batchScriptedRunner(scriptedTests, prescripted, instances, logger)
val parallelRunners = scriptedRunners.toParArray
val pool = new java.util.concurrent.ForkJoinPool(instances)
parallelRunners.tasksupport = new ForkJoinTaskSupport(pool)
runAllInParallel(parallelRunners)
parallelRunners.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(instances))
runAll(parallelRunners)
}
private def reportErrors(errors: Seq[String]): Unit =
private def reportErrors(errors: GenSeq[String]): Unit =
if (errors.nonEmpty) sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n")) else ()
def runAll(toRun: Seq[ScriptedTests.TestRunner]): Unit =
reportErrors(toRun.flatMap(test => test.apply().flatten.toSeq))
// We cannot reuse `runAll` because parallel collections != collections
def runAllInParallel(tests: ParSeq[ScriptedTests.TestRunner]): Unit = {
reportErrors(tests.flatMap(test => test.apply().flatten.toSeq).toList)
}
def runAll(toRun: GenSeq[ScriptedTests.TestRunner]): Unit =
reportErrors(toRun.flatMap(test => test.apply().flatten))
@deprecated("No longer used", "1.1.0")
def get(tests: Seq[String], baseDirectory: File, log: Logger): Seq[ScriptedTest] =
get(tests, baseDirectory, _ => true, log)
def get(tests: Seq[String],
baseDirectory: File,
accept: ScriptedTest => Boolean,
log: Logger): Seq[ScriptedTest] =
def get(
tests: Seq[String],
baseDirectory: File,
accept: ScriptedTest => Boolean,
log: Logger,
): Seq[ScriptedTest] =
if (tests.isEmpty) listTests(baseDirectory, accept, log) else parseTests(tests)
@deprecated("No longer used", "1.1.0")
def listTests(baseDirectory: File, log: Logger): Seq[ScriptedTest] =
listTests(baseDirectory, _ => true, log)
def listTests(baseDirectory: File,
accept: ScriptedTest => Boolean,
log: Logger): Seq[ScriptedTest] =
def listTests(
baseDirectory: File,
accept: ScriptedTest => Boolean,
log: Logger,
): Seq[ScriptedTest] =
(new ListTests(baseDirectory, accept, log)).listTests
def parseTests(in: Seq[String]): Seq[ScriptedTest] =
@ -575,7 +579,8 @@ class ScriptedRunner {
}
private def isTestCompatible(resourceBaseDirectory: File, sbtVersion: String)(
test: ScriptedTest): Boolean = {
test: ScriptedTest
): Boolean = {
import sbt.internal.librarymanagement.cross.CrossVersionUtil.binarySbtVersion
val buildProperties = new Properties()
val testDir = new File(new File(resourceBaseDirectory, test.group), test.name)
@ -592,36 +597,38 @@ class ScriptedRunner {
}
final case class ScriptedTest(group: String, name: String) {
override def toString = group + "/" + name
override def toString = s"$group/$name"
}
private[sbt] object ListTests {
def list(directory: File, filter: java.io.FileFilter) = wrapNull(directory.listFiles(filter))
}
import ListTests._
private[sbt] final class ListTests(baseDirectory: File,
accept: ScriptedTest => Boolean,
log: Logger) {
private[sbt] final class ListTests(
baseDirectory: File,
accept: ScriptedTest => Boolean,
log: Logger,
) {
def filter = DirectoryFilter -- HiddenFileFilter
def listTests: Seq[ScriptedTest] = {
list(baseDirectory, filter) flatMap { group =>
IO.listFiles(baseDirectory, filter) flatMap { group =>
val groupName = group.getName
listTests(group).map(ScriptedTest(groupName, _))
}
}
private[this] def listTests(group: File): Set[String] = {
val groupName = group.getName
val allTests = list(group, filter)
val allTests = IO.listFiles(group, filter)
if (allTests.isEmpty) {
log.warn("No tests in test group " + groupName)
log.warn(s"No tests in test group $groupName")
Set.empty
} else {
val (included, skipped) =
allTests.toList.partition(test => accept(ScriptedTest(groupName, test.getName)))
if (included.isEmpty)
log.warn("Test group " + groupName + " skipped.")
log.warn(s"Test group $groupName skipped.")
else if (skipped.nonEmpty) {
log.warn("Tests skipped in group " + group.getName + ":")
skipped.foreach(testName => log.warn(" " + testName.getName))
log.warn(s"Tests skipped in group $groupName:")
skipped.foreach(testName => log.warn(s" ${testName.getName}"))
}
Set(included.map(_.getName): _*)
}

View File

@ -33,6 +33,7 @@ object TaskRunnerForkTest extends Properties("TaskRunner Fork") {
def runDoubleJoin(a: Int, b: Int, workers: Int): Unit = {
def inner = List.range(0, b).map(j => task(j).named(j.toString)).join
tryRun(List.range(0, a).map(_ => inner).join, false, workers)
()
}
property("fork and reduce") = forAll(TaskListGen, MaxWorkersGen) { (m: List[Int], workers: Int) =>
m.nonEmpty ==> {

View File

@ -8,14 +8,16 @@
package sbt
import java.io.File
import sbt.io.IO
import scala.collection.mutable.Map
import sbt.io.IO
import sbt.protocol.testing.TestResult
import java.util.concurrent.ConcurrentHashMap
import scala.collection.concurrent
// Assumes exclusive ownership of the file.
private[sbt] class TestStatusReporter(f: File) extends TestsListener {
private lazy val succeeded = TestStatus.read(f)
private lazy val succeeded: concurrent.Map[String, Long] = TestStatus.read(f)
def doInit = ()
def startGroup(name: String): Unit = { succeeded remove name; () }
@ -32,13 +34,16 @@ private[sbt] class TestStatusReporter(f: File) extends TestsListener {
private[sbt] object TestStatus {
import java.util.Properties
def read(f: File): Map[String, Long] = {
def read(f: File): concurrent.Map[String, Long] = {
import scala.collection.JavaConverters._
val properties = new Properties
IO.load(properties, f)
properties.asScala map { case (k, v) => (k, v.toLong) }
val result = new ConcurrentHashMap[String, Long]()
properties.asScala.iterator.foreach { case (k, v) => result.put(k, v.toLong) }
result.asScala
}
def write(map: Map[String, Long], label: String, f: File): Unit = {
def write(map: collection.Map[String, Long], label: String, f: File): Unit = {
val properties = new Properties
for ((test, lastSuccessTime) <- map)
properties.setProperty(test, lastSuccessTime.toString)