mirror of https://github.com/sbt/sbt.git
Merge pull request #2705 from eed3si9n/wip/new_command
Adds templateResolvers and `new` command
This commit is contained in:
commit
240781566e
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) ++
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -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/
|
||||
|
|
@ -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", )
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue