* 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:
Mark Harrah 2010-04-08 18:46:33 -04:00
parent 121f765da4
commit 84e23d6e61
4 changed files with 55 additions and 32 deletions

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -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