mirror of https://github.com/sbt/sbt.git
* Fix multi-command whitespace intolerance
* Add 'builder' command that changes to the builder of the project definition. * Add autoUpdate method to PluginDefinition. Override to 'false' to not update plugins automatically. Update plugins manually by running these commands: builder, update-plugins, reload
This commit is contained in:
parent
121f765da4
commit
84e23d6e61
|
|
@ -533,7 +533,7 @@ import scala.collection.{Map, mutable}
|
|||
* that conforms to Task.*/
|
||||
trait ReflectiveTasks extends Project
|
||||
{
|
||||
def tasks: Map[String, Task] = reflectiveTaskMappings
|
||||
def tasks: Map[String, ManagedTask] = reflectiveTaskMappings
|
||||
def reflectiveTaskMappings : Map[String, Task] = Reflective.reflectiveMappings[Task](this)
|
||||
}
|
||||
/** A Project that determines its method tasks by reflectively finding all vals with a type
|
||||
|
|
|
|||
|
|
@ -4,16 +4,18 @@
|
|||
package sbt
|
||||
|
||||
import BasicProjectPaths._
|
||||
import scala.collection.{immutable, Map}
|
||||
import immutable.Map.{empty => emptyMap}
|
||||
|
||||
sealed abstract class InternalProject extends Project
|
||||
{
|
||||
override def defaultLoggingLevel = Level.Warn
|
||||
override final def historyPath = None
|
||||
override def tasks: Map[String, Task] = Map.empty
|
||||
override def tasks: Map[String, ManagedTask] = emptyMap
|
||||
override final protected def disableCrossPaths = false
|
||||
override final def shouldCheckOutputDirectories = false
|
||||
}
|
||||
private sealed abstract class BasicBuilderProject extends InternalProject
|
||||
sealed abstract class BasicBuilderProject extends InternalProject
|
||||
{
|
||||
def sourceFilter = "*.scala" | "*.java"
|
||||
def jarFilter: NameFilter = "*.jar"
|
||||
|
|
@ -110,11 +112,12 @@ private sealed abstract class BasicBuilderProject extends InternalProject
|
|||
case multipleDefinitions =>Left(multipleDefinitions.mkString("Multiple " + tpe + "s detected: \n\t","\n\t","\n"))
|
||||
}
|
||||
}
|
||||
override final def methods = Map.empty
|
||||
override final def methods = emptyMap
|
||||
}
|
||||
/** The project definition used to build project definitions. */
|
||||
private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path, rawLogger: Logger) extends BasicBuilderProject
|
||||
final class BuilderProject(val info: ProjectInfo, val pluginPath: Path, rawLogger: Logger) extends BasicBuilderProject with ReflectiveTasks
|
||||
{
|
||||
override def name = "Project Definition Builder"
|
||||
lazy val pluginProject =
|
||||
{
|
||||
if(pluginPath.exists)
|
||||
|
|
@ -126,16 +129,19 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
pluginProject.map(_.pluginClasspath).getOrElse(Path.emptyPathFinder)
|
||||
def tpe = "project definition"
|
||||
|
||||
override def compileTask = super.compileTask dependsOn(pluginProject.map(_.syncPlugins).toList : _*)
|
||||
override def compileTask = super.compileTask dependsOn(pluginProject.map(_.sync).toList : _*)
|
||||
override def tasks = immutable.Map() ++ super[ReflectiveTasks].tasks ++ pluginProject.toList.flatMap { _.tasks.map { case (k,v) => (k + "-plugins", v) } }
|
||||
|
||||
final class PluginBuilderProject(val info: ProjectInfo) extends BasicBuilderProject
|
||||
final class PluginBuilderProject(val info: ProjectInfo) extends BasicBuilderProject with ReflectiveTasks
|
||||
{
|
||||
override def name = "Plugin Builder"
|
||||
lazy val pluginUptodate = propertyOptional[Boolean](false)
|
||||
def tpe = "plugin definition"
|
||||
def managedSourcePath = path(BasicDependencyPaths.DefaultManagedSourceDirectoryName)
|
||||
def managedDependencyPath = crossPath(BasicDependencyPaths.DefaultManagedDirectoryName)
|
||||
override protected def definitionChanged() { setUptodate(false) }
|
||||
private def setUptodate(flag: Boolean)
|
||||
override def tasks: Map[String, ManagedTask] = super[ReflectiveTasks].tasks
|
||||
def setUptodate(flag: Boolean)
|
||||
{
|
||||
pluginUptodate() = flag
|
||||
saveEnvironment()
|
||||
|
|
@ -143,12 +149,14 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
|
||||
private def pluginTask(f: => Option[String]) = task { if(!pluginUptodate.value) f else None }
|
||||
|
||||
lazy val syncPlugins = pluginTask(sync()) dependsOn(extractSources)
|
||||
lazy val extractSources = pluginTask(extract()) dependsOn(update)
|
||||
lazy val update = pluginTask(loadAndUpdate()) dependsOn(compile)
|
||||
lazy val sync = pluginTask(doSync()) dependsOn(extract)
|
||||
lazy val extract = pluginTask(extractSources()) dependsOn(autoUpdate)
|
||||
lazy val autoUpdate = pluginTask(loadAndUpdate(false)) dependsOn(compile)
|
||||
// manual update. force uptodate = false
|
||||
lazy val update = task { setUptodate(false); loadAndUpdate(true) } dependsOn(compile)
|
||||
|
||||
private def sync() = pluginCompileConditional.run orElse { setUptodate(true); None }
|
||||
private def extract() =
|
||||
def doSync() = pluginCompileConditional.run orElse { setUptodate(true); None }
|
||||
def extractSources() =
|
||||
{
|
||||
FileUtilities.clean(managedSourcePath, log) orElse
|
||||
Control.lazyFold(plugins.get.toList) { jar =>
|
||||
|
|
@ -159,24 +167,32 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
|
|||
}
|
||||
}
|
||||
}
|
||||
private def loadAndUpdate() =
|
||||
def loadAndUpdate(forceUpdate: Boolean) =
|
||||
{
|
||||
Control.thread(projectDefinition) {
|
||||
case Some(definition) =>
|
||||
logInfo("\nUpdating plugins...")
|
||||
val pluginInfo = ProjectInfo(info.projectPath.asFile, Nil, None)(rawLogger, info.app, info.buildScalaVersion)
|
||||
val pluginBuilder = Project.constructProject(pluginInfo, Project.getProjectClass[PluginDefinition](definition, projectClasspath, getClass.getClassLoader))
|
||||
pluginBuilder.projectName() = "Plugin builder"
|
||||
pluginBuilder.projectVersion() = OpaqueVersion("1.0")
|
||||
val result = pluginBuilder.update.run
|
||||
if(result.isEmpty)
|
||||
if(forceUpdate || pluginBuilder.autoUpdate)
|
||||
{
|
||||
atInfo {
|
||||
log.success("Plugins updated successfully.")
|
||||
log.info("")
|
||||
logInfo("\nUpdating plugins...")
|
||||
pluginBuilder.projectName() = "Plugin Definition"
|
||||
pluginBuilder.projectVersion() = OpaqueVersion("1.0")
|
||||
val result = pluginBuilder.update.run
|
||||
if(result.isEmpty)
|
||||
{
|
||||
atInfo {
|
||||
log.success("Plugins updated successfully.")
|
||||
log.info("")
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Plugin definition recompiled, but autoUpdate is disabled.\n\tUsing already retrieved plugins...")
|
||||
None
|
||||
}
|
||||
result
|
||||
case None => None
|
||||
}
|
||||
}
|
||||
|
|
@ -205,9 +221,10 @@ class PluginDefinition(val info: ProjectInfo) extends InternalProject with Basic
|
|||
{
|
||||
override def defaultLoggingLevel = Level.Info
|
||||
override final def outputPattern = "[artifact](-[revision]).[ext]"
|
||||
override final val tasks = Map("update" -> update)
|
||||
override final val tasks = immutable.Map("update" -> update)
|
||||
override def projectClasspath(config: Configuration) = Path.emptyPathFinder
|
||||
override def dependencies = info.dependencies
|
||||
def autoUpdate = true
|
||||
}
|
||||
class PluginProject(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ class xMain extends xsbti.AppMain
|
|||
{
|
||||
project.log.debug("commands " + failAction.map("(on failure: " + _ + "): ").mkString + arguments.mkString(", "))
|
||||
def rememberCurrent(newArgs: List[String]) = rememberProject(rememberFail(newArgs))
|
||||
def rememberProject(newArgs: List[String]) = if(baseProject.name != project.name) (ProjectAction + " " + project.name) :: newArgs else newArgs
|
||||
def rememberProject(newArgs: List[String]) =
|
||||
if(baseProject.name != project.name && !internal(project)) (ProjectAction + " " + project.name) :: newArgs else newArgs
|
||||
def rememberFail(newArgs: List[String]) = failAction.map(f => (FailureHandlerPrefix + f)).toList ::: newArgs
|
||||
|
||||
def tryOrFail(action: => Trampoline) = try { action } catch { case e: Exception => logCommandError(project.log, e); failed(BuildErrorExitCode) }
|
||||
|
|
@ -156,10 +157,16 @@ class xMain extends xsbti.AppMain
|
|||
arguments match
|
||||
{
|
||||
case "" :: tail => continue(project, tail, failAction)
|
||||
case x :: tail if x.startsWith(";") => continue(project, x.split(";").toList ::: tail, failAction)
|
||||
case x :: tail if x.startsWith(";") => continue(project, x.split("""\s*;\s*""").toList ::: tail, failAction)
|
||||
case (ExitCommand | QuitCommand) :: _ => result( Exit(NormalExitCode) )
|
||||
case RebootCommand :: tail => reload( tail )
|
||||
case InteractiveCommand :: _ => continue(project, prompt(baseProject, project) :: arguments, interactiveContinue)
|
||||
case BuilderCommand :: tail =>
|
||||
Project.getProjectBuilder(project.info, project.log) match
|
||||
{
|
||||
case Some(b) => project.log.info("Set current project to builder of " + project.name); continue(b, tail, failAction)
|
||||
case None => project.log.error("No project/build directory for " + project.name + ".\n Not switching to builder project."); failed(BuildErrorExitCode)
|
||||
}
|
||||
case SpecificBuild(version, action) :: tail =>
|
||||
if(Some(version) != baseProject.info.buildScalaVersion)
|
||||
{
|
||||
|
|
@ -246,6 +253,7 @@ class xMain extends xsbti.AppMain
|
|||
}
|
||||
run(process(baseProject, arguments, ExitOnFailure))
|
||||
}
|
||||
private def internal(p: Project) = p.isInstanceOf[InternalProject]
|
||||
private def isInteractive(failureActions: Option[List[String]]) = failureActions == Some(InteractiveCommand :: Nil)
|
||||
private def getSource(action: String, baseDirectory: File) =
|
||||
{
|
||||
|
|
@ -341,7 +349,7 @@ class xMain extends xsbti.AppMain
|
|||
lazy val scalaVersions = baseProject.crossScalaVersions ++ Seq(baseProject.defScalaVersion.value)
|
||||
lazy val methods = project.methods
|
||||
lazy val methodCompletions = new ExtraCompletions { def names = methods.keys.toList; def completions(name: String) = methods(name).completions }
|
||||
lazy val completors = new Completors(ProjectAction, projectNames, interactiveCommands, List(GetAction, SetAction), SpecificBuildPrefix, scalaVersions, prefixes, project.taskNames, project.propertyNames, methodCompletions)
|
||||
lazy val completors = new Completors(ProjectAction, projectNames, basicCommands, List(GetAction, SetAction), SpecificBuildPrefix, scalaVersions, prefixes, project.taskNames, project.propertyNames, methodCompletions)
|
||||
val reader = new LazyJLineReader(baseProject.historyPath, MainCompletor(completors), baseProject.log)
|
||||
reader.readLine("> ").getOrElse(ExitCommand)
|
||||
}
|
||||
|
|
@ -358,6 +366,7 @@ class xMain extends xsbti.AppMain
|
|||
val ShowProjectsAction = "projects"
|
||||
val ExitCommand = "exit"
|
||||
val QuitCommand = "quit"
|
||||
val BuilderCommand = "builder"
|
||||
val InteractiveCommand = "shell"
|
||||
/** The list of lowercase command names that may be used to terminate the program.*/
|
||||
val TerminateActions: Iterable[String] = ExitCommand :: QuitCommand :: Nil
|
||||
|
|
@ -391,14 +400,11 @@ class xMain extends xsbti.AppMain
|
|||
/** The number of seconds between polling by the continuous compile command.*/
|
||||
val ContinuousCompilePollDelaySeconds = 1
|
||||
|
||||
/** The list of all available commands at the interactive prompt in addition to the tasks defined
|
||||
* by a project.*/
|
||||
protected def interactiveCommands: Iterable[String] = basicCommands.toList ++ logLevels.toList ++ TerminateActions
|
||||
/** The list of logging levels.*/
|
||||
private def logLevels: Iterable[String] = TreeSet.empty[String] ++ Level.levels.map(_.toString)
|
||||
/** The list of all interactive commands other than logging level.*/
|
||||
private def basicCommands: Iterable[String] = TreeSet(ShowProjectsAction, ShowActions, ShowCurrent, HelpAction,
|
||||
RebootCommand, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction)
|
||||
RebootCommand, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction, BuilderCommand) ++ logLevels.toList ++ TerminateActions
|
||||
|
||||
private def processAction(baseProject: Project, currentProject: Project, action: String, isInteractive: Boolean): Boolean =
|
||||
action match
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
|
|||
final type ManagerType = Project
|
||||
final type ManagedTask = Project#Task
|
||||
/** The tasks declared on this project. */
|
||||
def tasks: Map[String, Task]
|
||||
def tasks: Map[String, ManagedTask]
|
||||
/** The task methods declared on this project */
|
||||
def methods: Map[String, MethodTask]
|
||||
/** The names of all available tasks that may be called through `act`. These include
|
||||
|
|
|
|||
Loading…
Reference in New Issue