open up ExecuteProgress, and adds a few keys

Fixes #4461

This opens up ExecuteProgress API that's been around under private[sbt].
Since the state passing mechanism hasn't been used, I got rid of it.

The build user can configure the build using two keys Boolean `taskProgress` and `State => Seq[TaskProgress]` `progressReports`. `useSuperShell` is lightweight key on/off switch for the super shell that can be used as follows:

```scala
Global / SettingKey[Boolean]("useSuperShell") := false
```
This commit is contained in:
Eugene Yokota 2019-03-17 21:17:39 -04:00
parent 354f85cecb
commit 86abe3ed60
9 changed files with 144 additions and 87 deletions

View File

@ -291,7 +291,10 @@ lazy val taskProj = (project in file("tasks"))
name := "Tasks", name := "Tasks",
mimaSettings, mimaSettings,
mimaBinaryIssueFilters ++= Seq( mimaBinaryIssueFilters ++= Seq(
exclude[ReversedMissingMethodProblem]("sbt.ExecuteProgress.stop") // ok because sbt.ExecuteProgress has been under private[sbt]
exclude[IncompatibleResultTypeProblem]("sbt.ExecuteProgress.initial"),
exclude[DirectMissingMethodProblem]("sbt.ExecuteProgress.*"),
exclude[ReversedMissingMethodProblem]("sbt.ExecuteProgress.*"),
) )
) )
.configure(addSbtUtilControl) .configure(addSbtUtilControl)

View File

