Use extension method to encode the syntax

This commit is contained in:
Eugene Yokota 2023-01-17 01:04:55 -05:00
parent e5dd518deb
commit 84a5fcb035
17 changed files with 137 additions and 169 deletions

View File

@ -255,7 +255,7 @@ object Def extends Init[Scope] with TaskMacroExtra with InitializeImplicits:
): Initialize[Task[A2]] =
fab.zipWith(fin)((ab, in) => TaskExtra.select(ab, in))
import Scoped.syntax.* // { Def => _, DTask => _, Invisible => _, * }
import Scoped.syntax.{ *, given } // { Def => _, DTask => _, Invisible => _, * }
// derived from select
private[sbt] def branchS[A, B, C](

View File

@ -7,7 +7,7 @@
package sbt
import scala.language.experimental.macros
import scala.annotation.targetName
import sbt.internal.util.Types._
import sbt.internal.util.{ ~>, AList, AttributeKey, Settings, SourcePosition }
@ -372,27 +372,93 @@ object Scoped:
final def ??[T >: A1](or: => T): Initialize[T] = Def.optional(scopedKey)(_ getOrElse or)
}
private[sbt] trait Syntax:
// richInitialize
extension [A1](init: Initialize[A1])
@targetName("mapTaskInitialize")
def map[A2](f: A1 => A2): Initialize[Task[A2]] = init(s => mktask(f(s)))
@targetName("flatMapValueInitialize")
def flatMapTaskValue[A2](f: A1 => Task[A2]): Initialize[Task[A2]] = init(f)
// richInitializeTask
extension [A1](init: Initialize[Task[A1]])
protected def onTask[A2](f: Task[A1] => Task[A2]): Initialize[Task[A2]] =
init.apply(f)
def flatMapTaskValue[T](f: A1 => Task[T]): Initialize[Task[T]] =
onTask(_.result flatMap (f compose successM))
def map[A2](f: A1 => A2): Initialize[Task[A2]] =
onTask(_.result map (f compose successM))
def andFinally(fin: => Unit): Initialize[Task[A1]] =
onTask(_ andFinally fin)
def doFinally(t: Task[Unit]): Initialize[Task[A1]] =
onTask(_ doFinally t)
def ||[T >: A1](alt: Task[T]): Initialize[Task[T]] = onTask(_ || alt)
def &&[T](alt: Task[T]): Initialize[Task[T]] = onTask(_ && alt)
def tag(tags: Tag*): Initialize[Task[A1]] = onTask(_.tag(tags: _*))
def tagw(tags: (Tag, Int)*): Initialize[Task[A1]] = onTask(_.tagw(tags: _*))
// Task-specific extensions
def dependsOnTask[A2](task1: Initialize[Task[A2]]): Initialize[Task[A1]] =
dependsOnSeq(Seq[AnyInitTask](task1.asInstanceOf[AnyInitTask]))
def dependsOnSeq(tasks: Seq[AnyInitTask]): Initialize[Task[A1]] =
init.zipWith(
Initialize.joinAny[Task](coerceToAnyTaskSeq(tasks))
)((thisTask, deps) => thisTask.dependsOn(deps: _*))
def failure: Initialize[Task[Incomplete]] = init(_.failure)
def result: Initialize[Task[Result[A1]]] = init(_.result)
def xtriggeredBy[A2](tasks: Initialize[Task[A2]]*): Initialize[Task[A1]] =
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.triggeredBy)
def triggeredBy[A2](tasks: Initialize[Task[A2]]*): Initialize[Task[A1]] =
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.triggeredBy)
def runBefore[A2](tasks: Initialize[Task[A2]]*): Initialize[Task[A1]] =
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.runBefore)
private[this] def nonLocal(
tasks: Seq[AnyInitTask],
key: AttributeKey[Seq[Task[_]]]
): Initialize[Task[A1]] =
Initialize
.joinAny[Task](coerceToAnyTaskSeq(tasks))
.zipWith(init)((ts, i) => i.copy(info = i.info.set(key, ts)))
extension [A1](init: Initialize[InputTask[A1]])
@targetName("onTaskInitializeInputTask")
protected def onTask[T](f: Task[A1] => Task[T]): Initialize[InputTask[T]] =
init(_ mapTask f)
@targetName("flatMapTaskValueInitializeInputTask")
def flatMapTaskValue[T](f: A1 => Task[T]): Initialize[InputTask[T]] =
onTask(_.result flatMap (f compose successM))
@targetName("mapInitializeInputTask")
def map[A2](f: A1 => A2): Initialize[InputTask[A2]] =
onTask(_.result map (f compose successM))
@targetName("andFinallyInitializeInputTask")
def andFinally(fin: => Unit): Initialize[InputTask[A1]] = onTask(_ andFinally fin)
@targetName("doFinallyInitializeInputTask")
def doFinally(t: Task[Unit]): Initialize[InputTask[A1]] = onTask(_ doFinally t)
@targetName("||_InitializeInputTask")
def ||[T >: A1](alt: Task[T]): Initialize[InputTask[T]] = onTask(_ || alt)
@targetName("&&_InitializeInputTask")
def &&[T](alt: Task[T]): Initialize[InputTask[T]] = onTask(_ && alt)
@targetName("tagInitializeInputTask")
def tag(tags: Tag*): Initialize[InputTask[A1]] = onTask(_.tag(tags: _*))
@targetName("tagwInitializeInputTask")
def tagw(tags: (Tag, Int)*): Initialize[InputTask[A1]] = onTask(_.tagw(tags: _*))
// InputTask specific extensions
@targetName("dependsOnTaskInitializeInputTask")
def dependsOnTask[B1](task1: Initialize[Task[B1]]): Initialize[InputTask[A1]] =
dependsOnSeq(Seq[AnyInitTask](task1.asInstanceOf[AnyInitTask]))
@targetName("dependsOnSeqInitializeInputTask")
def dependsOnSeq(tasks: Seq[AnyInitTask]): Initialize[InputTask[A1]] =
init.zipWith(Initialize.joinAny[Task](coerceToAnyTaskSeq(tasks)))((thisTask, deps) =>
thisTask.mapTask(_.dependsOn(deps: _*))
)
end Syntax
// Duplicated with ProjectExtra.
private[sbt] object syntax {
implicit def richInitializeTask[T](init: Initialize[Task[T]]): Scoped.RichInitializeTask[T] =
new Scoped.RichInitializeTask(init)
implicit def richInitializeInputTask[T](
init: Initialize[InputTask[T]]
): Scoped.RichInitializeInputTask[T] =
new Scoped.RichInitializeInputTask(init)
implicit def richInitialize[T](i: Initialize[T]): Scoped.RichInitialize[T] =
new Scoped.RichInitialize[T](i)
}
/**
* Wraps an [[sbt.Def.Initialize]] instance to provide `map` and `flatMap` semantics.
*/
final class RichInitialize[S](init: Initialize[S]) {
def map[T](f: S => T): Initialize[Task[T]] = init(s => mktask(f(s)))
def flatMap[T](f: S => Task[T]): Initialize[Task[T]] = init(f)
}
private[sbt] object syntax extends Syntax
sealed trait DefinableTask[A1] { self: TaskKey[A1] =>
@ -449,109 +515,6 @@ object Scoped:
private def coerceToAnyTaskSeq(tasks: Seq[AnyInitTask]): Seq[Def.Initialize[Task[Any]]] =
tasks.asInstanceOf[Seq[Def.Initialize[Task[Any]]]]
/**
* Enriches `Initialize[Task[S]]` types.
*
* @param i the original `Initialize[Task[S]]` value to enrich
* @tparam S the type of the underlying value
*/
final class RichInitializeTask[S](i: Initialize[Task[S]]) extends RichInitTaskBase[S, Task] {
protected def onTask[T](f: Task[S] => Task[T]): Initialize[Task[T]] = i apply f
def dependsOn[B1](task1: Initialize[Task[B1]]): Initialize[Task[S]] =
dependsOn(Seq[AnyInitTask](task1.asInstanceOf[AnyInitTask]))
def dependsOn(tasks: Seq[AnyInitTask]): Initialize[Task[S]] =
i.zipWith(
Initialize.joinAny[Task](coerceToAnyTaskSeq(tasks))
)((thisTask, deps) => thisTask.dependsOn(deps: _*))
def failure: Initialize[Task[Incomplete]] = i(_.failure)
def result: Initialize[Task[Result[S]]] = i(_.result)
def xtriggeredBy[A1](tasks: Initialize[Task[A1]]*): Initialize[Task[S]] =
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.triggeredBy)
def triggeredBy[A1](tasks: Initialize[Task[A1]]*): Initialize[Task[S]] =
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.triggeredBy)
def runBefore[A1](tasks: Initialize[Task[A1]]*): Initialize[Task[S]] =
nonLocal(tasks.toSeq.asInstanceOf[Seq[AnyInitTask]], Def.runBefore)
private[this] def nonLocal(
tasks: Seq[AnyInitTask],
key: AttributeKey[Seq[Task[_]]]
): Initialize[Task[S]] =
Initialize
.joinAny[Task](coerceToAnyTaskSeq(tasks))
.zipWith(i)((ts, i) => i.copy(info = i.info.set(key, ts)))
}
/**
* Enriches `Initialize[InputTask[S]]` types.
*
* @param i the original `Initialize[InputTask[S]]` value to enrich
* @tparam S the type of the underlying value
*/
final class RichInitializeInputTask[S](i: Initialize[InputTask[S]])
extends RichInitTaskBase[S, InputTask] {
protected def onTask[T](f: Task[S] => Task[T]): Initialize[InputTask[T]] = i(_ mapTask f)
def dependsOn(tasks: AnyInitTask*): Initialize[InputTask[S]] =
i.zipWith(Initialize.joinAny[Task](coerceToAnyTaskSeq(tasks)))((thisTask, deps) =>
thisTask.mapTask(_.dependsOn(deps: _*))
)
}
/**
* Enriches `Initialize[R[S]]` types. Abstracts over the specific task-like type constructor.
*
* @tparam S the type of the underlying vault
* @tparam R the task-like type constructor (either Task or InputTask)
*/
sealed abstract class RichInitTaskBase[S, R[_]] {
protected def onTask[T](f: Task[S] => Task[T]): Initialize[R[T]]
def flatMap[T](f: S => Task[T]): Initialize[R[T]] =
onTask(_.result flatMap (f compose successM))
def map[T](f: S => T): Initialize[R[T]] = onTask(_.result map (f compose successM))
def andFinally(fin: => Unit): Initialize[R[S]] = onTask(_ andFinally fin)
def doFinally(t: Task[Unit]): Initialize[R[S]] = onTask(_ doFinally t)
def ||[T >: S](alt: Task[T]): Initialize[R[T]] = onTask(_ || alt)
def &&[T](alt: Task[T]): Initialize[R[T]] = onTask(_ && alt)
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(_.result flatMap 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(_.result map 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]] =
onTask(_.result flatMap (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]] = onTask(_.result map (f compose failM))
}
type AnyInitTask = Initialize[Task[_]]
implicit def richTaskSeq[T](in: Seq[Initialize[Task[T]]]): RichTaskSeq[T] = new RichTaskSeq(in)

