From d5bf8cf6d6f22f4b6bc07055c25d2f9642488118 Mon Sep 17 00:00:00 2001 From: Eugene Vigdorchik Date: Thu, 8 Mar 2012 18:58:44 +0400 Subject: [PATCH] Transitive compilation dependency tracking. --- main/Defaults.scala | 44 +++++++++++++++++++++----------- testing/TestStatusReporter.scala | 8 +----- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/main/Defaults.scala b/main/Defaults.scala index 36d8eeaea..66001d4d0 100644 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -50,7 +50,7 @@ object Defaults extends BuildCommon def thisBuildCore: Seq[Setting[_]] = inScope(GlobalScope.copy(project = Select(ThisBuild)))(Seq( managedDirectory <<= baseDirectory(_ / "lib_managed") )) - def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(Seq( + def globalCore: Seq[Setting[_]] = inScope(GlobalScope)(defaultTestTasks(test) ++ defaultTestTasks(testOnly) ++ defaultTestTasks(testQuick) ++ Seq( crossVersion :== CrossVersion.Disabled, buildDependencies <<= buildDependencies or Classpaths.constructBuildDependencies, taskTemporaryDirectory := IO.createTemporaryDirectory, @@ -117,6 +117,10 @@ object Defaults extends BuildCommon excludeFilter :== (".*" - ".") || HiddenFileFilter, pomIncludeRepository :== Classpaths.defaultRepositoryFilter )) + def defaultTestTasks(key: Scoped): Seq[Setting[_]] = Seq( + tags in key := Seq(Tags.Test -> 1), + logBuffered in key := true + ) def projectCore: Seq[Setting[_]] = Seq( name <<= thisProject(_.id), logManager <<= extraLoggers(LogManager.defaults), @@ -301,9 +305,7 @@ object Defaults extends BuildCommon TestLogger(s.log, testLogger(sm, test in sco.scope), buff) +: new TestStatusReporter(succeededFile(dir)) +: ls }, testOptions <<= (testOptions in TaskGlobal, testListeners) map { (options, ls) => Tests.Listeners(ls) +: options }, - testExecution <<= testExecutionTask(key), - logBuffered := true, - tags := Seq(Tags.Test -> 1) + testExecution <<= testExecutionTask(key) ) ) def testLogger(manager: Streams, baseKey: Scoped)(tdef: TestDefinition): Logger = { @@ -323,20 +325,32 @@ object Defaults extends BuildCommon (testOptions in task, parallelExecution in task, tags in task) map { (opts, par, ts) => new Tests.Execution(opts, par, ts) } def testQuickFilter: Initialize[Task[Seq[String] => String => Boolean]] = - (compile in test, cacheDirectory) map { - case (analysis, dir) => - val succeeded = new TestResultFilter(succeededFile(dir)) - args => test => selectedFilter(args)(test) && { - succeeded(test) match { + (fullClasspath in test, cacheDirectory) map { + (cp, dir) => + val ans = for(e <- cp; an <- e.metadata get Keys.analysis) yield an + val succeeded = TestStatus.read(succeededFile(dir)) + val stamps = collection.mutable.Map.empty[File, Long] + def stamp(dep: String): Long = { + val stamps = for (a <- ans; f <- a.relations.definesClass(dep)) yield intlStamp(f, a, Set.empty) + if (stamps.isEmpty) Long.MinValue else stamps.max + } + def intlStamp(f: File, analysis: inc.Analysis, s: Set[File]): Long = { + stamps.getOrElseUpdate(f, { + import analysis.{relations => rel, apis} + ( + rel.internalSrcDeps(f).map(intlStamp(_, analysis, s + f)) ++ + rel.externalDeps(f).map(stamp) + + apis.internal(f).compilation.startTime + ).max + }) + } + (args: Seq[String]) => (test: String) => selectedFilter(args)(test) && { + succeeded.get(test) match { case None => true - case Some(ts) => - import analysis.{relations => rel, apis} - rel.definesClass(test) flatMap { - f => ((rel.internalSrcDeps(f) + f) map apis.internal) ++ (rel.externalDeps(f) map apis.external) - } exists (_.compilation.startTime > ts) + case Some(ts) => stamp(test) > ts } } - } + } dependsOn (compile in test) def succeededFile(dir: File) = dir / "succeeded_tests" def inputTests(key: InputKey[_]): Initialize[InputTask[Unit]] = diff --git a/testing/TestStatusReporter.scala b/testing/TestStatusReporter.scala index b9e695c42..bab1d4b71 100644 --- a/testing/TestStatusReporter.scala +++ b/testing/TestStatusReporter.scala @@ -25,13 +25,7 @@ private[sbt] class TestStatusReporter(f: File) extends TestsListener } } -private[sbt] class TestResultFilter(f: File) extends NotNull -{ - private lazy val succeeded = TestStatus.read(f) - def apply(test: String): Option[Long] = succeeded.get(test) -} - -private object TestStatus +private[sbt] object TestStatus { import java.util.Properties def read(f: File): Map[String, Long] =