@ -259,6 +259,15 @@ object Defaults extends BuildCommon {
() => () =>
{ IO.delete(dir); IO.createDirectory(dir) } { IO.delete(dir); IO.createDirectory(dir) }
}, },
useSuperShell :== sbt.internal.TaskProgress.isEnabled,
progressReports := { (s: State) =>
val progress = useSuperShell.value
val rs = EvaluateTask.taskTimingProgress.toVector ++ {
if (progress) Vector(EvaluateTask.taskProgress(s))
else Vector()
}
rs map { Keys.TaskProgress(_) }
},
Previous.cache := new Previous( Previous.cache := new Previous(
Def.streamsManagerKey.value, Def.streamsManagerKey.value,
Previous.references.value.getReferences Previous.references.value.getReferences

View File

@ -162,19 +162,21 @@ object EvaluateTask {
import Keys.state import Keys.state
lazy private val sharedProgress = new TaskTimings(reportOnShutdown = true) lazy private val sharedProgress = new TaskTimings(reportOnShutdown = true)
def taskTimingProgress: Option[ExecuteProgress[Task]] =
// sbt-pgp calls this
private[sbt] def defaultProgress(): ExecuteProgress[Task] = ExecuteProgress.empty[Task]
private[sbt] def defaultProgress(currentRef: ProjectRef): ExecuteProgress[Task] =
if (java.lang.Boolean.getBoolean("sbt.task.timings")) { if (java.lang.Boolean.getBoolean("sbt.task.timings")) {
if (java.lang.Boolean.getBoolean("sbt.task.timings.on.shutdown")) if (java.lang.Boolean.getBoolean("sbt.task.timings.on.shutdown"))
sharedProgress Some(sharedProgress)
else else
new TaskTimings(reportOnShutdown = false) Some(new TaskTimings(reportOnShutdown = false))
} else { } else None
if (ConsoleAppender.showProgress) new TaskProgress(currentRef)
else ExecuteProgress.empty[Task] def taskProgress(state: State): ExecuteProgress[Task] = {
} new TaskProgress(Project.extract(state).currentRef)
}
// sbt-pgp calls this
@deprecated("No longer used", "1.3.0")
private[sbt] def defaultProgress(): ExecuteProgress[Task] = ExecuteProgress.empty[Task]
val SystemProcessors = Runtime.getRuntime.availableProcessors val SystemProcessors = Runtime.getRuntime.availableProcessors
@ -232,13 +234,22 @@ object EvaluateTask {
state: State state: State
): ExecuteProgress[Task] = { ): ExecuteProgress[Task] = {
import Types.const import Types.const
val maker: State => Keys.TaskProgress = getSetting( val maker: State => Seq[Keys.TaskProgress] = getSetting(
Keys.executeProgress, Keys.progressReports,
const(new Keys.TaskProgress(defaultProgress(extracted.currentRef))), const(Seq()),
extracted, extracted,
structure structure
) )
maker(state).progress val reporters = maker(state) map { _.progress }
// configure the logger for super shell
ConsoleAppender.setShowProgress((reporters collect {
case p: TaskProgress => ()
}).nonEmpty)
reporters match {
case xs if xs.isEmpty => ExecuteProgress.empty[Task]
case xs if xs.size == 1 => xs.head
case xs => ExecuteProgress.aggregate[Task](xs)
}
} }
// TODO - Should this pull from Global or from the project itself? // TODO - Should this pull from Global or from the project itself?
private[sbt] def forcegc(extracted: Extracted, structure: BuildStructure): Boolean = private[sbt] def forcegc(extracted: Extracted, structure: BuildStructure): Boolean =
@ -258,7 +269,7 @@ object EvaluateTask {
extracted: Extracted, extracted: Extracted,
structure: BuildStructure structure: BuildStructure
): T = ): T =
key in extracted.currentRef get structure.data getOrElse default (key in extracted.currentRef).get(structure.data).getOrElse(default)
def injectSettings: Seq[Setting[_]] = Seq( def injectSettings: Seq[Setting[_]] = Seq(
(state in Global) ::= dummyState, (state in Global) ::= dummyState,

View File

@ -456,6 +456,18 @@ object Keys {
val (executionRoots, dummyRoots) = Def.dummy[Seq[ScopedKey[_]]]("executionRoots", "The list of root tasks for this task execution. Roots are the top-level tasks that were directly requested to be run.") val (executionRoots, dummyRoots) = Def.dummy[Seq[ScopedKey[_]]]("executionRoots", "The list of root tasks for this task execution. Roots are the top-level tasks that were directly requested to be run.")
val state = Def.stateKey val state = Def.stateKey
val streamsManager = Def.streamsManagerKey val streamsManager = Def.streamsManagerKey
// wrapper to work around SI-2915
final class TaskProgress(val progress: ExecuteProgress[Task])
object TaskProgress {
def apply(progress: ExecuteProgress[Task]): TaskProgress = new TaskProgress(progress)
}
val useSuperShell = settingKey[Boolean]("Enables (true) or disables the super shell.")
// This key can be used to add custom ExecuteProgress instances
val progressReports = settingKey[State => Seq[TaskProgress]]("A function that returns a list of progress reporters.").withRank(DTask)
private[sbt] val postProgressReports = settingKey[Unit]("Internally used to modify logger.").withRank(DTask)
@deprecated("No longer used", "1.3.0")
private[sbt] val executeProgress = settingKey[State => TaskProgress]("Experimental task execution listener.").withRank(DTask)
private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[FileCacheEntry]]( private[sbt] val globalFileTreeView = AttributeKey[FileTreeDataView[FileCacheEntry]](
"globalFileTreeView", "globalFileTreeView",
"Provides a view into the file system that may or may not cache the tree in memory", "Provides a view into the file system that may or may not cache the tree in memory",
@ -468,10 +480,6 @@ object Keys {
val globalPluginUpdate = taskKey[UpdateReport]("A hook to get the UpdateReport of the global plugin.").withRank(DTask) val globalPluginUpdate = taskKey[UpdateReport]("A hook to get the UpdateReport of the global plugin.").withRank(DTask)
val classLoaderCache = taskKey[internal.ClassLoaderCache]("The cache of ClassLoaders to be used for layering in tasks that invoke other java code").withRank(DTask) val classLoaderCache = taskKey[internal.ClassLoaderCache]("The cache of ClassLoaders to be used for layering in tasks that invoke other java code").withRank(DTask)
private[sbt] val taskRepository = AttributeKey[TaskRepository.Repr]("task-repository", "A repository that can be used to cache arbitrary values for a given task key that can be read or filled during task evaluation.", 10000) private[sbt] val taskRepository = AttributeKey[TaskRepository.Repr]("task-repository", "A repository that can be used to cache arbitrary values for a given task key that can be read or filled during task evaluation.", 10000)
// wrapper to work around SI-2915
private[sbt] final class TaskProgress(val progress: ExecuteProgress[Task])
private[sbt] val executeProgress = settingKey[State => TaskProgress]("Experimental task execution listener.").withRank(DTask)
private[sbt] val taskCancelStrategy = settingKey[State => TaskCancellationStrategy]("Experimental task cancellation handler.").withRank(DTask) private[sbt] val taskCancelStrategy = settingKey[State => TaskCancellationStrategy]("Experimental task cancellation handler.").withRank(DTask)
// Experimental in sbt 0.13.2 to enable grabbing semantic compile failures. // Experimental in sbt 0.13.2 to enable grabbing semantic compile failures.

View File

@ -138,20 +138,6 @@ object StandardMain {
// This is to workaround https://github.com/sbt/io/issues/110 // This is to workaround https://github.com/sbt/io/issues/110
sys.props.put("jna.nosys", "true") sys.props.put("jna.nosys", "true")
ConsoleAppender.setTerminalWidth(JLine.usingTerminal(_.getWidth))
ConsoleAppender.setShowProgress(
ConsoleAppender.formatEnabledInEnv && sys.props
.get("sbt.progress")
.flatMap({ s =>
ConsoleAppender.parseLogOption(s) match {
case LogOption.Always => Some(true)
case LogOption.Never => Some(false)
case _ => None
}
})
.getOrElse(true)
)
import BasicCommandStrings.isEarlyCommand import BasicCommandStrings.isEarlyCommand
val userCommands = configuration.arguments.map(_.trim) val userCommands = configuration.arguments.map(_.trim)
val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand) val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand)

View File

@ -8,7 +8,7 @@
package sbt package sbt
package internal package internal
import sbt.internal.util.{ RMap, ConsoleOut } import sbt.internal.util.{ RMap, ConsoleOut, ConsoleAppender, LogOption, JLine }
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import scala.concurrent.{ blocking, Future, ExecutionContext } import scala.concurrent.{ blocking, Future, ExecutionContext }
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger } import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
@ -20,8 +20,6 @@ import TaskProgress._
* implements task progress display on the shell. * implements task progress display on the shell.
*/ */
private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecuteProgress[Task] { private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecuteProgress[Task] {
type S = Unit
private[this] val showScopedKey = Def.showRelativeKey2(currentRef) private[this] val showScopedKey = Def.showRelativeKey2(currentRef)
// private[this] var start = 0L // private[this] var start = 0L
private[this] val activeTasks = new ConcurrentHashMap[Task[_], Long] private[this] val activeTasks = new ConcurrentHashMap[Task[_], Long]
@ -33,9 +31,11 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
private[this] val isAllCompleted = new AtomicBoolean(false) private[this] val isAllCompleted = new AtomicBoolean(false)
private[this] val isStopped = new AtomicBoolean(false) private[this] val isStopped = new AtomicBoolean(false)
override def initial: Unit = () override def initial(): Unit = {
override def registered( ConsoleAppender.setTerminalWidth(JLine.usingTerminal(_.getWidth))
state: Unit, }
override def afterRegistered(
task: Task[_], task: Task[_],
allDeps: Iterable[Task[_]], allDeps: Iterable[Task[_]],
pendingDeps: Iterable[Task[_]] pendingDeps: Iterable[Task[_]]
@ -47,16 +47,16 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
} }
} }
} }
override def ready(state: Unit, task: Task[_]): Unit = { override def afterReady(task: Task[_]): Unit = {
isReady.set(true) isReady.set(true)
} }
override def workStarting(task: Task[_]): Unit = { override def beforeWork(task: Task[_]): Unit = {
activeTasks.put(task, System.nanoTime) activeTasks.put(task, System.nanoTime)
() ()
} }
override def workFinished[A](task: Task[A], result: Either[Task[A], Result[A]]): Unit = { override def afterWork[A](task: Task[A], result: Either[Task[A], Result[A]]): Unit = {
val start = activeTasks.get(task) val start = activeTasks.get(task)
timings.put(task, System.nanoTime - start) timings.put(task, System.nanoTime - start)
activeTasks.remove(task) activeTasks.remove(task)
@ -66,7 +66,7 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
} }
} }
override def completed[A](state: Unit, task: Task[A], result: Result[A]): Unit = () override def afterCompleted[A](task: Task[A], result: Result[A]): Unit = ()
override def stop(): Unit = { override def stop(): Unit = {
isStopped.set(true) isStopped.set(true)
@ -88,7 +88,7 @@ private[sbt] final class TaskProgress(currentRef: ProjectRef) extends ExecutePro
} }
private[this] val console = ConsoleOut.systemOut private[this] val console = ConsoleOut.systemOut
override def allCompleted(state: Unit, results: RMap[Task, Result]): Unit = { override def afterAllCompleted(results: RMap[Task, Result]): Unit = {
isAllCompleted.set(true) isAllCompleted.set(true)
// completionReport() // completionReport()
} }
@ -167,4 +167,17 @@ private[sbt] object TaskProgress {
def cursorUp(n: Int): String = s"\u001B[${n}A" def cursorUp(n: Int): String = s"\u001B[${n}A"
def cursorDown(n: Int): String = s"\u001B[${n}B" def cursorDown(n: Int): String = s"\u001B[${n}B"
final val CursorDown1 = cursorDown(1) final val CursorDown1 = cursorDown(1)
def isEnabled: Boolean =
ConsoleAppender.formatEnabledInEnv && sys.props
.get("sbt.supershell")
.flatMap(
str =>
ConsoleAppender.parseLogOption(str) match {
case LogOption.Always => Some(true)
case LogOption.Never => Some(false)
case _ => None
}
)
.getOrElse(true)
} }

