Add Initialize[Task[T]].taskValue: Task[T] to allow selecting the Task for use by *Generators. Fixes #866.

This commit is contained in:
Mark Harrah 2013-11-24 18:24:15 -05:00
parent 1d9b44d5d7
commit fcb35f3b8f
4 changed files with 39 additions and 26 deletions

View File

@ -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

View File

@ -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)

View File

@ -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]

View File

@ -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.