work on displaying task errors

This commit is contained in:
Mark Harrah 2011-03-20 22:54:01 -04:00
parent 7b4c16f294
commit 63b1c3441b
14 changed files with 118 additions and 53 deletions

View File

@ -5,7 +5,6 @@ package sbt
import Project.ScopedKey import Project.ScopedKey
import Keys.{sessionSettings, thisProject} import Keys.{sessionSettings, thisProject}
import CommandSupport.logger
import Load.BuildStructure import Load.BuildStructure
import complete.{DefaultParsers, Parser} import complete.{DefaultParsers, Parser}
import DefaultParsers._ import DefaultParsers._

View File

@ -77,7 +77,7 @@ final object Aggregation
import std.TaskExtra._ import std.TaskExtra._
val toRun = ts map { case KeyValue(k,t) => t.map(v => KeyValue(k,v)) } join; val toRun = ts map { case KeyValue(k,t) => t.map(v => KeyValue(k,v)) } join;
val start = System.currentTimeMillis val start = System.currentTimeMillis
val result = withStreams(structure){ str => runTask(toRun)(nodeView(s, str, extra.tasks, extra.values)) } val result = withStreams(structure){ str => runTask(toRun, str)(nodeView(s, str, extra.tasks, extra.values)) }
val stop = System.currentTimeMillis val stop = System.currentTimeMillis
val log = logger(s) val log = logger(s)
lazy val extracted = Project.extract(s) lazy val extracted = Project.extract(s)

View File

