mirror of https://github.com/sbt/sbt.git
enhance 'projects' to allow temporarily adding/removing builds to the session
This commit is contained in:
parent
0b1297d65f
commit
12d72facb1
|
|
@ -161,8 +161,20 @@ ProjectCommand +
|
|||
Use n+1 dots to change to the nth parent.
|
||||
For example, 'project ....' is equivalent to three consecutive 'project ..' commands."""
|
||||
|
||||
def projectsBrief = projectsDetailed
|
||||
def projectsDetailed = "Displays the names of available projects."
|
||||
def projectsBrief = "Displays the names of available projects or temporarily adds/removes extra builds to the session."
|
||||
def projectsDetailed =
|
||||
ProjectsCommand + """
|
||||
Displays the names of available builds and the projects defined in those builds.
|
||||
|
||||
""" + ProjectsCommand + """ add <URI>+
|
||||
Adds the builds at the provided URIs to this session.
|
||||
These builds may be selected using the """ + ProjectCommand + """ command.
|
||||
Alternatively, tasks from these builds may be run using the explicit syntax {URI}project/task
|
||||
|
||||
""" + ProjectsCommand + """ remove <URI>+
|
||||
Removes extra builds from this session.
|
||||
Builds explicitly listed in the build definition are not affected by this command.
|
||||
"""
|
||||
|
||||
def sbtrc = ".sbtrc"
|
||||
|
||||
|
|
|
|||
|
|
@ -27,14 +27,14 @@ object Load
|
|||
import Locate.DefinesClass
|
||||
|
||||
// note that there is State passed in but not pulled out
|
||||
def defaultLoad(state: State, baseDirectory: File, log: Logger, isPlugin: Boolean = false): (() => Eval, BuildStructure) =
|
||||
def defaultLoad(state: State, baseDirectory: File, log: Logger, isPlugin: Boolean = false, topLevelExtras: List[URI] = Nil): (() => Eval, BuildStructure) =
|
||||
{
|
||||
val globalBase = getGlobalBase(state)
|
||||
val base = baseDirectory.getCanonicalFile
|
||||
val definesClass = FileValueCache(Locate.definesClass _)
|
||||
val rawConfig = defaultPreGlobal(state, base, definesClass.get, globalBase, log)
|
||||
val config0 = defaultWithGlobal(state, base, rawConfig, globalBase, log)
|
||||
val config = if(isPlugin) enableSbtPlugin(config0) else config0
|
||||
val config = if(isPlugin) enableSbtPlugin(config0) else config0.copy(extraBuilds = topLevelExtras)
|
||||
val result = apply(base, state, config)
|
||||
definesClass.clear()
|
||||
result
|
||||
|
|
@ -51,7 +51,8 @@ object Load
|
|||
val delegates = defaultDelegates
|
||||
val pluginMgmt = PluginManagement(loader)
|
||||
val inject = InjectSettings(injectGlobal(state), Nil, const(Nil))
|
||||
new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass, delegates, EvaluateTask.injectStreams, pluginMgmt, inject, None, log)
|
||||
new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass, delegates,
|
||||
EvaluateTask.injectStreams, pluginMgmt, inject, None, Nil, log)
|
||||
}
|
||||
def injectGlobal(state: State): Seq[Project.Setting[_]] =
|
||||
(appConfiguration in GlobalScope :== state.configuration) +:
|
||||
|
|
@ -235,7 +236,7 @@ object Load
|
|||
if(srcs.isEmpty) const(Nil) else EvaluateConfigurations(eval(), srcs, imports)
|
||||
|
||||
def load(file: File, s: State, config: LoadBuildConfiguration): PartBuild =
|
||||
load(file, builtinLoader(s, config.copy(pluginManagement = config.pluginManagement.shift) ))
|
||||
load(file, builtinLoader(s, config.copy(pluginManagement = config.pluginManagement.shift, extraBuilds = Nil)), config.extraBuilds.toList )
|
||||
def builtinLoader(s: State, config: LoadBuildConfiguration): BuildLoader =
|
||||
{
|
||||
val fail = (uri: URI) => error("Invalid build URI (no handler available): " + uri)
|
||||
|
|
@ -244,11 +245,11 @@ object Load
|
|||
val components = BuildLoader.components(resolver, build, full = BuildLoader.componentLoader)
|
||||
BuildLoader(components, fail, s, config)
|
||||
}
|
||||
def load(file: File, loaders: BuildLoader): PartBuild = loadURI(IO.directoryURI(file), loaders)
|
||||
def loadURI(uri: URI, loaders: BuildLoader): PartBuild =
|
||||
def load(file: File, loaders: BuildLoader, extra: List[URI]): PartBuild = loadURI(IO.directoryURI(file), loaders, extra)
|
||||
def loadURI(uri: URI, loaders: BuildLoader, extra: List[URI]): PartBuild =
|
||||
{
|
||||
IO.assertAbsolute(uri)
|
||||
val (referenced, map, newLoaders) = loadAll(uri :: Nil, Map.empty, loaders, Map.empty)
|
||||
val (referenced, map, newLoaders) = loadAll(uri :: extra, Map.empty, loaders, Map.empty)
|
||||
checkAll(referenced, map)
|
||||
val build = new PartBuild(uri, map)
|
||||
newLoaders transformAll build
|
||||
|
|
@ -651,7 +652,8 @@ object Load
|
|||
final case class LoadBuildConfiguration(stagingDirectory: File, classpath: Seq[Attributed[File]], loader: ClassLoader,
|
||||
compilers: Compilers, evalPluginDef: (BuildStructure, State) => PluginData, definesClass: DefinesClass,
|
||||
delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal,
|
||||
pluginManagement: PluginManagement, injectSettings: InjectSettings, globalPlugin: Option[GlobalPlugin], log: Logger)
|
||||
pluginManagement: PluginManagement, injectSettings: InjectSettings, globalPlugin: Option[GlobalPlugin], extraBuilds: Seq[URI],
|
||||
log: Logger)
|
||||
{
|
||||
lazy val (globalPluginClasspath, globalPluginLoader) = pluginDefinitionLoader(this, Load.globalPluginClasspath(globalPlugin))
|
||||
lazy val globalPluginNames = if(globalPluginClasspath.isEmpty) Nil else getPluginNames(globalPluginClasspath, globalPluginLoader)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package sbt
|
|||
import compiler.EvalImports
|
||||
import Types.{const,idFun}
|
||||
import Aggregation.AnyKeys
|
||||
import Project.LoadAction
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import Path._
|
||||
|
|
@ -317,13 +318,35 @@ object BuiltinCommands
|
|||
else
|
||||
Help.empty
|
||||
|
||||
def projects = Command.command(ProjectsCommand, projectsBrief, projectsDetailed ) { s =>
|
||||
def projects = Command(ProjectsCommand, (ProjectsCommand, projectsBrief), projectsDetailed )(s => projectsParser(s).?) {
|
||||
case (s, Some(modifyBuilds)) => transformExtraBuilds(s, modifyBuilds)
|
||||
case (s, None) => showProjects(s); s
|
||||
}
|
||||
def showProjects(s: State)
|
||||
{
|
||||
val extracted = Project extract s
|
||||
import extracted._
|
||||
import currentRef.{build => curi, project => cid}
|
||||
listBuild(curi, structure.units(curi), true, cid, s.log)
|
||||
for( (uri, build) <- structure.units if curi != uri) listBuild(uri, build, false, cid, s.log)
|
||||
s
|
||||
}
|
||||
def transformExtraBuilds(s: State, f: List[URI] => List[URI]): State =
|
||||
{
|
||||
val original = Project.extraBuilds(s)
|
||||
val extraUpdated = Project.updateExtraBuilds(s, f)
|
||||
try doLoadProject(extraUpdated, LoadAction.Current)
|
||||
catch { case e: Exception =>
|
||||
s.log.error("Project loading failed: reverting to previous state.")
|
||||
Project.setExtraBuilds(s, original)
|
||||
}
|
||||
}
|
||||
|
||||
def projectsParser(s: State): Parser[List[URI] => List[URI]] =
|
||||
{
|
||||
val addBase = token(Space ~> "add") ~> token(Space ~> basicUri, "<build URI>").+
|
||||
val removeBase = token(Space ~> "remove") ~> token(Space ~> Uri(Project.extraBuilds(s).toSet) ).+
|
||||
addBase.map(toAdd => (xs: List[URI]) => (toAdd.toList ::: xs).removeDuplicates) |
|
||||
removeBase.map(toRemove => (xs: List[URI]) => xs.filterNot(toRemove.toSet))
|
||||
}
|
||||
|
||||
def project = Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
|
||||
|
|
@ -356,10 +379,12 @@ object BuiltinCommands
|
|||
def loadProjectCommands(arg: String) = (OnFailure + " " + LoadFailed) :: (LoadProjectImpl + " " + arg).trim :: ClearOnFailure :: FailureWall :: Nil
|
||||
def loadProject = Command(LoadProject, LoadProjectBrief, LoadProjectDetailed)(_ => matched(Project.loadActionParser)) { (s,arg) => loadProjectCommands(arg) ::: s }
|
||||
|
||||
def loadProjectImpl = Command(LoadProjectImpl)(_ => Project.loadActionParser) { (s0, action) =>
|
||||
def loadProjectImpl = Command(LoadProjectImpl)(_ => Project.loadActionParser)( doLoadProject )
|
||||
def doLoadProject(s0: State, action: LoadAction.Value): State =
|
||||
{
|
||||
val (s, base) = Project.loadAction(SessionVar.clear(s0), action)
|
||||
IO.createDirectory(base)
|
||||
val (eval, structure) = Load.defaultLoad(s, base, s.log, Project.inPluginProject(s))
|
||||
val (eval, structure) = Load.defaultLoad(s, base, s.log, Project.inPluginProject(s), Project.extraBuilds(s))
|
||||
val session = Load.initialSession(structure, eval, s0)
|
||||
SessionSettings.checkSession(session, s)
|
||||
Project.setProject(session, structure, s)
|
||||
|
|
|
|||
|
|
@ -361,6 +361,14 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
extracted.session.appendRaw(settings flatMap { x => rescope(x) } )
|
||||
}
|
||||
|
||||
val ExtraBuilds = AttributeKey[List[URI]]("extra-builds", "Extra build URIs to load in addition to the ones defined by the project.")
|
||||
def extraBuilds(s: State): List[URI] = getOrNil(s, ExtraBuilds)
|
||||
def getOrNil[T](s: State, key: AttributeKey[List[T]]): List[T] = s get key getOrElse Nil
|
||||
def setExtraBuilds(s: State, extra: List[URI]): State = s.put(ExtraBuilds, extra)
|
||||
def addExtraBuilds(s: State, extra: List[URI]): State = setExtraBuilds(s, extra ::: extraBuilds(s))
|
||||
def removeExtraBuilds(s: State, remove: List[URI]): State = updateExtraBuilds(s, _.filterNot(remove.toSet))
|
||||
def updateExtraBuilds(s: State, f: List[URI] => List[URI]): State = setExtraBuilds(s, f(extraBuilds(s)))
|
||||
|
||||
object LoadAction extends Enumeration {
|
||||
val Return, Current, Plugins = Value
|
||||
}
|
||||
|
|
@ -370,7 +378,7 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
val loadActionParser = token(Space ~> ("plugins" ^^^ Plugins | "return" ^^^ Return)) ?? Current
|
||||
|
||||
val ProjectReturn = AttributeKey[List[File]]("project-return", "Maintains a stack of builds visited using reload.")
|
||||
def projectReturn(s: State): List[File] = s get ProjectReturn getOrElse Nil
|
||||
def projectReturn(s: State): List[File] = getOrNil(s, ProjectReturn)
|
||||
def inPluginProject(s: State): Boolean = projectReturn(s).toList.length > 1
|
||||
def setProjectReturn(s: State, pr: List[File]): State = s.copy(attributes = s.attributes.put( ProjectReturn, pr) )
|
||||
def loadAction(s: State, action: LoadAction.Value) = action match {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ package sbt
|
|||
/**
|
||||
Data structure representing all command execution information.
|
||||
|
||||
@param `configuration` provides access to the launcher environment, including the application configuration, Scala versions, jvm/filesystem wide locking, and the launcher itself
|
||||
@param configuration provides access to the launcher environment, including the application configuration, Scala versions, jvm/filesystem wide locking, and the launcher itself
|
||||
@param definedCommands the list of command definitions that evaluate command strings. These may be modified to change the available commands.
|
||||
@param onFailure the command to execute when another command fails. `onFailure` is cleared before the failure handling command is executed.
|
||||
@param remainingCommands the sequence of commands to execute. This sequence may be modified to change the commands to be executed. Typically, the `::` and `:::` methods are used to prepend new commands to run.
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ trait Parsers
|
|||
def flag[T](p: Parser[T]): Parser[Boolean] = (p ^^^ true) ?? false
|
||||
|
||||
def trimmed(p: Parser[String]) = p map { _.trim }
|
||||
def Uri(ex: Set[URI]) = mapOrFail(URIClass)( uri => new URI(uri)) examples(ex.map(_.toString))
|
||||
lazy val basicUri = mapOrFail(URIClass)( uri => new URI(uri))
|
||||
def Uri(ex: Set[URI]) = basicUri examples(ex.map(_.toString))
|
||||
}
|
||||
object Parsers extends Parsers
|
||||
object DefaultParsers extends Parsers with ParserMain
|
||||
|
|
|
|||
Loading…
Reference in New Issue