View File

@ -39,8 +39,6 @@ private[sbt] final class TaskTimings(reportOnShutdown: Boolean) extends ExecuteP
("ms", 6) ("ms", 6)
} }
type S = Unit
if (reportOnShutdown) { if (reportOnShutdown) {
start = System.nanoTime start = System.nanoTime
Runtime.getRuntime.addShutdownHook(new Thread { Runtime.getRuntime.addShutdownHook(new Thread {
@ -48,35 +46,34 @@ private[sbt] final class TaskTimings(reportOnShutdown: Boolean) extends ExecuteP
}) })
} }
def initial = { override def initial(): Unit = {
if (!reportOnShutdown) if (!reportOnShutdown)
start = System.nanoTime start = System.nanoTime
} }
def registered( override def afterRegistered(
state: Unit,
task: Task[_], task: Task[_],
allDeps: Iterable[Task[_]], allDeps: Iterable[Task[_]],
pendingDeps: Iterable[Task[_]] pendingDeps: Iterable[Task[_]]
) = { ): Unit = {
pendingDeps foreach { t => pendingDeps foreach { t =>
if (transformNode(t).isEmpty) anonOwners.put(t, task) if (transformNode(t).isEmpty) anonOwners.put(t, task)
} }
} }
def ready(state: Unit, task: Task[_]) = () override def afterReady(task: Task[_]): Unit = ()
def workStarting(task: Task[_]) = { timings.put(task, System.nanoTime); () } override def beforeWork(task: Task[_]): Unit = { timings.put(task, System.nanoTime); () }
def workFinished[T](task: Task[T], result: Either[Task[T], Result[T]]) = { override def afterWork[T](task: Task[T], result: Either[Task[T], Result[T]]) = {
timings.put(task, System.nanoTime - timings.get(task)) timings.put(task, System.nanoTime - timings.get(task))
result.left.foreach { t => result.left.foreach { t =>
calledBy.put(t, task) calledBy.put(t, task)
} }
} }
def completed[T](state: Unit, task: Task[T], result: Result[T]) = () override def afterCompleted[T](task: Task[T], result: Result[T]): Unit = ()
def allCompleted(state: Unit, results: RMap[Task, Result]) = override def afterAllCompleted(results: RMap[Task, Result]): Unit =
if (!reportOnShutdown) { if (!reportOnShutdown) {
report() report()
} }
def stop(): Unit = () override def stop(): Unit = ()
private val reFilePath = raw"\{[^}]+\}".r private val reFilePath = raw"\{[^}]+\}".r

