Use and methods instead of mapR,mapFailure,flatFailure,flatMapR

This commit is contained in:
Mark Harrah 2012-12-02 03:17:19 -05:00
parent 5a45faf41b
commit 538f687208
5 changed files with 170 additions and 137 deletions

View File

@ -78,4 +78,22 @@ object Tags
// If there is only one task, allow it to execute.
tags.getOrElse(Tags.All, 0) == 1
}
/** Ensure that a task with the given tag only executes with tasks also tagged with the given tag.*/
def exclusiveGroup(exclusiveTag: Tag): Rule = customLimit { (tags: Map[Tag,Int]) =>
val exclusiveCount = tags.getOrElse(exclusiveTag, 0)
val allCount = tags.getOrElse(Tags.All, 0)
// If there are no exclusive tasks in this group, this rule adds no restrictions.
exclusiveCount == 0 ||
// If all tasks have this tag, allow them to execute.
exclusiveCount == allCount ||
// Always allow a group containing only one task to execute (fallthrough case).
allCount == 1
}
/** A task tagged with one of `exclusiveTags` will not execute with another task with any of the other tags in `exclusiveTags`.*/
def exclusiveGroups(exclusiveTags: Tag*): Rule = customLimit { (tags: Map[Tag,Int]) =>
val groups = exclusiveTags.count(tag => tags.getOrElse(tag, 0) > 0)
groups <= 1
}
}

View File

