diff --git a/main/settings/KeyMacro.scala b/main/settings/KeyMacro.scala new file mode 100644 index 000000000..7d7c2ee6e --- /dev/null +++ b/main/settings/KeyMacro.scala @@ -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")`.""") + "" + } + } +} \ No newline at end of file diff --git a/main/settings/Structure.scala b/main/settings/Structure.scala index 7e8b88f47..ea071e143 100644 --- a/main/settings/Structure.scala +++ b/main/settings/Structure.scala @@ -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] +} diff --git a/main/settings/src/test/scala/UsageTest.scala b/main/settings/src/test/scala/UsageTest.scala index 91906fdbc..e9484cc10 100644 --- a/main/settings/src/test/scala/UsageTest.scala +++ b/main/settings/src/test/scala/UsageTest.scala @@ -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 diff --git a/src/sphinx/Detailed-Topics/Artifacts.rst b/src/sphinx/Detailed-Topics/Artifacts.rst index 0eca6f8b6..59a391a0b 100644 --- a/src/sphinx/Detailed-Topics/Artifacts.rst +++ b/src/sphinx/Detailed-Topics/Artifacts.rst @@ -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: diff --git a/src/sphinx/Detailed-Topics/Best-Practices.rst b/src/sphinx/Detailed-Topics/Best-Practices.rst index ecb9dbd36..abd9a2269 100644 --- a/src/sphinx/Detailed-Topics/Best-Practices.rst +++ b/src/sphinx/Detailed-Topics/Best-Practices.rst @@ -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") diff --git a/src/sphinx/Detailed-Topics/Classpaths.rst b/src/sphinx/Detailed-Topics/Classpaths.rst index cac5e11ce..0ef80c777 100644 --- a/src/sphinx/Detailed-Topics/Classpaths.rst +++ b/src/sphinx/Detailed-Topics/Classpaths.rst @@ -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") diff --git a/src/sphinx/Detailed-Topics/Tasks.rst b/src/sphinx/Detailed-Topics/Tasks.rst index be4fa3fde..80b01d765 100644 --- a/src/sphinx/Detailed-Topics/Tasks.rst +++ b/src/sphinx/Detailed-Topics/Tasks.rst @@ -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``. diff --git a/src/sphinx/Extending/Input-Tasks.rst b/src/sphinx/Extending/Input-Tasks.rst index 599c11034..a4d18b642 100644 --- a/src/sphinx/Extending/Input-Tasks.rst +++ b/src/sphinx/Extending/Input-Tasks.rst @@ -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 /project/Build.scala or in /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 `_ applied to diff --git a/src/sphinx/Extending/Plugins-Best-Practices.rst b/src/sphinx/Extending/Plugins-Best-Practices.rst index 4250604d8..76a327241 100644 --- a/src/sphinx/Extending/Plugins-Best-Practices.rst +++ b/src/sphinx/Extending/Plugins-Best-Practices.rst @@ -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 diff --git a/src/sphinx/Extending/Plugins.rst b/src/sphinx/Extending/Plugins.rst index b8fa7d9f3..993f9df3d 100644 --- a/src/sphinx/Extending/Plugins.rst +++ b/src/sphinx/Extending/Plugins.rst @@ -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 diff --git a/src/sphinx/Getting-Started/Custom-Settings.rst b/src/sphinx/Getting-Started/Custom-Settings.rst index 4eb07797f..dd938e3f1 100644 --- a/src/sphinx/Getting-Started/Custom-Settings.rst +++ b/src/sphinx/Getting-Started/Custom-Settings.rst @@ -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 diff --git a/src/sphinx/Getting-Started/Full-Def.rst b/src/sphinx/Getting-Started/Full-Def.rst index 5e792c95d..83077e913 100644 --- a/src/sphinx/Getting-Started/Full-Def.rst +++ b/src/sphinx/Getting-Started/Full-Def.rst @@ -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()) diff --git a/src/sphinx/Getting-Started/Library-Dependencies.rst b/src/sphinx/Getting-Started/Library-Dependencies.rst index 3aa2e0eb5..f0267042d 100644 --- a/src/sphinx/Getting-Started/Library-Dependencies.rst +++ b/src/sphinx/Getting-Started/Library-Dependencies.rst @@ -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. diff --git a/src/sphinx/Getting-Started/More-About-Settings.rst b/src/sphinx/Getting-Started/More-About-Settings.rst index d5930a72a..92cb22ac7 100644 --- a/src/sphinx/Getting-Started/More-About-Settings.rst +++ b/src/sphinx/Getting-Started/More-About-Settings.rst @@ -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.) diff --git a/src/sphinx/faq.rst b/src/sphinx/faq.rst index 7f1ba2438..07abb09c4 100644 --- a/src/sphinx/faq.rst +++ b/src/sphinx/faq.rst @@ -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 ` 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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~