mirror of https://github.com/sbt/sbt.git
Add Initialize[Task[T]].taskValue: Task[T] to allow selecting the Task for use by *Generators. Fixes #866.
This commit is contained in:
parent
1d9b44d5d7
commit
fcb35f3b8f
|
|
@ -37,8 +37,8 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
case Some(c) => c + s + scala.Console.RESET
|
||||
case None => s
|
||||
}
|
||||
|
||||
override def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] =
|
||||
|
||||
override def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] =
|
||||
super.deriveAllowed(s, allowDynamic) orElse
|
||||
(if(s.key.scope != ThisScope) Some(s"Scope cannot be defined for ${definedSettingString(s)}") else None ) orElse
|
||||
s.dependencies.find(k => k.scope != ThisScope).map(k => s"Scope cannot be defined for dependency ${k.key.label} of ${definedSettingString(s)}")
|
||||
|
|
@ -51,7 +51,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
|
||||
/** A default Parser for splitting input into space-separated arguments.
|
||||
* `argLabel` is an optional, fixed label shown for an argument during tab completion.*/
|
||||
def spaceDelimited(argLabel: String = "<arg>"): Parser[Seq[String]] = complete.Parsers.spaceDelimited(argLabel)
|
||||
def spaceDelimited(argLabel: String = "<arg>"): Parser[Seq[String]] = complete.Parsers.spaceDelimited(argLabel)
|
||||
|
||||
/** Lifts the result of a setting initialization into a Task. */
|
||||
def toITask[T](i: Initialize[T]): Initialize[Task[T]] = map(i)(std.TaskExtra.inlineTask)
|
||||
|
|
@ -63,7 +63,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
import language.experimental.macros
|
||||
import std.TaskMacro.{inputTaskMacroImpl, inputTaskDynMacroImpl, taskDynMacroImpl, taskMacroImpl}
|
||||
import std.SettingMacro.{settingDynMacroImpl,settingMacroImpl}
|
||||
import std.{InputEvaluated, MacroValue, ParserInput}
|
||||
import std.{InputEvaluated, MacroValue, MacroTaskValue, ParserInput}
|
||||
|
||||
def task[T](t: T): Def.Initialize[Task[T]] = macro taskMacroImpl[T]
|
||||
def taskDyn[T](t: Def.Initialize[Task[T]]): Def.Initialize[Task[T]] = macro taskDynMacroImpl[T]
|
||||
|
|
@ -78,6 +78,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
implicit def macroValueI[T](in: Initialize[T]): MacroValue[T] = ???
|
||||
implicit def macroValueIT[T](in: Initialize[Task[T]]): MacroValue[T] = ???
|
||||
implicit def macroValueIInT[T](in: Initialize[InputTask[T]]): InputEvaluated[T] = ???
|
||||
implicit def taskMacroValueIT[T](in: Initialize[Task[T]]): MacroTaskValue[T] = ???
|
||||
|
||||
// The following conversions enable the types Parser[T], Initialize[Parser[T]], and Initialize[State => Parser[T]] to
|
||||
// be used in the inputTask macro as an input with an ultimate result of type T
|
||||
|
|
@ -101,7 +102,7 @@ object Def extends Init[Scope] with TaskMacroExtra
|
|||
private[sbt] val (stateKey, dummyState) = dummy[State]("state", "Current build state.")
|
||||
}
|
||||
// these need to be mixed into the sbt package object because the target doesn't involve Initialize or anything in Def
|
||||
trait TaskMacroExtra
|
||||
trait TaskMacroExtra
|
||||
{
|
||||
implicit def macroValueT[T](in: Task[T]): std.MacroValue[T] = ???
|
||||
implicit def macroValueIn[T](in: InputTask[T]): std.InputEvaluated[T] = ???
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ sealed abstract class SettingKey[T] extends ScopedTaskable[T] with KeyedInitiali
|
|||
final def := (v: T): Setting[T] = macro std.TaskMacro.settingAssignMacroImpl[T]
|
||||
final def +=[U](v: U)(implicit a: Append.Value[T, U]): Setting[T] = macro std.TaskMacro.settingAppend1Impl[T,U]
|
||||
final def ++=[U](vs: U)(implicit a: Append.Values[T, U]): Setting[T] = macro std.TaskMacro.settingAppendNImpl[T,U]
|
||||
final def <+= [V](v: Initialize[V])(implicit a: Append.Value[T, V]): Setting[T] = macro std.TaskMacro.settingAppend1Position[T,V]
|
||||
final def <+= [V](v: Initialize[V])(implicit a: Append.Value[T, V]): Setting[T] = macro std.TaskMacro.settingAppend1Position[T,V]
|
||||
final def <++= [V](vs: Initialize[V])(implicit a: Append.Values[T, V]): Setting[T] = macro std.TaskMacro.settingAppendNPosition[T,V]
|
||||
final def ~= (f: T => T): Setting[T] = macro std.TaskMacro.settingTransformPosition[T]
|
||||
final def transform(f: T => T, source: SourcePosition): Setting[T] = set( scopedKey(f), source )
|
||||
|
|
@ -108,7 +108,7 @@ object Scoped
|
|||
def in(p: Reference, c: ConfigKey, t: Scoped): Result = in(Select(p), Select(c), Select(t.key))
|
||||
def in(p: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]]): Result = in( Scope(p, c, t, This) )
|
||||
}
|
||||
|
||||
|
||||
def scopedSetting[T](s: Scope, k: AttributeKey[T]): SettingKey[T] = new SettingKey[T] { val scope = s; val key = k}
|
||||
def scopedInput[T](s: Scope, k: AttributeKey[InputTask[T]]): InputKey[T] = new InputKey[T] { val scope = s; val key = k }
|
||||
def scopedTask[T](s: Scope, k: AttributeKey[Task[T]]): TaskKey[T] = new TaskKey[T] { val scope = s; val key = k }
|
||||
|
|
@ -142,6 +142,7 @@ object Scoped
|
|||
def set(app: Initialize[Task[S]], source: SourcePosition): Setting[Task[S]] = Def.setting(scopedKey, app, source)
|
||||
def transform(f: S => S, source: SourcePosition): Setting[Task[S]] = set( scopedKey(_ map f), source)
|
||||
|
||||
@deprecated("No longer needed with new task syntax and SettingKey inheriting from Initialize.", "0.13.2")
|
||||
def task: SettingKey[Task[S]] = scopedSetting(scope, key)
|
||||
def get(settings: Settings[Scope]): Option[Task[S]] = settings.get(scope, key)
|
||||
|
||||
|
|
@ -214,7 +215,7 @@ object Scoped
|
|||
|
||||
implicit def richFileSetting(s: SettingKey[File]): RichFileSetting = new RichFileSetting(s)
|
||||
implicit def richFilesSetting(s: SettingKey[Seq[File]]): RichFilesSetting = new RichFilesSetting(s)
|
||||
|
||||
|
||||
final class RichFileSetting(s: SettingKey[File]) extends RichFileBase
|
||||
{
|
||||
@deprecated("Use a standard setting definition.", "0.13.0")
|
||||
|
|
@ -237,7 +238,7 @@ object Scoped
|
|||
protected[this] def finder(f: PathFinder => PathFinder): Seq[File] => Seq[File] =
|
||||
in => f(in).get
|
||||
}
|
||||
|
||||
|
||||
// this is the least painful arrangement I came up with
|
||||
implicit def t2ToTable2[A,B](t2: (ScopedTaskable[A], ScopedTaskable[B]) ): RichTaskable2[A,B] = new RichTaskable2(t2)
|
||||
implicit def t3ToTable3[A,B,C](t3: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C]) ): RichTaskable3[A,B,C] = new RichTaskable3(t3)
|
||||
|
|
@ -253,7 +254,7 @@ object Scoped
|
|||
implicit def t13ToTable13[A,B,C,D,E,F,G,H,I,J,K,L,N](t13: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J], ScopedTaskable[K], ScopedTaskable[L], ScopedTaskable[N]) ): RichTaskable13[A,B,C,D,E,F,G,H,I,J,K,L,N] = new RichTaskable13(t13)
|
||||
implicit def t14ToTable14[A,B,C,D,E,F,G,H,I,J,K,L,N,O](t14: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J], ScopedTaskable[K], ScopedTaskable[L], ScopedTaskable[N], ScopedTaskable[O]) ): RichTaskable14[A,B,C,D,E,F,G,H,I,J,K,L,N,O] = new RichTaskable14(t14)
|
||||
implicit def t15ToTable15[A,B,C,D,E,F,G,H,I,J,K,L,N,O,P](t15: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J], ScopedTaskable[K], ScopedTaskable[L], ScopedTaskable[N], ScopedTaskable[O], ScopedTaskable[P]) ): RichTaskable15[A,B,C,D,E,F,G,H,I,J,K,L,N,O,P] = new RichTaskable15(t15)*/
|
||||
|
||||
|
||||
sealed abstract class RichTaskables[K[L[x]]](final val keys: K[ScopedTaskable])(implicit a: AList[K])
|
||||
{
|
||||
type App[T] = Initialize[Task[T]]
|
||||
|
|
|
|||
|
|
@ -85,12 +85,23 @@ object InputWrapper
|
|||
InputWrapper.wrapInputTask[T](c)(ts,pos)
|
||||
else if(tpe <:< c.weakTypeOf[Initialize[InputTask[T]]])
|
||||
InputWrapper.wrapInitInputTask[T](c)(ts,pos)
|
||||
|
||||
else
|
||||
c.abort(pos, s"Internal sbt error. Unexpected type $tpe")
|
||||
c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.widen}")
|
||||
}
|
||||
def taskValueMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[Task[T]] =
|
||||
ContextUtil.selectMacroImpl[Task[T]](c) { (ts, pos) =>
|
||||
val tpe = ts.tree.tpe
|
||||
if(tpe <:< c.weakTypeOf[Initialize[Task[T]]])
|
||||
InputWrapper.wrapInit[Task[T]](c)(ts,pos)
|
||||
else
|
||||
c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.widen}")
|
||||
}
|
||||
}
|
||||
|
||||
sealed abstract class MacroTaskValue[T] {
|
||||
@compileTimeOnly("`taskValue` can only be used within a setting macro, such as :=, +=, ++=, or Def.setting.")
|
||||
def taskValue: Task[T] = macro InputWrapper.taskValueMacroImpl[T]
|
||||
}
|
||||
sealed abstract class MacroValue[T] {
|
||||
@compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.")
|
||||
def value: T = macro InputWrapper.valueMacroImpl[T]
|
||||
|
|
|
|||
|
|
@ -9,31 +9,31 @@ sbt provides standard hooks for adding source or resource generation tasks.
|
|||
:title: Generate sources
|
||||
:type: setting
|
||||
|
||||
sourceGenerators in Compile <+= <your Task[Seq[File]] here>
|
||||
sourceGenerators in Compile += <task of type Seq[File]>.taskValue
|
||||
|
||||
A source generation task should generate sources in a subdirectory of :key:`sourceManaged` and return a sequence of files generated. The key to add the task to is called :key:`sourceGenerators`. It should be scoped according to whether the generated files are main (`Compile`) or test (`Test`) sources. This basic structure looks like:
|
||||
A source generation task should generate sources in a subdirectory of :key:`sourceManaged` and return a sequence of files generated. The key to add the task to is called :key:`sourceGenerators`. Because we want to add the unexecuted task, we use `taskValue` instead of the usual `value`. :key:`sourceGenerators` should be scoped according to whether the generated files are main (`Compile`) or test (`Test`) sources. This basic structure looks like:
|
||||
|
||||
::
|
||||
|
||||
sourceGenerators in Compile <+= <your Task[Seq[File]] here>
|
||||
sourceGenerators in Compile += <task of type Seq[File]>.taskValue
|
||||
|
||||
For example, assuming a method `def makeSomeSources(base: File): Seq[File]`,
|
||||
|
||||
::
|
||||
|
||||
sourceGenerators in Compile <+=
|
||||
Def.task { makeSomeSources( (sourceManaged in Compile).value / "demo" ) }
|
||||
sourceGenerators in Compile +=
|
||||
Def.task( makeSomeSources( (sourceManaged in Compile).value / "demo" ) ).taskValue
|
||||
|
||||
|
||||
As a specific example, the following generates a hello world source file:
|
||||
|
||||
::
|
||||
|
||||
sourceGenerators in Compile <+= Def.task {
|
||||
sourceGenerators in Compile += Def.task {
|
||||
val file = (sourceManaged in Compile).value / "demo" / "Test.scala"
|
||||
IO.write(file, """object Test extends App { println("Hi") }""")
|
||||
Seq(file)
|
||||
}
|
||||
}.taskValue
|
||||
|
||||
Executing 'run' will print "Hi". Change `Compile` to `Test` to make it a test source. For efficiency, you would only want to generate sources when necessary and not every run.
|
||||
|
||||
|
|
@ -44,32 +44,32 @@ By default, generated sources are not included in the packaged source artifact.
|
|||
:title: Generate resources
|
||||
:type: setting
|
||||
|
||||
resourceGenerators in Compile <+= <your Task[Seq[File]] here>
|
||||
resourceGenerators in Compile += <task of type Seq[File]>
|
||||
|
||||
A resource generation task should generate resources in a subdirectory of :key:`resourceManaged` and return a sequence of files generated. The key to add the task to is called :key:`resourceGenerators`. It should be scoped according to whether the generated files are main (`Compile`) or test (`Test`) resources. This basic structure looks like:
|
||||
A resource generation task should generate resources in a subdirectory of :key:`resourceManaged` and return a sequence of files generated. The key to add the task to is called :key:`resourceGenerators`. Because we want to add the unexecuted task, we use `taskValue` instead of the usual `value`. It should be scoped according to whether the generated files are main (`Compile`) or test (`Test`) resources. This basic structure looks like:
|
||||
|
||||
::
|
||||
|
||||
resourceGenerators in Compile <+= <your Task[Seq[File]] here>
|
||||
resourceGenerators in Compile += <task of type Seq[File]>.taskValue
|
||||
|
||||
For example, assuming a method `def makeSomeResources(base: File): Seq[File]`,
|
||||
|
||||
::
|
||||
|
||||
resourceGenerators in Compile <+= Def.task {
|
||||
resourceGenerators in Compile += Def.task {
|
||||
makeSomeResources( (resourceManaged in Compile).value / "demo")
|
||||
}
|
||||
}.taskValue
|
||||
|
||||
As a specific example, the following generates a properties file containing the application name and version:
|
||||
|
||||
::
|
||||
|
||||
resourceGenerators in Compile <+= {
|
||||
resourceGenerators in Compile += Def.task {
|
||||
val file = (resourceManaged in Compile).value / "demo" / "myapp.properties"
|
||||
val contents = "name=%s\nversion=%s".format(name.value,version.value)
|
||||
IO.write(file, contents)
|
||||
Seq(file)
|
||||
}
|
||||
}.taskValue
|
||||
|
||||
Change `Compile` to `Test` to make it a test resource. Normally, you would only want to generate resources when necessary and not every run.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue