lazy val newKey = SettingKey[File]("new-key") in Configobject SuperSweetPlugin extends Plugin {
object supersweet {val Config = config("supersweet") extend(Runtime)
This is awesome, super sweet!
While at this, one might also want to look at a concrete usage (from plugin users' point of view). So here goes the scala-arm build config which uses this plugin [1]. See the last 3 lines :)
[1] https://github.com/jsuereth/scala-arm/blob/master/build.sbt
- Indrajit
> --
> You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
> To post to this group, send email to simple-b...@googlegroups.com (mailto:simple-b...@googlegroups.com).
> To unsubscribe from this group, send email to simple-build-t...@googlegroups.com (mailto:simple-build-t...@googlegroups.com).
> For more options, visit this group at http://groups.google.com/group/simple-build-tool?hl=en.
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
To post to this group, send email to simple-b...@googlegroups.com.
To unsubscribe from this group, send email to simple-build-t...@googlegroups.com.
supersweet.newKey <<= target(_ / "foo.txt")
goes automatically into the scope.
2. sbt imports up until SuperSweetPlugin._, which likely is in some
package (e.g. com.typesafe.sbteclipse). Making it an inner object
makes supersweet available in build.sbt.
-eugene
I think it's awesome. Does everything it should while the code is super clean.This should be the canonical way of writing plugins going forward.
--
Hey Doug,I see where you're going with things. I like the idea of keeping keys separate to ease understanding. However, I still like the solution I posted slightly better from a user's perspective. There's less namespacing, I can choose whatever I want for the plugin and the user only needs to know *one* namespace (the nested object). It prevents leakage into build.sbt scope.Maybe we can come up with a good compromise?- JoshP.S. Did you try the NYAN_ENABLED growl plugin branch?
I've already converted all my plugins to use this new Suereth Awesome Sauce.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
How can you be two places at once when you're not anywhere at all?
-- Firesign Theater
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
To post to this group, send email to simple-build-tool@googlegroups.com.
To unsubscribe from this group, send email to simple-build-tool+unsubscribe@googlegroups.com.
Here are my thoughts in response to the general discussion about
plugins: Josh's post and the various threads. Obviously best
practices are evolving. If I've said something contrary in the past
to the comments below, point me to it and I'll say whether I think it
was wrong or if it was a different situation.
1. Extending Plugin gets you two things: auto-imports and
auto-injected settings. If you don't want that, don't extend Plugin.
It isn't required for implementing an sbt plugin. Your users then do:
// top of build.sbt or in a block, submit a patch if you want it
allowed standalone in the middle
import org.example.MyPlugin
MyPlugin.myKey := 1
It might be possible to add an AutoImportName interface that signals
sbt to only import the plugin name automatically and not wildcard
import its contents.
2. Only exposing keys already scoped defeats the purpose of scopes and
introduces another syntax for a special case. If I want to apply
your source generation plugin to generate both Compile and Test
sources, for example, you've made it difficult by prescoping your keys
by a configuration (and for most users, I expect it is enough work
they won't do it).
On syntax, there are three commonly used axes to scopes. Applying one
of the other two axes (the project or task axis) will require the 'in'
method. 'in' isn't going away, so special casing the configuration
axis only adds a concept to learn that won't scale.
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 have trouble justifying to yourself the effort of
factoring keys out into a library because key definitions are
syntactically brief, don't worry, we can make them more verbose. In
my opinion, having consistent keys and configurations
sbt-ecosystem-wide is important for end users. I don't want to have
to lookup what the type of 'sources' is in your plugin.
5. Don't add implicits in plugins just to have a more pleasing syntax.
In 0.11.0, I did a lot of work reducing implicits to make the
settings API documentation easier and the API itself more accessible
to new users. Syntactic implicits obfuscate the API and are hard to
remove later if you want to rework your plugin.
6. Reuse existing keys. As a user, this is a problem:
val sources = SettingKey[Seq[File]]("source-files")
Here I have a different Scala identifier name from the runtime name
(beyond the camel/dash that I know how to translate). Also, the Scala
name is the same as a built-in setting, but it isn't that built-in
setting. Either reuse the built-in setting or use sourceFiles as a
Scala identifier.
1. Extending Plugin gets you two things: auto-imports and
auto-injected settings. If you don't want that, don't extend Plugin.
It isn't required for implementing an sbt plugin. Your users then do:
// top of build.sbt or in a block, submit a patch if you want it
allowed standalone in the middle
import org.example.MyPlugin
MyPlugin.myKey := 1
It might be possible to add an AutoImportName interface that signals
sbt to only import the plugin name automatically and not wildcard
import its contents.
2. Only exposing keys already scoped defeats the purpose of scopes and
introduces another syntax for a special case.
If I want to apply
your source generation plugin to generate both Compile and Test
sources, for example, you've made it difficult by prescoping your keys
by a configuration (and for most users, I expect it is enough work
they won't do it).
On syntax, there are three commonly used axes to scopes. Applying one
of the other two axes (the project or task axis) will require the 'in'
method. 'in' isn't going away, so special casing the configuration
axis only adds a concept to learn that won't scale.
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
Keys are the interfaces of the settings system.
If you have trouble justifying to yourself the effort of
factoring keys out into a library because key definitions are
syntactically brief, don't worry, we can make them more verbose.
In
my opinion, having consistent keys and configurations
sbt-ecosystem-wide is important for end users. I don't want to have
to lookup what the type of 'sources' is in your plugin.
--
https://github.com/softprops/np/blob/master/src/main/scala/np.scala#L11This is my counter example. A 'defaults' key in a particular plugin. It can only have one type for the *entire* build across *all* plugins. We need to fix this.
Some kind of namespaceing. We have three places where namespaces occur:(1) Scala source files and build.sbt (use packages and objects)(2) Key-Type conflict resolution (use string name)(3) SBT command line (use project/config:key namespace)We should try to keep these consistent...In any case, as for my full response: most of your points are correct but they fail to recognize the power a best practice.
I don't want to solve every solution. SBT is flexible enough for that. The "Best Practice" should solve the common situations. If most plugin developes jumped at this style, it means it was simple enough and flexible enough to be a good means of unifying all our interfaces.
The key here is unity. The plugins we write *should* look and feel consistent. I think we need to work towards something better, but I think this is a first good step. You could easily add the flexibility you suggest by doing the following:package myplugin {object Keys {val mykey = TaskKey[Unit]("my-key")}object Defaults {val settings = Seq(lazy val mykey0 = mykey <<= ....}object MyPlugin extends Plugin {object myplugin {val Config = config("myplugin")val mykey = Keys.mykey in Configval settings = inConfig(Config)(Defaults.settings)}}}
This would also be an easy refactor from an existing plugin to *not* break existing projects and still allow the flexibility you suggest. We need to make Simple things Simple and complex things possible. Let's not ignore simplification because it's not a 100% solution. I'm trying to hide too much complexity of SBT from users of my plugin so they don't have to hit it all at once. Ease them into the advanced for when they actually need the advanced functionality.
On Sat, Sep 17, 2011 at 5:26 PM, Josh Suereth <joshua....@gmail.com> wrote:
https://github.com/softprops/np/blob/master/src/main/scala/np.scala#L11This is my counter example. A 'defaults' key in a particular plugin. It can only have one type for the *entire* build across *all* plugins. We need to fix this.Don't be hatin' on my keys! j/k the Defaults here is more like a configuration object intended for user-specific global settings. But I agree it sucks keys names are taken on a first come first serve basis. I try to be explicit now in specifying an `in SomeConfig` where I can
This is a *real* problem for me, as a plugin writer. Here's a related
scenario:
- I write two separate plugins.
- Each plugin re-uses some of the pre-defined keys (e.g., "sources"),
which is fine.
- Each plugin also wants to define, say, a "variables" setting. (This is
actually the case with two of my plugins.)
- Absent some sort of common, shared definition of "variables",
I'm now in a bind. If I want to mix both plugins into the same project,
SBT's auto-import will cause a clash on "targetDirectory".
Per Mark's earlier email, I'm left with several alternatives.
1. Don't extend SBT Plugin. Um... That's a rather big, not to mention
counterintuitive, hammer to use on this particular nail. Surely, Plugin
serves more purposes than just "do the auto-import thing". If not, then,
at the very least, it should be renamed--perhaps to AutoImportPlugin.
Besides, avoiding Plugin doesn't really solve the key re-use problem.
It merely avoids the automatic compiler barfage.
2. Reuse an existing key. Okay, that one doesn't apply here. There's no
existing key called "variables".
3. Create a library--a dependency, shared by both plugins--that defines
a common "variables" key. That works for me, but what about some other
guy, out there in the ether, who *also* wants a "variables" setting key?
It doesn't exactly scale.
The problem is that scopes are DSL-scoped, but they're not *language*
scoped. The concept of scoped keys is grand, but it's clashing with Scala's
notion of scope, and it's causing some very real problems.
Also, if we're going to define some common library of keys, how is *that*
going to be managed, so it doesn't get completely out of control?
Plugin authors need a more robust solution than that, I think.
My enthusiasm for Josh's namespace solution is threefold.
First, it works without introducing any changes to the core SBT. We can use
it now.
Second, I'm still not yet 100% convinced that the value of scoped keys
exceeds the difficulties of their implementation (the problems with the
clashes between SBT's notion of scoped keys and Scala's notion of scoped
identifiers, as well as #2 and #3, above).
Third, some form of namespacing solves a very real problem that I, as a
plugin author, have *right now*.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
There has been an alarming increase in the number of things you know nothing
about.
p.s. I wonder if now would be a good time to split the sbt mailing list into sbt-user and sbt-internal, ect. Some of these topics may confusing for frightening someone just starting with sbt. At its core, sbt is still very simple for end users. I don't want to scare away anyone with topics like this. What do you guys think? It would also help me make better gmail filters for the sbt list.I think that could be a very good thing. I know I'm very focused on SBT internals right now, based on minor frustrations of using it on projects....
As long as someone tells me where the -internals list is, I'm all for this.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Classified material requires proper storage.
This hurts. I was under the illusion that solving Scala identifier
namespace somehow solved the key identifier, but of course it doesn't.
Configuration may not be the best way to group keys, but I would like
some way for plugins to use general key names such as "default" and
"jar-name" without knowledge of each other.
> 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.
Are you referring to declaring keys under the config (, which we can't
do today), or grouping settings under `Assembly` config like (jarName
in Assembly etc..)? I think scoping provides great way to reuse keys,
and provide build authors to tweak the behavior for specific tasks
like (test in Assembly := {}). How does this relate to using the
plugin multiple times? In the current situation, wouldn't one need to
redefine an assembly-like task to achieve calling it twice?
Suppose the assembly-related settings were carried around by Assembly
configuration. Also suppose, that by extending Assembly the build user
can declare another configuration named Assembly2 with underlying
settings now scoped in Assembly2, then he could call the plugin twice
a slightly changed setting:
Assembly.jarName := "foo.jar"
Assembly2.jarName := "bar.jar"
-eugene
+1
In my opinion, plugin authors really have no business injecting global
keys into the k-v map, with a few exceptions of top-level tasks. The
configuration scoping looked as if it had changed the sbt 0.7 prefixed
def days, but really it didn't.
It seems to me, the only time a name of the key is relevant is when
they are called from the shell, or you're inspecting the key
dependencies. In other words:
object APlugin extends Plugin {
object A {
val a = SettingKey[Int]("e61bd7")
}
}
object BPlugin extends Plugin {
object B {
val a = SettingKey[String]("f0002b")
}
}
// not ambiguous in a build.sbt
A.a := 1
What about the shell? What if it just acted like build.sbt, using
Scala identifiers?
-eugene
Originally, my intuition was that the names would be the only thing
that matters for all the keys... - and imho they probably should be.
However, they'd need some kind of automatic prefix per plugin, if you
ask me, in order to solve the namespace problems.
Grouping keys under configuration, and pre-scoping them to a config, I
think was my suggestion. That point, also, is up in air since it's the
"wrong axis." I think that's what Mark means by "3.", not scoping in
general.
Brian's original point of the need of two separate plugins to claim
the same key still stands, but there's no clear solution here besides
the common jar workaround.
As a long-term idea, I suggested abandoning String key and using Scala
identifier in the shell, but I don't know what that entails in terms
of the implementation.
-eugene
One consensus (I think) is that putting things in an inner object
doesn't solve the namespace issue, because the keys are not
namespaced. If multiple plugins claimed the same key name with
different types, it will break.
Thus, the validity of Josh's method as it stand is questionable.
Grouping keys under configuration, and pre-scoping them to a config, I
think was my suggestion. That point, also, is up in air since it's the
"wrong axis." I think that's what Mark means by "3.", not scoping in
general.
Brian's original point of the need of two separate plugins to claim
the same key still stands, but there's no clear solution here besides
the common jar workaround.
As a long-term idea, I suggested abandoning String key and using Scala
identifier in the shell, but I don't know what that entails in terms
of the implementation.
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
To view this discussion on the web visit https://groups.google.com/d/msg/simple-build-tool/-/nl6oDWHffusJ.
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
To view this discussion on the web visit https://groups.google.com/d/msg/simple-build-tool/-/x7g3Ofcx8dgJ.
I don't think that's right. If two same-named keys are in two inner objects,
they're scoped differently by Scala, so there's no breakage. There's only
breakage if the inner object's contents are wildcard-imported.
> Thus, the validity of Josh's method as it stand is questionable.
>
> Grouping keys under configuration, and pre-scoping them to a config, I
> think was my suggestion. That point, also, is up in air since it's the
> "wrong axis." I think that's what Mark means by "3.", not scoping in
> general.
>
> Brian's original point of the need of two separate plugins to claim
> the same key still stands, but there's no clear solution here besides
> the common jar workaround.
This one still bugs me, I have to admit. It puts the whole idea of shared keys
in doubt.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
We can't schedule an orgy, it might be construed as fighting
-- Stanley Sutton
> In sbt-xjc [1], when I reused built in keys I scoped them with the `xjc` task;
> when I defined new keys I prefixed them with "xjc". [2]
>
> This allows users to run the plugin over sources in Test, Compile, or even
> their own Scopes.
>
> Of course, if another loaded plugin chooses the same prefix for custom keys
> there will be a clash.
Conceptually, how is this any different than putting the keys in an inner
object, and requiring that they be dereferenced via the inner object's name?
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Marshall's generalized iceberg theorem: 7/8ths of everything cannot be seen.
> The question of how often this problem will arise, and when it does, how Joe
> Builder will discover such a feature, remain unanswered.
I've already hit it.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
If you'll excuse me a minute, I'm going to have a cup of coffee.
-- broadcast from Apollo 11's LEM, "Eagle", to Johnson Space Center,
Houston, July 20, 1969, 7:27 P.M.
object APlugin extends Plugin {
object A {
val a = SettingKey[Int]("a")
}
}
object BPlugin extends Plugin {
object B {
val a = SettingKey[String]("a")
}
}
----
> set A.a := 1
[info] Reapplying settings...
[info] Set current project to default-b65acd (in build
file:/Users/x/work/helloworld/)
> set B.a := "foo"
[info] Reapplying settings...
[error] AttributeKey ID collisions detected for: 'a' (Int, java.lang.String)
[error] Use 'last' for the full log.
-eugene
Ugh.
I find it confusing that SBT's internal scoping rules at such variance with
Scala's natural scoping rules.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
A man forgives only when he is in the wrong.
On 09/22/2011 05:09 AM, Jason Zaugg wrote:> In sbt-xjc [1], when I reused built in keys I scoped them with the `xjc` task;
> when I defined new keys I prefixed them with "xjc". [2]
>
> This allows users to run the plugin over sources in Test, Compile, or even
> their own Scopes.
>
> Of course, if another loaded plugin chooses the same prefix for custom keys
> there will be a clash.Conceptually, how is this any different than putting the keys in an inner
object, and requiring that they be dereferenced via the inner object's name?
object AssemblyPlugin extends Plugin {
val a = SettingKey[(String, Int)]("assembly-a")
val jarName = SettingKey[String]("assembly-jar-name")
}
Assuming no one really changes their jar name from the shell, current
users can continue with their build without changing anything. Should
some other plugin come up with the same name, like
object ObfuscatePlugin extends Plugin {
val a = SettingKey[Int]("obfuscate-a")
val jarName = SettingKey[String]("obfuscate-jar-name")
}
We can stay out of each other's way by doing an aliased import:
import sbtassembly.{Plugin => a}
a.jarName in Assembly := "foo.jar"
Easy things easy, no?
-eugene
Would it be crazy if I suggested we prefix only the key name, but not
the Scala identifier?
object AssemblyPlugin extends Plugin {
val a = SettingKey[(String, Int)]("assembly-a")
val jarName = SettingKey[String]("assembly-jar-name")
}
Assuming no one really changes their jar name from the shell, current
users can continue with their build without changing anything. Should
some other plugin come up with the same name, like
object ObfuscatePlugin extends Plugin {
val a = SettingKey[Int]("obfuscate-a")
val jarName = SettingKey[String]("obfuscate-jar-name")
}
We can stay out of each other's way by doing an aliased import:
import sbtassembly.{Plugin => a}
a.jarName in Assembly := "foo.jar"
Easy things easy, no?
-eugene
On Thu, Sep 22, 2011 at 8:15 AM, Josh Suereth <joshua....@gmail.com> wrote:
> Without proper namespaces, I think these are probably the best we can do.
> Try to re-use keys from Keys.scala and if you define a new one, prefix it
> with the plugin.
> Also, for Config axes I feel like we're missing something for plugin-based
> configuration for *every* task in the plugin. This is what I was using the
> Config for, but as Mark mentioned it's not the original intention for that
> axis. It appears the best practice is actually to namespace the key-name
> itself and assign it in global.
> For some plugins, I think making your own Config still makes sense. In
> fact, the ghpages plugin I feel having a "Site" configuration makes sense.
> The danger I still worry about now is *how* do I define a Site config that
> others can use without requiring a deploy to ghpages.
> In any case, looks like we're back to square one with trying to define some
> kind of common pattern of use. I think we should adopt Jason's key
> definition strategy for now and see how far that gets us.
> - Josh
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
a.jarName := "foo.jar"
So Mark wrote "4. Scopes are not namespaces.", so I branched out the
whole config topic to another thread.
But... let me mention that if we do adopt use of task as scoping axis,
it might work out because I am not sure if there'a a way to access it
from the shell.
-eugene
For now we can assume we all use global scope:
a.jarName := "foo.jar"
So Mark wrote "4. Scopes are not namespaces.", so I branched out the
whole config topic to another thread.
But... let me mention that if we do adopt use of task as scoping axis,
it might work out because I am not sure if there'a a way to access it
from the shell.
For now we can assume we all use global scope:a.jarName := "foo.jar"
So Mark wrote "4. Scopes are not namespaces.", so I branched out the
whole config topic to another thread.But... let me mention that if we do adopt use of task as scoping axis,
it might work out because I am not sure if there'a a way to access it
from the shell.
object Plugin extends sbt.Plugin {
lazy val obfuscate = TaskKey[Seq[File]]("obfuscate")
lazy val a = SettingKey[Int]("obfuscate-a")
lazy val jarName = SettingKey[String]("obfuscate-jar-name")
}
----
> set test in obfuscate := {}
[info] Reapplying settings...
> show obfuscate:[tab]
{invalid input}
> obfuscate:test
[error] Not a valid key: obfuscate (similar: state, update)
-eugene
> --
> You received this message because you are subscribed to the Google Groups
> "simple-build-tool" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/simple-build-tool/-/gr4niKamr0EJ.
ah!
I did not interpret `(for ` and `)` as literal.
> test(for obfuscate)
[success] Total time: 0 s, completed Sep 23, 2011 10:16:25 AM
-eugene
1. Josh's trick is out:
object FooPlugin extends sbt.Plugin {
object Foo {
val Config = ...
val myKey: SettingKey[Whatever]("...") in Config
}
}
2. To avoid Scala name clashes, use existing keys and types, if possible.
If that's not possible, use either:
a) A prefixed name, to avoid clashes with other plugins:
val fooVariables: SettingKey[...]("foovars")
b) A namespacing object:
object Foo {
val variables: SettingKey[...]("foovars")
}
Regardless of (a) or (b), make sure the string name is unique.
Is that what we're suggesting?
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
If I had a hammer, I'd use it on Peter, Paul and Mary.
-- Howard Rosenberg
c) A non-prefixed Scala val with a prefixed key
object FooPlugin extends sbt.Plugin {
val jarName: SettingKey[String]("foo-jar-name")
}
-eugene
Eugene, your suggestion still leads to Scala import clashes, if another plugin
defined a jarName val.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Satire does not look pretty upon a tombstone.
> Should some other plugin come up with the same name, like
object ObfuscatePlugin extends Plugin {
val a = SettingKey[Int]("obfuscate-a")
val jarName = SettingKey[String]("obfuscate-jar-name")
}
> We can stay out of each other's way by doing an aliased import:
import sbtassembly.{Plugin => a}
a.jarName in Assembly := "foo.jar"
-eugene
So, when we have a good list of particulars, I propose posting it to the wiki. After all, it's a wiki, not a book; it can evolve. But, in the meantime, we'll have a place to go to refer to the (current) canonical plugin approach.
--
A model is an artifice for helping you convince yourself that you
understand more about a system than you do.
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
To post to this group, send email to simple-build-tool@googlegroups.com.
To unsubscribe from this group, send email to simple-build-tool+unsubscribe@googlegroups.com.
I'm starting one now. I'll post its location, so other can hack on my mistakes.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Even the smallest candle burns brighter in the dark.
I agree.
Also, I forget: Does an explicit aliased import override SBT's automatic
import of the plugin's namespace?
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
So for 99% of the keys that do not conflict, ppl can use plain keys like
test in xx := {}
jarName := "foo.jar"
Only when two plugins use the same Scala val, one would need to either
fully qualify the name or use import. I don't think that's that
difficult to get.
import sbtfoo.{FooPlugin => f}
sbtassembly.Plugin.jarName := "foo.jar"
f.jarName := "bar.jar"
-eugene
I'm with Josh on this one: While it's powerful, I think it's potentially
confusing to the user.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Angels we have heard on High/Tell us to go out and Buy.
-- Tom Lehrer
Okay, I've hacked together an initial Plugins Best Practices page. Pull out
your red pens, and have at it.
https://github.com/harrah/xsbt/wiki/Plugins-Best-Practices
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Zimmerman's Law of Complaints: Nobody notices when things go right.
Brian Clapper, http://www.clapper.org/bmc/
Josh and I have been madly editing it. Hopefully, it makes sense so far.
Now I have to update my plugins. Again.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Chisolm's First Corollary to Murphy's Second Law:
When things just can't possibly get any worse, they will.
You perform a hugely valuable service in exercising the latest
designs, so I hope you don't feel like the time is wasted.
I don't. This is useful stuff.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
"Yes, that was Richard Nixon. He used to be President. When he left the
White House, the Secret Service would count the silverware."
-- Woody Allen, "Sleeper"
--BEGIN--
### Playing nice with configurations
Whether you ship with a configuration or not, a plugin should strive
to support multiple configurations, including those created by the
build user.
Provide unscoped settings in a sequence. This allows the build user to
load the family of settings using arbitrary configuration:
```scala
seq(Project.inConfig(Test)(sbtFoo.Plugin.fooSettings0): _*)
```
Alternatively, one could provide a utility method to load settings in
a given configuration:
```scala
seq(fooSettingsInConfig(Test): _*)
```
--END--
I wanted to make sure we have a consensus on this, before I edit
val pluginTask = TaskKey[Unit]("plugin-awesome-task")
val pluginSettings: Seq[Setting[_]] = Seq(
sources in Compile in pluginTask <<= ...,
pluginTask <<= (sources in Compile in pluginTask) map { source => ... }
)
into
val pluginTask = TaskKey[Unit]("plugin-awesome-task")
val pluginSettings = inConfig(Compile)(pluginSettings0) ++ Seq(
pluginTask <<= (pluginTask in Compile).identity
)
val pluginSettings0: Seq[Setting[_]] = Seq(
pluginTask <<= (sources in pluginTask) map { source => ... },
sources in pluginTask <<= ...
)
-eugene
> --
> You received this message because you are subscribed to the Google Groups
> "simple-build-tool" group.
> To post to this group, send email to simple-b...@googlegroups.com.
> To unsubscribe from this group, send email to
> simple-build-t...@googlegroups.com.
I don't think inner object would work for me, since I can't think of a
good name for it without using implicits.
-eugene
Looks like unprefixing approach is unpopular, so I'll give up and use
the prefix.
I don't think inner object would work for me, since I can't think of a
good name for it without using implicits.
The concern is that we're not going to make identifier `Keys`
ambiguous among my Keys, your Keys, and Mark's sbt.Keys, requiring
import statements.
-eugene
I could buy that.
The concern is that we're not going to make identifier `Keys`
ambiguous among my Keys, your Keys, and Mark's sbt.Keys, requiring
import statements.
-eugene
--
so if you do
seq(Project.inConfig(Test)(pluginSettings): _*)
Wouldn't `pluginTask in Test` still hit `sources in Compile in
pluginTask` as opposed to `sources in Test in pluginTask`?
On Fri, Sep 23, 2011 at 1:52 PM, Josh Suereth <joshua....@gmail.com> wrote:
> -1 on that. I don't think inConfig makes sense here and if you need to
> inConfig, you can do that on the pluginSettings. It actually violates the
> one rule about creating Configs that Mark was trying to get across to me
> these past couple weeks.
-eugene
--
--
OK, wiki is fixed based on your suggestion.
Brian Clapper, http://www.clapper.org/bmc/
Anyone can hold the helm when the sea is calm.
-- Publilius Syrus
As I started on sbt-assembly, I can't in good conscience get myself to
rename `packageScala` to `assemblyPackageScala` (the first key in the
code), so I'm going to try the inner object `AssemblyKeys`.
-eugene
-eugene
sbt-assembly is now best-practice compliant in master:
- https://github.com/eed3si9n/sbt-assembly/blob/master/src/main/scala/sbtassembly/Plugin.scala
Same here. Thanks, Eugene, for the example.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Some people like my advice so much that they frame it upon the wall
instead of using it
-- Gordon R. Dickson
On 09/24/2011 10:01 AM, Doug Tangren wrote:
<mailto:eed3...@gmail.com>> wrote:
sbt-assembly is now best-practice compliant in master:
-
https://github.com/eed3si9n/sbt-assembly/blob/master/src/main/scala/sbtassembly/Plugin.scala
Awesome. I'm going to see what I can do to get mine up this weekend.
Same here. Thanks, Eugene, for the example.
Coffee script compilation seems worthy enough of a configuration just like Compile and Test.
-eugene
The builtin Compile, Test, and Runtime fit the rule.
The example listed in best practice, namely PDFPlugin does not, in my
opinion. If target needs to be scoped, you can easily do that with
task scoping as follows:
target in pdf <<= baseDirectory(_ / "mytarget" / "pdf")
This way the pdf document could be generated either on Compile config
or Test config.
The only example I can think of is a modified sbt-izpack, which I've
never used. If it could add more *scala* files included in the
installer, it'd satisfy the rule. It if it's merely reading yml, then
it won't.
-eugene
I have to think about this one.
There are still enough scopes running around here that I get confused
sometimes. For instance, there are the various configs. But you can also
"scope" a setting to a task, which may or may not be confusing to the user,
depending on how the plugin is defined.
I think we need to be more explicit on when to scope settings to what, but I
don't have it straight enough in my head yet to take a pass at such a section.
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
I profoundly believe it takes a lot of practice to become a moral slob.
-- William F. Buckley
On 09/25/2011 04:18 PM, eugene yokota wrote:I have to think about this one.
I'd like to propose a rule:
"If you need a different set of scala files or jars, it's a config"
The builtin Compile, Test, and Runtime fit the rule.
The example listed in best practice, namely PDFPlugin does not, in my
opinion. If target needs to be scoped, you can easily do that with
task scoping as follows:
target in pdf<<= baseDirectory(_ / "mytarget" / "pdf")
This way the pdf document could be generated either on Compile config
or Test config.
The only example I can think of is a modified sbt-izpack, which I've
never used. If it could add more *scala* files included in the
installer, it'd satisfy the rule. It if it's merely reading yml, then
it won't.
There are still enough scopes running around here that I get confused sometimes. For instance, there are the various configs. But you can also "scope" a setting to a task, which may or may not be confusing to the user, depending on how the plugin is defined.
I think we need to be more explicit on when to scope settings to what, but I don't have it straight enough in my head yet to take a pass at such a section.
The second point is more philosophical ("gives more flexibility"), and
has lower priority.
The first issue has more immediate need of fixing because keys can
clash any time and build users can't do anything about it.
Plugin authors can opt out of task-scoping until ## is implemented.
I personally don't feel the pain as much, because none of my plugins
are REPL heavy. ppl can continue to say "assembly" to make a jar.
-eugene
-eugene
--
Do you have a usage example?
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Dykstra's Law: Everybody is somebody else's weirdo.
On 09/26/2011 01:08 PM, Josh Suereth wrote:
Ok guys, another things to discuss: Overriding settings in Plugin.
I found a great use case. For the sbt-site-plugin I want other plugins (e.g.,
Clapper's LWM plugin) to be able to hook into site generation. I also want
plugins (like ghpages) to be able to *use* site generation. However, I don't
want LWM or ghpages to *have* to use site generation.
To do so, I have a setting in the sites plugin that can be imported into all
projects automagically without causing issues and these two plugins can get
their hooks in. If the user also makes use of the site plugin, VIOLA magic.
If not, no harm, no foul.
See:
https://github.com/jsuereth/xsbt-ghpages-plugin/blob/0.2/src/main/scala/SitePlugin.scala
And let me know if I'm going insane :)
Do you have a usage example?
--
-Brian
Brian Clapper, http://www.clapper.org/bmc/
Dykstra's Law: Everybody is somebody else's weirdo.
--
You received this message because you are subscribed to the Google Groups "simple-build-tool" group.
To post to this group, send email to simple-build-tool@googlegroups.com.
To unsubscribe from this group, send email to simple-build-tool+unsubscribe@googlegroups.com.