Merge pull request #3875 from eed3si9n/wip/scripted

non-triggered scripted plugin: SbtPlugin
This commit is contained in:
eugene yokota 2018-01-23 20:27:03 -05:00 committed by GitHub
commit be6bbcd82e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 290 additions and 175 deletions

View File

@ -268,15 +268,23 @@ lazy val scriptedSbtProj = (project in scriptedPath / "sbt")
name := "Scripted sbt",
libraryDependencies ++= Seq(launcherInterface % "provided"),
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// sbt.test package is renamed to sbt.scriptedtest.
exclude[MissingClassProblem]("sbt.test.*"),
),
)
.configure(addSbtIO, addSbtUtilLogging, addSbtCompilerInterface, addSbtUtilScripted, addSbtLmCore)
lazy val scriptedPluginProj = (project in scriptedPath / "plugin")
.dependsOn(sbtProj)
.dependsOn(mainProj)
.settings(
baseSettings,
name := "Scripted Plugin",
mimaSettings,
mimaBinaryIssueFilters ++= Seq(
// scripted plugin has moved into sbt mothership.
exclude[MissingClassProblem]("sbt.ScriptedPlugin*")
),
)
.configure(addSbtCompilerClasspath)
@ -417,7 +425,7 @@ lazy val mainSettingsProj = (project in file("main-settings"))
// The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions.
lazy val mainProj = (project in file("main"))
.enablePlugins(ContrabandPlugin)
.dependsOn(logicProj, actionsProj, mainSettingsProj, runProj, commandProj, collectionProj)
.dependsOn(logicProj, actionsProj, mainSettingsProj, runProj, commandProj, collectionProj, scriptedSbtProj)
.settings(
testedBaseSettings,
name := "Main",

View File

@ -0,0 +1,186 @@
/*
* sbt
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under BSD-3-Clause license (see LICENSE)
*/
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 Def._
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 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")
}
import autoImport._
override lazy val globalSettings = Seq(
scriptedBufferLog := true,
scriptedLaunchOpts := Seq(),
)
override lazy val projectSettings = Seq(
ivyConfigurations ++= Seq(ScriptedConf, ScriptedLaunchConf),
scriptedSbt := (sbtVersion in pluginCrossBuild).value,
sbtLauncher := getJars(ScriptedLaunchConf).map(_.get.head).value,
sbtTestDirectory := sourceDirectory.value / "sbt-test",
libraryDependencies ++= (CrossVersion.partialVersion(scriptedSbt.value) match {
case Some((0, 13)) =>
Seq(
"org.scala-sbt" % "scripted-sbt" % scriptedSbt.value % ScriptedConf,
"org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf
)
case Some((1, _)) =>
Seq(
"org.scala-sbt" %% "scripted-sbt" % scriptedSbt.value % ScriptedConf,
"org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf
)
case Some((x, y)) => sys error s"Unknown sbt version ${scriptedSbt.value} ($x.$y)"
case None => sys error s"Unknown sbt version ${scriptedSbt.value}"
}),
scriptedClasspath := getJars(ScriptedConf).value,
scriptedTests := scriptedTestsTask.value,
scriptedParallelInstances := 1,
scriptedBatchExecution := false,
scriptedRun := scriptedRunTask.value,
scriptedDependencies := {
def use[A](@deprecated("unused", "") x: A*): Unit = () // avoid unused warnings
val analysis = (Keys.compile in Test).value
val pub = (publishLocal).value
use(analysis, pub)
},
scripted := scriptedTask.evaluated
)
private[sbt] def scriptedTestsTask: Initialize[Task[AnyRef]] =
Def.task {
val loader = ClasspathUtilities.toLoader(scriptedClasspath.value, scalaInstance.value.loader)
try {
ModuleUtilities.getObject("sbt.scriptedtest.ScriptedTests", loader)
} catch {
case _: ClassNotFoundException =>
ModuleUtilities.getObject("sbt.test.ScriptedTests", loader)
}
}
private[sbt] def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn {
val fCls = classOf[File]
val bCls = classOf[Boolean]
val asCls = classOf[Array[String]]
val lfCls = classOf[java.util.List[File]]
val iCls = classOf[Int]
val clazz = scriptedTests.value.getClass
val method =
if (scriptedBatchExecution.value)
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)
else
clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)
Def.task(method)
}
import DefaultParsers._
private[sbt] case class ScriptedTestPage(page: Int, total: Int)
private[sbt] def scriptedParser(scriptedBase: File): Parser[Seq[String]] = {
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 id = charClass(c => !c.isWhitespace && c != '/').+.string
val groupP = token(id.examples(pairMap.keySet.toSet)) <~ 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)
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])))
}
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)
}
private[sbt] def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask {
val args = scriptedParser(sbtTestDirectory.value).parsed
scriptedDependencies.value
try {
val method = scriptedRun.value
val scriptedInstance = scriptedTests.value
val dir = sbtTestDirectory.value
val log: java.lang.Boolean = 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
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 }
}
private[this] def getJars(config: Configuration): Initialize[Task[PathFinder]] = Def.task {
PathFinder(Classpaths.managedJars(config, classpathTypes.value, Keys.update.value).map(_.data))
}
}

