taskKey,settingKey,inputKey macros to get name from the defining val

This commit is contained in:
Mark Harrah 2012-12-02 03:17:20 -05:00
parent 538f687208
commit 3bff14d77a
15 changed files with 101 additions and 94 deletions

View File

@ -0,0 +1,43 @@
package sbt
package std
import language.experimental.macros
import scala.reflect._
import reflect.macros._
object KeyMacro
{
def settingKeyImpl[T: c.WeakTypeTag](c: Context)(description: c.Expr[String]): c.Expr[SettingKey[T]] =
keyImpl[T, SettingKey[T]](c) { (name, mf) =>
c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice) }
}
def taskKeyImpl[T: c.WeakTypeTag](c: Context)(description: c.Expr[String]): c.Expr[TaskKey[T]] =
keyImpl[T, TaskKey[T]](c) { (name, mf) =>
c.universe.reify { TaskKey[T](name.splice, description.splice)(mf.splice) }
}
def inputKeyImpl[T: c.WeakTypeTag](c: Context)(description: c.Expr[String]): c.Expr[InputKey[T]] =
keyImpl[T, InputKey[T]](c) { (name, mf) =>
c.universe.reify { InputKey[T](name.splice, description.splice)(mf.splice) }
}
def keyImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: Context)(f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S]): c.Expr[S] =
{
import c.universe.{Apply=>ApplyTree,_}
val enclosingValName = definingValName(c)
val name = c.Expr[String]( Literal(Constant(enclosingValName)) )
val mf = c.Expr[Manifest[T]](c.inferImplicitValue( weakTypeOf[Manifest[T]] ) )
f(name, mf)
}
def definingValName(c: Context): String =
{
import c.universe.{Apply=>ApplyTree,_}
val methodName = c.macroApplication.symbol.name.decoded
val enclosingTrees = c.asInstanceOf[reflect.macros.runtime.Context].callsiteTyper.context.enclosingContextChain.map(_.tree.asInstanceOf[Tree])
enclosingTrees match {
case vd @ ValDef(_, name, _, _) :: ts => name.decoded
case _ =>
c.error(c.enclosingPosition, s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""")
"<error>"
}
}
}

View File

@ -492,3 +492,16 @@ object SettingKey
def local[T: Manifest]: SettingKey[T] = apply[T](AttributeKey.local[T])
}
object settingKey
{
def apply[T](description: String): SettingKey[T] = macro std.KeyMacro.settingKeyImpl[T]
}
object taskKey
{
def apply[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
}
object inputKey
{
def apply[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
}

View File

@ -24,19 +24,19 @@ object Assign
import Def.{Initialize,macroValueT,parserToInput}
// import UseTask.{x,y,z,a,set,plain}
val ak = TaskKey[Int]("a")
val bk = TaskKey[Seq[Int]]("b")
val ck = SettingKey[File]("c")
val sk = TaskKey[Set[_]]("s")
val ak = taskKey[Int]("a")
val bk = taskKey[Seq[Int]]("b")
val ck = settingKey[File]("c")
val sk = taskKey[Set[_]]("s")
val ik = InputKey[Int]("i")
val isk = InputKey[String]("is")
val mk = SettingKey[Int]("m")
val tk = TaskKey[Int]("t")
val name = SettingKey[String]("name")
val dummyt = TaskKey[complete.Parser[String]]("dummyt")
val dummys = SettingKey[complete.Parser[String]]("dummys")
val dummy3 = SettingKey[complete.Parser[(String,Int)]]("dummy3")
val ik = inputKey[Int]("i")
val isk = inputKey[String]("is")
val mk = settingKey[Int]("m")
val tk = taskKey[Int]("t")
val name = settingKey[String]("name")
val dummyt = taskKey[complete.Parser[String]]("dummyt")
val dummys = settingKey[complete.Parser[String]]("dummys")
val dummy3 = settingKey[complete.Parser[(String,Int)]]("dummy3")
val tsk: complete.Parser[TaskKey[String]] = ???
/* def azy = sk.value

View File

@ -93,14 +93,14 @@ For example:
::
val myTask = taskKey[Unit]("My task.")
myTask := {
val (art, file) = packagedArtifact.in(Compile, packageBin).value
println("Artifact definition: " + art)
println("Packaged file: " + file.getAbsolutePath)
}
where ``val myTask = TaskKey[Unit]``.
Defining custom artifacts
=========================
@ -138,6 +138,8 @@ generates the artifact:
::
val myImageTask = taskKey[File](...)
myImageTask := {
val artifact: File = makeArtifact(...)
artifact
@ -145,8 +147,6 @@ generates the artifact:
addArtifact( Artifact("myproject", "image", "jpg"), myImageTask )
where ``val myImageTask = TaskKey[File](...)``.
``addArtifact`` returns a sequence of settings (wrapped in a
`SettingsDefinition <../../api/#sbt.Init$SettingsDefinition>`_).
In a full build configuration, usage looks like:

View File

@ -92,11 +92,10 @@ For example:
::
lazy val makeFile = TaskKey[File]("makeFile")
lazy val makeFile = taskKey[File]("Creates a file with some content.")
// define a task that creates a file,
// writes some content, and returns the File
// The write is completely
makeFile := {
val f: File = file("/tmp/data.txt")
IO.write(f, "Some content")

View File

@ -63,7 +63,7 @@ to the list of main source generators (``sourceGenerators in Compile``).
To insert a named task, which is the better approach for plugins:
::
val mySourceGenerator = TaskKey[Seq[File]](...)
val mySourceGenerator = taskKey[Seq[File]](...)
mySourceGenerator in Compile :=
generate( (sourceManaged in Compile).value / "some_directory")

View File

@ -57,7 +57,7 @@ build.sbt
::
val hello = TaskKey[Unit]("hello", "Prints 'Hello World'")
val hello = taskKey[Unit]("Prints 'Hello World'")
hello := println("hello world!")
@ -80,7 +80,7 @@ project/Build.scala
scalaVersion := "2.9.0-1"
)
val hello = TaskKey[Unit]("hello", "Prints 'Hello World'")
val hello = taskKey[Unit]("Prints 'Hello World'")
val helloTask = hello := {
println("Hello World")
@ -103,21 +103,17 @@ To declare a new task, define a val of type ``TaskKey``, either in ``.sbt`` or `
::
val sampleTask = TaskKey[Int]("sampleTask")
val sampleTask = taskKey[Int]("A sample task.")
The name of the ``val`` is used when referring to the task in Scala
code. The string passed to the ``TaskKey`` method is used at runtime,
such as at the command line. By convention, both the Scala identifier
and the runtime identifier are camelCase. The type parameter
passed to ``TaskKey`` (here, ``Int``) is the type of value produced by
the task.
code and at the command line. The string passed to the ``TaskKey`` method is a description of the task. The type parameter passed to ``TaskKey`` (here, ``Int``) is the type of value produced by the task.
We'll define a couple of other of tasks for the examples:
::
val intTask = TaskKey[Int]("intTask")
val stringTask = TaskKey[String]("stringTask")
val intTask = taskKey[Int]("An int task")
val stringTask = taskKey[String]("A string task")
The examples themselves are valid entries in a ``build.sbt`` or can be
provided as part of a sequence to ``Project.settings`` (see
@ -224,9 +220,9 @@ would go in a ``Build`` object in a ``.scala`` file or directly in a ``.sbt`` fi
::
val unitTask = TaskKey[Unit]("unitTask")
val intTask = TaskKey[Int]("intTask")
val stringTask = TaskKey[String]("stringTask")
val unitTask = taskKey[Unit]("A side-effecting task.")
val intTask = taskKey[Int]("A task that returns an integer.")
val stringTask = taskKey[String]("A task that returns String")
The examples themselves are valid settings in a ``build.sbt`` file or as
part of a sequence provided to ``Project.settings``.

View File

@ -14,12 +14,12 @@ Input Keys
A key for an input task is of type ``InputKey`` and represents the input
task like a ``SettingKey`` represents a setting or a ``TaskKey``
represents a task. Define a new input task key using the
``InputKey.apply`` factory method:
``inputKey.apply`` factory method:
::
// goes in <base>/project/Build.scala or in <base>/build.sbt
val demo = InputKey[Unit]("demo")
val demo = inputKey[Unit]("A demo input task.")
The definition of an input task is similar to that of a normal task, but it can
also use the result of a `Parser </Detailed-Topics/Parsing-Input>`_ applied to

View File

@ -39,7 +39,7 @@ Where possible, reuse them in your plugin. For instance, don't define:
::
val sourceFiles = SettingKey[Seq[File]]("sourceFiles")
val sourceFiles = settingKey[Seq[File]]("Some source files")
Instead, simply reuse SBT's existing ``sources`` key.
@ -58,7 +58,7 @@ Just use a ``val`` prefix
package sbtobfuscate
object Plugin extends sbt.Plugin {
val obfuscateStylesheet = SettingKey[File]("obfuscateStylesheet")
val obfuscateStylesheet = settingKey[File]("Obfuscate stylesheet")
}
In this approach, every ``val`` starts with ``obfuscate``. A user of the
@ -168,7 +168,7 @@ Configurations should *not* be used to namespace keys for a plugin. e.g.
::
val Config = config("my-plugin")
val pluginKey = SettingKey[String]("pluginSpecificKey")
val pluginKey = settingKey[String]("A plugin specific key")
val settings = pluginKey in Config // DON'T DO THIS!
Playing nice with configurations

View File

@ -269,8 +269,8 @@ An example of a typical plugin:
{
// configuration points, like the built in `version`, `libraryDependencies`, or `compile`
// by implementing Plugin, these are automatically imported in a user's `build.sbt`
val newTask = TaskKey[Unit]("newTask")
val newSetting = SettingKey[String]("newSetting")
val newTask = taskKey[Unit]("A new task.")
val newSetting = settingKey[String]("A new setting.")
// a group of settings ready to be added to a Project
// to automatically add them, do

View File

@ -24,8 +24,8 @@ Some examples from `Keys <../../sxr/Keys.scala.html>`_:
::
val scalaVersion = SettingKey[String]("scalaVersion", "The version of Scala used for building.")
val clean = TaskKey[Unit]("clean", "Deletes files produced by the build, such as generated sources, compiled classes, and task caches.")
val scalaVersion = settingKey[String]("The version of Scala used for building.")
val clean = taskKey[Unit]("Deletes files produced by the build, such as generated sources, compiled classes, and task caches.")
The key constructors have two string parameters: the name of the key
(``"scalaVersion"``) and a documentation string

View File

@ -103,10 +103,10 @@ The following two files illustrate. First, if your project is in
object HelloBuild extends Build {
val sampleKeyA = SettingKey[String]("sampleKeyA", "demo key A")
val sampleKeyB = SettingKey[String]("sampleKeyB", "demo key B")
val sampleKeyC = SettingKey[String]("sampleKeyC", "demo key C")
val sampleKeyD = SettingKey[String]("sampleKeyD", "demo key D")
val sampleKeyA = settingKey[String]("demo key A")
val sampleKeyB = settingKey[String]("demo key B")
val sampleKeyC = settingKey[String]("demo key C")
val sampleKeyD = settingKey[String]("demo key D")
override lazy val settings = super.settings ++
Seq(sampleKeyA := "A: in Build.settings in Build.scala", resolvers := Seq())

View File

@ -86,7 +86,7 @@ or like this, where ``configuration`` is also a string:
::
val libraryDependencies = SettingKey[Seq[ModuleID]]("library-dependencies", "Declares managed dependencies.")
val libraryDependencies = settingKey[Seq[ModuleID]]("Declares managed dependencies.")
The ``%`` methods create ``ModuleID`` objects from strings, then you add
those ``ModuleID`` to ``libraryDependencies``.
@ -185,7 +185,7 @@ this:
::
val resolvers = SettingKey[Seq[Resolver]]("resolvers", "The user-defined additional resolvers for automatically managed dependencies.")
val resolvers = settingKey[Seq[Resolver]]("The user-defined additional resolvers for automatically managed dependencies.")
The ``at`` method creates a ``Resolver`` object from two strings.

View File

@ -197,8 +197,8 @@ Take these two keys (from `Keys <../../sxr/Keys.scala.html>`_):
::
val scalacOptions = TaskKey[Seq[String]]("scalac-options", "Options for the Scala compiler.")
val checksums = SettingKey[Seq[String]]("checksums", "The list of checksums to generate and to verify for dependencies.")
val scalacOptions = taskKey[Seq[String]]("Options for the Scala compiler.")
val checksums = settingKey[Seq[String]]("The list of checksums to generate and to verify for dependencies.")
(``scalacOptions`` and ``checksums`` have nothing to do with each other,
they are just two keys with the same value type, where one is a task.)

View File

@ -434,20 +434,12 @@ A basic run task is created by:
::
// this lazy val has to go in a full configuration
lazy val myRunTask = TaskKey[Unit]("myRunTask")
lazy val myRunTask = taskKey[Unit]("A custom run task.")
// this can go either in a `build.sbt` or the settings member
// of a Project in a full configuration
fullRunTask(myRunTask, Test, "foo.Foo", "arg1", "arg2")
or, if you really want to define it inline (as in a basic ``build.sbt``
file):
::
fullRunTask(TaskKey[Unit]("myRunTask"), Test, "foo.Foo", "arg1", "arg2")
If you want to be able to supply arguments on the command line, replace
``TaskKey`` with ``InputKey`` and ``fullRunTask`` with
``fullRunInputTask``. The ``Test`` part can be replaced with another
@ -463,42 +455,6 @@ in the scope. For example:
javaOptions in myRunTask += "-Xmx6144m"
How can I delegate settings from one task to another task?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Settings :doc:`scoped </Getting-Started/Scopes>` to one task can fall back to
another task if undefined in the first task. This is called delegation.
The following key definitions specify that settings for ``myRun``
delegate to ``aRun``
::
val aRun = TaskKey[Unit]("aRun", "A run task.")
// The last parameter to TaskKey.apply here is a repeated one
val myRun = TaskKey[Unit]("myRun", "Custom run task.", aRun)
In use, this looks like:
::
// Make the run task as before.
fullRunTask(myRun, Compile, "pkg.Main", "arg1", "arg2")
// If fork in myRun is not explicitly set,
// then this also configures myRun to fork.
// If fork in myRun is set, it overrides this setting
// because it is more specific.
fork in aRun := true
// Appends "-Xmx2G" to the current options for myRun.
// Because we haven't defined them explicitly,
// the current options are delegated to aRun.
// So, this says to use the same options as aRun
// plus -Xmx2G.
javaOptions in myRun += "-Xmx2G"
How should I express a dependency on an outside tool such as proguard?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~