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( settings(
testedBaseSettings, testedBaseSettings,
name := "Command", 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 // 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, baseSettings,
name := "sbt-maven-resolver", name := "sbt-maven-resolver",
libraryDependencies ++= aetherLibs, libraryDependencies ++= aetherLibs,
dependencyOverrides += plexusUtils,
sbtPlugin := true sbtPlugin := true
) )

View File

@ -14,6 +14,7 @@ object BasicCommandStrings {
val CompletionsCommand = "completions" val CompletionsCommand = "completions"
val Exit = "exit" val Exit = "exit"
val Quit = "quit" val Quit = "quit"
val TemplateCommand = "new"
/** The command name to terminate the program.*/ /** The command name to terminate the program.*/
val TerminateAction: String = Exit 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 CompletionsDetailed = "Displays a list of completions for the given argument string (run 'completions <string>')."
def CompletionsBrief = (CompletionsCommand, CompletionsDetailed) 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 HistoryHelpBrief = (HistoryCommands.Start -> "History command help. Lists and describes all history commands.")
def historyHelp = Help(Nil, (HistoryHelpBrief +: HistoryCommands.descriptions).toMap, Set(HistoryCommands.Start)) 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 import scala.util.control.NonFatal
object BasicCommands { 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 nop = Command.custom(s => success(() => s))
def ignore = Command.command(FailureWall)(idFun) def ignore = Command.command(FailureWall)(idFun)
@ -69,6 +69,31 @@ object BasicCommands {
state 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]] = def multiParser(s: State): Parser[Seq[String]] =
{ {
val nonSemi = token(charClass(_ != ';').+, hide = const(true)) val nonSemi = token(charClass(_ != ';').+, hide = const(true))

View File

@ -1,6 +1,7 @@
package sbt package sbt
import java.io.File import java.io.File
import sbt.template.TemplateResolver
object BasicKeys { object BasicKeys {
val historyPath = AttributeKey[Option[File]]("history", "The location where command line history is persisted.", 40) 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 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 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 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, maxErrors :== 100,
fork :== false, fork :== false,
initialize :== {}, initialize :== {},
templateResolvers :== Nil,
forcegc :== sys.props.get("sbt.task.forcegc").map(java.lang.Boolean.parseBoolean).getOrElse(GCUtil.defaultForceGarbageCollection), forcegc :== sys.props.get("sbt.task.forcegc").map(java.lang.Boolean.parseBoolean).getOrElse(GCUtil.defaultForceGarbageCollection),
minForcegcInterval :== GCUtil.defaultMinForcegcInterval 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 Configurations.CompilerPlugin
import Types.Id import Types.Id
import KeyRanks._ import KeyRanks._
import sbt.template.TemplateResolver
object Keys { 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." 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 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 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 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 // special
val sessionVars = AttributeKey[SessionVar.Map]("session-vars", "Bindings that exist for the duration of the session.", Invisible) 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 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 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, projects, project, reboot, read, history, set, sessionCommand, inspect, loadProjectImpl, loadFailed, Cross.crossBuild, Cross.switchVersion,
setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, setLogLevel, plugin, plugins, setOnFailure, clearOnFailure, stashOnFailure, popOnFailure, setLogLevel, plugin, plugins,
ifLast, multi, shell, continuous, eval, alias, append, last, lastGrep, export, boot, nop, call, exit, early, initialize, act) ++ 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.IvyPlugin" -> sbt.plugins.IvyPlugin,
"sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin, "sbt.plugins.JvmPlugin" -> sbt.plugins.JvmPlugin,
"sbt.plugins.CorePlugin" -> sbt.plugins.CorePlugin, "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 detectedAutoPugins = discover[AutoPlugin](AutoPlugins)
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPugins.modules) map { val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPugins.modules) map {

View File

@ -7,11 +7,12 @@ import java.io.File
import java.net.URI import java.net.URI
import java.util.Locale import java.util.Locale
import Project.{ Initialize => _, Setting => _, _ } 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 Scope.{ GlobalScope, ThisScope }
import Def.{ Flattened, Initialize, ScopedKey, Setting } import Def.{ Flattened, Initialize, ScopedKey, Setting }
import Types.{ const, idFun } import Types.{ const, idFun }
import complete.DefaultParsers import complete.DefaultParsers
import sbt.template.TemplateResolver
import language.experimental.macros 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 allCommands = commandsIn(ref) ++ commandsIn(BuildRef(ref.build)) ++ (commands in Global get structure.data toList)
val history = get(historyPath) flatMap idFun val history = get(historyPath) flatMap idFun
val prompt = get(shellPrompt) val prompt = get(shellPrompt)
val trs = (templateResolvers in Global get structure.data).toList.flatten
val watched = get(watch) val watched = get(watch)
val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true)) val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true))
val newDefinedCommands = commandDefs ++ BasicCommands.removeTagged(s.definedCommands, projectCommand) 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) s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs), definedCommands = newDefinedCommands)
} }
def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap = 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 jline = "jline" % "jline" % "2.13"
lazy val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-2cc8d2761242b072cedb0a04cb39435c4fa24f9a" 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 sbinary = "org.scala-tools.sbinary" %% "sbinary" % "0.4.2"
lazy val sbtSerialization = "org.scala-sbt" %% "serialization" % "0.1.2" lazy val sbtSerialization = "org.scala-sbt" %% "serialization" % "0.1.2"
lazy val scalaCompiler = Def.setting { "org.scala-lang" % "scala-compiler" % scalaVersion.value } 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 junit = "junit" % "junit" % "4.11"
lazy val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0-M1" 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 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) = private def scala211Module(name: String, moduleVersion: String) =
Def.setting { Def.setting {
@ -50,6 +52,7 @@ object Dependencies {
val guice = "com.google.inject" % "guice" % "3.0" val guice = "com.google.inject" % "guice" % "3.0"
val guava = "com.google.guava" % "guava" % "18.0" val guava = "com.google.guava" % "guava" % "18.0"
val javaxInject = "javax.inject" % "javax.inject" % "1" 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", ) //val sisuGuice = ("org.eclipse.sisu" % "sisu-guice" % "3.1.0").classifier("no_aop").exclude("javax.enterprise", "cdi-api", )