summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2009-06-25 12:32:53 +0000
committerIulian Dragos <jaguarul@gmail.com>2009-06-25 12:32:53 +0000
commit7e3f53ed7d34296381f4b3b11de6fbc6c1d1e76c (patch)
tree0ed4974aee8fdf8b5a5cd4d99aede5ea84f322db /src
parentae85676cb414628f4bcc2a302e7de8dd031b75e8 (diff)
downloadscala-7e3f53ed7d34296381f4b3b11de6fbc6c1d1e76c.tar.gz
scala-7e3f53ed7d34296381f4b3b11de6fbc6c1d1e76c.tar.bz2
scala-7e3f53ed7d34296381f4b3b11de6fbc6c1d1e76c.zip
The dependecy file is read only when -make is n...
The dependecy file is read only when -make is not 'all'. Added a more refined builder that tracks dependency at the member level, and moved the build managers to package interactive.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala21
-rw-r--r--src/compiler/scala/tools/nsc/dependencies/Changes.scala16
-rw-r--r--src/compiler/scala/tools/nsc/dependencies/References.scala23
-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.scala175
-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]