@ -11,7 +11,7 @@ package sbt
import collection.mutable import collection.mutable
import Compiler.{Compilers,Inputs} import Compiler.{Compilers,Inputs}
import Project.{inScope, ScopedKey, ScopeLocal, Setting} import Project.{inScope, ScopedKey, ScopeLocal, Setting}
import Keys.{appConfiguration, baseDirectory, configuration, thisProject, thisProjectRef} import Keys.{appConfiguration, baseDirectory, configuration, streams, thisProject, thisProjectRef}
import TypeFunctions.{Endo,Id} import TypeFunctions.{Endo,Id}
import tools.nsc.reporters.ConsoleReporter import tools.nsc.reporters.ConsoleReporter
import Build.{analyzed, data} import Build.{analyzed, data}
@ -109,9 +109,10 @@ object EvaluateTask
val SystemProcessors = Runtime.getRuntime.availableProcessors val SystemProcessors = Runtime.getRuntime.availableProcessors
val isDummyTask = AttributeKey[Boolean]("is-dummy-task")
val taskDefinitionKey = AttributeKey[ScopedKey[_]]("task-definition-key")
val (state, dummyState) = dummy[State]("state") val (state, dummyState) = dummy[State]("state")
val (streamsManager, dummyStreamsManager) = dummy[Streams]("streams-manager") val (streamsManager, dummyStreamsManager) = dummy[Streams]("streams-manager")
val streams = TaskKey[TaskStreams]("streams")
val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped") val resolvedScoped = SettingKey[ScopedKey[_]]("resolved-scoped")
private[sbt] val parseResult: TaskKey[_] = TaskKey("$parse-result") private[sbt] val parseResult: TaskKey[_] = TaskKey("$parse-result")
@ -121,7 +122,12 @@ object EvaluateTask
) )
def dummy[T](name: String): (TaskKey[T], Task[T]) = (TaskKey[T](name), dummyTask(name)) def dummy[T](name: String): (TaskKey[T], Task[T]) = (TaskKey[T](name), dummyTask(name))
def dummyTask[T](name: String): Task[T] = task( error("Dummy task '" + name + "' did not get converted to a full task.") ) named name def dummyTask[T](name: String): Task[T] =
{
val base: Task[T] = task( error("Dummy task '" + name + "' did not get converted to a full task.") ) named name
base.copy(info = base.info.set(isDummyTask, true))
}
def isDummy(t: Task[_]): Boolean = t.info.attributes.get(isDummyTask) getOrElse false
def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): Seq[Attributed[File]] = def evalPluginDef(log: Logger)(pluginDef: BuildStructure, state: State): Seq[Attributed[File]] =
{ {
@ -134,9 +140,22 @@ object EvaluateTask
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] = def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
withStreams(structure) { str => withStreams(structure) { str =>
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
runTask(task, checkCycles, maxWorkers)(toNode) runTask(task, str, checkCycles, maxWorkers)(toNode)
} }
def logIncResult(result: Result[_], streams: Streams) = result match { case Inc(i) => logIncomplete(i, streams); case _ => () }
def logIncomplete(result: Incomplete, streams: Streams)
{
val log = streams(ScopedKey(GlobalScope, Keys.logged)).log
val all = for(Incomplete(Some(key: Project.ScopedKey[_]), _, msg, _, ex) <- Incomplete linearize result) yield (key, msg, ex)
for( (key, _, Some(ex)) <- all)
getStreams(key, streams).log.trace(ex)
log.error("Incomplete task(s):")
for( (key, msg, ex) <- all if(msg.isDefined || ex.isDefined) )
getStreams(key, streams).log.error(" " + Project.display(key) + ": " + (msg.toList ++ ex.toList).mkString("\n\t"))
log.error("Run 'last <task>' for the full log(s).")
}
def getStreams(key: ScopedKey[_], streams: Streams): TaskStreams =
streams(ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key))
def withStreams[T](structure: BuildStructure)(f: Streams => T): T = def withStreams[T](structure: BuildStructure)(f: Streams => T): T =
{ {
val str = std.Streams.closeable(structure.streams) val str = std.Streams.closeable(structure.streams)
@ -153,13 +172,25 @@ object EvaluateTask
def nodeView[HL <: HList](state: State, streams: Streams, extraDummies: KList[Task, HL] = KNil, extraValues: HL = HNil): Execute.NodeView[Task] = def nodeView[HL <: HList](state: State, streams: Streams, extraDummies: KList[Task, HL] = KNil, extraValues: HL = HNil): Execute.NodeView[Task] =
Transform(dummyStreamsManager :^: KCons(dummyState, extraDummies), streams :+: HCons(state, extraValues)) Transform(dummyStreamsManager :^: KCons(dummyState, extraDummies), streams :+: HCons(state, extraValues))
def runTask[Task[_] <: AnyRef, T](root: Task[T], checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] = def runTask[Task[_] <: AnyRef, T](root: Task[T], streams: Streams, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors)(implicit taskToNode: Execute.NodeView[Task]): Result[T] =
{ {
val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers) val (service, shutdown) = CompletionService[Task[_], Completed](maxWorkers)
val x = new Execute[Task](checkCycles)(taskToNode) val x = new Execute[Task](checkCycles)(taskToNode)
try { x.run(root)(service) } finally { shutdown() } val result = try { x.run(root)(service) } finally { shutdown() }
val replaced = transformInc(result)
logIncResult(replaced, streams)
replaced
} }
def transformInc[T](result: Result[T]): Result[T] =
result.toEither.left.map { i =>
Incomplete.transform(i) {
case in @ Incomplete(Some(node: Task[_]), _, _, _, _) => in.copy(node = transformNode(node))
case _ => i
}
}
def transformNode(node: Task[_]): Option[ScopedKey[_]] =
node.info.attributes get taskDefinitionKey
def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T = def processResult[T](result: Result[T], log: Logger, show: Boolean = false): T =
onResult(result, log) { v => if(show) println("Result: " + v); v } onResult(result, log) { v => if(show) println("Result: " + v); v }
@ -167,9 +198,7 @@ object EvaluateTask
result match result match
{ {
case Value(v) => f(v) case Value(v) => f(v)
case Inc(inc) => case Inc(inc) => throw inc
log.error(Incomplete.show(inc, true))
error("Task did not complete successfully")
} }
// if the return type Seq[Setting[_]] is not explicitly given, scalac hangs // if the return type Seq[Setting[_]] is not explicitly given, scalac hangs
@ -287,7 +316,7 @@ object Load
// additionally, set the task axis to the defining key if it is not set // additionally, set the task axis to the defining key if it is not set
def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] = def finalTransforms(ss: Seq[Setting[_]]): Seq[Setting[_]] =
{ {
import EvaluateTask.{parseResult, resolvedScoped, streams} import EvaluateTask.{parseResult, resolvedScoped}
def isSpecial(key: AttributeKey[_]) = key == streams.key || key == resolvedScoped.key || key == parseResult.key def isSpecial(key: AttributeKey[_]) = key == streams.key || key == resolvedScoped.key || key == parseResult.key
def mapSpecial(to: ScopedKey[_]) = new (ScopedKey ~> ScopedKey){ def apply[T](key: ScopedKey[T]) = def mapSpecial(to: ScopedKey[_]) = new (ScopedKey ~> ScopedKey){ def apply[T](key: ScopedKey[T]) =
if(isSpecial(key.key)) if(isSpecial(key.key))
@ -298,7 +327,11 @@ object Load
} }
else key else key
} }
ss.map(s => s mapReferenced mapSpecial(s.key) ) def setDefining[T] = (key: ScopedKey[T], value: T) => value match {
case tk: Task[t] if !EvaluateTask.isDummy(tk) => Task(tk.info.set(EvaluateTask.taskDefinitionKey, key), tk.work).asInstanceOf[T]
case _ => value
}
ss.map(s => s mapReferenced mapSpecial(s.key) mapInit setDefining )
} }
def structureIndex(settings: Settings[Scope]): StructureIndex = def structureIndex(settings: Settings[Scope]): StructureIndex =

