Files
sbt/project/Scripted.scala
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

197 lines
7.1 KiB
Scala
Raw Normal View History

2018-07-30 20:28:42 -04:00
package local
2018-01-24 13:42:18 +00:00
import java.lang.reflect.InvocationTargetException
import sbt.*
2017-05-03 15:52:36 +01:00
import sbt.internal.inc.ScalaInstance
2018-01-24 13:42:18 +00:00
import sbt.internal.inc.classpath.{ ClasspathUtilities, FilteredLoader }
2024-09-28 18:01:48 -04:00
import scala.annotation.nowarn
import scala.collection.JavaConverters.*
2015-07-10 11:53:48 +02:00
2018-07-30 20:28:42 -04:00
object LocalScriptedPlugin extends AutoPlugin {
2016-03-30 23:48:20 -04:00
override def requires = plugins.JvmPlugin
2018-01-24 13:42:18 +00:00
2018-07-10 00:58:45 -04:00
object autoImport extends ScriptedKeys
2016-03-30 23:48:20 -04:00
}
trait ScriptedKeys {
val publishLocalBinAll = taskKey[Unit]("")
2019-01-30 17:25:00 -08:00
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 scriptedKeepTempDirectory = settingKey[Boolean](
"If true, keeps the temporary directory after scripted tests complete for debugging."
)
2016-03-30 23:48:20 -04:00
}
2015-01-12 22:01:16 -05:00
2016-03-30 23:48:20 -04:00
object Scripted {
2017-12-21 00:08:56 -05:00
// This is to workaround https://github.com/sbt/io/issues/110
if (!sys.props.contains("jna.nosys")) sys.props.put("jna.nosys", "true")
2017-12-21 00:08:56 -05:00
2018-01-17 15:19:21 +00:00
val RepoOverrideTest = config("repoOverrideTest") extend Compile
2014-12-17 23:38:10 -05:00
val sbtWindowsExcludeFilter: FileFilter =
if (scala.util.Properties.isWin)
new SimpleFileFilter(f =>
(f.getParentFile.getName, f.getName) match {
2026-05-31 16:30:32 -04:00
case ("classloader-cache", "jni") => true // no native lib is built for windows
case ("classloader-cache", "spark") =>
true // the test spark server is unable to bind to a local socket on Visual Studio 2019
case ("nio", "make-clone") => true // uses gcc which isn't set up on all systems
case ("watch", "symlinks") => true // symlinks don't work the same on windows
case ("cache", "compile-io-failure") =>
true // a read-only dir doesn't block writes on windows, so the I/O failure won't reproduce
case _ => false
}
)
else NothingFilter
import sbt.complete.*
2018-01-24 13:42:18 +00:00
2014-12-17 23:38:10 -05:00
// Paging, 1-index based.
2018-01-24 13:42:18 +00:00
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
2017-04-21 03:14:31 -04:00
def scriptedParser(scriptedBase: File): Parser[Seq[String]] = {
import DefaultParsers.*
2018-01-24 13:42:18 +00:00
2017-04-21 03:14:31 -04:00
val scriptedFiles: NameFilter = ("test": NameFilter) | "pending"
val pairs = (scriptedBase * AllPassFilter * AllPassFilter * scriptedFiles).get() map {
2017-04-21 03:14:31 -04:00
(f: File) =>
2014-12-17 23:38:10 -05:00
val p = f.getParentFile
(p.getParentFile.getName, p.getName)
2017-04-21 03:14:31 -04:00
}
2018-01-24 13:42:18 +00:00
val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet)
2014-12-17 23:38:10 -05:00
2017-04-21 03:14:31 -04:00
val id = charClass(c => !c.isWhitespace && c != '/').+.string
2018-01-24 13:42:18 +00:00
val groupP = token(id.examples(pairMap.keySet)) <~ token('/')
2014-12-17 23:38:10 -05:00
2017-04-21 03:14:31 -04:00
// A parser for page definitions
2019-10-06 14:05:56 -07:00
val pageNumber = (NatBasic & not('0', "zero page number")).flatMap { i =>
if (i <= pairs.size) Parser.success(i)
else Parser.failure(s"$i exceeds the number of tests (${pairs.size})")
}
val pageP: Parser[ScriptedTestPage] = ("*" ~> pageNumber ~ ("of" ~> pageNumber)) flatMap {
case (page, total) if page <= total => success(ScriptedTestPage(page, total))
case (page, total) => failure(s"Page $page was greater than $total")
2017-04-21 03:14:31 -04:00
}
2018-01-24 13:42:18 +00:00
2017-04-21 03:14:31 -04:00
// Grabs the filenames from a given test group in the current page definition.
def pagedFilenames(group: String, page: ScriptedTestPage): Seq[String] = {
2019-10-06 14:01:56 -07:00
val files = pairMap.get(group).toSeq.flatten.sortBy(_.toLowerCase)
2019-08-08 10:18:43 -07:00
val pageSize = if (page.total == 0) files.size else files.size / page.total
2017-04-21 03:14:31 -04:00
// The last page may loose some values, so we explicitly keep them
val dropped = files.drop(pageSize * (page.page - 1))
if (page.page == page.total) dropped
else dropped.take(pageSize)
}
2018-01-24 13:42:18 +00:00
2017-04-21 03:14:31 -04:00
def nameP(group: String) = {
token("*".id | id.examples(pairMap.getOrElse(group, Set.empty[String])))
2014-12-17 23:38:10 -05:00
}
2018-01-24 13:42:18 +00:00
2017-04-21 03:14:31 -04:00
val PagedIds: Parser[Seq[String]] =
for {
group <- groupP
page <- pageP
files = pagedFilenames(group, page)
// TODO - Fail the parser if we don't have enough files for the given page size
2022-01-30 15:07:23 -05:00
// if !files.isEmpty
2018-01-24 13:42:18 +00:00
} yield files map (f => s"$group/$f")
2017-04-21 03:14:31 -04:00
val testID = (for (group <- groupP; name <- nameP(group)) yield (group, name))
val testIdAsGroup = matched(testID) map (test => Seq(test))
2018-01-24 13:42:18 +00:00
2022-01-30 15:07:23 -05:00
// (token(Space) ~> matched(testID)).*
2017-04-21 03:14:31 -04:00
(token(Space) ~> (PagedIds | testIdAsGroup)).* map (_.flatten)
}
2014-12-17 23:38:10 -05:00
2024-09-28 18:01:48 -04:00
@nowarn
2018-01-24 13:42:18 +00:00
def doScripted(
scriptedSbtInstance: ScalaInstance,
sourcePath: File,
bufferLog: Boolean,
args: Seq[String],
prescripted: File => Unit,
launchOpts: Seq[String],
scalaVersion: String,
sbtVersion: String,
classpath: Seq[File],
2022-10-02 01:58:37 -04:00
launcherJar: File,
logger: Logger,
keepTempDirectory: Boolean,
includeFilter: java.io.FileFilter,
excludeFilter: java.io.FileFilter,
2018-01-24 13:42:18 +00:00
): Unit = {
logger.info(s"Tests selected: ${args.mkString("\n * ", "\n * ", "\n")}")
2019-11-17 12:34:19 -08:00
logger.info("")
2018-01-24 13:42:18 +00:00
2017-11-16 15:09:25 +01:00
// 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"
2018-01-24 13:42:18 +00:00
val noJLine = new FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil)
val loader = ClasspathUtilities.toLoader(classpath, noJLine)
2018-01-13 17:08:48 -05:00
val bridgeClass = Class.forName("sbt.scriptedtest.ScriptedRunner", true, loader)
2018-01-24 13:42:18 +00:00
// Interface to cross class loader
type SbtScriptedRunner = {
def runInParallel(
2019-01-30 17:25:00 -08:00
resourceBaseDirectory: File,
bufferLog: Boolean,
tests: java.util.List[String],
2022-10-02 01:58:37 -04:00
launcherJar: File,
javaCommand: String,
launchOpts: java.util.List[String],
2019-01-30 17:25:00 -08:00
prescripted: java.util.List[File],
2022-10-02 01:58:37 -04:00
instance: Int,
keepTempDirectory: Boolean,
includeFilter: java.io.FileFilter,
excludeFilter: java.io.FileFilter,
2018-01-24 13:42:18 +00:00
): Unit
}
2019-01-30 17:25:00 -08:00
val initLoader = Thread.currentThread.getContextClassLoader
try {
2019-01-30 17:25:00 -08:00
Thread.currentThread.setContextClassLoader(loader)
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 }
def get(x: Int): sbt.File = ???
def size(): Int = 0
}
2019-01-30 18:27:39 -08:00
val instances: Int = (System.getProperty("sbt.scripted.parallel.instances") match {
case null => 1
case i => scala.util.Try(i.toInt).getOrElse(1)
}) match {
case i if i > 0 => i
case _ => 1
}
2019-01-30 17:25:00 -08:00
import scala.language.reflectiveCalls
2022-10-02 01:58:37 -04:00
2019-01-30 17:25:00 -08:00
bridge.runInParallel(
sourcePath,
bufferLog,
args.toList.asJava,
2022-10-02 01:58:37 -04:00
launcherJar,
"java",
launchOpts.toList.asJava,
2019-01-30 17:25:00 -08:00
callback,
instances,
keepTempDirectory,
includeFilter,
excludeFilter,
2019-01-30 17:25:00 -08:00
)
} catch { case ite: InvocationTargetException => throw ite.getCause }
} finally {
Thread.currentThread.setContextClassLoader(initLoader)
}
2014-12-17 23:38:10 -05:00
}
}