summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
blob: fa7a5f94aef411686e1032fe170b33becc939a8a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package scala.tools.nsc
package dependencies;
import util.SourceFile;
import io.AbstractFile

trait DependencyAnalysis extends SubComponent with Files {
  import global._

  val phaseName = "dependencyAnalysis";

  def off = settings.make.value == "all"

  def newPhase(prev : Phase) = new AnalysisPhase(prev)

  lazy val maxDepth = settings.make.value match {
    case "changed" => 0
    case "transitive" => Int.MaxValue
    case "immediate" => 1
  }

  def nameToFile(src: AbstractFile, name : String) =
    settings.outputDirs.outputDirFor(src)
      .lookupPathUnchecked(name.toString.replace(".", java.io.File.separator) + ".class", false)

  private var depFile: Option[AbstractFile] = None

  def dependenciesFile_=(file: AbstractFile) {
    assert(file ne null)
    depFile = Some(file)
  }

  def dependenciesFile: Option[AbstractFile] = depFile

  def classpath = settings.classpath.value
  def newDeps = new FileDependencies(classpath);

  var dependencies = newDeps

  /** Write dependencies to the current file. */
  def saveDependencies() =
    if(dependenciesFile.isDefined)
      dependencies.writeTo(dependenciesFile.get)

  /** Load dependencies from the given file and save the file reference for
   *  future saves.
   */
  def loadFrom(f: AbstractFile) {
    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
  }

  def filter(files : List[SourceFile]) : List[SourceFile] =
    if (off) files
    else if (dependencies.isEmpty){
      if(settings.debug.value){
        println("No known dependencies. Compiling everything");
      }
      files
    }
    else {
      val (direct, indirect) = dependencies.invalidatedFiles(maxDepth);
      val filtered = files.filter(x => {
        val f = x.path.absolute;
        direct(f) || indirect(f) || !dependencies.containsFile(f);
      })
      filtered match {
        case Nil => println("No changes to recompile");
        case x => println("Recompiling " + (
          if(settings.debug.value) x.mkString(", ")
          else x.length + " files")
        )
      }
      filtered
    }

  class AnalysisPhase(prev : Phase) extends StdPhase(prev){
    def apply(unit : global.CompilationUnit) {
      val f = unit.source.file.file;
      // When we're passed strings by the interpreter
      // they  have no source file. We simply ignore this case
      // as irrelevant to dependency analysis.
      if (f != null){
        val source: AbstractFile = unit.source.file;
        for (d <- unit.icode){
          dependencies.emits(source, nameToFile(unit.source.file, d.toString))
        }

        for (d <- unit.depends; if (d.sourceFile != null)){
          dependencies.depends(source, d.sourceFile);
        }
      }
    }
  }
}