sbt/project/Scripted.scala

134 lines
5.2 KiB
Scala
Raw Normal View History

2014-12-18 05:38:10 +01:00
import sbt._
import Keys._
import Def.Initialize
Upgrade the build to sbt 1.0.0-M5 Some plugins remain commented out, for now. sbt-doge is no longer needed because a variant of it has been folded into sbt 1. For some reason scripted requires src/doc jars of sbt, so switch back to using `publishAll` rather than `publishLocalBinAll`. :( Also, the sys.prop change in scripted is to force log4j2 to not use a thread context classloader, and avoid the following: ERROR StatusLogger Unable to create custom ContextSelector. Falling back to default. java.lang.ClassCastException: Cannot cast org.apache.logging.log4j.core.async.AsyncLoggerContextSelector to org.apache.logging.log4j.core.selector.ContextSelector at java.lang.Class.cast(Class.java:3369) at org.apache.logging.log4j.util.LoaderUtil.newCheckedInstanceOf(LoaderUtil.java:201) at org.apache.logging.log4j.util.LoaderUtil.newCheckedInstanceOfProperty(LoaderUtil.java:226) at org.apache.logging.log4j.core.impl.Log4jContextFactory.createContextSelector(Log4jContextFactory.java:97) at org.apache.logging.log4j.core.impl.Log4jContextFactory.<init>(Log4jContextFactory.java:58) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at java.lang.Class.newInstance(Class.java:442) at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:94) at org.apache.logging.log4j.spi.ThreadContextMapFactory.createThreadContextMap(ThreadContextMapFactory.java:73) at org.apache.logging.log4j.ThreadContext.init(ThreadContext.java:223) at org.apache.logging.log4j.ThreadContext.<clinit>(ThreadContext.java:202) at org.apache.logging.log4j.core.impl.ContextDataInjectorFactory.createDefaultInjector(ContextDataInjectorFactory.java:83) at org.apache.logging.log4j.core.impl.ContextDataInjectorFactory.createInjector(ContextDataInjectorFactory.java:67) at org.apache.logging.log4j.core.lookup.ContextMapLookup.<init>(ContextMapLookup.java:34) at org.apache.logging.log4j.core.lookup.Interpolator.<init>(Interpolator.java:117) at org.apache.logging.log4j.core.config.AbstractConfiguration.<init>(AbstractConfiguration.java:125) at org.apache.logging.log4j.core.config.DefaultConfiguration.<init>(DefaultConfiguration.java:46) at org.apache.logging.log4j.core.layout.PatternLayout$Builder.build(PatternLayout.java:650) at org.apache.logging.log4j.core.layout.PatternLayout.createDefaultLayout(PatternLayout.java:487) at sbt.internal.util.ConsoleAppender.<init>(ConsoleAppender.scala:245) at sbt.internal.util.ConsoleAppender$.apply(ConsoleAppender.scala:196) at sbt.internal.util.ConsoleLogger.<init>(ConsoleAppender.scala:42) at sbt.internal.util.ConsoleLogger$.apply(ConsoleAppender.scala:34) at sbt.test.ScriptedRunner.run(ScriptedTests.scala:221) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at Scripted$.doScripted(Scripted.scala:125) at $0483e89d182e9d240274$.$anonfun$scriptedTask$5(build.sbt:301)
2017-05-03 16:52:36 +02:00
import sbt.internal.inc.ScalaInstance
import sbt.internal.inc.classpath
2014-12-18 05:38:10 +01:00
2015-07-10 11:53:48 +02:00
import scala.language.reflectiveCalls
object ScriptedPlugin extends sbt.AutoPlugin {
override def requires = plugins.JvmPlugin
object autoImport extends ScriptedKeys {
def scriptedPath = file("scripted")
}
import autoImport._
override def globalSettings = super.globalSettings ++ Seq(
scriptedBufferLog := true,
scriptedPrescripted := { _ => },
)
}
trait ScriptedKeys {
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")
}
2015-01-13 04:01:16 +01:00
object Scripted {
// This is to workaround https://github.com/sbt/io/issues/110
sys.props.put("jna.nosys", "true")
2018-01-17 16:19:21 +01:00
val RepoOverrideTest = config("repoOverrideTest") extend Compile
2014-12-18 05:38:10 +01:00
import sbt.complete._
import DefaultParsers._
// Paging, 1-index based.
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 09:14:31 +02:00
def scriptedParser(scriptedBase: File): Parser[Seq[String]] = {
val scriptedFiles: NameFilter = ("test": NameFilter) | "pending"
val pairs = (scriptedBase * AllPassFilter * AllPassFilter * scriptedFiles).get map {
(f: File) =>
2014-12-18 05:38:10 +01:00
val p = f.getParentFile
(p.getParentFile.getName, p.getName)
2017-04-21 09:14:31 +02:00
}
val pairMap = pairs.groupBy(_._1).mapValues(_.map(_._2).toSet);
2014-12-18 05:38:10 +01:00
2017-04-21 09:14:31 +02:00
val id = charClass(c => !c.isWhitespace && c != '/').+.string
val groupP = token(id.examples(pairMap.keySet.toSet)) <~ token('/')
2014-12-18 05:38:10 +01:00
2017-04-21 09:14:31 +02:00
// 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)
val pageSize = files.size / page.total
// 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)
}
def nameP(group: String) = {
token("*".id | id.examples(pairMap.getOrElse(group, Set.empty[String])))
2014-12-18 05:38:10 +01:00
}
2017-04-21 09:14:31 +02: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
//if !files.isEmpty
} yield files map (f => 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)
}
2014-12-18 05:38:10 +01:00
// 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
}
2017-04-21 09:14:31 +02:00
def doScripted(launcher: File,
scriptedSbtClasspath: Seq[Attributed[File]],
scriptedSbtInstance: ScalaInstance,
sourcePath: File,
bufferLog: Boolean,
args: Seq[String],
prescripted: File => Unit,
launchOpts: Seq[String]): Unit = {
2014-12-18 05:38:10 +01:00
System.err.println(s"About to run tests: ${args.mkString("\n * ", "\n * ", "\n")}")
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"
2014-12-18 05:38:10 +01:00
val noJLine = new classpath.FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil)
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine)
val bridgeClass = Class.forName("sbt.scriptedtest.ScriptedRunner", true, 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
}
bridge.runInParallel(sourcePath,
bufferLog,
args.toArray,
launcher,
launchOpts.toArray,
callback)
} catch { case ite: java.lang.reflect.InvocationTargetException => throw ite.getCause }
2014-12-18 05:38:10 +01:00
}
}