* Make tab completion of method tasks lazy, so that javap and test-only don't slow down initial tab completion.

* cleanup of builder related code, including removing duplication from classpath in console-project
 * only replace relevant version in build.scala.versions when user enters new Scala version to replace bad entry
This commit is contained in:
Mark Harrah 2010-02-13 16:29:09 -05:00
parent a863e866d5
commit 8bea7ec316
8 changed files with 58 additions and 35 deletions

View File

@ -1,5 +1,5 @@
project.organization=org.scala-tools.sbt
project.name=Simple Build Tool
sbt.version=0.5.6
project.version=0.6.13
project.version=0.7.0-SNAPSHOT
scala.version=2.7.7

View File

@ -576,7 +576,7 @@ object Reflective
{
def reflectiveMappings[T](obj: AnyRef)(implicit m: scala.reflect.Manifest[T]): Map[String, T] =
{
val mappings = new mutable.OpenHashMap[String, T]
val mappings = new mutable.HashMap[String, T]
for ((name, value) <- ReflectUtilities.allVals[T](obj))
mappings(ReflectUtilities.transformCamelCase(name, '-')) = value
mappings

View File

@ -59,7 +59,7 @@ private sealed abstract class BasicBuilderProject extends InternalProject
import xsbt.ScalaInstance
val definitionCompileConditional = new BuilderCompileConditional(definitionCompileConfiguration, buildCompiler, tpe)
lazy val definitionCompileConditional = new BuilderCompileConditional(definitionCompileConfiguration, buildCompiler, tpe)
final class BuilderCompileConditional(config: BuilderCompileConfiguration, compiler: xsbt.AnalyzingCompiler, tpe: String) extends AbstractCompileConditional(config, compiler)
{
type AnalysisType = BuilderCompileAnalysis
@ -111,9 +111,9 @@ private sealed abstract class BasicBuilderProject extends InternalProject
override final def methods = Map.empty
}
/** The project definition used to build project definitions. */
private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path, additional: PathFinder, rawLogger: Logger) extends BasicBuilderProject
private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path, rawLogger: Logger) extends BasicBuilderProject
{
private lazy val pluginProject =
lazy val pluginProject =
{
if(pluginPath.exists)
Some(new PluginBuilderProject(ProjectInfo(pluginPath.asFile, Nil, None)(rawLogger, info.app, info.buildScalaVersion)))
@ -121,15 +121,14 @@ private final class BuilderProject(val info: ProjectInfo, val pluginPath: Path,
None
}
override def projectClasspath = super.projectClasspath +++
pluginProject.map(_.pluginClasspath).getOrElse(Path.emptyPathFinder) +++
additional
pluginProject.map(_.pluginClasspath).getOrElse(Path.emptyPathFinder)
def tpe = "project definition"
override def compileTask = super.compileTask dependsOn(pluginProject.map(_.syncPlugins).toList : _*)
final class PluginBuilderProject(val info: ProjectInfo) extends BasicBuilderProject
{
val pluginUptodate = propertyOptional[Boolean](false)
lazy val pluginUptodate = propertyOptional[Boolean](false)
def tpe = "plugin definition"
def managedSourcePath = path(BasicDependencyPaths.DefaultManagedSourceDirectoryName)
def managedDependencyPath = crossPath(BasicDependencyPaths.DefaultManagedDirectoryName)

View File

@ -10,8 +10,15 @@ trait LineReader extends NotNull
class Completors(val projectAction: String, val projectNames: Iterable[String],
val generalCommands: Iterable[String], val propertyActions: Iterable[String],
val specificPrefix: String, val scalaVersions: Iterable[String],
val prefixes: Iterable[String], val taskNames: Iterable[String],
val propertyNames: Iterable[String], val extra: Iterable[(String, Iterable[String])]) extends NotNull
val prefixes: Iterable[String], val taskNames: Iterable[String],
val propertyNames: Iterable[String], val extra: ExtraCompletions) extends NotNull
trait ExtraCompletions extends NotNull
{
def names: Iterable[String]
def completions(name: String): Iterable[String]
}
import jline.{Completor, ConsoleReader}
abstract class JLine extends LineReader
@ -91,8 +98,10 @@ object MainCompletor
val specific = simpleCompletor(specificPrefix :: Nil) // TODO
new ArgumentCompletor( Array( specific, simpleCompletor(scalaVersions), baseCompletor ) )
}
def extraCompletor(name: String) =
repeatedArgumentCompletor(simpleCompletor(name :: Nil), new LazyCompletor(simpleCompletor(extra.completions(name))))
val taskCompletor = simpleCompletor(TreeSet(taskNames.toSeq : _*))
val extraCompletors = for( (first, repeat) <- extra) yield repeatedArgumentCompletor(simpleCompletor(first :: Nil), simpleCompletor(repeat))
val extraCompletors = extra.names.map(extraCompletor)
val baseCompletors = generalCompletor :: taskCompletor :: projectCompletor :: propertyCompletor(propertyNames) :: extraCompletors.toList
val baseCompletor = new MultiCompletor(baseCompletors.toArray)

View File

@ -310,7 +310,8 @@ class xMain extends xsbti.AppMain
lazy val projectNames = baseProject.projectClosure.map(_.name)
val prefixes = ContinuousExecutePrefix :: CrossBuildPrefix :: Nil
lazy val scalaVersions = baseProject.crossScalaVersions ++ Seq(baseProject.defScalaVersion.value)
lazy val methodCompletions = for( (name, method) <- project.methods) yield (name, method.completions)
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)
val reader = new LazyJLineReader(baseProject.historyPath, MainCompletor(completors), baseProject.log)
reader.readLine("> ").getOrElse(ExitCommand)

