diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 21 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/dependencies/Changes.scala | 16 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/dependencies/References.scala | 23 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interactive/BuildManager.scala (renamed from src/compiler/scala/tools/nsc/BuildManager.scala) | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala | 175 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala (renamed from src/compiler/scala/tools/nsc/SimpleBuildManager.scala) | 4 |
6 files changed, 223 insertions, 23 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 2f28468f3c..3e8f7655e1 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -217,16 +217,14 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } } - settings.dependenciesFile.value match { - case "none" => () - case x => - val jfile = new java.io.File(x) - if (!jfile.exists) jfile.createNewFile - - dependencyAnalysis.loadFrom(AbstractFile.getFile(jfile)) - } - - + if (settings.make.value != "all") + settings.dependenciesFile.value match { + case "none" => () + case x => + val jfile = new java.io.File(x) + if (!jfile.exists) jfile.createNewFile + else dependencyAnalysis.loadFrom(AbstractFile.getFile(jfile)) + } lazy val classPath0 = new ClassPath(false && onlyPresentation) @@ -590,7 +588,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable if (forJVM) { phasesSet += liftcode // generate reified trees phasesSet += genJVM // generate .class files - phasesSet += dependencyAnalysis + if (settings.make.value != "all") + phasesSet += dependencyAnalysis } if (forMSIL) { phasesSet += genMSIL // generate .msil files diff --git a/src/compiler/scala/tools/nsc/dependencies/Changes.scala b/src/compiler/scala/tools/nsc/dependencies/Changes.scala index 6ac6b71487..01b0afd3a1 100644 --- a/src/compiler/scala/tools/nsc/dependencies/Changes.scala +++ b/src/compiler/scala/tools/nsc/dependencies/Changes.scala @@ -36,14 +36,14 @@ abstract class Changes { case class Removed(e: Entity) extends Change case class Changed(e: Entity) extends Change - /** Return the list of changes between 'from' and 'to'. */ def changeSet(from: Symbol, to: Symbol): List[Change] = { +// println("changeSet " + from + "(" + from.info + ")" +// + " vs " + to + "(" + to.info + ")") val cs = new mutable.ListBuffer[Change] - if (((from.info.parents intersect to.info.parents).size - != from.info.parents.size) + if (((from.info.parents zip to.info.parents) exists { case (t1, t2) => !(t1 =:= t2) }) || (from.typeParams != to.typeParams)) cs += Changed(toEntity(from)) @@ -59,8 +59,14 @@ abstract class Changes { cs ++= changeSet(o, n) else if (n == NoSymbol) cs += Removed(toEntity(o)) - else if (n.suchThat(_.tpe == o.tpe) == NoSymbol) - cs += Changed(toEntity(o)) + else { + val newSym = n.suchThat(_.tpe =:= o.tpe) + if (newSym == NoSymbol) { +// println(n + " changed from " + o.tpe + " to " + n.tpe) + cs += Changed(toEntity(o)) + } else + newMembers -= newSym + } } cs ++= (newMembers map (Added compose toEntity)) diff --git a/src/compiler/scala/tools/nsc/dependencies/References.scala b/src/compiler/scala/tools/nsc/dependencies/References.scala index 89fa71f6c1..4e4fff575d 100644 --- a/src/compiler/scala/tools/nsc/dependencies/References.scala +++ b/src/compiler/scala/tools/nsc/dependencies/References.scala @@ -1,5 +1,7 @@ package scala.tools.nsc.dependencies; import util.SourceFile; +import io.AbstractFile +import symtab.Flags import collection._ @@ -11,23 +13,40 @@ abstract class References extends SubComponent with Files { def newPhase(prev: Phase) = new ReferenceAnalysisPhase(prev) /** Top level definitions per source file. */ - val definitions: mutable.Map[String, List[Symbol]] = new mutable.HashMap + val definitions: mutable.Map[AbstractFile, List[Symbol]] = new mutable.HashMap[AbstractFile, List[Symbol]] + + /** External references used by source file. */ + var references: immutable.Map[AbstractFile, immutable.Set[String]] = + new immutable.HashMap[AbstractFile, immutable.Set[String]] class ReferenceAnalysisPhase(prev: Phase) extends StdPhase(prev) { def apply(unit: global.CompilationUnit) { + val file = unit.source.file + references += file -> immutable.Set.empty[String] + val buf = new mutable.ListBuffer[Symbol] + (new Traverser { override def traverse(tree: Tree) { + if ((tree.symbol ne null) + && (tree.symbol != NoSymbol) + && (!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) + } tree match { case cdef: ClassDef if !cdef.symbol.isModuleClass => buf += cdef.symbol.cloneSymbol + super.traverse(tree) + case _ => super.traverse(tree) } } }).apply(unit.body) - definitions(unit.source.file.path) = buf.toList + definitions(unit.source.file) = buf.toList } } } diff --git a/src/compiler/scala/tools/nsc/BuildManager.scala b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala index 6ee1e6547b..5747982e18 100644 --- a/src/compiler/scala/tools/nsc/BuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/BuildManager.scala @@ -1,4 +1,4 @@ -package scala.tools.nsc +package scala.tools.nsc.interactive import scala.collection._ @@ -27,7 +27,7 @@ trait BuildManager { /** Save dependency information to `file'. */ def saveTo(file: AbstractFile) - def compiler: Global + def compiler: nsc.Global } @@ -52,7 +52,8 @@ object BuildManagerTest extends EvalLoop { val settings = new Settings(error) val command = new CompilerCommand(List.fromArray(args), settings, error, false) // settings.make.value = "off" - val buildManager: BuildManager = new SimpleBuildManager(settings) +// val buildManager: BuildManager = new SimpleBuildManager(settings) + val buildManager: BuildManager = new RefinedBuildManager(settings) buildManager.addSourceFiles(command.files) diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala new file mode 100644 index 0000000000..ebdcec0105 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -0,0 +1,175 @@ +package scala.tools.nsc.interactive + +import scala.collection._ +import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} +import scala.util.control.Breaks._ + +import dependencies._ +import util.FakePos +import nsc.io.AbstractFile + +/** A more defined build manager, based on change sets. For each + * updated source file, it computes the set of changes to its + * definitions, then checks all dependent units to see if the + * changes require a compilation. It repeats this process until + * a fixpoint is reached. + */ +class RefinedBuildManager(val settings: Settings) extends Changes with BuildManager { + + class BuilderGlobal(settings: Settings) extends nsc.Global(settings) { + + object referencesAnalysis extends { + val global: BuilderGlobal.this.type = BuilderGlobal.this + val runsAfter = List("typer") + val runsRightAfter = None + } with References + + override def computeInternalPhases() { + super.computeInternalPhases + phasesSet += dependencyAnalysis + phasesSet += referencesAnalysis + } + } + val compiler = new BuilderGlobal(settings) + import compiler.Symbol + + /** Managed source files. */ + private val sources: mutable.Set[AbstractFile] = new mutable.HashSet[AbstractFile] + + private val definitions: mutable.Map[AbstractFile, List[Symbol]] = + new mutable.HashMap[AbstractFile, List[Symbol]] { + override def default(key: AbstractFile) = Nil + } + + /** External references used by source file. */ + private var references: immutable.Map[AbstractFile, immutable.Set[String]] = _ + + /** Add the given source files to the managed build process. */ + def addSourceFiles(files: Set[AbstractFile]) { + sources ++= files + update(files) + } + + /** Remove the given files from the managed build process. */ + def removeFiles(files: Set[AbstractFile]) { + sources --= files + } + + /** The given files have been modified by the user. Recompile + * them and all files that depend on them. Only files that + * have been previously added as source files are recompiled. + */ + def update(files: Set[AbstractFile]) { + val deps = compiler.dependencyAnalysis.dependencies + val run = new compiler.Run() + compiler.inform("compiling " + files) + + run.compileFiles(files.toList) + if (compiler.reporter.hasErrors) { + compiler.reporter.reset + return + } + + val changesOf = new mutable.HashMap[Symbol, List[Change]] + + val defs = compiler.referencesAnalysis.definitions + for (val src <- files; val syms = defs(src); val sym <- syms) { + definitions(src).find(_.fullNameString == sym.fullNameString) match { + case Some(oldSym) => + changesOf(oldSym) = changeSet(oldSym, sym) + case _ => + // a new top level definition, no need to process + } + } + println("Changes: " + changesOf) + updateDefinitions + invalidated(files, changesOf) + } + + /** Return the set of source files that are invalidated by the given changes. */ + def invalidated(files: Set[AbstractFile], changesOf: collection.Map[Symbol, List[Change]]): Set[AbstractFile] = { + val buf = new mutable.HashSet[AbstractFile] + var directDeps = + compiler.dependencyAnalysis.dependencies.dependentFiles(1, files) + + def invalidate(file: AbstractFile, reason: String, change: Change) = { + println("invalidate " + file + " because " + reason + " [" + change + "]") + buf += file + directDeps -= file + break + } + + 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) + + case Added(Definition(_)) if parentChange => + invalidate(file, "inherited new method", change) + + case Removed(Definition(_)) if parentChange => + invalidate(file, "inherited method removed", 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) + } + } + + def checkReferences(file: AbstractFile) { + println(references) + 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 _ => () + } + } + + breakable { + for (file <- directDeps) { + for (cls <- definitions(file)) checkParents(cls, file) + for (cls <- definitions(file)) checkInterface(cls, file) + checkReferences(file) + } + } + } + buf + } + + /** Update the map of definitions per source file */ + private def updateDefinitions { + for ((src, localDefs) <- compiler.referencesAnalysis.definitions) { + definitions(src) = (localDefs map (_.cloneSymbol)) + } + this.references = compiler.referencesAnalysis.references + } + + /** Load saved dependency information. */ + def loadFrom(file: AbstractFile) { + compiler.dependencyAnalysis.loadFrom(file) + } + + /** Save dependency information to `file'. */ + def saveTo(file: AbstractFile) { + compiler.dependencyAnalysis.dependenciesFile = file + compiler.dependencyAnalysis.saveDependencies() + } +} diff --git a/src/compiler/scala/tools/nsc/SimpleBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala index 4f8d5948d2..bd8e28e759 100644 --- a/src/compiler/scala/tools/nsc/SimpleBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/SimpleBuildManager.scala @@ -1,4 +1,4 @@ -package scala.tools.nsc +package scala.tools.nsc.interactive import scala.collection._ @@ -17,7 +17,7 @@ import nsc.io.AbstractFile */ class SimpleBuildManager(val settings: Settings) extends BuildManager { - val compiler: Global = new Global(settings) + val compiler: nsc.Global = new nsc.Global(settings) /** Managed source files. */ private val sources: mutable.Set[AbstractFile] = new mutable.HashSet[AbstractFile] |