Port Task Std

This commit is contained in:
Eugene Yokota 2022-01-04 02:48:36 -05:00
parent d4920a3b0c
commit 01d7f400d7
15 changed files with 353 additions and 272 deletions

View File

@ -190,6 +190,7 @@ lazy val sbtRoot: Project = (project in file("."))
logicProj,
utilCache,
taskProj,
stdTaskProj,
)
.settings(
minimalSettings,

View File

@ -8,6 +8,7 @@ import scala.reflect.TypeTest
import scala.quoted.*
import sbt.util.Applicative
import sbt.util.Monad
import Types.Id
/**
* Implementation of a macro that provides a direct syntax for applicative functors and monads. It
@ -197,11 +198,13 @@ trait Cont:
case '[inputTypeTpe] =>
'{
given Applicative[F] = $instanceExpr
AList.tuple.mapN[F, A1, inputTypeTpe & Tuple](${
br.tupleExpr.asInstanceOf[Expr[Tuple.Map[inputTypeTpe & Tuple, F]]]
})(
${ lambda.asExprOf[inputTypeTpe => A1] }
)
AList
.tuple[inputTypeTpe & Tuple]
.mapN[F, A1](${
br.tupleExpr.asInstanceOf[Expr[Tuple.Map[inputTypeTpe & Tuple, F]]]
})(
${ lambda.asExprOf[Tuple.Map[inputTypeTpe & Tuple, Id] => A1] }
)
}
eitherTree match

View File

@ -1,132 +0,0 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
import sbt.internal.util.Types._
import sbt.internal.util.{ ~>, AList, AttributeKey, AttributeMap }
import ConcurrentRestrictions.{ Tag, TagMap, tagsKey }
// Action, Task, and Info are intentionally invariant in their type parameter.
// Various natural transformations used, such as PMap, require invariant type constructors for correctness
/** Defines a task computation */
sealed trait Action[T] {
// TODO: remove after deprecated InputTask constructors are removed
private[sbt] def mapTask(f: Task ~> Task): Action[T]
}
/**
* A direct computation of a value. If `inline` is true, `f` will be evaluated on the scheduler
* thread without the overhead of normal scheduling when possible. This is intended as an
* optimization for already evaluated values or very short pure computations.
*/
final case class Pure[T](f: () => T, `inline`: Boolean) extends Action[T] {
private[sbt] def mapTask(f: Task ~> Task) = this
}
/** Applies a function to the result of evaluating a heterogeneous list of other tasks. */
final case class Mapped[T, K[L[x]]](in: K[Task], f: K[Result] => T, alist: AList[K])
extends Action[T] {
private[sbt] def mapTask(g: Task ~> Task) = Mapped[T, K](alist.transform(in, g), f, alist)
}
/** Computes another task to evaluate based on results from evaluating other tasks. */
final case class FlatMapped[T, K[L[x]]](in: K[Task], f: K[Result] => Task[T], alist: AList[K])
extends Action[T] {
private[sbt] def mapTask(g: Task ~> Task) =
FlatMapped[T, K](alist.transform(in, g), g.fn[T] compose f, alist)
}
/** A computation `in` that requires other tasks `deps` to be evaluated first. */
final case class DependsOn[T](in: Task[T], deps: Seq[Task[_]]) extends Action[T] {
private[sbt] def mapTask(g: Task ~> Task) = DependsOn[T](g(in), deps.map(t => g(t)))
}
/**
* A computation that operates on the results of a homogeneous list of other tasks. It can either
* return another task to be evaluated or the final value.
*/
final case class Join[T, U](in: Seq[Task[U]], f: Seq[Result[U]] => Either[Task[T], T])
extends Action[T] {
private[sbt] def mapTask(g: Task ~> Task) =
Join[T, U](in.map(g.fn[U]), sr => f(sr).left.map(g.fn[T]))
}
/**
* A computation that conditionally falls back to a second transformation. This can be used to
* encode `if` conditions.
*/
final case class Selected[A, B](fab: Task[Either[A, B]], fin: Task[A => B]) extends Action[B] {
private def ml = AList.single[Either[A, B]]
type K[L[x]] = L[Either[A, B]]
private[sbt] def mapTask(g: Task ~> Task) =
Selected[A, B](g(fab), g(fin))
/**
* Encode this computation as a flatMap.
*/
private[sbt] def asFlatMapped: FlatMapped[B, K] = {
val f: Either[A, B] => Task[B] = {
case Right(b) => std.TaskExtra.task(b)
case Left(a) => std.TaskExtra.singleInputTask(fin).map(_(a))
}
FlatMapped[B, K](
fab, {
f compose std.TaskExtra.successM
},
ml
)
}
}
/** Combines metadata `info` and a computation `work` to define a task. */
final case class Task[T](info: Info[T], work: Action[T]) {
override def toString = info.name getOrElse ("Task(" + info + ")")
override def hashCode = info.hashCode
def tag(tags: Tag*): Task[T] = tagw(tags.map(t => (t, 1)): _*)
def tagw(tags: (Tag, Int)*): Task[T] = {
val tgs: TagMap = info.get(tagsKey).getOrElse(TagMap.empty)
val value = tags.foldLeft(tgs)((acc, tag) => acc + tag)
val nextInfo = info.set(tagsKey, value)
copy(info = nextInfo)
}
def tags: TagMap = info get tagsKey getOrElse TagMap.empty
}
/**
* Used to provide information about a task, such as the name, description, and tags for controlling
* concurrent execution.
* @param attributes
* Arbitrary user-defined key/value pairs describing this task
* @param post
* a transformation that takes the result of evaluating this task and produces user-defined
* key/value pairs.
*/
final case class Info[T](
attributes: AttributeMap = AttributeMap.empty,
post: T => AttributeMap = Info.defaultAttributeMap
) {
import Info._
def name = attributes.get(Name)
def description = attributes.get(Description)
def setName(n: String) = set(Name, n)
def setDescription(d: String) = set(Description, d)
def set[A](key: AttributeKey[A], value: A) = copy(attributes = this.attributes.put(key, value))
def get[A](key: AttributeKey[A]): Option[A] = attributes.get(key)
def postTransform(f: (T, AttributeMap) => AttributeMap) = copy(post = (t: T) => f(t, post(t)))
override def toString = if (attributes.isEmpty) "_" else attributes.toString
}
object Info {
val Name = AttributeKey[String]("name")
val Description = AttributeKey[String]("description")
val defaultAttributeMap = const(AttributeMap.empty)
}

View File

@ -0,0 +1,59 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
import sbt.internal.util.Types._
import sbt.internal.util.{ ~>, AList, AttributeKey, AttributeMap }
import ConcurrentRestrictions.{ Tag, TagMap, tagsKey }
import sbt.internal.Action
/** Combines metadata `info` and a computation `work` to define a task. */
final case class Task[A](info: Info[A], work: Action[A]) {
override def toString = info.name getOrElse ("Task(" + info + ")")
override def hashCode = info.hashCode
def tag(tags: Tag*): Task[A] = tagw(tags.map(t => (t, 1)): _*)
def tagw(tags: (Tag, Int)*): Task[A] = {
val tgs: TagMap = info.get(tagsKey).getOrElse(TagMap.empty)
val value = tags.foldLeft(tgs)((acc, tag) => acc + tag)
val nextInfo = info.set(tagsKey, value)
copy(info = nextInfo)
}
def tags: TagMap = info get tagsKey getOrElse TagMap.empty
}
/**
* Used to provide information about a task, such as the name, description, and tags for controlling
* concurrent execution.
* @param attributes
* Arbitrary user-defined key/value pairs describing this task
* @param post
* a transformation that takes the result of evaluating this task and produces user-defined
* key/value pairs.
*/
final case class Info[T](
attributes: AttributeMap = AttributeMap.empty,
post: T => AttributeMap = Info.defaultAttributeMap
) {
import Info._
def name = attributes.get(Name)
def description = attributes.get(Description)
def setName(n: String) = set(Name, n)
def setDescription(d: String) = set(Description, d)
def set[A](key: AttributeKey[A], value: A) = copy(attributes = this.attributes.put(key, value))
def get[A](key: AttributeKey[A]): Option[A] = attributes.get(key)
def postTransform(f: (T, AttributeMap) => AttributeMap) = copy(post = (t: T) => f(t, post(t)))
override def toString = if (attributes.isEmpty) "_" else attributes.toString
}
object Info {
val Name = AttributeKey[String]("name")
val Description = AttributeKey[String]("description")
val defaultAttributeMap = const(AttributeMap.empty)
}

View File

@ -0,0 +1,83 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package internal
import sbt.internal.util.AList
// Action, Task, and Info are intentionally invariant in their type parameter.
// Various natural transformations used, such as PMap, require invariant type constructors for correctness
/** Defines a task computation */
enum Action[A]:
// TODO: remove after deprecated InputTask constructors are removed
// private[sbt] def mapTask(f: [A1] => Task[A1] => Task[A1]): Action[A]
/**
* A direct computation of a value. If `inline` is true, `f` will be evaluated on the scheduler
* thread without the overhead of normal scheduling when possible. This is intended as an
* optimization for already evaluated values or very short pure computations.
*/
case Pure[A](f: () => A, `inline`: Boolean) extends Action[A]
// private[sbt] def mapTask(f: [A1] => Task[A1] => Task[A1]) = this
/** Applies a function to the result of evaluating a heterogeneous list of other tasks. */
case Mapped[A, K[F[_]]](in: K[Task], f: K[Result] => A, alist: AList[K]) extends Action[A]
// private[sbt] def mapTask(g: Task ~> Task) = Mapped[A, K](alist.transform(in, g), f, alist)
/** Computes another task to evaluate based on results from evaluating other tasks. */
case FlatMapped[A, K[F[_]]](
in: K[Task],
f: K[Result] => Task[A],
alist: AList[K],
) extends Action[A]
// private[sbt] def mapTask(g: Task ~> Task) =
// FlatMapped[A, K](alist.transform(in, g), g.fn[A] compose f, alist)
/** A computation `in` that requires other tasks `deps` to be evaluated first. */
case DependsOn[A](in: Task[A], deps: Seq[Task[_]]) extends Action[A]
// private[sbt] def mapTask(g: Task ~> Task) = DependsOn[A](g(in), deps.map(t => g(t)))
/**
* A computation that operates on the results of a homogeneous list of other tasks. It can either
* return another task to be evaluated or the final value.
*/
case Join[A, U](in: Seq[Task[U]], f: Seq[Result[U]] => Either[Task[A], A]) extends Action[A]
// private[sbt] def mapTask(g: Task ~> Task) =
// Join[A, U](in.map(g.fn[U]), sr => f(sr).left.map(g.fn[A]))
/**
* A computation that conditionally falls back to a second transformation. This can be used to
* encode `if` conditions.
*/
case Selected[A1, A2](fab: Task[Either[A1, A2]], fin: Task[A1 => A2]) extends Action[A2]
// private[sbt] def mapTask(g: Task ~> Task) =
// Selected[A, B](g(fab), g(fin))
end Action
object Action:
/**
* Encode this computation as a flatMap.
*/
private[sbt] def asFlatMapped[A1, A2](
s: Action.Selected[A1, A2]
): Action.FlatMapped[A2, [F[_]] =>> Tuple1[F[Either[A1, A2]]]] =
val alist = AList.tuple[Tuple1[Either[A1, A2]]]
val f: Either[A1, A2] => Task[A2] = {
case Right(b) => std.TaskExtra.task(b)
case Left(a) => std.TaskExtra.singleInputTask(s.fin).map(_(a))
}
Action.FlatMapped[A2, [F[_]] =>> Tuple1[F[Either[A1, A2]]]](
Tuple1(s.fab),
{ case Tuple1(r) => (f compose std.TaskExtra.successM)(r) },
alist,
)
end Action

View File

@ -14,14 +14,15 @@ import sbt.internal.util.{ AList, AttributeMap }
import sbt.internal.util.Types._
import java.io.{ BufferedInputStream, BufferedReader, File, InputStream }
import sbt.io.IO
import sbt.internal.Action
sealed trait MultiInTask[K[L[x]]] {
def flatMap[T](f: K[Id] => Task[T]): Task[T]
def flatMapR[T](f: K[Result] => Task[T]): Task[T]
def map[T](f: K[Id] => T): Task[T]
def mapR[T](f: K[Result] => T): Task[T]
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T]
def mapFailure[T](f: Seq[Incomplete] => T): Task[T]
sealed trait MultiInTask[K[F[_]]] {
def flatMap[A](f: K[Id] => Task[A]): Task[A]
def flatMapR[A](f: K[Result] => Task[A]): Task[A]
def map[A](f: K[Id] => A): Task[A]
def mapR[A](f: K[Result] => A): Task[A]
def flatFailure[A](f: Seq[Incomplete] => Task[A]): Task[A]
def mapFailure[A](f: Seq[Incomplete] => A): Task[A]
}
sealed trait SingleInTask[S] {
@ -99,7 +100,7 @@ trait TaskExtra0 {
joinTasks0[Any](existToAny(in))
private[sbt] def joinTasks0[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
def join: Task[Seq[S]] =
Task[Seq[S]](Info(), new Join(in, (s: Seq[Result[S]]) => Right(TaskExtra.all(s))))
Task[Seq[S]](Info(), Action.Join(in, (s: Seq[Result[S]]) => Right(TaskExtra.all(s))))
def reduced(f: (S, S) => S): Task[S] = TaskExtra.reduced(in.toIndexedSeq, f)
}
private[sbt] def existToAny(in: Seq[Task[_]]): Seq[Task[Any]] = in.asInstanceOf[Seq[Task[Any]]]
@ -109,9 +110,9 @@ trait TaskExtra extends TaskExtra0 {
final def nop: Task[Unit] = constant(())
final def constant[T](t: T): Task[T] = task(t)
final def task[T](f: => T): Task[T] = toTask(f _)
final implicit def toTask[T](f: () => T): Task[T] = Task(Info(), new Pure(f, false))
final def inlineTask[T](value: T): Task[T] = Task(Info(), new Pure(() => value, true))
final def task[T](f: => T): Task[T] = toTask(() => f)
final implicit def toTask[T](f: () => T): Task[T] = Task(Info(), Action.Pure(f, false))
final def inlineTask[T](value: T): Task[T] = Task(Info(), Action.Pure(() => value, true))
final implicit def upcastTask[A >: B, B](t: Task[B]): Task[A] = t map { x =>
x: A
@ -126,61 +127,83 @@ trait TaskExtra extends TaskExtra0 {
final implicit def joinTasks[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
def join: Task[Seq[S]] =
Task[Seq[S]](Info(), new Join(in, (s: Seq[Result[S]]) => Right(TaskExtra.all(s))))
Task[Seq[S]](Info(), Action.Join(in, (s: Seq[Result[S]]) => Right(TaskExtra.all(s))))
def reduced(f: (S, S) => S): Task[S] = TaskExtra.reduced(in.toIndexedSeq, f)
}
final implicit def multT2Task[A, B](in: (Task[A], Task[B])) =
multInputTask[λ[L[x] => (L[A], L[B])]](in)(AList.tuple2[A, B])
final implicit def multT2Task[A1, A2](
in: (Task[A1], Task[A2])
): MultiInTask[[F[_]] =>> Tuple.Map[(A1, A2), F]] =
given AList[[F[_]] =>> Tuple.Map[(A1, A2), F]] = AList.tuple[(A1, A2)]
multInputTask[[F[_]] =>> Tuple.Map[(A1, A2), F]](in)
final implicit def multInputTask[K[L[X]]](tasks: K[Task])(implicit a: AList[K]): MultiInTask[K] =
new MultiInTask[K] {
def flatMapR[T](f: K[Result] => Task[T]): Task[T] =
Task(Info(), new FlatMapped[T, K](tasks, f, a))
def flatMap[T](f: K[Id] => Task[T]): Task[T] =
Task(Info(), new FlatMapped[T, K](tasks, f compose allM, a))
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T] =
Task(Info(), new FlatMapped[T, K](tasks, f compose anyFailM, a))
final implicit def multInputTask[K[F[_]]: AList](tasks: K[Task]): MultiInTask[K] =
new MultiInTask[K]:
override def flatMap[A](f: K[Id] => Task[A]): Task[A] =
Task(Info(), Action.FlatMapped[A, K](tasks, f compose allM, AList[K]))
override def flatMapR[A](f: K[Result] => Task[A]): Task[A] =
Task(Info(), Action.FlatMapped[A, K](tasks, f, AList[K]))
override def map[A](f: K[Id] => A): Task[A] =
Task(Info(), Action.Mapped[A, K](tasks, f compose allM, AList[K]))
override def mapR[A](f: K[Result] => A): Task[A] =
Task(Info(), Action.Mapped[A, K](tasks, f, AList[K]))
override def flatFailure[A](f: Seq[Incomplete] => Task[A]): Task[A] =
Task(Info(), Action.FlatMapped[A, K](tasks, f compose anyFailM, AList[K]))
override def mapFailure[A](f: Seq[Incomplete] => A): Task[A] =
Task(Info(), Action.Mapped[A, K](tasks, f compose anyFailM, AList[K]))
def mapR[T](f: K[Result] => T): Task[T] = Task(Info(), new Mapped[T, K](tasks, f, a))
def map[T](f: K[Id] => T): Task[T] = Task(Info(), new Mapped[T, K](tasks, f compose allM, a))
def mapFailure[T](f: Seq[Incomplete] => T): Task[T] =
Task(Info(), new Mapped[T, K](tasks, f compose anyFailM, a))
}
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] =
new SingleInTask[S]:
// type K[L[x]] = L[S]
given alist: AList[[F[_]] =>> Tuple.Map[Tuple1[S], F]] = AList.tuple[Tuple1[S]]
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 failure: Task[Incomplete] = mapFailure(idFun)
def result: Task[Result[S]] = mapR(idFun)
private def newInfo[A]: Info[A] = TaskExtra.newInfo(in.info)
private def newInfo[A]: Info[A] = TaskExtra.newInfo(in.info)
override def flatMapR[A](f: Result[S] => Task[A]): Task[A] =
Task(
newInfo,
Action.FlatMapped[A, [F[_]] =>> Tuple.Map[Tuple1[S], F]](
AList.toTuple(in),
AList.fromTuple(f),
alist,
)
)
def flatMapR[T](f: Result[S] => Task[T]): Task[T] =
Task(newInfo, new FlatMapped[T, K](in, f, ml))
def mapR[T](f: Result[S] => T): Task[T] = Task(newInfo, new Mapped[T, K](in, f, ml))
def dependsOn(tasks: Task[_]*): Task[S] = Task(newInfo, new DependsOn(in, tasks))
override def mapR[A](f: Result[S] => A): Task[A] =
Task(
newInfo,
Action.Mapped[A, [F[_]] =>> Tuple.Map[Tuple1[S], F]](
AList.toTuple(in),
AList.fromTuple(f),
alist,
)
)
def flatMap[T](f: S => Task[T]): Task[T] = flatMapR(f compose successM)
def flatFailure[T](f: Incomplete => Task[T]): Task[T] = flatMapR(f compose failM)
override def dependsOn(tasks: Task[_]*): Task[S] = Task(newInfo, Action.DependsOn(in, tasks))
def map[T](f: S => T): Task[T] = mapR(f compose successM)
def mapFailure[T](f: Incomplete => T): Task[T] = mapR(f compose failM)
override def flatMap[T](f: S => Task[T]): Task[T] = flatMapR(f compose successM)
def andFinally(fin: => Unit): Task[S] = mapR(x => Result.tryValue[S]({ fin; x }))
def doFinally(t: Task[Unit]): Task[S] =
flatMapR(x =>
t.result.map { tx =>
Result.tryValues[S](tx :: Nil, x)
}
)
def ||[T >: S](alt: Task[T]): Task[T] = flatMapR {
case Value(v) => task(v: T)
case Inc(_) => alt
}
def &&[T](alt: Task[T]): Task[T] = flatMap(_ => alt)
}
override def flatFailure[T](f: Incomplete => Task[T]): Task[T] = flatMapR(f compose failM)
override def map[T](f: S => T): Task[T] = mapR(f compose successM)
override def mapFailure[T](f: Incomplete => T): Task[T] = mapR(f compose failM)
def andFinally(fin: => Unit): Task[S] = mapR(x => Result.tryValue[S]({ fin; x }))
def doFinally(t: Task[Unit]): Task[S] =
flatMapR(x =>
t.result.map { tx =>
Result.tryValues[S](tx :: Nil, x)
}
)
def ||[T >: S](alt: Task[T]): Task[T] = flatMapR {
case Result.Value(v) => task(v: T)
case Result.Inc(_) => alt
}
def &&[T](alt: Task[T]): Task[T] = flatMap(_ => alt)
final implicit def toTaskInfo[S](in: Task[S]): TaskInfo[S] = new TaskInfo[S] {
def describedAs(s: String): Task[S] = in.copy(info = in.info.setDescription(s))
@ -263,35 +286,49 @@ object TaskExtra extends TaskExtra {
reducePair(reduced(a, f), reduced(b, f), f)
}
def reducePair[S](a: Task[S], b: Task[S], f: (S, S) => S): Task[S] =
multInputTask[λ[L[x] => (L[S], L[S])]]((a, b))(AList.tuple2[S, S]) map f.tupled
def reducePair[A1](a: Task[A1], b: Task[A1], f: (A1, A1) => A1): Task[A1] =
given AList[[F[_]] =>> Tuple.Map[(A1, A1), F]] = AList.tuple[(A1, A1)]
multInputTask[[F[_]] =>> Tuple.Map[(A1, A1), F]]((a, b)) map f.tupled
def anyFailM[K[L[x]]](implicit a: AList[K]): K[Result] => Seq[Incomplete] = in => {
val incs = failuresM(a)(in)
if (incs.isEmpty) expectedFailure else incs
def anyFailM[K[F[_]]: AList]: K[Result] => Seq[Incomplete] = in => {
val incs = failuresM[K](AList[K])(in)
if incs.isEmpty then expectedFailure
else incs
}
def failM[A]: Result[A] => Incomplete = {
case Result.Inc(i) => i
case _ => expectedFailure
}
def failM[T]: Result[T] => Incomplete = { case Inc(i) => i; case _ => expectedFailure }
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 allM[K[L[x]]](implicit a: AList[K]): K[Result] => K[Id] = in => {
val incs = failuresM(a)(in)
if (incs.isEmpty) a.transform(in, Result.tryValue) else throw incompleteDeps(incs)
def successM[A]: Result[A] => A = {
case Result.Inc(i) => throw i
case Result.Value(a) => a
}
def failuresM[K[L[x]]](implicit a: AList[K]): K[Result] => Seq[Incomplete] =
x => failures[Any](a.toList(x))
def allM[K[F[_]]: AList]: K[Result] => K[Id] = in => {
val incs = failuresM[K](AList[K])(in)
if incs.isEmpty then AList[K].transform[Result, Id](in)(Result.tryValue) // .asInstanceOf
else throw incompleteDeps(incs)
}
def failuresM[K[F[_]]: AList]: K[Result] => Seq[Incomplete] = x =>
failures[Any](AList[K].toList(x))
def all[D](in: Seq[Result[D]]): Seq[D] = {
val incs = failures(in)
if (incs.isEmpty) in.map(Result.tryValue.fn[D]) else throw incompleteDeps(incs)
if incs.isEmpty then in.map(Result.tryValue[D])
else throw incompleteDeps(incs)
}
def failures[A](results: Seq[Result[A]]): Seq[Incomplete] = results.collect {
case Result.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, causes = incs)
def select[A, B](fab: Task[Either[A, B]], f: Task[A => B]): Task[B] =
Task(newInfo(fab.info), new Selected[A, B](fab, f))
Task(newInfo(fab.info), Action.Selected[A, B](fab, f))
// The "taskDefinitionKey" is used, at least, by the ".previous" functionality.
// But apparently it *cannot* survive a task map/flatMap/etc. See actions/depends-on.

View File

@ -8,24 +8,28 @@
package sbt
package std
import sbt.internal.Action
import sbt.internal.util.Types._
import sbt.internal.util.{ ~>, AList, DelegatingPMap, RMap }
import TaskExtra.{ all, existToAny }
import sbt.internal.util.Types.*
object Transform:
def fromDummy[A](original: Task[A])(action: => A): Task[A] =
Task(original.info, Action.Pure(() => action, false))
object Transform {
def fromDummy[T](original: Task[T])(action: => T): Task[T] =
Task(original.info, Pure(action _, false))
def fromDummyStrict[T](original: Task[T], value: T): Task[T] = fromDummy(original)(value)
implicit def to_~>|[K[_], V[_]](map: RMap[K, V]): K ~>| V = new (K ~>| V) {
def apply[T](k: K[T]): Option[V[T]] = map.get(k)
}
implicit def to_~>|[K[_], V[_]](map: RMap[K, V]): ~>|[K, V] =
[A] => (k: K[A]) => map.get(k)
final case class DummyTaskMap(mappings: List[TaskAndValue[_]]) {
def ::[T](tav: (Task[T], T)): DummyTaskMap =
DummyTaskMap(new TaskAndValue(tav._1, tav._2) :: mappings)
}
final class TaskAndValue[T](val task: Task[T], val value: T)
def dummyMap(dummyMap: DummyTaskMap): Task ~>| Task = {
val pmap = new DelegatingPMap[Task, Task](new collection.mutable.ListMap)
def add[T](dummy: TaskAndValue[T]): Unit = {
@ -36,35 +40,43 @@ object Transform {
}
/** Applies `map`, returning the result if defined or returning the input unchanged otherwise. */
implicit def getOrId(map: Task ~>| Task): Task ~> Task =
λ[Task ~> Task](in => map(in).getOrElse(in))
implicit def getOrId(map: Task ~>| Task): [A] => Task[A] => Task[A] =
[A] => (in: Task[A]) => map(in).getOrElse(in)
def apply(dummies: DummyTaskMap) = taskToNode(getOrId(dummyMap(dummies)))
def taskToNode(pre: Task ~> Task): NodeView[Task] = new NodeView[Task] {
def apply[T](t: Task[T]): Node[Task, T] = pre(t).work match {
case Pure(eval, _) => uniform(Nil)(_ => Right(eval()))
case m: Mapped[t, k] => toNode[t, k](m.in)(right m.f)(m.alist)
case m: FlatMapped[t, k] => toNode[t, k](m.in)(left m.f)(m.alist)
case s: Selected[_, t] => val m = s.asFlatMapped; toNode(m.in)(left m.f)(m.alist)
case DependsOn(in, deps) => uniform(existToAny(deps))(const(Left(in)) compose all)
case Join(in, f) => uniform(in)(f)
}
def inline[T](t: Task[T]): Option[() => T] = t.work match {
case Pure(eval, true) => Some(eval)
case _ => None
}
}
def taskToNode(pre: [A] => Task[A] => Task[A]): NodeView[Task] =
new NodeView[Task]:
import Action.*
def apply[T](t: Task[T]): Node[Task, T] = pre(t).work match
case Pure(eval, _) => uniform(Nil)(_ => Right(eval()))
case m: Mapped[a, k] => toNode[a, k](m.in)(right[a] compose m.f)(m.alist)
case m: FlatMapped[a, k] =>
toNode[a, k](m.in)(left[Task[a]] compose m.f)(m.alist) // (m.alist)
case s: Selected[a1, a2] =>
val m = Action.asFlatMapped[a1, a2](s)
toNode[a2, [F[_]] =>> Tuple1[F[Either[a1, a2]]]](m.in)(left[Task[a2]] compose m.f)(
m.alist
)
case DependsOn(in, deps) => uniform(existToAny(deps))(const(Left(in)) compose all)
case Join(in, f) => uniform(in)(f)
def uniform[T, D](tasks: Seq[Task[D]])(f: Seq[Result[D]] => Either[Task[T], T]): Node[Task, T] =
toNode[T, λ[L[x] => List[L[D]]]](tasks.toList)(f)(AList.seq[D])
def inline1[T](t: Task[T]): Option[() => T] = t.work match
case Action.Pure(eval, true) => Some(eval)
case _ => None
def toNode[T, k[L[x]]](
inputs: k[Task]
)(f: k[Result] => Either[Task[T], T])(implicit a: AList[k]): Node[Task, T] = new Node[Task, T] {
type K[L[x]] = k[L]
val in = inputs
val alist = a
def work(results: K[Result]) = f(results)
}
}
def uniform[A1, D](tasks: Seq[Task[D]])(
f: Seq[Result[D]] => Either[Task[A1], A1]
): Node[Task, A1] =
toNode[A1, [F[_]] =>> List[F[D]]](tasks.toList)(f)(AList.list[D])
def toNode[A1, K1[F[_]]: AList](
inputs: K1[Task]
)(f: K1[Result] => Either[Task[A1], A1]): Node[Task, A1] =
new Node[Task, A1]:
type K[F[_]] = K1[F]
val in = inputs
lazy val alist: AList[K] = AList[K]
def work(results: K[Result]) = f(results)
end Transform

View File

@ -30,14 +30,14 @@ object TaskGen extends std.TaskExtra {
ExecuteProgress.empty[Task]
)(std.Transform(dummies))
try {
x.run(root)(service)
x.run(root)(service.asInstanceOf)
} finally {
shutdown()
}
}
def tryRun[T](root: Task[T], checkCycles: Boolean, maxWorkers: Int): T =
run(root, checkCycles, maxWorkers) match {
case Value(v) => v
case Inc(i) => throw i
case Result.Value(v) => v
case Result.Inc(i) => throw i
}
}

View File

@ -90,7 +90,7 @@ object TaskTest {
ExecuteProgress.empty[Task]
)(taskToNode(idK[Task]))
try {
x.run(root)(service)
x.run(root)(service.asInstanceOf)
} finally {
shutdown()
}
@ -101,7 +101,7 @@ object TaskTest {
restrictions: ConcurrentRestrictions[Task[_]]
): T =
run(root, checkCycles, restrictions) match {
case Value(v) => v
case Inc(i) => throw i
case Result.Value(v) => v
case Result.Inc(i) => throw i
}
}

View File

@ -9,11 +9,13 @@ package sbt
import sbt.internal.util.AList
object Test extends std.TaskExtra {
def t2[A, B](a: Task[A], b: Task[B]) =
multInputTask[λ[L[x] => (L[A], L[B])]]((a, b))(AList.tuple2)
def t3[A, B, C](a: Task[A], b: Task[B], c: Task[C]) =
multInputTask[λ[L[x] => (L[A], L[B], L[C])]]((a, b, c))(AList.tuple3)
object Test extends std.TaskExtra:
def t2[A1, A2](a1: Task[A1], a2: Task[A2]) =
given AList[[F[_]] =>> Tuple.Map[(A1, A2), F]] = AList.tuple[(A1, A2)]
multInputTask[[F[_]] =>> Tuple.Map[(A1, A2), F]]((a1, a2))
def t3[A1, A2, A3](a1: Task[A1], a2: Task[A2], a3: Task[A3]) =
given AList[[F[_]] =>> Tuple.Map[(A1, A2, A3), F]] = AList.tuple[(A1, A2, A3)]
multInputTask[[F[_]] =>> Tuple.Map[(A1, A2, A3), F]]((a1, a2, a3))
val a = task(3)
val b = task[Boolean](sys.error("test"))
@ -26,22 +28,22 @@ object Test extends std.TaskExtra {
type Values = (Result[Int], Result[Boolean], Result[String])
val f: Values => Any = {
case (Value(aa), Value(bb), Value(cc)) => s"$aa $bb $cc"
case (Result.Value(aa), Result.Value(bb), Result.Value(cc)) => s"$aa $bb $cc"
case x =>
val cs = x.productIterator.toList.collect { case Inc(x) =>
val cs = x.productIterator.toList.collect { case Result.Inc(x) =>
x
} // workaround for double definition bug
throw Incomplete(None, causes = cs)
}
val d2 = t3(a, b2, c) mapR f
val f2: Values => Task[Any] = {
case (Value(aa), Value(bb), Value(cc)) => task(s"$aa $bb $cc")
case _ => d3
case (Result.Value(aa), Result.Value(bb), Result.Value(cc)) => task(s"$aa $bb $cc")
case _ => d3
}
lazy val d = t3(a, b, c) flatMapR f2
val f3: Values => Task[Any] = {
case (Value(aa), Value(bb), Value(cc)) => task(s"$aa $bb $cc")
case _ => d2
case (Result.Value(aa), Result.Value(bb), Result.Value(cc)) => task(s"$aa $bb $cc")
case _ => d2
}
lazy val d3 = t3(a, b, c) flatMapR f3
@ -63,4 +65,4 @@ object Test extends std.TaskExtra {
run(h1)
run(h2)
}
}
end Test

View File

@ -44,17 +44,21 @@ object CompletionService {
)
(apply[A, T](pool), () => { pool.shutdownNow(); () })
}
def apply[A, T](x: Executor): CompletionService[A, T] =
apply(new ExecutorCompletionService[T](x))
def apply[A, T](completion: JCompletionService[T]): CompletionService[A, T] =
new CompletionService[A, T] {
def submit(node: A, work: () => T) = { CompletionService.submit(work, completion); () }
def take() = completion.take().get()
}
def submit[T](work: () => T, completion: JCompletionService[T]): () => T = {
val future = submitFuture[T](work, completion)
() => future.get
}
private[sbt] def submitFuture[A](work: () => A, completion: JCompletionService[A]): JFuture[A] = {
val future =
try completion.submit {

View File

@ -39,13 +39,16 @@ private[sbt] object Execute {
final val checkPreAndPostConditions =
sys.props.get("sbt.execute.extrachecks").exists(java.lang.Boolean.parseBoolean)
}
sealed trait Completed {
def process(): Unit
}
private[sbt] trait NodeView[F[_]] {
def apply[A](a: F[A]): Node[F, A]
def inline1[A](a: F[A]): Option[() => A]
}
final class Triggers[F[_]](
val runBefore: collection.Map[F[Any], Seq[F[Any]]],
val injectFor: collection.Map[F[Any], Seq[F[Any]]],
@ -278,7 +281,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
/** Send the work for this node to the provided Strategy. */
def submit[A](node: F[A])(implicit strategy: Strategy): Unit = {
val v = viewCache(node)
val rs = AList.tuple.transform[F, Result, v.Tup](v.in)(getResult)
val rs = v.alist.transform[F, Result](v.in)(getResult)
// v.alist.transform(v.in)(getResult)
strategy.submit(node.asInstanceOf, () => work(node, v.work(rs)))
}
@ -321,8 +324,7 @@ private[sbt] final class Execute[F[_] <: AnyRef](
def dependencies[A](node: F[A]): Iterable[F[Any]] = dependencies(viewCache(node.asInstanceOf))
def dependencies[A](v: Node[F, A]): Iterable[F[Any]] =
AList.tuple.toList[F, v.Tup](v.in).filter(dep => view.inline1(dep).isEmpty)
// v.alist.toList(v.in).filter(dep => view.inline(dep).isEmpty)
v.alist.toList[F](v.in).filter(dep => view.inline1(dep).isEmpty)
def runBefore[A](node: F[A]): Seq[F[A]] =
getSeq[A](triggers.runBefore, node)

View File

@ -7,6 +7,8 @@
package sbt
import sbt.internal.util.AList
/**
* Represents a task node in a format understood by the task evaluation engine Execute.
*
@ -15,10 +17,11 @@ package sbt
* @tparam A
* the type computed by this node
*/
trait Node[Effect[_], A] {
type Tup <: Tuple
def in: Tuple.Map[Tup, Effect]
private[sbt] trait Node[Effect[_], A]:
type K[L[x]]
def in: K[Effect]
def alist: AList[K]
/** Computes the result of this task given the results from the inputs. */
def work(inputs: Tuple.Map[Tup, Result]): Either[Effect[A], A]
}
def work(inputs: K[Result]): Either[Effect[A], A]
end Node

View File

@ -12,7 +12,7 @@ import sbt.util.Applicative
import Types._
/**
* Arity-generic List. An abstraction over structured Tuple type constructor `X1[f[a]]`.
* Arity-generic List. An abstraction over structured Tuple/List type constructor `X1[f[a]]`.
*/
trait AList[K[F[_]]]:
import AList.idPoly
@ -42,6 +42,8 @@ trait AList[K[F[_]]]:
end AList
object AList:
inline def apply[K[F[_]]: AList]: AList[K] = summon[AList[K]]
type Tail[X <: Tuple] <: Tuple = X match
case _ *: xs => xs

View File

@ -19,11 +19,11 @@ trait TypeFunctions:
sealed trait Compose[A[_], B[_]] { type Apply[T] = A[B[T]] }
sealed trait [A[_], B[_]] { type l[T] = A[B[T]] }
private type AnyLeft[T] = Left[T, Nothing]
private type AnyRight[T] = Right[Nothing, T]
final val left: [A] => A => Left[A, Nothing] = [A] => (a: A) => Left(a)
private type AnyLeft[A] = Left[A, Nothing]
private type AnyRight[A] = Right[Nothing, A]
final val left: [A] => A => AnyLeft[A] = [A] => (a: A) => Left(a)
final val right: [A] => A => Right[Nothing, A] = [A] => (a: A) => Right(a)
final val right: [A] => A => AnyRight[A] = [A] => (a: A) => Right(a)
final val some: [A] => A => Some[A] = [A] => (a: A) => Some(a)
// Id ~> Left[*, Nothing] =
@ -34,9 +34,10 @@ trait TypeFunctions:
final def idFun[A]: A => A = ((a: A) => a) // .setToString("TypeFunctions.id")
final def const[A, B](b: B): A => B = ((_: A) => b) // .setToString(s"TypeFunctions.const($b)")
/*
final def idK[M[_]]: M ~> M = λ[M ~> M](m => m).setToString("TypeFunctions.idK")
final def idK[F[_]]: [a] => F[a] => F[a] = [a] =>
(fa: F[a]) => fa // .setToString("TypeFunctions.idK")
/*
def nestCon[M[_], N[_], G[_]](f: M ~> N): (M G)#l ~> (N G)#l =
f.asInstanceOf[(M G)#l ~> (N G)#l] // implemented with a cast to avoid extra object+method call.
// castless version:
@ -47,6 +48,10 @@ trait TypeFunctions:
*/
type ~>|[F1[_], F2[_]] = [A] => F1[A] => Option[F2[A]]
extension (f: [a] => a => AnyRight[a])
def compose[F3[_]](g: [a] => F3[a] => a): [a] => F3[a] => AnyRight[a] =
[a] => (f3: F3[a]) => f(g(f3))
end TypeFunctions
/*