View File

@ -47,6 +47,8 @@ object PluginDiscovery {
"sbt.plugins.IvyPlugin" -> sbt.plugins.IvyPlugin,
"sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin,
"sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin,
"sbt.ScriptedPlugin" -> sbt.ScriptedPlugin,
"sbt.plugins.SbtPlugin" -> sbt.plugins.SbtPlugin,
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin,
"sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin
)

View File

@ -0,0 +1,19 @@
/*
* sbt
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under BSD-3-Clause license (see LICENSE)
*/
package sbt
package plugins
import Keys._
object SbtPlugin extends AutoPlugin {
override def requires = ScriptedPlugin
override lazy val projectSettings = Seq(
sbtPlugin := true
)
}

View File

@ -0,0 +1,27 @@
### Fixes with compatibility implications
- In sbt 1.2, `ScriptedPlugin` is no longer triggered automatically. This allows easier use of the plugin in a multi-project build. We recommend migration to `SbtPlugin`. [#3514][3514]/[#3875][3875] by [@eed3si9n][@eed3si9n]
- `scriptedBufferLog` and `scriptedLaunchOpts` settings are changed so they are scoped globally.
### Features
- Adds `SbtPlugin`. See below.
### Bug fixes
### SbtPlugin
`SbtPlugin` is a new plugin that represents sbt plugin projects.
lazy val fooPlugin = (project in file("plugin"))
.enablePlugins(SbtPlugin)
This sets `sbtPlugin` setting to `true`, and brings in the new non-triggered `ScriptedPlugin`.
[#3875][3875] by [@eed3si9n][@eed3si9n]
[@eed3si9n]: https://github.com/eed3si9n
[3514]: https://github.com/sbt/sbt/issues/3514
[3875]: https://github.com/sbt/sbt/pull/3875

View File

@ -115,7 +115,7 @@ object Scripted {
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 bridgeClass = Class.forName("sbt.test.ScriptedRunner", true, loader)
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.

View File

@ -0,0 +1,2 @@
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)

View File

@ -0,0 +1,2 @@
lazy val root = (project in file("."))
.enablePlugins(SbtPlugin)

View File

@ -1,3 +0,0 @@
libraryDependencies += {
"org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
}

View File

@ -1 +0,0 @@
sbt.ScriptedPlugin

View File

@ -7,163 +7,4 @@
package sbt
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
object ScriptedPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
override def trigger = allRequirements
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 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")
}
import autoImport._
override lazy val projectSettings = Seq(
ivyConfigurations ++= Seq(ScriptedConf, ScriptedLaunchConf),
scriptedSbt := (sbtVersion in pluginCrossBuild).value,
sbtLauncher := getJars(ScriptedLaunchConf).map(_.get.head).value,
sbtTestDirectory := sourceDirectory.value / "sbt-test",
libraryDependencies ++= (CrossVersion.partialVersion(scriptedSbt.value) match {
case Some((0, 13)) =>
Seq(
"org.scala-sbt" % "scripted-sbt" % scriptedSbt.value % ScriptedConf,
"org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf
)
case Some((1, _)) =>
Seq(
"org.scala-sbt" %% "scripted-sbt" % scriptedSbt.value % ScriptedConf,
"org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf
)
case Some((x, y)) => sys error s"Unknown sbt version ${scriptedSbt.value} ($x.$y)"
case None => sys error s"Unknown sbt version ${scriptedSbt.value}"
}),
scriptedBufferLog := true,
scriptedClasspath := getJars(ScriptedConf).value,
scriptedTests := scriptedTestsTask.value,
scriptedParallelInstances := 1,
scriptedBatchExecution := false,
scriptedRun := scriptedRunTask.value,
scriptedDependencies := {
def use[A](@deprecated("unused", "") x: A*): Unit = () // avoid unused warnings
val analysis = (compile in Test).value
val pub = (publishLocal).value
use(analysis, pub)
},
scriptedLaunchOpts := Seq(),
scripted := scriptedTask.evaluated
)
def scriptedTestsTask: Initialize[Task[AnyRef]] =
Def.task {
val loader = ClasspathUtilities.toLoader(scriptedClasspath.value, scalaInstance.value.loader)
ModuleUtilities.getObject("sbt.test.ScriptedTests", loader)
}
def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn {
val fCls = classOf[File]
val bCls = classOf[Boolean]
val asCls = classOf[Array[String]]
val lfCls = classOf[java.util.List[File]]
val iCls = classOf[Int]
val clazz = scriptedTests.value.getClass
val method =
if (scriptedBatchExecution.value)
clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls)
else
clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)
Def.task(method)
}
import DefaultParsers._
case class ScriptedTestPage(page: Int, total: Int)
private[sbt] def scriptedParser(scriptedBase: File): Parser[Seq[String]] = {
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 id = charClass(c => !c.isWhitespace && c != '/').+.string
val groupP = token(id.examples(pairMap.keySet.toSet)) <~ 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)
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])))
}
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)
}
def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask {
val args = scriptedParser(sbtTestDirectory.value).parsed
scriptedDependencies.value
try {
val method = scriptedRun.value
val scriptedInstance = scriptedTests.value
val dir = sbtTestDirectory.value
val log: java.lang.Boolean = 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
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 }
}
private[this] def getJars(config: Configuration): Initialize[Task[PathFinder]] = Def.task {
PathFinder(Classpaths.managedJars(config, classpathTypes.value, update.value).map(_.data))
}
}
// ScriptedPlugin has moved to main.

