scala.tools.nsc.dependencies.Files.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the Scala Programming Language
package scala.tools.nsc
package dependencies
import java.io.{InputStream, OutputStream, PrintStream, InputStreamReader, BufferedReader}
import io.{AbstractFile, PlainFile, VirtualFile}
import scala.collection._
trait Files { self : SubComponent =>
class FileDependencies(val classpath: String) {
import FileDependencies._
class Tracker extends mutable.OpenHashMap[AbstractFile, mutable.Set[AbstractFile]] {
override def default(key: AbstractFile) = {
this(key) = new mutable.HashSet[AbstractFile]
this(key)
}
}
val dependencies = new Tracker
val targets = new Tracker
def isEmpty = dependencies.isEmpty && targets.isEmpty
def emits(source: AbstractFile, result: AbstractFile) =
targets(source) += result
def depends(from: AbstractFile, on: AbstractFile) =
dependencies(from) += on
def reset(file: AbstractFile) = dependencies -= file
def cleanEmpty = {
dependencies foreach {case (_, value) =>
value retain (x => x.exists && (x ne removedFile))}
dependencies retain ((key, value) => key.exists && !value.isEmpty)
targets foreach {case (_, value) => value retain (_.exists)}
targets retain ((key, value) => key.exists && !value.isEmpty)
}
def containsFile(f: AbstractFile) = targets.contains(f.absolute)
def invalidatedFiles(maxDepth: Int) = {
val direct = new mutable.HashSet[AbstractFile]
for ((file, products) <- targets) {
// This looks a bit odd. It may seem like one should invalidate a file
// if *any* of its dependencies are older than it. The forall is there
// to deal with the fact that a) Some results might have been orphaned
// and b) Some files might not need changing.
direct(file) ||= products.forall(d => d.lastModified < file.lastModified)
}
val indirect = dependentFiles(maxDepth, direct)
for ((source, targets) <- targets
if direct(source) || indirect(source) || (source eq removedFile)) {
targets foreach (_.delete)
targets -= source
}
(direct, indirect)
}
/** 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] = {
val indirect = new mutable.HashSet[AbstractFile]
val newInvalidations = new mutable.HashSet[AbstractFile]
def invalid(file: AbstractFile) =
indirect(file) || changed(file) || (file eq removedFile)
def go(i: Int) : Unit = if(i > 0) {
newInvalidations.clear
for((target, depends) <- dependencies if !invalid(target);
d <- depends)
newInvalidations(target) ||= invalid(d)
indirect ++= newInvalidations
if (!newInvalidations.isEmpty) go(i - 1)
}
go(depth)
indirect --= changed
}
def writeTo(file: AbstractFile, fromFile: AbstractFile => String): Unit =
writeToFile(file)(out => writeTo(new PrintStream(out), fromFile))
def writeTo(print: PrintStream, fromFile: AbstractFile => String): Unit = {
def emit(tracker: Tracker) =
for ((f, ds) <- tracker; d <- ds) print.println(fromFile(f) + arrow + fromFile(d))
cleanEmpty
print.println(classpath)
print.println(separator)
emit(dependencies)
print.println(separator)
emit(targets)
}
}
object FileDependencies {
private val separator:String = "-------"
private val arrow = " -> "
private val removedFile = new VirtualFile("removed")
private def validLine(l: String) = (l != null) && (l != separator)
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)
def readLines(valid: Boolean)(f: (AbstractFile, AbstractFile) => Unit): Boolean = {
var continue = valid
var line: String = null
while (continue && {line = reader.readLine; validLine(line)}) {
line.split(arrow) match {
case Array(from, on) => f(toFile(from), toFile(on))
case _ =>
global.inform("Parse error: Unrecognised string " + line)
continue = false
}
}
continue
}
reader.readLine
val dResult = readLines(true)(
(_, _) match {
case (null, _) => // fromFile is removed, it's ok
case (fromFile, null) =>
// onFile is removed, should recompile fromFile
it.depends(fromFile, removedFile)
case (fromFile, onFile) => it.depends(fromFile, onFile)
})
readLines(dResult)(
(_, _) match {
case (null, null) =>
// source and target are all removed, it's ok
case (null, targetFile) =>
// source is removed, should remove relative target later
it.emits(removedFile, targetFile)
case (_, null) =>
// it may has been cleaned outside, or removed during last phase
case (sourceFile, targetFile) => it.emits(sourceFile, targetFile)
})
Some(it)
}
}
def writeToFile[T](file: AbstractFile)(f: OutputStream => T) : T = {
val out = file.bufferedOutput
try {
f(out)
} finally {
out.close
}
}
def readFromFile[T](file: AbstractFile)(f: InputStream => T) : T = {
val in = file.input
try{
f(in)
} finally {
in.close
}
}
}