View File

@ -41,21 +41,22 @@ EvalCommand + """ <expression>
Evaluates the given Scala expression and prints the result and type. Evaluates the given Scala expression and prints the result and type.
""" """
val LastCommand = "last"
val LastGrepCommand = "last-grep" val LastGrepCommand = "last-grep"
val lastGrepBrief = (LastGrepCommand + " <pattern> <key>", "Shows lines from the last output for 'key' that match 'pattern'.") val lastGrepBrief = (LastGrepCommand + " <pattern> <key>", "Shows lines from the last output for 'key' that match 'pattern'.")
val lastGrepDetailed = val lastGrepDetailed =
LastGrepCommand + """ <pattern> <key> LastGrepCommand + """ <pattern> <key>
<pattern> is a regular expression interpreted by java.util.Pattern <pattern> is a regular expression interpreted by java.util.Pattern.
Lines that match 'pattern' from the last streams output associated with the key are displayed. Lines that match 'pattern' from the last streams output associated with the key are displayed.
See also """ + LastCommand + "." See also """ + LastCommand + "."
val LastCommand = "last"
val lastBrief = (LastCommand + " <key>", "Prints the last output associated with 'key'.") val lastBrief = (LastCommand + " <key>", "Prints the last output associated with 'key'.")
val lastDetailed = val lastDetailed =
LastCommand + """ <key> LastCommand + """ <key>
The last streams output associated with the key (typically a task key) is redisplayed. Redisplays the last streams output associated with the key (typically a task key).
See also """ + LastGrepCommand + "." See also """ + LastGrepCommand + "."
val InspectCommand = "inspect" val InspectCommand = "inspect"

View File

@ -8,7 +8,7 @@ package sbt
import compiler.Discovery import compiler.Discovery
import Project.{inConfig, Initialize, inScope, inTask, ScopedKey, Setting} import Project.{inConfig, Initialize, inScope, inTask, ScopedKey, Setting}
import Configurations.{Compile => CompileConf, Test => TestConf} import Configurations.{Compile => CompileConf, Test => TestConf}
import EvaluateTask.{resolvedScoped, streams} import EvaluateTask.resolvedScoped
import complete._ import complete._
import std.TaskExtra._ import std.TaskExtra._

View File

@ -4,7 +4,7 @@
package sbt package sbt
import java.io.File import java.io.File
import EvaluateTask.{resolvedScoped, streams} import EvaluateTask.resolvedScoped
import complete._ import complete._
import inc.Analysis import inc.Analysis
import std.TaskExtra._ import std.TaskExtra._
@ -19,6 +19,7 @@ object Keys
val logLevel = SettingKey[Level.Value]("log-level") val logLevel = SettingKey[Level.Value]("log-level")
val persistLogLevel = SettingKey[Level.Value]("persist-log-level") val persistLogLevel = SettingKey[Level.Value]("persist-log-level")
val traceLevel = SettingKey[Int]("trace-level") val traceLevel = SettingKey[Int]("trace-level")
val persistTraceLevel = SettingKey[Int]("persist-trace-level")
val showSuccess = SettingKey[Boolean]("show-success") val showSuccess = SettingKey[Boolean]("show-success")
val showTiming = SettingKey[Boolean]("show-timing") val showTiming = SettingKey[Boolean]("show-timing")
val timingFormat = SettingKey[java.text.DateFormat]("timing-format") val timingFormat = SettingKey[java.text.DateFormat]("timing-format")
@ -201,4 +202,5 @@ object Keys
// special // special
val settings = TaskKey[Settings[Scope]]("settings") val settings = TaskKey[Settings[Scope]]("settings")
val streams = TaskKey[BuildStreams.TaskStreams]("streams")
} }

View File

@ -7,16 +7,18 @@ package sbt
import LogManager._ import LogManager._
import std.Transform import std.Transform
import Project.ScopedKey import Project.ScopedKey
import Keys.{logLevel, persistLogLevel} import Keys.{logLevel, persistLogLevel, persistTraceLevel, traceLevel}
object LogManager object LogManager
{ {
def construct(data: Settings[Scope]) = (task: ScopedKey[_], to: PrintWriter) => def construct(data: Settings[Scope]) = (task: ScopedKey[_], to: PrintWriter) =>
{ {
val scope = task.scope val scope = task.scope
def level(key: AttributeKey[Level.Value], default: Level.Value): Level.Value = data.get(scope, key) getOrElse default def getOr[T](key: AttributeKey[T], default: T): T = data.get(scope, key) getOrElse default
val screenLevel = level(logLevel.key, Level.Info) val screenLevel = getOr(logLevel.key, Level.Info)
val backingLevel = level(persistLogLevel.key, Level.Debug) val backingLevel = getOr(persistLogLevel.key, Level.Debug)
val screenTrace = getOr(traceLevel.key, -1)
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
val console = ConsoleLogger() val console = ConsoleLogger()
val backed = ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = false) // TODO: wrap this with a filter that strips ANSI codes val backed = ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = false) // TODO: wrap this with a filter that strips ANSI codes
@ -27,6 +29,8 @@ object LogManager
// set the specific levels // set the specific levels
console setLevel screenLevel console setLevel screenLevel
backed setLevel backingLevel backed setLevel backingLevel
console setTrace screenTrace
backed setTrace backingTrace
multi: Logger multi: Logger
} }
} }

View File

@ -323,10 +323,17 @@ object BuiltinCommands
Project.setProject(session, structure, s) Project.setProject(session, structure, s)
} }
def handleException(e: Throwable, s: State, trace: Boolean = true): State = { def handleException(e: Throwable, s: State): State =
val log = logger(s) handleException(e, s, logger(s))
if(trace) log.trace(e) def handleException(e: Throwable, s: State, log: Logger): State =
{
e match
{
case i: Incomplete => () // already handled by evaluateTask
case _ =>
log.trace(e)
log.error(e.toString) log.error(e.toString)
}
s.fail s.fail
} }

View File

@ -8,7 +8,7 @@ package sbt
import Project._ import Project._
import Types.Endo import Types.Endo
import Keys.{appConfiguration, buildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, thisProject, thisProjectRef, watch} import Keys.{appConfiguration, buildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, thisProject, thisProjectRef, watch}
import Scope.ThisScope import Scope.{GlobalScope,ThisScope}
import CommandSupport.logger import CommandSupport.logger
import compiler.Eval import compiler.Eval
@ -72,6 +72,7 @@ object Project extends Init[Scope]
def getOrError[T](state: State, key: AttributeKey[T], msg: String): T = state get key getOrElse error(msg) def getOrError[T](state: State, key: AttributeKey[T], msg: String): T = state get key getOrElse error(msg)
def structure(state: State): Load.BuildStructure = getOrError(state, buildStructure, "No build loaded.") def structure(state: State): Load.BuildStructure = getOrError(state, buildStructure, "No build loaded.")
def session(state: State): SessionSettings = getOrError(state, sessionSettings, "Session not initialized.") def session(state: State): SessionSettings = getOrError(state, sessionSettings, "Session not initialized.")
def extract(state: State): Extracted = def extract(state: State): Extracted =
{ {
val se = session(state) val se = session(state)

View File

@ -201,11 +201,6 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
reverse(node) = Seq() reverse(node) = Seq()
viewCache.getOrUpdate(node, view(node)) viewCache.getOrUpdate(node, view(node))
} }
def incomplete(in: A[_]): Option[(A[_], Incomplete)] =
results(in) match {
case Value(v) => None
case Inc(inc) => Some( (in, inc) )
}
/** Send the work for this node to the provided Strategy. */ /** Send the work for this node to the provided Strategy. */
def submit[T]( node: A[T] )(implicit strategy: Strategy) def submit[T]( node: A[T] )(implicit strategy: Strategy)
{ {
@ -219,8 +214,8 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
def work[T](node: A[T], f: => Either[A[T], T])(implicit strategy: Strategy): Completed = def work[T](node: A[T], f: => Either[A[T], T])(implicit strategy: Strategy): Completed =
{ {
val result = wideConvert(f).left.map { val result = wideConvert(f).left.map {
case i: Incomplete => i case i: Incomplete => if(i.node.isEmpty) i.copy(node = Some(node)) else i
case e => Incomplete(Incomplete.Error, directCause = Some(e)) case e => Incomplete(Some(node), Incomplete.Error, directCause = Some(e))
} }
completed { completed {
result match { result match {
@ -304,7 +299,7 @@ final class Execute[A[_] <: AnyRef](checkCycles: Boolean)(implicit view: NodeVie
allCallers(node) allCallers(node)
if(all contains target) cyclic(node, target, "Cyclic reference") if(all contains target) cyclic(node, target, "Cyclic reference")
} }
def cyclic[T](caller: A[T], target: A[T], msg: String) = throw new Incomplete(message = Some(msg), directCause = Some( new CyclicException(caller, target, msg) ) ) def cyclic[T](caller: A[T], target: A[T], msg: String) = throw new Incomplete(Some(caller), message = Some(msg), directCause = Some( new CyclicException(caller, target, msg) ) )
final class CyclicException[T](val caller: A[T], val target: A[T], msg: String) extends Exception(msg) final class CyclicException[T](val caller: A[T], val target: A[T], msg: String) extends Exception(msg)
// state testing // state testing

View File

@ -4,31 +4,47 @@
package sbt package sbt
import Incomplete.{Error, Value => IValue} import Incomplete.{Error, Value => IValue}
final case class Incomplete(tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None) final case class Incomplete(node: Option[AnyRef], tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None)
extends Exception(message.orNull, directCause.orNull) extends Exception(message.orNull, directCause.orNull) {
override def toString = "Incomplete(node=" + node + ", tpe=" + tpe + ", msg=" + message + ", causes=" + causes + ", directCause=" + directCause +")"
}
object Incomplete extends Enumeration { object Incomplete extends Enumeration {
val Skipped, Error = Value val Skipped, Error = Value
def show(i: Incomplete, traces: Boolean): String =
{
val exceptions = allExceptions(i)
val traces = exceptions.map(ex => ex.getStackTrace.mkString(ex.toString + "\n\t", "\n\t", "\n"))
val causeStr = if(i.causes.isEmpty) "" else (i.causes.length + " cause(s)")
"Incomplete (" + show(i.tpe) + ") " + i.message.getOrElse("") + causeStr + "\n" + traces
}
def allExceptions(is: Seq[Incomplete]): Iterable[Throwable] = def transform(i: Incomplete)(f: Incomplete => Incomplete): Incomplete =
allExceptions(new Incomplete(causes = is)) {
def allExceptions(i: Incomplete): Iterable[Throwable] = import collection.JavaConversions._
val visited: collection.mutable.Map[Incomplete,Incomplete] = new java.util.IdentityHashMap[Incomplete, Incomplete]
def visit(inc: Incomplete): Incomplete =
visited.getOrElseUpdate(inc, visitCauses(f(inc)) )
def visitCauses(inc: Incomplete): Incomplete =
inc.copy(causes = inc.causes.map(visit) )
visit(i)
}
def visitAll(i: Incomplete)(f: Incomplete => Unit)
{ {
val exceptions = IDSet.create[Throwable]
val visited = IDSet.create[Incomplete] val visited = IDSet.create[Incomplete]
def visit(inc: Incomplete): Unit = def visit(inc: Incomplete): Unit =
visited.process(inc)( () ) { visited.process(inc)( () ) {
exceptions ++= inc.directCause.toList f(inc)
inc.causes.foreach(visit) inc.causes.foreach(visit)
} }
visit(i) visit(i)
}
def linearize(i: Incomplete): Seq[Incomplete] =
{
var ordered = List[Incomplete]()
visitAll(i) { ordered ::= _ }
ordered
}
def allExceptions(is: Seq[Incomplete]): Iterable[Throwable] =
allExceptions(new Incomplete(None, causes = is))
def allExceptions(i: Incomplete): Iterable[Throwable] =
{
val exceptions = IDSet.create[Throwable]
visitAll(i) { exceptions ++= _.directCause.toList }
exceptions.all exceptions.all
} }
def show(tpe: Value) = tpe match { case Skipped=> "skipped"; case Error => "error" } def show(tpe: Value) = tpe match { case Skipped=> "skipped"; case Error => "error" }

View File

@ -36,4 +36,8 @@ object Result
r foreach tryValue[Unit] r foreach tryValue[Unit]
tryValue[S](v) tryValue[S](v)
} }
implicit def fromEither[T](e: Either[Incomplete, T]): Result[T] = e match {
case Left(i) => Inc(i)
case Right(v) => Value(v)
}
} }

View File

@ -206,20 +206,22 @@ object TaskExtra extends TaskExtra
} }
def failM[T]: Result[T] => Incomplete = { case Inc(i) => i; case x => expectedFailure } def failM[T]: Result[T] => Incomplete = { case Inc(i) => i; case x => expectedFailure }
def expectedFailure = throw Incomplete(message = Some("Expected failure")) def expectedFailure = throw Incomplete(None, message = Some("Expected dependency to fail."))
def successM[T]: Result[T] => T = { case Inc(i) => throw i; case Value(t) => t } def successM[T]: Result[T] => T = { case Inc(i) => throw i; case Value(t) => t }
def allM[In <: HList]: Results[In] => In = in => def allM[In <: HList]: Results[In] => In = in =>
{ {
val incs = failuresM(in) val incs = failuresM(in)
if(incs.isEmpty) in.down(Result.tryValue) else throw Incomplete(causes = incs) if(incs.isEmpty) in.down(Result.tryValue) else throw incompleteDeps(incs)
} }
def failuresM[In <: HList]: Results[In] => Seq[Incomplete] = x => failures[Any](x.toList) def failuresM[In <: HList]: Results[In] => Seq[Incomplete] = x => failures[Any](x.toList)
def all[D](in: Seq[Result[D]]) = def all[D](in: Seq[Result[D]]) =
{ {
val incs = failures(in) val incs = failures(in)
if(incs.isEmpty) in.map(Result.tryValue.fn[D]) else throw Incomplete(causes = incs) if(incs.isEmpty) in.map(Result.tryValue.fn[D]) else throw incompleteDeps(incs)
} }
def failures[A](results: Seq[Result[A]]): Seq[Incomplete] = results.collect { case Inc(i) => i } def failures[A](results: Seq[Result[A]]): Seq[Incomplete] = results.collect { case Inc(i) => i }
def incompleteDeps(incs: Seq[Incomplete]): Incomplete = Incomplete(None, message = Some("Dependency did not complete successfully."), causes = incs)
} }

View File

@ -180,6 +180,7 @@ trait Init[Scope]
def dependsOn: Seq[ScopedKey[_]] = remove(init.dependsOn, key) def dependsOn: Seq[ScopedKey[_]] = remove(init.dependsOn, key)
def mapReferenced(g: MapScoped): Setting[T] = new Setting(key, init mapReferenced g) def mapReferenced(g: MapScoped): Setting[T] = new Setting(key, init mapReferenced g)
def mapKey(g: MapScoped): Setting[T] = new Setting(g(key), init) def mapKey(g: MapScoped): Setting[T] = new Setting(g(key), init)
def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = new Setting(key, init.map(t => f(key,t)))
override def toString = "setting(" + key + ")" override def toString = "setting(" + key + ")"
} }