From de4f870d5ad294408b6fa97a3a6ff1d02a4273b8 Mon Sep 17 00:00:00 2001 From: mmcbride Date: Wed, 12 Oct 2011 11:17:05 -0700 Subject: [split] Fixing mtime recompile checks for eval --- libraries/eval/Eval.scala | 65 ++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 24 deletions(-) (limited to 'libraries/eval') diff --git a/libraries/eval/Eval.scala b/libraries/eval/Eval.scala index a15b126..b9d225a 100644 --- a/libraries/eval/Eval.scala +++ b/libraries/eval/Eval.scala @@ -31,7 +31,7 @@ import scala.tools.nsc.io.{AbstractFile, VirtualDirectory} import scala.tools.nsc.reporters.AbstractReporter import scala.tools.nsc.util.{BatchSourceFile, Position} -case class LastMod(timestamp: Long, code: String) +case class LastMod(timestamp: Option[Long], code: String) /** * Evaluate a file or string and return the result. @@ -116,10 +116,15 @@ class Eval(target: Option[File]) { * to the text. * Last modified is computed here because we support includes */ - def sourceForString(code: String): LastMod = { - preprocessors.foldLeft(LastMod(0L, code)) { (acc, p) => - val processed = p(acc.code) - LastMod(acc.timestamp max processed.timestamp, processed.code) + def sourceForString(code: String, lastModified: Option[Long]): LastMod = { + preprocessors.foldLeft(LastMod(lastModified, code)) { (acc, p) => + val processed = p(acc.code, lastModified) + + // timestamp of the newest processed file. + // if both are defined, take the max. otherwise + // take any defined timestamp + val newestProcessed = Seq(processed.timestamp, acc.timestamp).max + LastMod(newestProcessed, processed.code) } } @@ -127,7 +132,7 @@ class Eval(target: Option[File]) { * Eval[Int]("1 + 1") // => 2 */ def apply[T](code: String, resetState: Boolean = true): T = { - val processed = sourceForString(code) + val processed = sourceForString(code, None) applyProcessed(processed.code, resetState) } @@ -138,11 +143,22 @@ class Eval(target: Option[File]) { if (target.isDefined) { val targetDir = target.get val unprocessedSource = files.map { scala.io.Source.fromFile(_).mkString }.mkString("\n") - val processed = sourceForString(unprocessedSource) - val oldestTarget = targetDir.listFiles.foldLeft(Long.MaxValue)((oldest, f) => f.lastModified min oldest) - if (processed.timestamp > oldestTarget) { - compiler.reset() + val lastModified = files.foldLeft(None: Option[Long]) { (acc, f) => Seq(acc, Some(f.lastModified)).max } + val processed = sourceForString(unprocessedSource, lastModified) + val oldestTarget = targetDir.listFiles.foldLeft(Long.MaxValue) { (oldest, f) => + f.lastModified min oldest } + processed.timestamp match { + // if we got a last-modified-source timestamp threaded through, use it to check compiler resets + case Some(newestSource) => { + if (newestSource > oldestTarget) { + compiler.reset() + } + } + // if there are no timestamps anywhere, just reset the compiler + case None => compiler.reset() + } + val className = "Evaluator__" + files(0).getName.split("\\.")(0) applyProcessed(className, processed.code, false) } else { @@ -154,7 +170,7 @@ class Eval(target: Option[File]) { * Eval[Int](getClass.getResourceAsStream("...")) */ def apply[T](stream: InputStream): T = { - apply(sourceForString(Source.fromInputStream(stream).mkString).code) + apply(sourceForString(Source.fromInputStream(stream).mkString, None).code) } /** @@ -189,14 +205,14 @@ class Eval(target: Option[File]) { * converts the given file to evaluable source. */ def toSource(code: String): String = { - sourceForString(code).code + sourceForString(code, None).code } /** * Compile an entire source file into the virtual classloader. */ def compile(code: String) { - compiler(sourceForString(code).code) + compiler(sourceForString(code, None).code) } /** @@ -212,7 +228,7 @@ class Eval(target: Option[File]) { * @throws CompilerException if not Eval-able. */ def check(code: String) { - val id = uniqueId(sourceForString(code).code) + val id = uniqueId(sourceForString(code, None).code) val className = "Evaluator__" + id val wrappedCode = wrapCodeInClass(className, code) compile(wrappedCode) // may throw CompilerException @@ -291,7 +307,7 @@ class Eval(target: Option[File]) { } trait Preprocessor { - def apply(code: String): LastMod + def apply(code: String, lastModified: Option[Long]): LastMod } trait Resolver { @@ -348,9 +364,12 @@ class Eval(target: Option[File]) { */ class IncludePreprocessor(resolvers: Seq[Resolver]) extends Preprocessor { def maximumRecursionDepth = 100 - def apply(code: String): LastMod = apply(code, maximumRecursionDepth) - def apply(code: String, maxDepth: Int): LastMod = { - var lastMod = 0L + + def apply(code: String, lastModified: Option[Long]): LastMod = + apply(code, lastModified, maximumRecursionDepth) + + def apply(code: String, lastModified: Option[Long], maxDepth: Int): LastMod = { + var lastMod = lastModified val lines = code.lines map { line: String => val tokens = line.trim.split(' ') if (tokens.length == 2 && tokens(0).equals("#include")) { @@ -359,16 +378,14 @@ class Eval(target: Option[File]) { resolver.resolvable(path) } match { case Some(r: Resolver) => { - lastMod = lastMod max r.lastModified(path) + lastMod = Seq(lastMod, Some(r.lastModified(path))).max // recursively process includes if (maxDepth == 0) { throw new IllegalStateException("Exceeded maximum recusion depth") } else { - apply(StreamIO.buffer(r.get(path)).toString, maxDepth - 1) match { - case LastMod(timestamp, code) => - lastMod = lastMod max timestamp - code - } + val subLastMod = apply(StreamIO.buffer(r.get(path)).toString, lastMod, maxDepth - 1) + lastMod = Seq(lastMod, subLastMod.timestamp).max + subLastMod.code } } case _ => -- cgit v1.2.3