From 76ab8f8e532159920426ce625a3654e7a3f6ed0a Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Thu, 7 Apr 2011 22:51:25 -0400 Subject: [PATCH] detect cyclic references after freeze and translate nodes to keys --- main/Build.scala | 15 ++++++++++++++- tasks/Execute.scala | 12 +++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/main/Build.scala b/main/Build.scala index ef4dcf079..854d72440 100644 --- a/main/Build.scala +++ b/main/Build.scala @@ -178,11 +178,24 @@ object EvaluateTask replaced } def transformInc[T](result: Result[T]): Result[T] = - result.toEither.left.map { i => Incomplete.transformBU(i)(liftAnonymous andThen taskToKey ) } + result.toEither.left.map { i => Incomplete.transformBU(i)(convertCyclicInc andThen liftAnonymous andThen taskToKey ) } def taskToKey: Incomplete => Incomplete = { case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node)) case i => i } + type AnyCyclic = Execute[Task]#CyclicException[_] + def convertCyclicInc: Incomplete => Incomplete = { + case in @ Incomplete(_, _, _, _, Some(c: AnyCyclic)) => in.copy(directCause = Some(new RuntimeException(convertCyclic(c))) ) + case i => i + } + def convertCyclic(c: AnyCyclic): String = + (c.caller, c.target) match { + case (caller: Task[_], target: Task[_]) => + c.toString + (if(caller eq target) "(task: " + name(caller) + ")" else "(caller: " + name(caller) + ", target: " + name(target) + ")" ) + case _ => c.toString + } + def name(node: Task[_]): String = + node.info.name orElse transformNode(node).map(Project.display) getOrElse ("") def liftAnonymous: Incomplete => Incomplete = { case i @ Incomplete(node, tpe, None, causes, None) => causes.find( inc => inc.message.isDefined || inc.directCause.isDefined) match { diff --git a/tasks/Execute.scala b/tasks/Execute.scala index 9844321f5..695930a6b 100644 --- a/tasks/Execute.scala +++ b/tasks/Execute.scala @@ -47,7 +47,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie def dump: String = "State: " + state.toString + "\n\nResults: " + results + "\n\nCalls: " + callers + "\n\n" - def run[T](root: A[T])(implicit strategy: Strategy): Result[T] = runKeep(root)(strategy)(root) + def run[T](root: A[T])(implicit strategy: Strategy): Result[T] = try { runKeep(root)(strategy)(root) } catch { case i: Incomplete => Inc(i) } def runKeep[T](root: A[T])(implicit strategy: Strategy): RMap[A,Result] = { assert(state.isEmpty, "Execute already running/ran.") @@ -64,7 +64,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie { pre { assert( !reverse.isEmpty, "Nothing to process." ) - assert( state.values.exists( _ == Running ), "Nothing running") + if( !state.values.exists( _ == Running ) ) { + snapshotCycleCheck() + assert(false, "Nothing running.") + } } (strategy.take()).process() @@ -78,7 +81,6 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie } } - def call[T](node: A[T], target: A[T])(implicit strategy: Strategy) { if(checkCycles) cycleCheck(node, target) @@ -296,6 +298,10 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie // cyclic reference checking + def snapshotCycleCheck(): Unit = + for( (called: A[c], callers) <- callers.toSeq; caller <- callers) + cycleCheck(caller.asInstanceOf[A[c]], called) + def cycleCheck[T](node: A[T], target: A[T]) { if(node eq target) cyclic(node, target, "Cannot call self")