View File

@ -233,16 +233,21 @@ trait Project extends TaskManager with Dag[Project] with BasicEnvironment
def buildScalaInstance = buildScalaInstance0
final def buildScalaInstance0: ScalaInstance =
{
try { getScalaInstance(buildScalaVersion) }
val scalaVersion = buildScalaVersion
try { getScalaInstance(scalaVersion) }
catch { case e: xsbti.RetrieveException if info.buildScalaVersion.isEmpty => // only catch the exception if this is the default Scala version
log.error(e.getMessage)
SimpleReader.readLine("\nProvide a new Scala version or press enter to exit: ") match
{
case Some(v) if v.length > 0=> buildScalaVersions() = v; saveEnvironment(); buildScalaInstance0
case Some(v) if v.length > 0=>
buildScalaVersions() = replace(scalaVersion, v)
saveEnvironment()
buildScalaInstance0
case _ => throw e
}
}
}
private def replace(originalV: String, newV: String) = buildScalaVersions.value.replaceAll("""\b\Q""" + originalV + """\E\b""", newV)
def getScalaInstance(version: String) =
localScalaInstances.find(_.version == version) getOrElse
xsbt.ScalaInstance(version, info.launcher)
@ -407,28 +412,40 @@ object Project
}
val useName = p.projectName.get.getOrElse("at " + p.info.projectDirectory.getAbsolutePath)
checkDependencies(useName, p.info.dependencies, log)
p.buildScalaInstance // done so that build Scala version is initialized on project startup
p
}
/** Compiles the project definition classes and returns the project definition class name
* and the class loader that should be used to load the definition. */
private def getProjectDefinition(info: ProjectInfo, buildLog: Logger): Either[String, Class[P] forSome { type P <: Project }] =
{
val builderProjectPath = info.builderPath / BuilderProjectDirectoryName
if(builderProjectPath.asFile.isDirectory)
getProjectBuilder(info, buildLog) match
{
val pluginProjectPath = info.builderPath / PluginProjectDirectoryName
val additionalPaths = info.sbtClasspath
val builderInfo = ProjectInfo(builderProjectPath.asFile, Nil, None)(buildLog, info.app, Some(info.definitionScalaVersion))
val builderProject = new BuilderProject(builderInfo, pluginProjectPath, additionalPaths, buildLog)
builderProject.compile.run.toLeft(()).right.flatMap { ignore =>
builderProject.projectDefinition.right.map {
case Some(definition) => getProjectClass[Project](definition, builderProject.projectClasspath, getClass.getClassLoader)
case None => DefaultBuilderClass
}
case Some(builder) => buildProjectDefinition(builder)
case None => Right(DefaultBuilderClass)
}
private def buildProjectDefinition(builderProject: BuilderProject): Either[String, Class[P] forSome { type P <: Project }] =
builderProject.compile.run.toLeft(()).right.flatMap { ignore =>
builderProject.projectDefinition.right.map {
case Some(definition) => getProjectClass[Project](definition, builderProject.projectClasspath, getClass.getClassLoader)
case None => DefaultBuilderClass
}
}
private[sbt] def getProjectClasspath(project: Project): PathFinder =
getProjectBuilder(project.info, project.log) match
{
case Some(builder) => builder.projectClasspath
case None => project.info.sbtClasspath
}
private[sbt] def getProjectBuilder(info: ProjectInfo, buildLog: Logger): Option[BuilderProject] =
{
if(info.builderProjectPath.asFile.isDirectory)
{
val builderInfo = ProjectInfo(info.builderProjectPath.asFile, Nil, None)(buildLog, info.app, Some(info.definitionScalaVersion))
val builderProject = new BuilderProject(builderInfo, info.pluginsPath, buildLog)
Some(builderProject)
}
else
Right(DefaultBuilderClass)
None
}
/** Verifies that the given list of project dependencies contains no nulls. The
* String argument should be the project name with the dependencies.*/

View File

@ -109,11 +109,8 @@ object Run
override def createInterpreter()
{
val projectLoader = project.getClass.getClassLoader
val launcherJar = FileUtilities.classLocationFile[xsbti.Launcher]
val app = project.info.app
val projectJars: Array[File] = projectLoader.asInstanceOf[URLClassLoader].getURLs.flatMap(ClasspathUtilities.asFile).toArray[File]
val classpathFiles = app.mainClasspath ++ app.scalaProvider.jars ++ Array(launcherJar) ++ projectJars
compilerSettings.classpath.value = classpathFiles.map(_.getAbsolutePath).mkString(File.pathSeparator)
val classpath = Project.getProjectClasspath(project)
compilerSettings.classpath.value = Path.makeString(classpath.get ++ Path.fromFiles(project.info.app.scalaProvider.jars))
project.log.debug(" Compiler classpath: " + compilerSettings.classpath.value)
in = InteractiveReader.createDefault()

View File

@ -125,7 +125,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
val Private = Value("private")
}
def javapTask(classpath: PathFinder, conditional: CompileConditional, compilePath: Path) =
def javapTask(classpath: PathFinder, conditional: => CompileConditional, compilePath: Path) =
task { args =>
val cp = classpath +++ Path.fromFile(FileUtilities.scalaLibraryJar) +++ Path.fromFile(FileUtilities.scalaCompilerJar)
execOut { Process("javap" :: "-classpath" :: Path.makeString(cp.get) :: args.toList) }
@ -179,9 +179,9 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
}
private def toTask(testTask: NamedTestTask) = task(testTask.run()) named(testTask.name)
def graphSourcesTask(outputDirectory: Path, roots: PathFinder, analysis: CompileAnalysis): Task =
def graphSourcesTask(outputDirectory: Path, roots: PathFinder, analysis: => CompileAnalysis): Task =
task { DotGraph.sources(analysis, outputDirectory, roots.get, log) }
def graphPackagesTask(outputDirectory: Path, roots: PathFinder, analysis: CompileAnalysis): Task =
def graphPackagesTask(outputDirectory: Path, roots: PathFinder, analysis: => CompileAnalysis): Task =
task { DotGraph.packages(analysis, outputDirectory, roots.get, log) }
def scaladocTask(label: String, sources: PathFinder, outputDirectory: Path, classpath: PathFinder, options: ScaladocOption*): Task =
scaladocTask(label, sources, outputDirectory, classpath, options)