View File

@ -0,0 +1,32 @@
/*
* sbt
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under BSD-3-Clause license (see LICENSE)
*/
package sbt.test
import java.io.File
/**
* This is a bincompat place holder sbt.test package that we are now trying to hide
* because of the name conflict with Keys.test.
*/
@deprecated("Use sbt.scriptedtest.ScriptedRunner.", "1.2.0")
private[sbt] class ScriptedRunner extends sbt.scriptedtest.ScriptedRunner
/**
* This is a bincompat place holder for sbt.test package that we are now trying to hide
* because of the name conflict with Keys.test.
*/
@deprecated("Use sbt.scriptedtest.ScriptedTests.", "1.2.0")
private[sbt] 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 =
sbt.scriptedtest.ScriptedTests.main(args)
}

View File

@ -6,10 +6,10 @@
*/
package sbt
package test
package scriptedtest
import sbt.internal.scripted._
import sbt.test.BatchScriptRunner.States
import sbt.scriptedtest.BatchScriptRunner.States
/** Defines an alternative script runner that allows batch execution. */
private[sbt] class BatchScriptRunner extends ScriptRunner {

View File

@ -6,7 +6,7 @@
*/
package sbt
package test
package scriptedtest
import java.io.{ File, IOException }
import xsbt.IPC

View File

@ -6,7 +6,7 @@
*/
package sbt
package test
package scriptedtest
import java.io.File
import java.util.Properties
@ -466,13 +466,13 @@ class ScriptedRunner {
final case class ScriptedTest(group: String, name: String) {
override def toString = group + "/" + name
}
private[test] object ListTests {
private[sbt] object ListTests {
def list(directory: File, filter: java.io.FileFilter) = wrapNull(directory.listFiles(filter))
}
import ListTests._
private[test] 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 =>