Merge pull request #2705 from eed3si9n/wip/new_command

Adds templateResolvers and `new` command
This commit is contained in:
eugene yokota 2016-08-23 03:17:40 -04:00 committed by GitHub
commit 240781566e
13 changed files with 115 additions and 7 deletions

View File

@ -449,7 +449,8 @@ lazy val commandProj = (project in mainPath / "command").
settings(
testedBaseSettings,
name := "Command",
libraryDependencies += launcherInterface
libraryDependencies ++= Seq(launcherInterface, templateResolverApi, giter8),
dependencyOverrides += plexusUtils
)
// Fixes scope=Scope for Setting (core defined in collectionProj) to define the settings system used in build definitions
@ -488,6 +489,7 @@ lazy val mavenResolverPluginProj = (project in file("sbt-maven-resolver")).
baseSettings,
name := "sbt-maven-resolver",
libraryDependencies ++= aetherLibs,
dependencyOverrides += plexusUtils,
sbtPlugin := true
)

View File

@ -14,6 +14,7 @@ object BasicCommandStrings {
val CompletionsCommand = "completions"
val Exit = "exit"
val Quit = "quit"
val TemplateCommand = "new"
/** The command name to terminate the program.*/
val TerminateAction: String = Exit
@ -35,6 +36,10 @@ object BasicCommandStrings {
def CompletionsDetailed = "Displays a list of completions for the given argument string (run 'completions <string>')."
def CompletionsBrief = (CompletionsCommand, CompletionsDetailed)
def templateBrief = (TemplateCommand, "Creates a new sbt build.")
def templateDetailed = TemplateCommand + """ [--options] <template>
Create a new sbt build based on the given template."""
def HistoryHelpBrief = (HistoryCommands.Start -> "History command help. Lists and describes all history commands.")
def historyHelp = Help(Nil, (HistoryHelpBrief +: HistoryCommands.descriptions).toMap, Set(HistoryCommands.Start))

View File

@ -16,7 +16,7 @@ import java.io.File
import scala.util.control.NonFatal
object BasicCommands {
lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, read, alias) ++ compatCommands
lazy val allBasicCommands = Seq(nop, ignore, help, completionsCommand, templateCommand, multi, ifLast, append, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, reboot, call, early, exit, continuous, history, shell, read, alias) ++ compatCommands
def nop = Command.custom(s => success(() => s))
def ignore = Command.command(FailureWall)(idFun)
@ -69,6 +69,31 @@ object BasicCommands {
state
}
def templateCommand = Command.make(TemplateCommand, templateBrief, templateDetailed)(templateCommandParser)
def templateCommandParser(state: State) =
{
val p = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF) map { case _ => Nil })
val trs = (state get templateResolvers) match {
case Some(trs) => trs.toList
case None => Nil
}
applyEffect(p)({ inputArg =>
val arguments = inputArg.toList ++
(state.remainingCommands.toList match {
case "shell" :: Nil => Nil
case xs => xs
})
trs find { tr =>
tr.isDefined(arguments.toArray)
} match {
case Some(tr) => tr.run(arguments.toArray)
case None =>
System.err.println("Template not found for: " + arguments.mkString(" "))
}
"exit" :: state.copy(remainingCommands = Nil)
})
}
def multiParser(s: State): Parser[Seq[String]] =
{
val nonSemi = token(charClass(_ != ';').+, hide = const(true))

View File

@ -1,6 +1,7 @@
package sbt
import java.io.File
import sbt.template.TemplateResolver
object BasicKeys {
val historyPath = AttributeKey[Option[File]]("history", "The location where command line history is persisted.", 40)
@ -10,4 +11,5 @@ object BasicKeys {
private[sbt] val classLoaderCache = AttributeKey[classpath.ClassLoaderCache]("class-loader-cache", "Caches class loaders based on the classpath entries and last modified times.", 10)
private[sbt] val OnFailureStack = AttributeKey[List[Option[String]]]("on-failure-stack", "Stack that remembers on-failure handlers.", 10)
private[sbt] val explicitGlobalLogLevels = AttributeKey[Boolean]("explicit-global-log-levels", "True if the global logging levels were explicitly set by the user.", 10)
private[sbt] val templateResolvers = AttributeKey[Seq[TemplateResolver]]("templateResolvers", "List of template resolvers.", 1000)
}

View File

@ -160,6 +160,7 @@ object Defaults extends BuildCommon {
maxErrors :== 100,
fork :== false,
initialize :== {},
templateResolvers :== Nil,
forcegc :== sys.props.get("sbt.task.forcegc").map(java.lang.Boolean.parseBoolean).getOrElse(GCUtil.defaultForceGarbageCollection),
minForcegcInterval :== GCUtil.defaultMinForcegcInterval
))

View File

@ -0,0 +1,32 @@
package sbt
import sbt.template.TemplateResolver
object Giter8TemplateResolver extends TemplateResolver {
def isDefined(args0: Array[String]): Boolean =
{
val args = args0.toList filterNot { _.startsWith("-") }
// Mandate .g8
val Github = """^([^\s/]+)/([^\s/]+?)(?:\.g8)$""".r
val Local = """^file://(\S+)(?:\.g8)(?:/)?$""".r
object GitUrl {
val NativeUrl = "^(git[@|://].*)$".r
val HttpsUrl = "^(https://.*)$".r
val HttpUrl = "^(http://.*)$".r
val SshUrl = "^(ssh://.*)$".r
def unapplySeq(s: Any): Option[List[String]] =
NativeUrl.unapplySeq(s) orElse
HttpsUrl.unapplySeq(s) orElse
HttpUrl.unapplySeq(s) orElse
SshUrl.unapplySeq(s)
}
args.headOption match {
case Some(Github(_, _)) => true
case Some(Local(_)) => true
case GitUrl(uri) => uri contains (".g8")
case _ => false
}
}
def run(args: Array[String]): Unit =
giter8.Giter8.run(args)
}

View File

@ -20,6 +20,7 @@ import testing.Framework
import Configurations.CompilerPlugin
import Types.Id
import KeyRanks._
import sbt.template.TemplateResolver
object Keys {
val TraceValues = "-1 to disable, 0 for up to the first sbt frame, or a positive number to set the maximum number of frames shown."
@ -345,6 +346,7 @@ object Keys {
val sbtVersion = SettingKey[String]("sbt-version", "Provides the version of sbt. This setting should be not be modified.", AMinusSetting)
val sbtBinaryVersion = SettingKey[String]("sbt-binary-version", "Defines the binary compatibility version substring.", BPlusSetting)
val skip = TaskKey[Boolean]("skip", "For tasks that support it (currently only 'compile' and 'update'), setting skip to true will force the task to not to do its work. This exact semantics may vary by task.", BSetting)
val templateResolvers = SettingKey[Seq[TemplateResolver]]("templateResolvers", "Template resolvers used for 'new'.", BSetting)
// special
val sessionVars = AttributeKey[SessionVar.Map]("session-vars", "Bindings that exist for the duration of the session.", Invisible)

View File

@ -83,7 +83,7 @@ object BuiltinCommands {
def ConsoleCommands: Seq[Command] = Seq(ignore, exit, IvyConsole.command, setLogLevel, early, act, nop)
def ScriptCommands: Seq[Command] = Seq(ignore, exit, Script.command, setLogLevel, early, act, nop)
def DefaultCommands: Seq[Command] = Seq(ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject,
def DefaultCommands: Seq[Command] = Seq(ignore, help, completionsCommand, about, tasks, settingsCommand, loadProject, templateCommand,
projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, setLogLevel, plugin, plugins,
ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++

View File

@ -32,7 +32,8 @@ object PluginDiscovery {
"sbt.plugins.IvyPlugin" -> sbt.plugins.IvyPlugin,
"sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin,
"sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin,
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin,
"sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin
)
val detectedAutoPugins = discover[AutoPlugin](AutoPlugins)
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPugins.modules) map {

View File

@ -7,11 +7,12 @@ import java.io.File
import java.net.URI
import java.util.Locale
import Project.{ Initialize => _, Setting => _, _ }
import Keys.{ appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, thisProject, thisProjectRef, watch }
import Keys.{ appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, templateResolvers, thisProject, thisProjectRef, watch }
import Scope.{ GlobalScope, ThisScope }
import Def.{ Flattened, Initialize, ScopedKey, Setting }
import Types.{ const, idFun }
import complete.DefaultParsers
import sbt.template.TemplateResolver
import language.experimental.macros
@ -337,10 +338,13 @@ object Project extends ProjectExtra {
val allCommands = commandsIn(ref) ++ commandsIn(BuildRef(ref.build)) ++ (commands in Global get structure.data toList)
val history = get(historyPath) flatMap idFun
val prompt = get(shellPrompt)
val trs = (templateResolvers in Global get structure.data).toList.flatten
val watched = get(watch)
val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true))
val newDefinedCommands = commandDefs ++ BasicCommands.removeTagged(s.definedCommands, projectCommand)
val newAttrs = setCond(Watched.Configuration, watched, s.attributes).put(historyPath.key, history)
val newAttrs = setCond(Watched.Configuration, watched, s.attributes).
put(historyPath.key, history).
put(templateResolvers.key, trs)
s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs), definedCommands = newDefinedCommands)
}
def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap =

View File

@ -0,0 +1,17 @@
package sbt
package plugins
import Def.Setting
import Keys._
/** An experimental plugin that adds the ability for Giter8 templates to be resolved
*/
object Giter8TemplatePlugin extends AutoPlugin {
override def requires = CorePlugin
override def trigger = allRequirements
override lazy val globalSettings: Seq[Setting[_]] =
Seq(
templateResolvers += Giter8TemplateResolver
)
}

View File

@ -0,0 +1,14 @@
### new command and templateResolvers
sbt 0.13.13 adds `new` command, which helps create a new build definition.
The `new` command is extensible via a mechanism called the template resolver,
which evaluates the arguments passed to the command to find and run a template.
As a reference implementation [Giter8][g8] is provided as follows:
sbt new eed3si9n/hello.g8
This will run eed3si9n/hello.g8 using Giter8.
[@eed3si9n]: https://github.com/eed3si9n
[g8]: http://www.foundweekends.org/giter8/

View File

@ -10,7 +10,7 @@ object Dependencies {
lazy val jline = "jline" % "jline" % "2.13"
lazy val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-2cc8d2761242b072cedb0a04cb39435c4fa24f9a"
lazy val jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive ()
lazy val jsch = "com.jcraft" % "jsch" % "0.1.50" intransitive ()
lazy val sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.4.2"
lazy val sbtSerialization = "org.scala-sbt" %% "serialization" % "0.1.2"
lazy val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value }
@ -21,6 +21,8 @@ object Dependencies {
lazy val junit = "junit" % "junit" % "4.11"
lazy val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0-M1"
lazy val rawLauncher = "org.scala-sbt" % "launcher" % "1.0.0-M1"
lazy val templateResolverApi = "org.scala-sbt" % "template-resolver" % "0.1"
lazy val giter8 = "org.foundweekends.giter8" %% "giter8" % "0.7.1"
private def scala211Module(name: String, moduleVersion: String) =
Def.setting {
@ -50,6 +52,7 @@ object Dependencies {
val guice = "com.google.inject" % "guice" % "3.0"
val guava = "com.google.guava" % "guava" % "18.0"
val javaxInject = "javax.inject" % "javax.inject" % "1"
val plexusUtils = "org.codehaus.plexus" % "plexus-utils" % "3.0.18"
//val sisuGuice = ("org.eclipse.sisu" % "sisu-guice" % "3.1.0").classifier("no_aop").exclude("javax.enterprise", "cdi-api", )