From d339959ff129d9c3ccabad25d924618d21b7a471 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Sun, 2 Aug 2009 18:01:22 +0000 Subject: More work on build manager. --- src/compiler/scala/tools/nsc/Global.scala | 35 ++++++- .../nsc/dependencies/DependencyAnalysis.scala | 42 +++++--- .../scala/tools/nsc/dependencies/Files.scala | 69 +++++--------- .../scala/tools/nsc/interactive/BuildManager.scala | 5 +- .../nsc/interactive/RefinedBuildManager.scala | 106 ++++++++++++--------- .../tools/nsc/interactive/SimpleBuildManager.scala | 11 ++- 6 files changed, 150 insertions(+), 118 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 20053d112b..9ceb470565 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -221,7 +221,27 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable case x => val jfile = new java.io.File(x) if (!jfile.exists) jfile.createNewFile - else dependencyAnalysis.loadFrom(AbstractFile.getFile(jfile)) + else { + // This logic moved here from scala.tools.nsc.dependencies.File. + // Note that it will trip an assertion in lookupPathUnchecked + // if the path being looked at is absolute. + + /** The directory where file lookup should start at. */ + val rootDirectory: AbstractFile = { + AbstractFile.getDirectory(".") +// val roots = java.io.File.listRoots() +// assert(roots.length > 0) +// new PlainFile(roots(0)) + } + + def toFile(path: String) = { + val file = rootDirectory.lookupPathUnchecked(path, false) + assert(file ne null, path) + file + } + + dependencyAnalysis.loadFrom(AbstractFile.getFile(jfile), toFile) + } } lazy val classPath0 = new ClassPath(false && onlyPresentation) @@ -830,8 +850,17 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable for ((sym, file) <- symSource.iterator) resetPackageClass(sym.owner) informTime("total", startTime) - if (!dependencyAnalysis.off) - dependencyAnalysis.saveDependencies() + if (!dependencyAnalysis.off) { + + def fromFile(file: AbstractFile): String = { + val path = file.path + if (path.startsWith("./")) + path.substring(2, path.length) + else path + } + + dependencyAnalysis.saveDependencies(fromFile) + } } /** Compile list of abstract files */ diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala index b6ad064f0f..ec1bacfd59 100644 --- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala +++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala @@ -38,6 +38,8 @@ trait DependencyAnalysis extends SubComponent with Files { var dependencies = newDeps + def managedFiles = dependencies.dependencies.keySet + /** Top level definitions per source file. */ val definitions: mutable.Map[AbstractFile, List[Symbol]] = new mutable.HashMap[AbstractFile, List[Symbol]] { @@ -45,28 +47,34 @@ trait DependencyAnalysis extends SubComponent with Files { } /** External references used by source file. */ - var references: immutable.Map[AbstractFile, immutable.Set[String]] = - new immutable.HashMap[AbstractFile, immutable.Set[String]] { + val references: mutable.Map[AbstractFile, immutable.Set[String]] = + new mutable.HashMap[AbstractFile, immutable.Set[String]] { override def default(f : AbstractFile) = immutable.Set() } /** Write dependencies to the current file. */ - def saveDependencies() = + def saveDependencies(fromFile: AbstractFile => String) = if(dependenciesFile.isDefined) - dependencies.writeTo(dependenciesFile.get) + dependencies.writeTo(dependenciesFile.get, fromFile) /** Load dependencies from the given file and save the file reference for * future saves. */ - def loadFrom(f: AbstractFile) { + def loadFrom(f: AbstractFile, toFile: String => AbstractFile) : Boolean = { dependenciesFile = f - val fd = FileDependencies.readFrom(f); - dependencies = if (fd.classpath != classpath) { - if(settings.debug.value){ - println("Classpath has changed. Nuking dependencies"); - } - newDeps - } else fd + FileDependencies.readFrom(f, toFile) match { + case Some(fd) => + val success = fd.classpath == classpath + dependencies = if (success) fd else { + if(settings.debug.value){ + println("Classpath has changed. Nuking dependencies"); + } + newDeps + } + + success + case None => false + } } def filter(files : List[SourceFile]) : List[SourceFile] = @@ -80,7 +88,7 @@ trait DependencyAnalysis extends SubComponent with Files { else { val (direct, indirect) = dependencies.invalidatedFiles(maxDepth); val filtered = files.filter(x => { - val f = x.path.absolute; + val f = x.file.absolute direct(f) || indirect(f) || !dependencies.containsFile(f); }) filtered match { @@ -102,7 +110,11 @@ trait DependencyAnalysis extends SubComponent with Files { if (f != null){ val source: AbstractFile = unit.source.file; for (d <- unit.icode){ - dependencies.emits(source, nameToFile(unit.source.file, d.toString)) + val name = d.symbol match { + case _ : ModuleClassSymbol => d.toString+"$" + case _ => d.toString + } + dependencies.emits(source, nameToFile(unit.source.file, name)) } for (d <- unit.depends; if (d.sourceFile != null)){ @@ -124,7 +136,7 @@ trait DependencyAnalysis extends SubComponent with Files { && (!tree.symbol.hasFlag(Flags.JAVA)) && ((tree.symbol.sourceFile eq null) || (tree.symbol.sourceFile.path != file.path))) { - references = references.updated(file, references(file) + tree.symbol.fullNameString) + references += file -> (references(file) + tree.symbol.fullNameString) } tree match { case cdef: ClassDef if !cdef.symbol.isModuleClass && !cdef.symbol.hasFlag(Flags.PACKAGE) => diff --git a/src/compiler/scala/tools/nsc/dependencies/Files.scala b/src/compiler/scala/tools/nsc/dependencies/Files.scala index b4852ee0ce..501936ee4e 100644 --- a/src/compiler/scala/tools/nsc/dependencies/Files.scala +++ b/src/compiler/scala/tools/nsc/dependencies/Files.scala @@ -6,22 +6,7 @@ import io.{AbstractFile, PlainFile} import scala.collection._; -trait Files { - - /** Resolve the given name to a file. */ - implicit def toFile(name: String): AbstractFile = { - val file = rootDirectory.lookupPathUnchecked(name, false) - assert(file ne null, name) - file - } - - /** The directory where file lookup should start at. */ - var rootDirectory: AbstractFile = { - AbstractFile.getDirectory(".") -// val roots = java.io.File.listRoots() -// assert(roots.length > 0) -// new PlainFile(roots(0)) - } +trait Files { self : SubComponent => class FileDependencies(val classpath : String) { @@ -73,7 +58,7 @@ trait Files { (direct, indirect); } - /** Return the sef of files that depend on the given changed files. + /** Return the set of files that depend on the given changed files. * It computes the transitive closure up to the given depth. */ def dependentFiles(depth: Int, changed: Set[AbstractFile]): Set[AbstractFile] = { @@ -99,21 +84,16 @@ trait Files { indirect --= changed } - def writeTo(file: AbstractFile) { - writeToFile(file)(out => writeTo(new PrintStream(out))) + def writeTo(file: AbstractFile, fromFile : AbstractFile => String) { + writeToFile(file)(out => writeTo(new PrintStream(out), fromFile)) } - def writeTo(print : PrintStream) : Unit = { - def prettify(path: String): String = - if (path.startsWith("./")) - path.substring(2, path.length) - else path - + def writeTo(print : PrintStream, fromFile : AbstractFile => String) : Unit = { cleanEmpty(); def emit(tracker : Tracker){ for ((f, ds) <- tracker; d <- ds){ - print.println(prettify(f.toString) + " -> " + prettify(d.toString)); + print.println(fromFile(f) + " -> " + fromFile(d)); } } @@ -126,52 +106,47 @@ trait Files { } } - - object FileDependencies{ val Separator = "-------"; - def readFrom(file: AbstractFile): FileDependencies = readFromFile(file) { in => - val reader = new BufferedReader(new InputStreamReader(in)); - val it = new FileDependencies(reader.readLine); - reader.readLine; - var line : String = null; + def readFrom(file: AbstractFile, toFile : String => AbstractFile): Option[FileDependencies] = readFromFile(file) { in => + val reader = new BufferedReader(new InputStreamReader(in)) + val it = new FileDependencies(reader.readLine) + reader.readLine + var line : String = null while ({line = reader.readLine; (line != null) && (line != Separator)}){ line.split(" -> ") match { - case Array(from, on) => it.depends(from, on); - case x => error("Parse error: Unrecognised string " + line); - }; + case Array(from, on) => it.depends(toFile(from), toFile(on)); + case x => global.inform("Parse error: Unrecognised string " + line); return None + } } while ({line = reader.readLine; (line != null) && (line != Separator)}){ line.split(" -> ") match { - case Array(source, target) => it.emits(source, target); - case x => error("Parse error: Unrecognised string " + line); - }; + case Array(source, target) => it.emits(toFile(source), toFile(target)); + case x => global.inform("Parse error: Unrecognised string " + line); return None + } } - it; + Some(it) } } - def writeToFile[T](file: AbstractFile)(f: OutputStream => T) : T = { val out = file.output try { - f(out); + f(out) } finally { - out.close; + out.close } } def readFromFile[T](file: AbstractFile)(f: InputStream => T) : T = { val in = file.input try{ - f(in); + f(in) } finally { - in.close; + in.close } } } - -object Files extends Files; diff --git a/src/compiler/scala/tools/nsc/interactive/BuildManager.scala b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala index 0b35920ca2..3512d17bb8 100644 --- a/src/compiler/scala/tools/nsc/interactive/BuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala @@ -26,16 +26,15 @@ trait BuildManager { def buildingFiles(included: Set[AbstractFile]) {} /** Load saved dependency information. */ - def loadFrom(file: AbstractFile) + def loadFrom(file: AbstractFile, toFile: String => AbstractFile) : Boolean /** Save dependency information to `file'. */ - def saveTo(file: AbstractFile) + def saveTo(file: AbstractFile, fromFile: AbstractFile => String) def compiler: scala.tools.nsc.Global } - /** Simple driver for testing the build manager. It presents * the user to a 'resident compiler' prompt. Each line is * interpreted as a set of files that have changed. The builder diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala index c0849065f6..c07cb62277 100644 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -41,7 +41,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana } /** External references used by source file. */ - private var references: immutable.Map[AbstractFile, immutable.Set[String]] = _ + private var references: mutable.Map[AbstractFile, immutable.Set[String]] = _ /** Add the given source files to the managed build process. */ def addSourceFiles(files: Set[AbstractFile]) { @@ -114,60 +114,71 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana break } - for ((oldSym, changes) <- changesOf; change <- changes) { + // changesOf will be empty just after initialization with a saved + // dependencies file. + if (changesOf.isEmpty) + buf ++= directDeps + else { + for ((oldSym, changes) <- changesOf; change <- changes) { - def checkParents(cls: Symbol, file: AbstractFile) { - val parentChange = cls.info.parents.exists(_.typeSymbol.fullNameString == oldSym.fullNameString) -// println("checkParents " + cls + " oldSym: " + oldSym + " parentChange: " + parentChange + " " + cls.info.parents) - change match { - case Changed(Class(_)) if parentChange => - invalidate(file, "parents have changed", change) + def checkParents(cls: Symbol, file: AbstractFile) { + val parentChange = cls.info.parents.exists(_.typeSymbol.fullNameString == oldSym.fullNameString) +// println("checkParents " + cls + " oldSym: " + oldSym + " parentChange: " + parentChange + " " + cls.info.parents) + change match { + case Changed(Class(_)) if parentChange => + invalidate(file, "parents have changed", change) - case Added(Definition(_)) if parentChange => - invalidate(file, "inherited new method", change) + case Added(Definition(_)) if parentChange => + invalidate(file, "inherited new method", change) - case Removed(Definition(_)) if parentChange => - invalidate(file, "inherited method removed", change) + case Removed(Definition(_)) if parentChange => + invalidate(file, "inherited method removed", change) - case _ => () + case _ => () + } } - } - def checkInterface(cls: Symbol, file: AbstractFile) { - change match { - case Added(Definition(name)) => - if (cls.info.decls.iterator.exists(_.fullNameString == name)) - invalidate(file, "of new method with existing name", change) - case Changed(Class(name)) => - if (cls.info.typeSymbol.fullNameString == name) - invalidate(file, "self type changed", change) - case _ => - () + def checkInterface(cls: Symbol, file: AbstractFile) { + change match { + case Added(Definition(name)) => + if (cls.info.decls.iterator.exists(_.fullNameString == name)) + invalidate(file, "of new method with existing name", change) + case Changed(Class(name)) => + if (cls.info.typeSymbol.fullNameString == name) + invalidate(file, "self type changed", change) + case _ => + () + } } - } - def checkReferences(file: AbstractFile) { -// println(file + ":" + references(file)) - val refs = references(file) - change match { - case Removed(Definition(name)) if refs(name) => - invalidate(file, " it references deleted definition", change) - case Removed(Class(name)) if (refs(name)) => - invalidate(file, " it references deleted class", change) - case Changed(Definition(name)) if (refs(name)) => - invalidate(file, " it references changed definition", change) - case _ => () + def checkReferences(file: AbstractFile) { +// println(file + ":" + references(file)) + val refs = references(file) + if (refs.isEmpty) + invalidate(file, "it is a direct dependency and we don't yet have finer-grained dependency information", change) + else { + change match { + case Removed(Definition(name)) if refs(name) => + invalidate(file, "it references deleted definition", change) + case Removed(Class(name)) if (refs(name)) => + invalidate(file, "it references deleted class", change) + case Changed(Definition(name)) if (refs(name)) => + invalidate(file, "it references changed definition", change) + case _ => () + } + } } - } - breakable { - for (file <- directDeps) { - for (cls <- definitions(file)) checkParents(cls, file) - for (cls <- definitions(file)) checkInterface(cls, file) - checkReferences(file) + breakable { + for (file <- directDeps) { + for (cls <- definitions(file)) checkParents(cls, file) + for (cls <- definitions(file)) checkInterface(cls, file) + checkReferences(file) + } } } } + buf } @@ -180,13 +191,16 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana } /** Load saved dependency information. */ - def loadFrom(file: AbstractFile) { - compiler.dependencyAnalysis.loadFrom(file) + def loadFrom(file: AbstractFile, toFile: String => AbstractFile) : Boolean = { + val success = compiler.dependencyAnalysis.loadFrom(file, toFile) + if (success) + sources ++= compiler.dependencyAnalysis.managedFiles + success } /** Save dependency information to `file'. */ - def saveTo(file: AbstractFile) { + def saveTo(file: AbstractFile, fromFile: AbstractFile => String) { compiler.dependencyAnalysis.dependenciesFile = file - compiler.dependencyAnalysis.saveDependencies() + compiler.dependencyAnalysis.saveDependencies(fromFile) } } diff --git a/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala index 37ed782868..9ecf51ae42 100644 --- a/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala @@ -68,13 +68,16 @@ class SimpleBuildManager(val settings: Settings) extends BuildManager { } /** Load saved dependency information. */ - def loadFrom(file: AbstractFile) { - compiler.dependencyAnalysis.loadFrom(file) + def loadFrom(file: AbstractFile, toFile: String => AbstractFile) : Boolean = { + val success = compiler.dependencyAnalysis.loadFrom(file, toFile) + if (success) + sources ++= compiler.dependencyAnalysis.managedFiles + success } /** Save dependency information to `file'. */ - def saveTo(file: AbstractFile) { + def saveTo(file: AbstractFile, fromFile: AbstractFile => String) { compiler.dependencyAnalysis.dependenciesFile = file - compiler.dependencyAnalysis.saveDependencies() + compiler.dependencyAnalysis.saveDependencies(fromFile) } } -- cgit v1.2.3