@ -28,7 +28,7 @@ sealed trait ScopedTaskable[T] extends Scoped {
* The scope is represented by a value of type Scope.
* The name and the type are represented by a value of type AttributeKey[T].
* Instances are constructed using the companion object. */
sealed trait SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] with Scoped.ScopingSetting[SettingKey[T]] with Scoped.DefinableSetting[T]
sealed abstract class SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] with Scoped.ScopingSetting[SettingKey[T]] with Scoped.DefinableSetting[T]
{
val key: AttributeKey[T]
final def toTask: Initialize[Task[T]] = this apply inlineTask
@ -48,7 +48,7 @@ sealed trait SettingKey[T] extends ScopedTaskable[T] with KeyedInitialize[T] wit
* The scope is represented by a value of type Scope.
* The name and the type are represented by a value of type AttributeKey[Task[T]].
* Instances are constructed using the companion object. */
sealed trait TaskKey[T] extends ScopedTaskable[T] with KeyedInitialize[Task[T]] with Scoped.ScopingSetting[TaskKey[T]] with Scoped.DefinableTask[T]
sealed abstract class TaskKey[T] extends ScopedTaskable[T] with KeyedInitialize[Task[T]] with Scoped.ScopingSetting[TaskKey[T]] with Scoped.DefinableTask[T]
{
val key: AttributeKey[Task[T]]
def toTask: Initialize[Task[T]] = this
@ -144,6 +144,9 @@ object Scoped
def dependsOn(tasks: AnyInitTask*): Initialize[Task[S]] = (i, Initialize.joinAny[Task](tasks)) { (thisTask, deps) => thisTask.dependsOn(deps : _*) }
def failure: Initialize[Task[Incomplete]] = i(_.failure)
def result: Initialize[Task[Result[S]]] = i(_.result)
def triggeredBy(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Def.triggeredBy)
def runBefore(tasks: AnyInitTask*): Initialize[Task[S]] = nonLocal(tasks, Def.runBefore)
private[this] def nonLocal(tasks: Seq[AnyInitTask], key: AttributeKey[Seq[Task[_]]]): Initialize[Task[S]] =
@ -159,12 +162,8 @@ object Scoped
{
protected def onTask[T](f: Task[S] => Task[T]): Initialize[R[T]]
def flatMapR[T](f: Result[S] => Task[T]): Initialize[R[T]] = onTask(_ flatMapR f)
def flatMap[T](f: S => Task[T]): Initialize[R[T]] = flatMapR(f compose successM)
def map[T](f: S => T): Initialize[R[T]] = mapR(f compose successM)
def mapR[T](f: Result[S] => T): Initialize[R[T]] = onTask(_ mapR f)
def flatFailure[T](f: Incomplete => Task[T]): Initialize[R[T]] = flatMapR(f compose failM)
def mapFailure[T](f: Incomplete => T): Initialize[R[T]] = mapR(f compose failM)
def andFinally(fin: => Unit): Initialize[R[S]] = onTask(_ andFinally fin)
def doFinally(t: Task[Unit]): Initialize[R[S]] = onTask(_ doFinally t)
@ -173,6 +172,18 @@ object Scoped
def tag(tags: Tag*): Initialize[R[S]] = onTask(_.tag(tags: _*))
def tagw(tags: (Tag, Int)*): Initialize[R[S]] = onTask(_.tagw(tags : _*))
@deprecated("Use the `result` method to create a task that returns the full Result of this task. Then, call `flatMap` on the new task.", "0.13.0")
def flatMapR[T](f: Result[S] => Task[T]): Initialize[R[T]] = onTask(_ flatMapR f)
@deprecated("Use the `result` method to create a task that returns the full Result of this task. Then, call `map` on the new task.", "0.13.0")
def mapR[T](f: Result[S] => T): Initialize[R[T]] = onTask(_ mapR f)
@deprecated("Use the `failure` method to create a task that returns Incomplete when this task fails and then call `flatMap` on the new task.", "0.13.0")
def flatFailure[T](f: Incomplete => Task[T]): Initialize[R[T]] = flatMapR(f compose failM)
@deprecated("Use the `failure` method to create a task that returns Incomplete when this task fails and then call `map` on the new task.", "0.13.0")
def mapFailure[T](f: Incomplete => T): Initialize[R[T]] = mapR(f compose failM)
}
type AnyInitTask = Initialize[Task[T]] forSome { type T }

View File

@ -0,0 +1,28 @@
package sbt
import org.scalacheck._
import Gen.{genInt,listOf,genString}
import Prop.forAll
import Tags._
object TagsTest extends Properties("Tags")
{
def tagMap: Gen[TagMap] = for(ts <- listOf(tagAndFrequency)) yield ts.toMap
def tagAndFrequency: Gen[(Tag, Int)] = for(t <- tag; count <- genInt) yield (t, count)
def tag: Gen[Tag] = for(s <- genString) yield Tag(s)
property("exclusive allows all groups without the exclusive tag") = forAll { (tm: TagMap, tag: Tag) =>
excl(tag)(tm - tag)
}
property("exclusive only allows a group with an excusive tag when the size is one") = forAll { (tm: TagMap, size: Int, etag: Tag) =>
val tm: TagMap = tm.updated(etag, math.abs(size))
excl(etag)(tm) == (size <= 1)
}
property("exclusive always allows a group of size one") = forAll { (etag: Tag, mapTag: Tag) =>
val tm: TagMap = Map(mapTag -> 1)
excl(etag)(tm)
}
private[this] def excl(tag: Tag): TagMap => Boolean = predicate(exclusive(tag) :: Nil)
}

View File

@ -383,9 +383,97 @@ do not affect already logged information.
Handling Failure
----------------
This section discusses the ``andFinally``, ``mapFailure``, and ``mapR``
This section discusses the ``failure``, ``result``, and ``andFinally``
methods, which are used to handle failure of other tasks.
``failure``
~~~~~~~~~~~
The ``failure`` method creates a new task that returns the ``Incomplete`` value
when the original task fails to complete normally. If the original task succeeds,
the new task fails.
`Incomplete <https://github.com/harrah/xsbt/latest/api/sbt/Incomplete.html>`_
is an exception with information about any tasks that caused the failure
and any underlying exceptions thrown during task execution.
For example:
::
intTask := error("Failed.")
val intTask := {
println("Ignoring failure: " + intTask.failure.value)
3
}
This overrides the ``intTask`` so that the original exception is printed and the constant ``3`` is returned.
``failure`` does not prevent other tasks that depend on the target
from failing. Consider the following example:
::
intTask := if(shouldSucceed) 5 else error("Failed.")
// Return 3 if intTask fails. If intTask succeeds, this task will fail.
aTask := intTask.failure.value - 2
// A new task that increments the result of intTask.
bTask := intTask.value + 1
cTask := aTask.value + bTask.value
The following table lists the results of each task depending on the initially invoked task:
============== =============== ============= ============== ============== ==============
invoked task intTask result aTask result bTask result cTask result overall result
============== =============== ============= ============== ============== ==============
intTask failure not run not run not run failure
aTask failure success not run not run success
bTask failure not run failure not run failure
cTask failure success failure failure failure
intTask success not run not run not run success
aTask success failure not run not run failure
bTask success not run success not run success
cTask success failure success failure failure
============== =============== ============= ============== ============== ==============
The overall result is always the same as the root task (the directly
invoked task). A ``failure`` turns a success into a failure, and a failure into an ``Incomplete``.
A normal task definition fails when any of its inputs fail and computes its value otherwise.
``result``
~~~~~~~~~~
The ``result`` method creates a new task that returns the full ``Result[T]`` value for the original task.
`Result <https://github.com/harrah/xsbt/latest/api/sbt/Result.html>`_
has the same structure as ``Either[Incomplete, T]`` for a task result of
type ``T``. That is, it has two subtypes:
- ``Inc``, which wraps ``Incomplete`` in case of failure
- ``Value``, which wraps a task's result in case of success.
Thus, the task created by ``result`` executes whether or not the original task succeeds or fails.
For example:
::
intTask := error("Failed.")
intTask := intTask.result.value match {
case Inc(inc: Incomplete) =>
println("Ignoring failure: " + inc)
3
case Value(v) =>
println("Using successful result: " + v)
v
}
This overrides the original ``intTask`` definition so that if the original task fails, the exception is printed and the constant ``3`` is returned. If it succeeds, the value is printed and returned.
andFinally
~~~~~~~~~~
@ -434,129 +522,3 @@ execution. This case is similar to the following plain Scala code:
It is obvious here that calling intTask() will never result in "finally"
being printed.
mapFailure
~~~~~~~~~~
``mapFailure`` accepts a function of type ``Incomplete => T``, where
``T`` is a type parameter. In the case of multiple inputs, the function
has type ``Seq[Incomplete] => T``.
`Incomplete <https://github.com/harrah/xsbt/latest/api/sbt/Incomplete.html>`_
is an exception with information about any tasks that caused the failure
and any underlying exceptions thrown during task execution. The
resulting task defined by ``mapFailure`` fails if its input succeeds and
evaluates the provided function if it fails.
For example:
::
intTask := error("Failed.")
val intTaskImpl = intTask mapFailure { (inc: Incomplete) =>
println("Ignoring failure: " + inc)
3
}
intTask := intTaskImpl.value
This overrides the ``intTask`` so that the original exception is printed and the constant ``3`` is returned.
``mapFailure`` does not prevent other tasks that depend on the target
from failing. Consider the following example:
::
intTask := if(shouldSucceed) 5 else error("Failed.")
// return 3 if intTask fails. if it succeeds, this task will fail
val aTaskImpl = intTask mapFailure { (inc: Incomplete) => 3 }
aTask := aTaskImpl.value
// a new task that increments the result of intTask
bTask := intTask.value + 1
cTask := aTask.value + bTask.value
The following table lists the results of each task depending on the initially invoked task:
============== =============== ============= ============== ============== ==============
invoked task intTask result aTask result bTask result cTask result overall result
============== =============== ============= ============== ============== ==============
intTask failure not run not run not run failure
aTask failure success not run not run success
bTask failure not run failure not run failure
cTask failure success failure failure failure
intTask success not run not run not run success
aTask success failure not run not run failure
bTask success not run success not run success
cTask success failure success failure failure
============== =============== ============= ============== ============== ==============
The overall result is always the same as the root task (the directly
invoked task). A ``mapFailure`` turns a success into a failure, and a
failure into whatever the result of evaluating the supplied function is.
A normal task definition fails when the input fails and applies the supplied function
to a successfully completed input.
In the case of more than one input, ``mapFailure`` fails if all inputs
succeed. If at least one input fails, the supplied function is provided
with the list of ``Incomplete``\ s. For example:
::
val cTaskImpl = (aTask, bTask) mapFailure { (incs: Seq[Incomplete]) => 3 }
cTask := cTaskImpl.value
The following table lists the results of invoking ``cTask``, depending
on the success of ``aTask`` and ``bTask``:
============= ============= =============
aTask result bTask result cTask result
============= ============= =============
failure failure success
failure success success
success failure success
success success failure
============= ============= =============
mapR
~~~~
``mapR`` accepts a function of type ``Result[S] => T``, where ``S`` is
the type of the task being mapped and ``T`` is a type parameter. In the
case of multiple inputs, the function has type
``(Result[A], Result[B], ...) => T``.
`Result <https://github.com/harrah/xsbt/latest/api/sbt/Result.html>`_
has the same structure as ``Either[Incomplete, S]`` for a task result of
type ``S``. That is, it has two subtypes:
- ``Inc``, which wraps ``Incomplete`` in case of failure
- ``Value``, which wraps a task's result in case of success.
Thus, ``mapR`` is always invoked whether or not the original task
succeeds or fails.
For example:
::
intTask := error("Failed.")
val intTaskImpl = intTask mapR {
case Inc(inc: Incomplete) =>
println("Ignoring failure: " + inc)
3
case Value(v) =>
println("Using successful result: " + v)
v
}
intTask := intTaskImpl.value
This overrides the original ``intTask`` definition so that if the original task fails, the exception is printed and the constant ``3`` is returned. If it succeeds, the value is printed and returned.

View File

@ -20,18 +20,29 @@ sealed trait MultiInTask[K[L[x]]]
sealed trait SingleInTask[S]
{
def flatMapR[T](f: Result[S] => Task[T]): Task[T]
def flatMap[T](f: S => Task[T]): Task[T]
def map[T](f: S => T): Task[T]
def mapR[T](f: Result[S] => T): Task[T]
def flatFailure[T](f: Incomplete => Task[T]): Task[T]
def mapFailure[T](f: Incomplete => T): Task[T]
def dependsOn(tasks: Task[_]*): Task[S]
def andFinally(fin: => Unit): Task[S]
def doFinally(t: Task[Unit]): Task[S]
def || [T >: S](alt: Task[T]): Task[T]
def && [T](alt: Task[T]): Task[T]
def failure: Task[Incomplete]
def result: Task[Result[S]]
@deprecated("Use the `result` method to create a task that returns the full Result of this task. Then, call `map` on the new task.", "0.13.0")
def mapR[T](f: Result[S] => T): Task[T]
@deprecated("Use the `failure` method to create a task that returns Incomplete when this task fails and then call `flatMap` on the new task.", "0.13.0")
def flatFailure[T](f: Incomplete => Task[T]): Task[T]
@deprecated("Use the `failure` method to create a task that returns Incomplete when this task fails and then call `mapFailure` on the new task.", "0.13.0")
def mapFailure[T](f: Incomplete => T): Task[T]
@deprecated("Use the `result` method to create a task that returns the full Result of this task. Then, call `flatMap` on the new task.", "0.13.0")
def flatMapR[T](f: Result[S] => Task[T]): Task[T]
}
sealed trait TaskInfo[S]
{
@ -114,6 +125,9 @@ trait TaskExtra
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] = new SingleInTask[S] {
type K[L[x]] = L[S]
private def ml = AList.single[S]
def failure: Task[Incomplete] = mapFailure(idFun)
def result: Task[Result[S]] = mapR(idFun)
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = new FlatMapped[T, K](in, f, ml)
def mapR[T](f: Result[S] => T): Task[T] = new Mapped[T, K](in, f, ml)