View File

@ -69,14 +69,14 @@ private[sbt] final class Execute[F[_] <: AnyRef](
case None => results(a) case None => results(a)
} }
) )
private[this] var progressState: progress.S = progress.initial
private[this] type State = State.Value private[this] type State = State.Value
private[this] object State extends Enumeration { private[this] object State extends Enumeration {
val Pending, Running, Calling, Done = Value val Pending, Running, Calling, Done = Value
} }
import State.{ Pending, Running, Calling, Done } import State.{ Pending, Running, Calling, Done }
val init = progress.initial()
def dump: String = def dump: String =
"State: " + state.toString + "\n\nResults: " + results + "\n\nCalls: " + callers + "\n\n" "State: " + state.toString + "\n\nResults: " + results + "\n\nCalls: " + callers + "\n\n"
@ -90,7 +90,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
processAll() processAll()
assert(results contains root, "No result for root node.") assert(results contains root, "No result for root node.")
val finalResults = triggers.onComplete(results) val finalResults = triggers.onComplete(results)
progressState = progress.allCompleted(progressState, finalResults) progress.afterAllCompleted(finalResults)
finalResults finalResults
} }
@ -153,7 +153,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
results(node) = result results(node) = result
state(node) = Done state(node) = Done
progressState = progress.completed(progressState, node, result) progress.afterCompleted(node, result)
remove(reverse, node) foreach { dep => remove(reverse, node) foreach { dep =>
notifyDone(node, dep) notifyDone(node, dep)
} }
@ -210,8 +210,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
val v = register(node) val v = register(node)
val deps = dependencies(v) ++ runBefore(node) val deps = dependencies(v) ++ runBefore(node)
val active = IDSet[F[_]](deps filter notDone) val active = IDSet[F[_]](deps filter notDone)
progressState = progress.registered( progress.afterRegistered(
progressState,
node, node,
deps, deps,
active.toList active.toList
@ -245,7 +244,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
} }
state(node) = Running state(node) = Running
progressState = progress.ready(progressState, node) progress.afterReady(node)
submit(node) submit(node)
post { post {
@ -274,13 +273,13 @@ private[sbt] final class Execute[F[_] <: AnyRef](
* This returns a Completed instance, which contains the post-processing to perform after the result is retrieved from the Strategy. * This returns a Completed instance, which contains the post-processing to perform after the result is retrieved from the Strategy.
*/ */
def work[A](node: F[A], f: => Either[F[A], A])(implicit strategy: Strategy): Completed = { def work[A](node: F[A], f: => Either[F[A], A])(implicit strategy: Strategy): Completed = {
progress.workStarting(node) progress.beforeWork(node)
val rawResult = wideConvert(f).left.map { val rawResult = wideConvert(f).left.map {
case i: Incomplete => if (config.overwriteNode(i)) i.copy(node = Some(node)) else i case i: Incomplete => if (config.overwriteNode(i)) i.copy(node = Some(node)) else i
case e => Incomplete(Some(node), Incomplete.Error, directCause = Some(e)) case e => Incomplete(Some(node), Incomplete.Error, directCause = Some(e))
} }
val result = rewrap(rawResult) val result = rewrap(rawResult)
progress.workFinished(node, result) progress.afterWork(node, result)
completed { completed {
result match { result match {
case Right(v) => retire(node, v) case Right(v) => retire(node, v)

View File

@ -14,32 +14,29 @@ import sbt.internal.util.RMap
* All methods are called from the same thread except `started` and `finished`, * All methods are called from the same thread except `started` and `finished`,
* which is called from the executing task's thread. * which is called from the executing task's thread.
* All methods should return quickly to avoid task execution overhead. * All methods should return quickly to avoid task execution overhead.
*
* This class is experimental and subject to binary and source incompatible changes at any time.
*/ */
private[sbt] trait ExecuteProgress[F[_]] { trait ExecuteProgress[F[_]] {
type S def initial(): Unit
def initial: S
/** /**
* Notifies that a `task` has been registered in the system for execution. * Notifies that a `task` has been registered in the system for execution.
* The dependencies of `task` are `allDeps` and the subset of those dependencies that * The dependencies of `task` are `allDeps` and the subset of those dependencies that
* have not completed are `pendingDeps`. * have not completed are `pendingDeps`.
*/ */
def registered(state: S, task: F[_], allDeps: Iterable[F[_]], pendingDeps: Iterable[F[_]]): S def afterRegistered(task: F[_], allDeps: Iterable[F[_]], pendingDeps: Iterable[F[_]]): Unit
/** /**
* Notifies that all of the dependencies of `task` have completed and `task` is therefore * Notifies that all of the dependencies of `task` have completed and `task` is therefore
* ready to run. The task has not been scheduled on a thread yet. * ready to run. The task has not been scheduled on a thread yet.
*/ */
def ready(state: S, task: F[_]): S def afterReady(task: F[_]): Unit
/** /**
* Notifies that the work for `task` is starting after this call returns. * Notifies that the work for `task` is starting after this call returns.
* This is called from the thread the task executes on, unlike most other methods in this callback. * This is called from the thread the task executes on, unlike most other methods in this callback.
* It is called immediately before the task's work starts with minimal intervening executor overhead. * It is called immediately before the task's work starts with minimal intervening executor overhead.
*/ */
def workStarting(task: F[_]): Unit def beforeWork(task: F[_]): Unit
/** /**
* Notifies that the work for `task` work has finished. The task may have computed the next task to * Notifies that the work for `task` work has finished. The task may have computed the next task to
@ -49,34 +46,68 @@ private[sbt] trait ExecuteProgress[F[_]] {
* This is called from the thread the task executes on, unlike most other methods in this callback. * This is called from the thread the task executes on, unlike most other methods in this callback.
* It is immediately called after the task's work is complete with minimal intervening executor overhead. * It is immediately called after the task's work is complete with minimal intervening executor overhead.
*/ */
def workFinished[A](task: F[A], result: Either[F[A], Result[A]]): Unit def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit
/** /**
* Notifies that `task` has completed. * Notifies that `task` has completed.
* The task's work is done with a final `result`. * The task's work is done with a final `result`.
* Any tasks called by `task` have completed. * Any tasks called by `task` have completed.
*/ */
def completed[A](state: S, task: F[A], result: Result[A]): S def afterCompleted[A](task: F[A], result: Result[A]): Unit
/** All tasks have completed with the final `results` provided. */ /** All tasks have completed with the final `results` provided. */
def allCompleted(state: S, results: RMap[F, Result]): S def afterAllCompleted(results: RMap[F, Result]): Unit
/** Notifies that either all tasks have finished or cancelled. */ /** Notifies that either all tasks have finished or cancelled. */
def stop(): Unit def stop(): Unit
} }
/** This module is experimental and subject to binary and source incompatible changes at any time. */ /** This module is experimental and subject to binary and source incompatible changes at any time. */
private[sbt] object ExecuteProgress { object ExecuteProgress {
def empty[F[_]]: ExecuteProgress[F] = new ExecuteProgress[F] { def empty[F[_]]: ExecuteProgress[F] = new ExecuteProgress[F] {
type S = Unit override def initial(): Unit = ()
def initial = () override def afterRegistered(
def registered(state: Unit, task: F[_], allDeps: Iterable[F[_]], pendingDeps: Iterable[F[_]]) = task: F[_],
allDeps: Iterable[F[_]],
pendingDeps: Iterable[F[_]]
): Unit =
() ()
def ready(state: Unit, task: F[_]) = () override def afterReady(task: F[_]): Unit = ()
def workStarting(task: F[_]) = () override def beforeWork(task: F[_]): Unit = ()
def workFinished[A](task: F[A], result: Either[F[A], Result[A]]) = () override def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit = ()
def completed[A](state: Unit, task: F[A], result: Result[A]) = () override def afterCompleted[A](task: F[A], result: Result[A]): Unit = ()
def allCompleted(state: Unit, results: RMap[F, Result]) = () override def afterAllCompleted(results: RMap[F, Result]): Unit = ()
def stop(): Unit = () override def stop(): Unit = ()
}
def aggregate[F[_]](reporters: Seq[ExecuteProgress[F]]) = new ExecuteProgress[F] {
override def initial(): Unit = {
reporters foreach { _.initial() }
}
override def afterRegistered(
task: F[_],
allDeps: Iterable[F[_]],
pendingDeps: Iterable[F[_]]
): Unit = {
reporters foreach { _.afterRegistered(task, allDeps, pendingDeps) }
}
override def afterReady(task: F[_]): Unit = {
reporters foreach { _.afterReady(task) }
}
override def beforeWork(task: F[_]): Unit = {
reporters foreach { _.beforeWork(task) }
}
override def afterWork[A](task: F[A], result: Either[F[A], Result[A]]): Unit = {
reporters foreach { _.afterWork(task, result) }
}
override def afterCompleted[A](task: F[A], result: Result[A]): Unit = {
reporters foreach { _.afterCompleted(task, result) }
}
override def afterAllCompleted(results: RMap[F, Result]): Unit = {
reporters foreach { _.afterAllCompleted(results) }
}
override def stop(): Unit = {
reporters foreach { _.stop() }
}
} }
} }