View File

@ -29,7 +29,7 @@ import sbt.Project.{
// richTaskSessionVar,
// sbtRichTaskPromise
}
import sbt.ProjectExtra.*
import sbt.ProjectExtra.{ *, given }
import sbt.Scope.{ GlobalScope, ThisScope, fillTaskAxis }
import sbt.State.StateOpsImpl
import sbt.coursierint._
@ -716,7 +716,7 @@ object Defaults extends BuildCommon {
crossPaths.value
),
cleanIvy := IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log),
clean := clean.dependsOn(cleanIvy).value,
clean := clean.dependsOnTask(cleanIvy).value,
scalaCompilerBridgeBinaryJar := Def.settingDyn {
val sv = scalaVersion.value
if (ScalaArtifacts.isScala3(sv)) fetchBridgeBinaryJarTask(sv)
@ -998,7 +998,11 @@ object Defaults extends BuildCommon {
console := consoleTask.value,
collectAnalyses := Definition.collectAnalysesTask.map(_ => ()).value,
consoleQuick := consoleQuickTask.value,
discoveredMainClasses := (compile map discoverMainClasses storeAs discoveredMainClasses xtriggeredBy compile).value,
discoveredMainClasses := compile
.map(discoverMainClasses)
.storeAs(discoveredMainClasses)
.xtriggeredBy(compile)
.value,
discoveredSbtPlugins := discoverSbtPluginNames.value,
// This fork options, scoped to the configuration is used for tests
forkOptions := forkOptionsTask.value,
@ -3012,7 +3016,6 @@ object Classpaths {
}
},
bootResolvers := {
import Scoped.syntax.richInitialize
(appConfiguration map bootRepositories).value
},
fullResolvers :=
@ -4012,7 +4015,7 @@ object Classpaths {
}
def projectResolverTask: Initialize[Task[Resolver]] =
projectDescriptors map { m =>
projectDescriptors.map { m =>
val resolver = new ProjectResolver(ProjectResolver.InterProject, m)
new RawRepository(resolver, resolver.getName)
}

View File

@ -148,7 +148,7 @@ object ProjectExtra extends ProjectExtra:
val projectReturnKey: AttributeKey[List[File]] =
AttributeKey[List[File]]("project-return", "Maintains a stack of builds visited using reload.")
trait ProjectExtra:
trait ProjectExtra extends Scoped.Syntax:
import ProjectExtra.projectReturnKey
def inConfig(conf: Configuration)(ss: Seq[Setting[_]]): Seq[Setting[_]] =
@ -747,8 +747,8 @@ trait ProjectExtra:
// These used to be in Project so that they didn't need to get imported (due to Initialize being nested in Project).
// Moving Initialize and other settings types to Def and decoupling Project, Def, and Structure means these go here for now
implicit def richInitializeTask[T](init: Initialize[Task[T]]): Scoped.RichInitializeTask[T] =
new Scoped.RichInitializeTask(init)
// given richInitializeTask[T]: Conversion[Initialize[Task[T]], Scoped.RichInitializeTask[T]] =
// (init: Initialize[Task[T]]) => new Scoped.RichInitializeTask(init)
/*
implicit def richInitializeInputTask[T](

View File

@ -17,9 +17,8 @@ import org.apache.ivy.core.resolve.DownloadOptions
import org.apache.ivy.plugins.resolver.DependencyResolver
import sbt.Defaults.prefix
import sbt.Keys._
import sbt.Project.*
import sbt.ProjectExtra.inConfig
import sbt.ProjectExtra.richInitializeTask
import sbt.Project.{ inConfig => _, * }
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make._
import sbt.SlashSyntax0._
import sbt.coursierint.LMCoursier

View File

@ -12,7 +12,7 @@ import java.io.File
import sbt.Def._
import sbt.Keys._
import sbt.nio.Keys._
import sbt.ProjectExtra.richInitializeTask
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make._
import sbt.SlashSyntax0._
import sbt.internal.inc.ModuleUtilities

View File

@ -14,7 +14,7 @@ import sbt.internal.util.complete.{ DefaultParsers, Parser }
import Aggregation.{ KeyValue, Values }
import DefaultParsers._
import sbt.internal.util.Types.idFun
import sbt.ProjectExtra.*
import sbt.ProjectExtra.{ failure => _, * }
import java.net.URI
import sbt.internal.CommandStrings.{ MultiTaskCommand, ShowCommand, PrintCommand }
import sbt.internal.util.{ AttributeEntry, AttributeKey, AttributeMap, IMap, Settings, Util }
@ -188,12 +188,12 @@ object Act {
show: Show[ScopedKey[_]]
): Parser[ParsedKey] =
seq(allKeys) flatMap { ss =>
val default = ss.headOption match {
val default: Parser[ParsedKey] = ss.headOption match
case None => noValidKeys
case Some(x) => success(x)
}
selectFromValid(ss filter isValid(data), default)
}
def selectFromValid(ss: Seq[ParsedKey], default: Parser[ParsedKey])(implicit
show: Show[ScopedKey[_]]
): Parser[ParsedKey] =

View File

@ -90,7 +90,12 @@ object BuildUtil {
}
def baseImports: Seq[String] =
"import _root_.scala.xml.{TopScope=>$scope}" :: "import _root_.sbt.*" :: "import _root_.sbt.given" :: "import _root_.sbt.Keys.*" :: "import _root_.sbt.nio.Keys.*" :: Nil
("import _root_.scala.xml.{TopScope=>$scope}"
:: "import _root_.sbt.*"
:: "import _root_.sbt.given"
:: "import _root_.sbt.Keys.*"
:: "import _root_.sbt.nio.Keys.*"
:: Nil)
def getImports(unit: BuildUnit): Seq[String] =
unit.plugins.detected.imports ++ unit.definitions.dslDefinitions.imports
@ -106,7 +111,9 @@ object BuildUtil {
def importNamesRoot(names: Seq[String]): Seq[String] = importNames(names map rootedName)
/** Wildcard import `._` for all values. */
def importAll(values: Seq[String]): Seq[String] = importNames(values map { _ + "._" })
def importAll(values: Seq[String]): Seq[String] = importNames(values.flatMap { (x: String) =>
Seq(s"$x.*", s"$x.given")
})
def importAllRoot(values: Seq[String]): Seq[String] = importAll(values map rootedName)
def rootedName(s: String): String = if (s contains '.') "_root_." + s else s

View File

@ -14,7 +14,7 @@ import java.nio.file.{ DirectoryNotEmptyException, Files, Path }
import sbt.Def._
import sbt.Keys._
// import sbt.Project.richInitializeTask
import sbt.ProjectExtra.*
import sbt.ProjectExtra.{ *, given }
import sbt.SlashSyntax0._
import sbt.io.syntax._
import sbt.nio.Keys._

View File

@ -19,7 +19,7 @@ import sbt.librarymanagement.syntax._
import sbt.util.{ CacheStore, CacheStoreFactory, Level, Logger, Tracked }
import sbt.io.IO
import sbt.io.syntax._
import sbt.ProjectExtra.richInitializeTask
import sbt.ProjectExtra.*
import sjsonnew.JsonFormat
import scala.compat.Platform.EOL
import scala.concurrent.duration.FiniteDuration

View File

@ -11,7 +11,7 @@ package internal
import sbt.Def._
import sbt.Keys._
// import sbt.Project.richInitializeTask
import sbt.ProjectExtra.{ delegates, extract, richInitializeTask }
import sbt.ProjectExtra.*
import sbt.internal.io.Source
import sbt.internal.nio.Globs
import sbt.internal.util.AttributeMap
@ -63,7 +63,7 @@ private[sbt] object WatchTransitiveDependencies {
): Def.Initialize[Task[Arguments]] =
import sbt.TupleSyntax.*
(
(streamsManager map { mgr =>
(streamsManager.map { mgr =>
val stream = mgr(scopedKey)
stream.open()
stream

View File

@ -22,7 +22,7 @@ import sbt.Keys.{
publishConfiguration,
useCoursier
}
import sbt.ProjectExtra.richInitializeTask
import sbt.ProjectExtra.*
import sbt.librarymanagement.PublishConfiguration
import scala.collection.JavaConverters._
import scala.xml.{ Node, PrefixedAttribute }
@ -198,7 +198,7 @@ object IvyXml {
task: TaskKey[T],
shadedConfigOpt: Option[Configuration]
): Setting[Task[T]] =
task := task.dependsOn {
task := task.dependsOnTask {
Def.taskIf {
if useCoursier.value then
val currentProject = {

View File

@ -16,7 +16,7 @@ import sbt.Def._
import sbt.Def.{ parsed }
import sbt.Keys._
import sbt.Project._
import sbt.ProjectExtra.richInitializeTask
import sbt.ProjectExtra.*
import sbt.ScopeFilter.Make._
import sbt.Scoped.richTaskSeq
import sbt.SlashSyntax0._

View File

@ -14,8 +14,7 @@ import sbt.Def._
import sbt.Keys._
import sbt.SlashSyntax0._
import sbt.Project._
import sbt.ProjectExtra.storeAs
import sbt.ProjectExtra.richInitializeTask
import sbt.ProjectExtra.*
import sbt.internal.graph._
import sbt.internal.graph.backend.SbtUpdateReport
import sbt.internal.graph.rendering.{ DagreHTML, TreeView }

View File

@ -27,7 +27,7 @@ package object sbt
with sbt.BuildSyntax
with sbt.OptionSyntax
with sbt.SlashSyntax
with sbt.Import {
with sbt.Import:
// IO
def uri(s: String): URI = new URI(s)
def file(s: String): File = new File(s)
@ -62,4 +62,4 @@ package object sbt
inline def config(name: String): Configuration = ${
ConfigurationMacro.configMacroImpl('{ name })
}
}
end sbt

View File

@ -1,25 +1,24 @@
// tests that errors are properly propagated for dependsOn, map, and flatMap
import sbt.TupleSyntax.*
lazy val root = (project in file(".")).
settings(
a := (baseDirectory mapN (b => if ((b / "succeed").exists) () else sys.error("fail"))).value,
b := (a.task(at => nop dependsOn(at))).value,
c := (a mapN { _ => () }).value,
d := (a flatMapN { _ => task { () } }).value
)
lazy val root = (project in file(".")).settings(
a := (baseDirectory mapN (b => if ((b / "succeed").exists) () else sys.error("fail"))).value,
// deprecated?
// b := (a.task(at => nop dependsOn(at))).value,
c := (a mapN { _ => () }).value,
d := (a flatMapN { _ => task { () } }).value
)
lazy val a = taskKey[Unit]("")
lazy val b = taskKey[Unit]("")
lazy val c = taskKey[Unit]("")
lazy val d = taskKey[Unit]("")
lazy val input = (project in file("input")).
settings(
f := (if (Def.spaceDelimited().parsed.head == "succeed") () else sys.error("fail")),
j := sys.error("j"),
g := (f dependsOn(j)).evaluated,
h := (f mapTask { _ => IO.touch(file("h")) }).evaluated
)
lazy val input = (project in file("input")).settings(
f := (if (Def.spaceDelimited().parsed.head == "succeed") () else sys.error("fail")),
j := sys.error("j"),
g := f.dependsOnTask(j).evaluated,
h := (f map { _ => IO.touch(file("h")) }).evaluated
)
lazy val f = inputKey[Unit]("")
lazy val g = inputKey[Unit]("")
lazy val h = inputKey[Unit]("")

View File

@ -1,11 +1,9 @@
-> a
-> b
-> c
-> d
$ touch succeed
> a
> b
> c
> d