mirror of https://github.com/sbt/sbt.git
Implement Def.inputTaskDyn
**Problem/Solution** This implements Def.inputTaskDyn using Scala 3 macros.
This commit is contained in:
parent
c60142e061
commit
11cd58daa7
|
|
@ -319,6 +319,9 @@ object Def extends BuildSyntax with Init with InitializeImplicits:
|
||||||
inline def inputTask[A1](inline a: A1): Def.Initialize[InputTask[A1]] =
|
inline def inputTask[A1](inline a: A1): Def.Initialize[InputTask[A1]] =
|
||||||
${ InputTaskMacro.inputTaskMacroImpl[A1]('a) }
|
${ InputTaskMacro.inputTaskMacroImpl[A1]('a) }
|
||||||
|
|
||||||
|
inline def inputTaskDyn[A1](inline a: Def.Initialize[Task[A1]]): Def.Initialize[InputTask[A1]] =
|
||||||
|
${ InputTaskMacro.inputTaskDynMacroImpl[A1]('a) }
|
||||||
|
|
||||||
inline def taskIf[A1](inline a: A1): Def.Initialize[Task[A1]] =
|
inline def taskIf[A1](inline a: A1): Def.Initialize[Task[A1]] =
|
||||||
${ TaskMacro.taskIfImpl[A1]('a, cached = true) }
|
${ TaskMacro.taskIfImpl[A1]('a, cached = true) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ package std
|
||||||
import sbt.internal.util.Types.Id
|
import sbt.internal.util.Types.Id
|
||||||
import sbt.internal.util.complete.Parser
|
import sbt.internal.util.complete.Parser
|
||||||
import scala.quoted.*
|
import scala.quoted.*
|
||||||
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
object InputTaskMacro:
|
object InputTaskMacro:
|
||||||
import TaskMacro.ContSyntax.*
|
import TaskMacro.ContSyntax.*
|
||||||
|
|
@ -20,8 +21,10 @@ object InputTaskMacro:
|
||||||
): Expr[Def.Initialize[InputTask[A1]]] =
|
): Expr[Def.Initialize[InputTask[A1]]] =
|
||||||
inputTaskMacro0[A1](tree)
|
inputTaskMacro0[A1](tree)
|
||||||
|
|
||||||
// def inputTaskDynMacroImpl[A1: Type](t: c.Expr[Initialize[Task[A1]]])(using qctx: Quotes): c.Expr[Initialize[InputTask[A1]]] =
|
def inputTaskDynMacroImpl[A1: Type](tree: Expr[Def.Initialize[Task[A1]]])(using
|
||||||
// inputTaskDynMacro0[A1](c)(t)
|
qctx: Quotes
|
||||||
|
): Expr[Def.Initialize[InputTask[A1]]] =
|
||||||
|
inputTaskDynMacro0[A1](tree)
|
||||||
|
|
||||||
private def inputTaskMacro0[A1: Type](tree: Expr[A1])(using
|
private def inputTaskMacro0[A1: Type](tree: Expr[A1])(using
|
||||||
Quotes
|
Quotes
|
||||||
|
|
@ -110,92 +113,78 @@ object InputTaskMacro:
|
||||||
private def iTaskMacro[A1: Type](tree: Expr[A1])(using qctx: Quotes): Expr[Task[A1]] =
|
private def iTaskMacro[A1: Type](tree: Expr[A1])(using qctx: Quotes): Expr[Task[A1]] =
|
||||||
val convert1 = new TaskConvert(qctx, 2000)
|
val convert1 = new TaskConvert(qctx, 2000)
|
||||||
convert1.contMapN[A1, Task, Id](tree, convert1.appExpr, None)
|
convert1.contMapN[A1, Task, Id](tree, convert1.appExpr, None)
|
||||||
/*
|
|
||||||
private def inputTaskDynMacro0[A1: Type](
|
private def inputTaskDynMacro0[A1: Type](
|
||||||
expr: Expr[Def.Initialize[Task[A1]]]
|
expr: Expr[Def.Initialize[Task[A1]]]
|
||||||
)(using qctx: Quotes): Expr[Def.Initialize[InputTask[A1]]] = {
|
)(using qctx: Quotes): Expr[Def.Initialize[InputTask[A1]]] =
|
||||||
import qctx.reflect.{ Apply => ApplyTree, * }
|
// convert1 detects x.parsed where x is Parser[a], State => Parser[a], Initialize[Parser[a]], or Initialize[State => Parser[a]]
|
||||||
// import internal.decorators._
|
// val it6a = Def.inputTaskDyn {
|
||||||
val tag: Type[A1] = summon[Type[A1]]
|
// val d3 = dummy3.parsed
|
||||||
// val util = ContextUtil[c.type](c)
|
// val i = d3._2
|
||||||
val convert1 = new InitParserConvert(qctx)
|
// Def.task { tk.value + i }
|
||||||
import convert1.Converted
|
// }
|
||||||
|
val convert1 = InitParserConvert(qctx, 0)
|
||||||
// val it = Ident(convert1.singleton(InputTask))
|
import convert1.qctx.reflect.*
|
||||||
val isParserWrapper = new InitParserConvert(qctx).asPredicate
|
def expandTask[A2: Type](dyn: Boolean, tree: Tree): Expr[Def.Initialize[Task[A2]]] =
|
||||||
val isTaskWrapper = new FullConvert(qctx).asPredicate
|
if dyn then
|
||||||
val isAnyWrapper =
|
TaskMacro.taskDynMacroImpl[A2](
|
||||||
(n: String, tpe: TypeRepr, tr: Term) =>
|
tree.asExprOf[Def.Initialize[Task[A2]]]
|
||||||
isParserWrapper(n, tpe, tr) || isTaskWrapper(n, tpe, tr)
|
|
||||||
val ttree = expr.asTerm
|
|
||||||
val defs = convert1.collectDefs(ttree, isAnyWrapper)
|
|
||||||
val checkQual =
|
|
||||||
util.checkReferences(defs, isAnyWrapper, weakTypeOf[Def.Initialize[InputTask[Any]]])
|
|
||||||
|
|
||||||
// the Symbol for the anonymous function passed to the appropriate Instance.map/flatMap/pure method
|
|
||||||
// this Symbol needs to be known up front so that it can be used as the owner of synthetic vals
|
|
||||||
|
|
||||||
// val functionSym = util.functionSymbol(ttree.pos)
|
|
||||||
var result: Option[(Term, TypeRepr, ValDef)] = None
|
|
||||||
|
|
||||||
// original is the Tree being replaced. It is needed for preserving attributes.
|
|
||||||
def subWrapper(tpe: TypeRepr, qual: Term, original: Term): Tree =
|
|
||||||
if result.isDefined then
|
|
||||||
report.errorAndAbort(
|
|
||||||
"implementation restriction: a dynamic InputTask can only have a single input parser.",
|
|
||||||
qual.pos,
|
|
||||||
)
|
)
|
||||||
Literal(UnitConstant())
|
else TaskMacro.taskMacroImpl[A2](tree.asExprOf[A2], false)
|
||||||
else {
|
val inputBuf = ListBuffer[(String, TypeRepr, Term, Term)]()
|
||||||
// qual.foreach(checkQual)
|
val record = [a] =>
|
||||||
val vd = util.freshValDef(tpe, qual.symbol.pos, functionSym) // val $x: <tpe>
|
(name: String, tpe: Type[a], qual: Term, oldTree: Term) =>
|
||||||
result = Some((qual, tpe, vd))
|
given t: Type[a] = tpe
|
||||||
val tree = util.refVal(original, vd) // $x
|
convert1
|
||||||
tree.setPos(
|
.convert[a](name, qual)
|
||||||
qual.pos
|
.transform { (replacement: Term) =>
|
||||||
) // position needs to be set so that wrapKey passes the position onto the wrapper
|
inputBuf.append((name, TypeRepr.of[a](using tpe), qual, replacement))
|
||||||
assert(tree.tpe != null, "Null type: " + tree)
|
oldTree
|
||||||
tree.setType(tpe)
|
|
||||||
tree
|
|
||||||
}
|
}
|
||||||
// Tree for InputTask.<name>[<tpeA>, <tpeB>](arg1)(arg2)
|
def genCreateFree(body: Term) =
|
||||||
def inputTaskCreate(name: String, tpeA: Type, tpeB: Type, arg1: Tree, arg2: Tree) = {
|
val init = expandTask[A1](true, body)
|
||||||
val typedApp = TypeApply(util.select(it, name), TypeTree(tpeA) :: TypeTree(tpeB) :: Nil)
|
'{
|
||||||
val app = ApplyTree(ApplyTree(typedApp, arg1 :: Nil), arg2 :: Nil)
|
InputTask.createFree($init)
|
||||||
Expr[Def.Initialize[InputTask[A1]]](app)
|
|
||||||
}
|
}
|
||||||
// Tree for InputTask.createFree[<tpe>](arg1)
|
// This is roughly based on getMap in Cont.scala
|
||||||
def inputTaskCreateFree(tpe: Type, arg: Tree) = {
|
def genCreateDyn[Arg: Type](parser: Term, body: Term) =
|
||||||
val typedApp = TypeApply(util.select(it, InputTaskCreateFreeName), TypeTree(tpe) :: Nil)
|
val param = parser.asExprOf[Def.Initialize[State => Parser[Arg]]]
|
||||||
val app = ApplyTree(typedApp, arg :: Nil)
|
val tpe =
|
||||||
Expr[Def.Initialize[InputTask[A1]]](app)
|
MethodType(List("$p"))(
|
||||||
|
_ => List(TypeRepr.of[Arg]),
|
||||||
|
_ => TypeRepr.of[Def.Initialize[Task[A1]]]
|
||||||
|
)
|
||||||
|
val lambda = Lambda(
|
||||||
|
owner = Symbol.spliceOwner,
|
||||||
|
tpe = tpe,
|
||||||
|
rhsFn = (sym, params) => {
|
||||||
|
val param = params.head.asInstanceOf[Term]
|
||||||
|
val substitute = [a] =>
|
||||||
|
(name: String, tpe: Type[a], qual: Term, replace: Term) =>
|
||||||
|
given t: Type[a] = tpe
|
||||||
|
convert1
|
||||||
|
.convert[a](name, qual)
|
||||||
|
.transform { _ => Ref(param.symbol) }
|
||||||
|
val modifiedBody =
|
||||||
|
convert1
|
||||||
|
.transformWrappers(body.changeOwner(sym), substitute, sym)
|
||||||
|
modifiedBody
|
||||||
}
|
}
|
||||||
def expandTask[I: Type](dyn: Boolean, tx: Tree): c.Expr[Initialize[Task[I]]] =
|
)
|
||||||
if dyn then taskDynMacroImpl[I](c)(c.Expr[Initialize[Task[I]]](tx))
|
val action = expandTask[Arg => Def.Initialize[Task[A1]]](false, lambda)
|
||||||
else taskMacroImpl[I](c)(c.Expr[I](tx))
|
'{
|
||||||
def wrapTag[I: Type]: Type[Initialize[Task[I]]] = weakTypeTag
|
InputTask.createDyn[Arg, A1](p = $param)(action = $action)
|
||||||
|
|
||||||
def sub(name: String, tpe: TypeRepr, qual: Term, oldTree: Term): Converted =
|
|
||||||
convert1.convert[A1](name, qual) transform { (tree: Term) =>
|
|
||||||
subWrapper(tpe, tree, oldTree)
|
|
||||||
}
|
}
|
||||||
|
val body = convert1.transformWrappers(expr.asTerm, record, Symbol.spliceOwner)
|
||||||
val tx =
|
inputBuf.toList match
|
||||||
convert1.transformWrappers(expr.asTerm, sub, Symbol.spliceOwner)
|
case Nil => genCreateFree(body)
|
||||||
result match {
|
case (_, tpe, _, paramTree) :: Nil =>
|
||||||
case Some((p, tpe, param)) =>
|
tpe.asType match
|
||||||
val fCore = util.createFunction(param :: Nil, tx, functionSym)
|
case '[a] => genCreateDyn[a](paramTree, body)
|
||||||
val bodyTpe = wrapTag(tag).tpe
|
case xs =>
|
||||||
val fTpe = util.functionType(tpe :: Nil, bodyTpe)
|
report.errorAndAbort("a dynamic InputTask can only have a single input parser.")
|
||||||
val fTag = Type[Any](fTpe) // don't know the actual type yet, so use Any
|
???
|
||||||
val fInit = expandTask(false, fCore)(fTag).tree
|
end inputTaskDynMacro0
|
||||||
inputTaskCreate(InputTaskCreateDynName, tpe, tag.tpe, p, fInit)
|
|
||||||
case None =>
|
|
||||||
val init = expandTask[A1](true, tx).tree
|
|
||||||
inputTaskCreateFree(tag.tpe, init)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
def parserGenInputTaskMacroImpl[A1: Type, A2: Type](
|
def parserGenInputTaskMacroImpl[A1: Type, A2: Type](
|
||||||
parserGen: Expr[ParserGen[A1]],
|
parserGen: Expr[ParserGen[A1]],
|
||||||
|
|
|
||||||
|
|
@ -129,11 +129,15 @@ object Assign {
|
||||||
dummys.parsed
|
dummys.parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
// val it6 = Def.inputTaskDyn {
|
val it6a = Def.inputTaskDyn {
|
||||||
// val d3 = dummy3.parsed
|
val d3 = dummy3.parsed
|
||||||
// val i = d3._2
|
val i = d3._2
|
||||||
// Def.task { tk.value + i }
|
Def.task { tk.value + i }
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
val it6b = Def.inputTaskDyn {
|
||||||
|
Def.task { 1 }
|
||||||
|
}
|
||||||
|
|
||||||
val it7 = Def.inputTask {
|
val it7 = Def.inputTask {
|
||||||
it5.parsed
|
it5.parsed
|
||||||
|
|
|
||||||
|
|
@ -1038,7 +1038,13 @@ object Defaults extends BuildCommon {
|
||||||
val r = run.evaluated
|
val r = run.evaluated
|
||||||
val service = bgJobService.value
|
val service = bgJobService.value
|
||||||
service.waitForTry(r.handle).get
|
service.waitForTry(r.handle).get
|
||||||
r
|
()
|
||||||
|
},
|
||||||
|
runMainBlock := {
|
||||||
|
val r = runMain.evaluated
|
||||||
|
val service = bgJobService.value
|
||||||
|
service.waitForTry(r.handle).get
|
||||||
|
()
|
||||||
},
|
},
|
||||||
fgRun := runTask(fullClasspath, (run / mainClass), (run / runner)).evaluated,
|
fgRun := runTask(fullClasspath, (run / mainClass), (run / runner)).evaluated,
|
||||||
fgRunMain := runMainTask(fullClasspath, (run / runner)).evaluated,
|
fgRunMain := runMainTask(fullClasspath, (run / runner)).evaluated,
|
||||||
|
|
|
||||||
|
|
@ -316,8 +316,9 @@ object Keys {
|
||||||
val selectMainClass = taskKey[Option[String]]("Selects the main class to run.").withRank(BMinusTask)
|
val selectMainClass = taskKey[Option[String]]("Selects the main class to run.").withRank(BMinusTask)
|
||||||
val mainClass = taskKey[Option[String]]("Defines the main class for packaging or running.").withRank(BPlusTask)
|
val mainClass = taskKey[Option[String]]("Defines the main class for packaging or running.").withRank(BPlusTask)
|
||||||
val run = inputKey[EmulateForeground]("Runs a main class, passing along arguments provided on the command line.").withRank(APlusTask)
|
val run = inputKey[EmulateForeground]("Runs a main class, passing along arguments provided on the command line.").withRank(APlusTask)
|
||||||
val runBlock = inputKey[EmulateForeground]("Runs a main class, and blocks until it's done.").withRank(DTask)
|
val runBlock = inputKey[Unit]("Runs a main class, and blocks until it's done.").withRank(DTask)
|
||||||
val runMain = inputKey[EmulateForeground]("Runs the main class selected by the first argument, passing the remaining arguments to the main method.").withRank(ATask)
|
val runMain = inputKey[EmulateForeground]("Runs the main class selected by the first argument, passing the remaining arguments to the main method.").withRank(ATask)
|
||||||
|
val runMainBlock = inputKey[Unit]("Runs the main class selected by the first argument, passing the remaining arguments to the main method.").withRank(DTask)
|
||||||
val discoveredMainClasses = taskKey[Seq[String]]("Auto-detects main classes.").withRank(BMinusTask)
|
val discoveredMainClasses = taskKey[Seq[String]]("Auto-detects main classes.").withRank(BMinusTask)
|
||||||
val runner = taskKey[ScalaRun]("Implementation used to run a main class.").withRank(DTask)
|
val runner = taskKey[ScalaRun]("Implementation used to run a main class.").withRank(DTask)
|
||||||
val trapExit = settingKey[Boolean]("If true, enables exit trapping and thread management for 'run'-like tasks. This was removed in sbt 1.6.0 due to JDK 17 deprecating Security Manager.").withRank(CSetting)
|
val trapExit = settingKey[Boolean]("If true, enables exit trapping and thread management for 'run'-like tasks. This was removed in sbt 1.6.0 due to JDK 17 deprecating Security Manager.").withRank(CSetting)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,14 @@ import complete.Parser
|
||||||
val runFoo = inputKey[Unit]("Runs Foo with passed arguments")
|
val runFoo = inputKey[Unit]("Runs Foo with passed arguments")
|
||||||
val check = taskKey[Unit]("")
|
val check = taskKey[Unit]("")
|
||||||
|
|
||||||
|
scalaVersion := "3.6.3"
|
||||||
|
|
||||||
lazy val root = (project in file(".")).
|
lazy val root = (project in file(".")).
|
||||||
settings(
|
settings(
|
||||||
name := "run-test",
|
name := "run-test",
|
||||||
runFoo := Def.inputTaskDyn {
|
runFoo := Def.inputTaskDyn {
|
||||||
val args = Def.spaceDelimited().parsed
|
val args = Def.spaceDelimited().parsed
|
||||||
(Compile / runMain).toTask(s" Foo " + args.mkString(" "))
|
(Compile / runMainBlock).toTask(s" Foo " + args.mkString(" "))
|
||||||
}.evaluated,
|
}.evaluated,
|
||||||
check := {
|
check := {
|
||||||
val x = runFoo.toTask(" hi ho").value
|
val x = runFoo.toTask(" hi ho").value
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue