I'm trying to write plugin with tasks within plugin scope (for example: "my-plugin:update"). This is my sample code (sbt 0.13.0):
import sbt._
import Keys._
object MyPlugin extends Plugin
{
lazy val conf = config("my-plugin")
val update = taskKey[Unit]("Wow!.") in conf
override lazy val settings = inConfig(conf)(Seq(
update := println("wow")
))
}
but when I'm trying to use this plugin I got this error:
AttributeKey ID collisions detected for: 'update' (sbt.Task[Unit], sbt.Task[sbt.UpdateReport])
Is it possible or not to define task only within plugin scope without collisions?
A quick answer to your question is, no you can't define a task key with the name "update"
.
To work around this issue, those who participated in Plugin namespacing: A good solution? coauthored Plugins Best Practices, which still stands today. See Avoid namespace clashes section:
Avoid namespace clashes
Sometimes, you need a new key, because there is no existing sbt key. In this case, use a plugin-specific prefix, both in the (string) key name used in the sbt namespace and in the Scala val. There are two acceptable ways to accomplish this goal.
Just use a val prefix
package sbtobfuscate object Plugin extends sbt.Plugin { val obfuscateStylesheet = settingKey[File]("Obfuscate stylesheet") }
In this approach, every val starts with obfuscate. A user of the plugin would refer to the settings like this:
obfuscateStylesheet := ...
Use a nested object
package sbtobfuscate object Plugin extends sbt.Plugin { object ObfuscateKeys { val stylesheet = SettingKey[File]("obfuscateStylesheet") } }
In this approach, all non-common settings are in a nested object. A user of the plugin would refer to the settings like this:
import ObfuscateKeys._ // place this at the top of build.sbt stylesheet := ...
I think the choice depends your preference and the number of keys you are going to define. For sbt-buildinfo I've opted to use "Just use a val prefix" approach for the sake of simplicity, but for any other plugins I've gone for "Use a nested object" approach including sbt-assembly.
For more details, see also Mark's post to the thread:
:3. Related, configurations are the wrong axis to group settings for a plugin. Use the main task of your plugin (say 'assembly') to do this. Otherwise I can only easily use your plugin once per project. This may be fine for some plugins, but it is unnecessarily limiting.
:4. Scopes are not namespaces. For any given key name, there can only be one type. That is, you cannot have:
SettingKey[Int]("value")
and
SettingKey[Double]("value")
Putting keys in nested objects only helps when you have the same key/type defined by two different Plugins and so at the Scala identifier level, it is ambiguous:
object A extends Plugin { val a = SettingKey[Int]("a") } object B extends Plugin { val a = SettingKey[Int]("a") } // ambiguous in a build.sbt a := 3
The proper solution is a) reusing built-in keys and b) defining a common library of keys. Keys are the interfaces of the settings system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With