Plugin namespacing: A good solution?

221 views
Skip to first unread message

Josh Suereth

unread,
Sep 16, 2011, 9:49:17 AM9/16/11
to simple-b...@googlegroups.com
Hey guys,

Based on our discussions on plugins and namespaces, I worked a new idea I had into a plugin.   Here it is:



The basic just is this:


object SuperSweetPlugin extends Plugin {

  object supersweet {
    val Config = config("supersweet") extend(Runtime)
lazy val newKey = SettingKey[File]("new-key") in Config
}
}

This has quite a few benefits:

  • Everything for your plugin is hidden in the <plugin> nested object (supersweet).
  • Keys can be referneced from the nested object *in the plugin config*
  • Defining settings for the plugin does not need to use inConfig
  • You can include references to settings in Keys in your namespace. e.g: lazy val newKey = Keys.sources in Config
In general, the usage pattern felt pretty natural and easy to me. I wanted to get the opinions of everyone here....

- Josh

Indrajit Raychaudhuri

unread,
Sep 16, 2011, 10:16:29 AM9/16/11
to simple-b...@googlegroups.com
Hey Josh,

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.


eugene yokota

unread,
Sep 16, 2011, 10:36:15 AM9/16/11
to simple-b...@googlegroups.com
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.

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

Heiko Seeberger

unread,
Sep 16, 2011, 10:37:53 AM9/16/11
to simple-b...@googlegroups.com
Nice. What's the difference to using a separate object (SuperSweet) on the same level as SuperSweetPlugin? Like in sbteclipse?

Heiko

--

Heiko Seeberger
Twitter: hseeberger
Company: Typesafe - Enterprise-Grade Scala from the Experts
Author of Durchstarten mit Scala, a German tutorial-style Scala book

eugene yokota

unread,
Sep 16, 2011, 10:52:33 AM9/16/11
to simple-b...@googlegroups.com
1. This puts the key in the config, so

    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

Doug Tangren

unread,
Sep 16, 2011, 10:52:56 AM9/16/11
to simple-b...@googlegroups.com

-Doug Tangren
http://lessis.me


On Fri, Sep 16, 2011 at 10:36 AM, eugene yokota <eed3...@gmail.com> wrote:
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.



I've been starting to follow  a few new patterns which `feel` like they are starting to clean some things up

1. remove redundany in package plugin naming. Its annoying to say foo.FooPlugin.fooSettings

so now I start saying foo.Plugin.options. I now options may not be the best name but it feels redundant to say x.xPlugin.xsettings

2. keep your keys in a separate Keys object (ala sbt.Keys). I think this makes things a little cleaner when reading code.

[1]: https://github.com/softprops/jot/blob/master/src/main/scala/jot.scala

Josh Suereth

unread,
Sep 16, 2011, 11:45:00 AM9/16/11
to simple-b...@googlegroups.com
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?  

- Josh

P.S. Did you try the NYAN_ENABLED growl plugin branch? 

--

Doug Tangren

unread,
Sep 16, 2011, 11:50:43 AM9/16/11
to simple-b...@googlegroups.com
On Fri, Sep 16, 2011 at 11:45 AM, Josh Suereth <joshua....@gmail.com> wrote:
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?  

- Josh

P.S. Did you try the NYAN_ENABLED growl plugin branch? 


lol. I did. I have not forgotten. I have 2 or 3 new plugins I'm about to publish this weekend. I just can't help myself.
 

Josh Suereth

unread,
Sep 16, 2011, 12:45:02 PM9/16/11
to simple-b...@googlegroups.com
I tried to write up the complete motivation for this mechanism here: http://suereth.blogspot.com/2011/09/sbt-and-plugin-design.html

Brian Clapper

unread,
Sep 16, 2011, 1:13:41 PM9/16/11
to simple-b...@googlegroups.com
On 09/16/2011 12:45 PM, Josh Suereth wrote:
> I tried to write up the complete motivation for this mechanism here:
> http://suereth.blogspot.com/2011/09/sbt-and-plugin-design.html

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

Doug Tangren

unread,
Sep 17, 2011, 2:19:28 PM9/17/11
to simple-b...@googlegroups.com
If you name the inner object named what you would have previously named the package after, what is the recommended name for the package?

package foo

object ModuleA {
  ...
}

object ModuleB {
  ...
}

object MyPlugin extends sbt.Plugin {
  object foo {
    // awkward referencing objects from the package foo here :/
  }
}


seems confusing. is the new recommended practiced to put everything that wasn't bundled directly in the plugin before in a global namespace of to shovel it all in object MyPlugin and treat object MyPlugin if it were a package that needs to have its contents declared in one file?

-Doug Tangren
http://lessis.me


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

Doug Tangren

unread,
Sep 17, 2011, 2:57:57 PM9/17/11
to simple-b...@googlegroups.com
I did a trial run with a very basic plugin [1] and it wasn't that bad of a transition

What's kind of funny though is that since the inner object is automatically in scope, when I declare my .sbt settings for the plugin it appears as if I am referencing a package since the convention was to use an all lowercase name for the object

// new recommended design (np is an inner obj)

seq(np.settings:_*)

(np.Keys.defaults in np.Config) <<= (np.Keys.defaults in np.Config)(d =>
  d.copy(org="me.lessis", version="0.1.0-SNAPSHOT")
)

this is essentially what I had before

// previous design (np is a package)

// settings were auto-mixed

(np.Keys.defaults in Np) <<= (np.Keys.defaults in Np)(d =>
  d.copy(org="me.lessis", version="0.1.0-SNAPSHOT")
)

Are their any known caveats with this pattern I should know about before converting all my plugins?

I've got a busy weekend!

On a side note, it kind of feels like we are reinventing something there. I know the idea was to get away from class hierarchies and mixins from the 0.7 era, but now I see we are having come up with schemes emulate classes and traits to make settings hierarchical, and now, even emulate a package system.




-Doug Tangren
http://lessis.me


Mark Harrah

unread,
Sep 17, 2011, 3:17:45 PM9/17/11
to simple-b...@googlegroups.com
On 9/16/11, Josh Suereth <joshua....@gmail.com> wrote:
> I tried to write up the complete motivation for this mechanism here:
> http://suereth.blogspot.com/2011/09/sbt-and-plugin-design.html

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.

Doug Tangren

unread,
Sep 17, 2011, 3:48:12 PM9/17/11
to simple-b...@googlegroups.com

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.


+1

 
2. Only exposing keys already scoped defeats the purpose of scopes and
introduces another syntax for a special case.  

I'm spending some time today branching plugins I'm working on to lean in this new direction and I get that feeling too. We are not proving the "Scala is Simple" argument :)
 
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.


Another point that I was going to follow up on but you put it much better than I could
 

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

One thing I haven't figured out though in doing that is how do you define a description specific to your version of the key? Say, if I wanted to reuse the existing `compile` key for coffeescript how would I enable coffeescript specific documentation for the key already defined in sbt.Keys?
 
 
Keys are the interfaces of the settings system.

Yes!
 
 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.

NOO!! Keep them concise!
 
 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.


Is putting keys in another object module also an option to the general collision problem? I started doing this with some of my plugins because they were starting to feel cluttered. I am guessing thats the general Idea behind sbt.Keys and sbt.Defaults. I starting adding my own separate Key's module [1] to contain them.

That would require someone to use the object as a namespace to remove any key ambiguity.

myplugin.Keys.a := 1

This is inline with what you mentioned above about the use of settings that are provided outside the context of an object that extends sbt.Plugin

We should have a panel/workshop on the topic of sbt plugins and best practices at the next nescala conf. Every time I think I've got it, I feel like I have to spend time re-writing plugins according to new conventions that come up on the list. I want to work on new stuff!

Josh Suereth

unread,
Sep 17, 2011, 5:26:55 PM9/17/11
to simple-b...@googlegroups.com
https://github.com/softprops/np/blob/master/src/main/scala/np.scala#L11

This 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 Config
       val 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.

- Josh


--

Doug Tangren

unread,
Sep 17, 2011, 5:43:41 PM9/17/11
to simple-b...@googlegroups.com
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#L11

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

Don't get me wrong, I'll for best practices.
 
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 Config
       val settings = inConfig(Config)(Defaults.settings)
    }
  }
}

 
I ended up doing something like that on an innerobj branch 


It feels weird having an object with the same name as the package, but it works.
 

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.


Yea, agreed. 

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.

Josh Suereth

unread,
Sep 17, 2011, 5:53:09 PM9/17/11
to simple-b...@googlegroups.com
On Sat, Sep 17, 2011 at 5:43 PM, Doug Tangren <d.ta...@gmail.com> wrote:


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#L11

This 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

Hey, I'm not hating, I *want* to use default too!  The issue is that even if you namespace in a config, it's the fact that your "defaults" type is *different* than mine (checked with Manifests) that makes it blow up at runtime.  If you define defaults with type Foo, I *have* to use type Foo as well.  Config doesn't namespace that away for us.  There is no namespace for Keys in that sense...  One name has one type.   Not entirely a bad thing if there were other namespaces (a.la. packages.....).   This is why I thought using Configuration (or perhaps another axis in a modified Scope) would be a way to add to the namespace of a Key in such a way that you and I can both define a "defaults" key with different type...  Or at least, mine would be namespaced to be not-the-same as yours....
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....


- Josh

Brian Clapper

unread,
Sep 17, 2011, 5:57:48 PM9/17/11
to simple-b...@googlegroups.com
On 09/17/2011 05:26 PM, Josh Suereth wrote:
> https://github.com/softprops/np/blob/master/src/main/scala/np.scala#L11
>
> This 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.

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.

Doug Tangren

unread,
Sep 17, 2011, 6:08:27 PM9/17/11
to simple-b...@googlegroups.com

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


\me nudges Mark  

Doug Tangren

unread,
Sep 17, 2011, 6:16:18 PM9/17/11
to simple-b...@googlegroups.com
Ding! This just popped up in my head too. 

I'll sometimes see end-user posts about a particular plugin come across this list with questions that have more to do with the plugin than sbt itself. If we go the route of breaking up the list, it may be worth while to have an sbt-plugin or sbt-plugin-user list too. Not sure if that would be going too far, but it would keep questions about sbt in one list and sbt-plugins in another separated. That may also help Mark out as he's reading every single one of these posts even though some have more to do with a target plugin than sbt. Just food for thought.

Brian Clapper

unread,
Sep 17, 2011, 6:22:18 PM9/17/11
to simple-b...@googlegroups.com

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.

eugene yokota

unread,
Sep 17, 2011, 6:30:03 PM9/17/11
to simple-b...@googlegroups.com
On Sat, Sep 17, 2011 at 3:17 PM, Mark Harrah <dmha...@gmail.com> wrote:
> 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")

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

eugene yokota

unread,
Sep 17, 2011, 8:02:48 PM9/17/11
to simple-b...@googlegroups.com
On Sat, Sep 17, 2011 at 5:26 PM, Josh Suereth <joshua....@gmail.com> wrote:
> 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...

+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

Josh Suereth

unread,
Sep 19, 2011, 8:51:58 AM9/19/11
to simple-b...@googlegroups.com
If the shell used object identitifes like build.sbt, we might be able to *just* use object identity for keys and drop the whole "name" business....

Still, that's a hefty change.   Would love to hear what mark thinks.

Joachim Hofer

unread,
Sep 19, 2011, 11:16:08 AM9/19/11
to simple-build-tool
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.

Cheers
- joachim

Doug Tangren

unread,
Sep 19, 2011, 11:39:39 AM9/19/11
to simple-b...@googlegroups.com
On Mon, Sep 19, 2011 at 11:16 AM, Joachim Hofer <jmhofer...@johoop.de> wrote:
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.


I think that the whole idea behind settings is that they provided a uniform interface for both core sbt behavior and extension behavior. The introduction of scopes seemed like a way to namespace keys but I think things get confusing when people talk about keys because they conflate  the term with both the scala identifier and the string key name used in the repl. I think at some point there was confusion around the idea of scopes enabling custom return types for a given key name when prefixed with a configs name. This will currently result in conflict if the bound return types are not the same.

foo:bar

baz:bar

If foo and baz were config identifiers defined in different plugins which both defined their own bar key name with different types there would actually be a collision from the repls perspective.

With regard to auto-prefixing key names, what we wanted to get away from was having long prefixes to identify tasks like we had in 0.7

foo-bar
baz-bar

That style prohibited the reuse of the same key in different contexts (scopes). Reuse of key names is one of the fundamental ideas in the new setting system. Reuse of key names reduces duplication and adds flexibility

(compile in Main)
(compile in Test)

With the idea that someone could extend the context of compile in their own config

(compile in Foo)


The problem I think we are trying to resolve is the isolation and removal of  cases where one key can unintentionally affect the usage and or definition of another key based on name alone.

Doug Tangren

unread,
Sep 21, 2011, 11:31:28 PM9/21/11
to simple-b...@googlegroups.com
Did we ever reach a consensus on this thread? I've got a few plugins that I'd like to do a release on and would like to follow what ever is the best recommended practice. I could go one way or the other but if the inner object idiom wil save some collision grief Ill take that path.

Also I'd like to get some clarification on 

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


Up until now I've been running with the assumption that the following is a best practice

val Mine = config("mine")

val mySettings: Seq[Setting[_]] = inConfig(Mine)(Seq(
  // most config scoped settings here
  myScopedSetting <<= some binding
)) ++ Seq(
   // few global settings here
   myMainSetting <<= (myScopedSetting in Mine).identity

Is this the wrong pattern to follow?

-Doug Tangren
http://lessis.me


eugene yokota

unread,
Sep 22, 2011, 12:04:59 AM9/22/11
to simple-b...@googlegroups.com
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.

-eugene

Doug Tangren

unread,
Sep 22, 2011, 12:43:53 AM9/22/11
to simple-b...@googlegroups.com
On Thu, Sep 22, 2011 at 12:04 AM, eugene yokota <eed3...@gmail.com> wrote:
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.


I was hoping for the bearer of good news :/

 I re-read the plugin section, with a focus on what mark mentioned above and his extending Plugin [1] just for the sake of extending Plugin statement now makes a little more sense. All it buys you is literally the auto import (lexical scoping of keys) and auto-mixing of settings. I think the way its used currently, myself included, can be confusing for end users.

This actually tripped me up for a bit yesterday where I (thought) configured a plugin and included its settings and it took me a bit to realize that the reason why I wasn't able to resolve the plugins settings was that I was including ThatPlugin.settings:_* instead of the custom settings method the plugin defined. It this is a bit of a bastardization to extend an interface with one method and leave that method set to the default when really you just want something like a tagging interface, sans behavior, for what we are currently using it for. I'd propose something like object MyPlugin extends Imported or something similar or just remove the settings method from the base Plugin trait. It's really only there for plugins that want the auto mix style settings which we have learned in practice is the minority of plugins. This is also mentioned in the plugin docs as something you should be careful with because it talks more control away from the build author which can cause unintended surprises.

What I am more interested in now is this notion of scoping a setting by task. I can conceptually understand grouping settings in a config. In practice, we've been doing this by using a config conventionally named after the plugin. I realize this doesn't solve the key name problem but I'm trying to focus on understand one design problem at a time. I feel confortable saying (key in AConfig) where AConfig can be his or her plugin but I am not yet sure I conceptually understand what is ment by scoping by a task. I re-skimmed through Defaults.scala to see what Mark (the ultimate source of what path to follow) does for sbt's own settings and came across inTask [2]. Im not quite sure I really understand what it means for a setting to be in a task besides my understand of resolving settings via map and applying them to a task. Can someone provide some insight? I've yet to see inTask come up in plugin examples.



Jason Zaugg

unread,
Sep 22, 2011, 5:09:24 AM9/22/11
to simple-b...@googlegroups.com
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.

-jason 


Josh Suereth

unread,
Sep 22, 2011, 8:15:17 AM9/22/11
to simple-b...@googlegroups.com
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.
To view this discussion on the web visit https://groups.google.com/d/msg/simple-build-tool/-/nl6oDWHffusJ.

Jason Zaugg

unread,
Sep 22, 2011, 10:31:27 AM9/22/11
to simple-b...@googlegroups.com
In the absense of a central registry for the key prefixes, we could provide a means to resolve clashes in the names of keys defined in two plugins that chose the same prefix. (Clashes in the scala identifiers for these can be resolved with qualification or renamed imports).

  trait PrefixedPlugin {
     def prefix: String
     def prefixedSettingKey[X: Manifest](name: String, desc: String = "") = SettingKey[X](name, string)
  }

  trait MyPlugin extends PrefixedPlugin {
    def prefix = "my"
    val myKey = prefixedSettingKey("key") 
    // or maybe, val key = ...
  }

  object MyPlugin extends MyPlugin

If a clash needs to be resolved:

  object RenamedMyPlugin extends MyPlugin { def prefix = "your" }

The question of how often this problem will arise, and when it does, how Joe Builder will discover such a feature, remain unanswered.

-jason

Josh Suereth

unread,
Sep 22, 2011, 10:48:51 AM9/22/11
to simple-b...@googlegroups.com
I think our #1 issue as plugin developers is to aim for consistency.   If what we do is consistent, it seriously helps people picking up SBT.   I don't really think we're there yet, and I'd like to see this change, hence my efforts to make a simple way for us to stay consistent.

I'd be willing to use the PrefixedPlugin idea if we all decide to go with this convention.

- Josh

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

Brian Clapper

unread,
Sep 22, 2011, 11:34:59 AM9/22/11
to simple-b...@googlegroups.com
On 09/22/2011 12:04 AM, eugene yokota wrote:
> 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.

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

Brian Clapper

unread,
Sep 22, 2011, 11:36:10 AM9/22/11
to simple-b...@googlegroups.com
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?
--
-Brian

Brian Clapper, http://www.clapper.org/bmc/
Marshall's generalized iceberg theorem: 7/8ths of everything cannot be seen.

Brian Clapper

unread,
Sep 22, 2011, 11:37:04 AM9/22/11
to simple-b...@googlegroups.com
On 09/22/2011 10:31 AM, Jason Zaugg wrote:

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

eugene yokota

unread,
Sep 22, 2011, 11:48:14 AM9/22/11
to simple-b...@googlegroups.com
Try this:

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

Brian Clapper

unread,
Sep 22, 2011, 12:56:19 PM9/22/11
to simple-b...@googlegroups.com
On 09/22/2011 11:48 AM, eugene yokota wrote:
> Try this:
>
> 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.

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.

Jason Zaugg

unread,
Sep 22, 2011, 6:16:20 PM9/22/11
to simple-b...@googlegroups.com
On Thursday, September 22, 2011 5:36:10 PM UTC+2, Brian Clapper wrote:
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?

The string label for the key should be unique, not just the qualified identifier that is used to refer to it. It's basically the same situation as:

  val key1 = "key"
  val key2 = "key"
  Map(key1 => 1, key2 => 2) // clobber!

-jason

eugene yokota

unread,
Sep 22, 2011, 11:51:07 PM9/22/11
to simple-b...@googlegroups.com
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

Doug Tangren

unread,
Sep 23, 2011, 12:01:25 AM9/23/11
to simple-b...@googlegroups.com
On Thu, Sep 22, 2011 at 11:51 PM, eugene yokota <eed3...@gmail.com> wrote:
Would it be crazy if I suggested we prefix only the key name, but not
the Scala identifier?

Well, that's just crazy talk! :P (j/k)
 

   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?



I wouldn't be opposed to this kind of thing. It may feel a little awkward before with what Mark mentioned in his last point though. I may be misinterpreting that though. For now though I just want to come to a consensus so we can all provide some kind of consistency between plugins.

Are we dropping plugin configs?


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

eugene yokota

unread,
Sep 23, 2011, 1:18:31 AM9/23/11
to simple-b...@googlegroups.com
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.

-eugene

Doug Tangren

unread,
Sep 23, 2011, 1:23:05 AM9/23/11
to simple-b...@googlegroups.com

On Fri, Sep 23, 2011 at 1:18 AM, eugene yokota <eed3...@gmail.com> wrote:
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.


ok. sounds good to me.

Jason Zaugg

unread,
Sep 23, 2011, 9:27:06 AM9/23/11
to simple-b...@googlegroups.com
On Friday, September 23, 2011 7:18:31 AM UTC+2, eugene yokota wrote:
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.

scope:key(for task)

-jason

eugene yokota

unread,
Sep 23, 2011, 10:08:24 AM9/23/11
to simple-b...@googlegroups.com
Does that work when for task scopes?

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.

Josh Suereth

unread,
Sep 23, 2011, 10:12:44 AM9/23/11
to simple-b...@googlegroups.com
You probably want `show test(for obfuscate)`

SBT will handle task scope just fine.   There's still some oddities if you want build-wide config.  I'll be following up my earlier blog post with another in the series of SBT best practices, bascially renigging most of my earlier conclussions for new ones.

In any case, scoping per task certainly does work and from the command line.   Just remember that:

key in Scope(project, task, config, ???)   translates into:

project/config:key(for task)

eugene yokota

unread,
Sep 23, 2011, 10:17:03 AM9/23/11
to simple-b...@googlegroups.com
On Fri, Sep 23, 2011 at 10:12 AM, Josh Suereth <joshua....@gmail.com> wrote:
> You probably want `show test(for obfuscate)`

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

Brian Clapper

unread,
Sep 23, 2011, 10:36:38 AM9/23/11
to simple-b...@googlegroups.com
Since I can't keep track of where we've landed, can we summarize? Here's my
understanding. Please correct where I'm wrong.

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

eugene yokota

unread,
Sep 23, 2011, 10:49:35 AM9/23/11
to simple-b...@googlegroups.com
I am also proposing

c) A non-prefixed Scala val with a prefixed key

   object FooPlugin extends sbt.Plugin {
     val jarName: SettingKey[String]("foo-jar-name")
   }

-eugene

Josh Suereth

unread,
Sep 23, 2011, 10:57:15 AM9/23/11
to simple-b...@googlegroups.com
I don't think we should drop a prefixed scala-val if we're prefixing the key.  Seems like we're just asking for confusion...

I would be cool with:

object FooPlugin extends sbt.Plugin {
   object foo {
      val jarName = SettingKey[String]("foo-jar-name")
   }
}

It's verbose, but the pseudo-namespace for foo is duplciated in Scala and the raw string.

Brian Clapper

unread,
Sep 23, 2011, 10:58:51 AM9/23/11
to simple-b...@googlegroups.com
On 09/23/2011 10:57 AM, Josh Suereth wrote:
> I don't think we should drop a prefixed scala-val if we're prefixing the key.
> Seems like we're just asking for confusion...
>
> I would be cool with:
>
> object FooPlugin extends sbt.Plugin {
> object foo {
> val jarName = SettingKey[String]("foo-jar-name")
> }
> }
>
> It's verbose, but the pseudo-namespace for foo is duplciated in Scala and the
> raw string.
>
> On Fri, Sep 23, 2011 at 10:49 AM, eugene yokota <eed3...@gmail.com
> <mailto:eed3...@gmail.com>> wrote:
>
> I am also proposing
>
> c) A non-prefixed Scala val with a prefixed key
>
> object FooPlugin extends sbt.Plugin {
> val jarName: SettingKey[String]("foo-jar-name")
> }

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.

eugene yokota

unread,
Sep 23, 2011, 11:06:44 AM9/23/11
to simple-b...@googlegroups.com
From my original email:

> 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

Josh Suereth

unread,
Sep 23, 2011, 11:07:17 AM9/23/11
to simple-b...@googlegroups.com
+ 10.  Let's get a page started for this.   If nothing else, I think we should try to keep the plugins as consistent as possible.

On Fri, Sep 23, 2011 at 11:00 AM, Brian Clapper <brian....@gmail.com> wrote:
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.

--
-Brian

Brian Clapper, http://www.clapper.org/bmc/
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.

Josh Suereth

unread,
Sep 23, 2011, 11:08:28 AM9/23/11
to simple-b...@googlegroups.com
-1 for the import aliasing.   Doesn't really follow principle of least surprise for users.   I can imagine the ML traffic for that.

Brian Clapper

unread,
Sep 23, 2011, 11:12:54 AM9/23/11
to simple-b...@googlegroups.com
On 09/23/2011 11:07 AM, Josh Suereth wrote:
> + 10. Let's get a page started for this. If nothing else, I think we should
> try to keep the plugins as consistent as possible.

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.

Brian Clapper

unread,
Sep 23, 2011, 11:13:30 AM9/23/11
to simple-b...@googlegroups.com
On 09/23/2011 11:08 AM, Josh Suereth wrote:
> -1 for the import aliasing. Doesn't really follow principle of least
> surprise for users. I can imagine the ML traffic for that.

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/

eugene yokota

unread,
Sep 23, 2011, 11:19:19 AM9/23/11
to simple-b...@googlegroups.com
You'd have both `import sbtfoo.FooPlugin._` and `import
sbtfoo.{FooPlugin => f}` available.

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

Brian Clapper

unread,
Sep 23, 2011, 11:32:24 AM9/23/11
to simple-b...@googlegroups.com
On 09/23/2011 11:19 AM, eugene yokota wrote:
> You'd have both `import sbtfoo.FooPlugin._` and `import
> sbtfoo.{FooPlugin => f}` available.
>
> 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"

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

Brian Clapper

unread,
Sep 23, 2011, 11:38:24 AM9/23/11
to simple-b...@googlegroups.com
On 09/23/2011 11:07 AM, Josh Suereth wrote:
> + 10. Let's get a page started for this. If nothing else, I think we should
> try to keep the plugins as consistent as possible.

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

unread,
Sep 23, 2011, 11:00:31 AM9/23/11
to simple-b...@googlegroups.com
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.
--
-Brian

Brian Clapper, http://www.clapper.org/bmc/

Doug Tangren

unread,
Sep 23, 2011, 12:06:39 PM9/23/11
to simple-b...@googlegroups.com
man, I barely get time to track these threads during the day and it takes me all night to catch up. + 500 for a new wiki page. I can't keep up with this stuff during the day.

-Doug Tangren
http://lessis.me


Brian Clapper

unread,
Sep 23, 2011, 12:15:05 PM9/23/11
to simple-b...@googlegroups.com
On 09/23/2011 12:06 PM, Doug Tangren wrote:
> man, I barely get time to track these threads during the day and it takes me
> all night to catch up. + 500 for a new wiki page. I can't keep up with this
> stuff during the day.

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.

Paul Phillips

unread,
Sep 23, 2011, 12:25:42 PM9/23/11
to simple-b...@googlegroups.com
On Fri, Sep 23, 2011 at 9:15 AM, Brian Clapper <brian....@gmail.com> wrote:
> Josh and I have been madly editing it. Hopefully, it makes sense so far.
>
> Now I have to update my plugins. Again.

You perform a hugely valuable service in exercising the latest
designs, so I hope you don't feel like the time is wasted.

Brian Clapper

unread,
Sep 23, 2011, 12:32:37 PM9/23/11
to simple-b...@googlegroups.com

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"

eugene yokota

unread,
Sep 23, 2011, 12:43:57 PM9/23/11
to simple-b...@googlegroups.com
Added the following section, since I thought `Defaults.scala`/sbt-xjc
idea is worth elevating to best-practices status:

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

eugene yokota

unread,
Sep 23, 2011, 1:05:40 PM9/23/11
to simple-b...@googlegroups.com
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.

-eugene

Doug Tangren

unread,
Sep 23, 2011, 1:17:43 PM9/23/11
to simple-b...@googlegroups.com
On Fri, Sep 23, 2011 at 1:05 PM, eugene yokota <eed3...@gmail.com> wrote:
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.

Im going to follow mark's lead and put my keys in a module called Keys under my plugin's package
 

eugene yokota

unread,
Sep 23, 2011, 1:21:58 PM9/23/11
to simple-b...@googlegroups.com
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

Doug Tangren

unread,
Sep 23, 2011, 1:27:31 PM9/23/11
to simple-b...@googlegroups.com
On Fri, Sep 23, 2011 at 1:21 PM, eugene yokota <eed3...@gmail.com> wrote:
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.



That's true. Maybe I could just  claim Keyz.

eugene yokota

unread,
Sep 23, 2011, 1:45:28 PM9/23/11
to simple-b...@googlegroups.com
or, like Mark said, "don't extend Plugin."
Non-command plugins are recommended not to use `settings` anyway, so
all it's providing is auto-import.

-eugene

Josh Suereth

unread,
Sep 23, 2011, 1:52:40 PM9/23/11
to simple-b...@googlegroups.com
-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.

Now, I think we need a section to describe *good* inConfig usage, YES.  I like what you added but I don't think we need to change what's there.   The example is actually fine as it is.

Josh Suereth

unread,
Sep 23, 2011, 1:54:34 PM9/23/11
to simple-b...@googlegroups.com
Yeah, I don't think it helps creating an object named exactly `Keys`.   You could go for PluginKeys.   We still have to figure out what we're going to do about shared Keys.

e.g.  I want to make a "xsbt-site-plugin" with generic site creation stuff that "xsbt-ghpages-plugin" would extend to provide the publishing mechanism....

--

eugene yokota

unread,
Sep 23, 2011, 2:11:30 PM9/23/11
to simple-b...@googlegroups.com
original:

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 => ... }
)

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

Josh Suereth

unread,
Sep 23, 2011, 2:16:09 PM9/23/11
to simple-b...@googlegroups.com
From what I see in the source code, this should work just fine.  I'm writing a test currently to try it out...


--

Josh Suereth

unread,
Sep 23, 2011, 2:17:27 PM9/23/11
to simple-b...@googlegroups.com
Ah nevermind.  I have my own 'inConfig' that overwrites these....

Josh Suereth

unread,
Sep 23, 2011, 2:20:30 PM9/23/11
to simple-b...@googlegroups.com
Ok, so given my new-found knowledge of why I made my own inConfig, perhaps we should change the example to use inConfig *and* have task-scoped keys....

We probably need another few words around how/when to extend configurations so you don't have to reprovide all settings in a new config.

- Josh



--

Josh Suereth

unread,
Sep 23, 2011, 3:02:43 PM9/23/11
to simple-b...@googlegroups.com

OK, wiki is fixed based on your suggestion.

Brian Clapper

unread,
Sep 23, 2011, 3:09:22 PM9/23/11
to simple-b...@googlegroups.com
This page is shaping up. I think we need a few more examples, though. For
instance, I think the assertion "Provide unscoped settings in a sequence."
should be followed by an example of how one defines such a thing. There's an
example of use, within a build.sbt, but not of how to define it in the plugin
source. A new plugin author might find that useful.
--
-Brian

Brian Clapper, http://www.clapper.org/bmc/
Anyone can hold the helm when the sea is calm.
-- Publilius Syrus

eugene yokota

unread,
Sep 24, 2011, 6:24:04 AM9/24/11
to simple-b...@googlegroups.com
+1

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 yokota

unread,
Sep 24, 2011, 9:55:46 AM9/24/11
to simple-b...@googlegroups.com
sbt-assembly is now best-practice compliant in master:
- https://github.com/eed3si9n/sbt-assembly/blob/master/src/main/scala/sbtassembly/Plugin.scala

-eugene

Doug Tangren

unread,
Sep 24, 2011, 10:01:38 AM9/24/11
to simple-b...@googlegroups.com
On Sat, Sep 24, 2011 at 9:55 AM, eugene yokota <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. Thanks for putting up that summary page on the wiki guys. That helps a lot. 

Brian Clapper

unread,
Sep 24, 2011, 10:22:14 AM9/24/11
to simple-b...@googlegroups.com
On 09/24/2011 10:01 AM, Doug Tangren wrote:
>
>
> On Sat, Sep 24, 2011 at 9:55 AM, eugene yokota <eed3...@gmail.com
> <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.
--
-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

Doug Tangren

unread,
Sep 24, 2011, 12:37:04 PM9/24/11
to simple-b...@googlegroups.com
On Sat, Sep 24, 2011 at 10:22 AM, Brian Clapper <brian....@gmail.com> wrote:
On 09/24/2011 10:01 AM, Doug Tangren wrote:


On Sat, Sep 24, 2011 at 9:55 AM, eugene yokota <eed3...@gmail.com
<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.


Here is my immediate usability concern. I'm porting coffeescripted-sbt over now and realized immediately there is now no way to discover, from the command line, what keys are available for a given plugin which is a HUGE burden on the build user. I use tab completion in the sbt console to discover just about everything. Before this was simple when using configs,

coffee:<tab> BAM! all the keys associated with my plugin.

Now, there is no way from the command line to discover keys when targeting a particular plugin

sources(for coffee) is an example. I really how we aren't planning on asking users to go through all

> <tab>
Display all 177 possibilities? (y or n) 

and append (for <tab>) to discover keys.

This makes me sad :/

This also forces me to rethink and other plugin I started for generating setting documentation [1] which was largely easily scannable based on configs, in particular plugin configs. since there is little way to tie a "main task" back to a plugin it makes it very difficult to document the provider of the setting.

I'll continue on the path of consistency for the community's sake, but I'd like to hear your thoughts on the issue of setting discoverability from a build users perspective.


 

Doug Tangren

unread,
Sep 24, 2011, 1:06:05 PM9/24/11
to simple-b...@googlegroups.com
Also, is there a trick to defining builtin keys in scope of a task w/o clobbering the builtings

Given the key definition (reusing builtin key unmanaged-sources)

...
unmanagedSources in coffee <<= coffeeSourcesTask,
...


a project configured to use the plugin is now unable to resolve the unmanagedSources not scoped to coffee

> show unmanaged-sources
[error] No such setting/task: {file:/Users/dougtangren/Desktop/tests/coffeetest/}default-c572a5/*:unmanaged-sources
[error] show unmanaged-sources
[error]                       ^

^ huh? ^ this should still be global scope right??

> show unmanaged-sources(for coffee)
[info] List(/Users/dougtangren/Desktop/tests/coffeetest/src/main/coffee/foo.coffee)
[success] Total time: 0 s, completed Sep 24, 2011 12:56:14 PM


In a project not using the plugin, this key is globally scoped

> show unmanaged-sources
[info] List(/Users/dougtangren/Desktop/tests/heroictest/src/main/scala/app.scala, /Users/dougtangren/Desktop/tests/heroictest/src/main/scala/Page.scala)
[success] Total time: 0 s, completed Sep 24, 2011 12:55:29 PM


When we were using configs, I could define keys with someKey in MyConfig ... which would protect against clobbering the key in global scope.

Does changing the axis from config to task not protect against that or am I missing something?
 

Josh Suereth

unread,
Sep 24, 2011, 1:42:17 PM9/24/11
to simple-b...@googlegroups.com

Coffee script compilation seems worthy enough of a configuration just like Compile and Test.

On Sep 24, 2011 12:37 PM, "Doug Tangren" <d.ta...@gmail.com> wrote:
> On Sat, Sep 24, 2011 at 10:22 AM, Brian Clapper <brian....@gmail.com>wrote:
>
>> On 09/24/2011 10:01 AM, Doug Tangren wrote:
>>
>>>
>>>
>>> On Sat, Sep 24, 2011 at 9:55 AM, eugene yokota <eed3...@gmail.com
>>> <mailto:eed3...@gmail.com>> wrote:
>>>
>>> sbt-assembly is now best-practice compliant in master:
>>> -

philcali

unread,
Sep 25, 2011, 3:06:34 PM9/25/11
to simple-build-tool
Great job to all who were involved in writing the "Plugins Best
Practices". I believe this is a huge step in getting everyone on the
same page.

Just to make sure I fully understand the proposals, I'll play back
what I'm taking away from the document and the message list here:

1. Re-use keys as much as possible.
2. Namespace keys with either an inner object or string prefixes.
3. Keep raw settings / tasks simple enough, where they can be thrown
into multiple configurations at ease.
4. __Use__ custom configs when defining new build behaviors or
concepts.
5. Do __not__ use custom configs when simply extending an existing
behavior or concept.

When reading the best practice document, I find 3 and 4 confuse me,
because I feel the idea of a "new concept" is a little subjective. I
think I understand the goal, but most of the plugin's I see out there
define a new concept. I suppose the author would go with his/her best
judgment, and ask others what they think.

Let me know if I'm missing a glaring point, or completely
misunderstood others!

On Sep 24, 12:42 pm, Josh Suereth <joshua.suer...@gmail.com> wrote:
> Coffee script compilation seems worthy enough of a configuration just like
> Compile and Test.
> On Sep 24, 2011 12:37 PM, "Doug Tangren" <d.tang...@gmail.com> wrote:> On Sat, Sep 24, 2011 at 10:22 AM, Brian Clapper <brian.clap...@gmail.com
> >wrote:
>
> >> On 09/24/2011 10:01 AM, Doug Tangren wrote:
>
> >>> On Sat, Sep 24, 2011 at 9:55 AM, eugene yokota <eed3s...@gmail.com
> >>> <mailto:eed3s...@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<
>
> https://github.com/eed3si9n/sbt-assembly/blob/master/src/main/scala/s...

eugene yokota

unread,
Sep 25, 2011, 3:16:04 PM9/25/11
to simple-b...@googlegroups.com
I don't think I am still completely solid on the point either,
but check out my updated unofficial guide (still a beta) on the config:
- http://eed3si9n.com/sbt-010-guide

-eugene

philcali

unread,
Sep 25, 2011, 3:25:56 PM9/25/11
to simple-build-tool
I meant 4 and 5 up there, but I think you understood me.

This is a very good writeup, thank you. Now it's time for me to start
converting my plugins to the best practices!

On Sep 25, 2:16 pm, eugene yokota <eed3s...@gmail.com> wrote:
> I don't think I am still completely solid on the point either,
> but check out my updated unofficial guide (still a beta) on the config:
> -http://eed3si9n.com/sbt-010-guide

eugene yokota

unread,
Sep 25, 2011, 4:18:21 PM9/25/11
to simple-b...@googlegroups.com
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.

-eugene

Doug Tangren

unread,
Sep 25, 2011, 7:30:47 PM9/25/11
to simple-b...@googlegroups.com
A note of possible confusion in the plugin best practices doc.

"settings are for commands" sounds potentially as if it's conflating the the differentiation between settings and commands. Settings aren't commands. Commands are for commands. However, `commands` the _name_ is a setting [1]. Commands are so similar to settings though, so it's almost confusing for both the build user and the plugin author on when to use them.  I think that should be changed to "`settings` is for auto included settings" because that's really exactly what that method does in the context of a plugin.


-Doug Tangren
http://lessis.me


Brian Clapper

unread,
Sep 26, 2011, 9:34:05 AM9/26/11
to eugene yokota, simple-b...@googlegroups.com
On 09/25/2011 04:18 PM, eugene yokota wrote:
> 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.

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

Doug Tangren

unread,
Sep 26, 2011, 9:51:20 AM9/26/11
to simple-b...@googlegroups.com
On Mon, Sep 26, 2011 at 9:34 AM, Brian Clapper <brian....@gmail.com> wrote:
On 09/25/2011 04:18 PM, eugene yokota wrote:
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.

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.


I spent the entire weekend refactoring a bunch of sbt plugins into the new best practices and the end result was having the the ideal piping under the covers but had a huge impact on usability from the repl for me.

the net result is the user has to already know the keys you are using ahead of time and have to use a more awkard syntax for executing commands

key(for task)

I spend a day saturday factoring the plugins into the best practices design then a day sunday playing the role of the user and kept getting frustrated with the key(for task) repl syntax.

We talked about this on irc and there was a suggestion to change the way the repl resolution for keys to something more along the lines of

task::key which would make this new best practices design 100% more usable from a build user's perspective.

At the moment, most plugin authors used configs to achieve this repl behavior to make it easy for users to discover keys.

I'm afraid that, while I fully support the best practices design, the trade off on usability in the repl is outweighing the benefit of the ideal best practice design and that I may end out sticking with configs regardless for now.

I'd like to hear other plugin authors' ideas and experience after converting their plugins to being task scoped rather than config scoped.
 

eugene yokota

unread,
Sep 26, 2011, 10:00:23 AM9/26/11
to simple-b...@googlegroups.com
As noted in irc, in my opinion there are two issues at play:
1. namespacing fix via prefixed keys
2. config-scoping vs task-scoping

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

Josh Suereth

unread,
Sep 26, 2011, 10:59:50 AM9/26/11
to simple-b...@googlegroups.com
I don't think we can ignore users.   The current 'best practice' gives the best flexibility to users, but we loose REPL auto-discovery and add a bit of confusion.  I'm trying to figure out if there's any way I can simplify this for users.  Right now I alias all my keys for usage in build.sbt (https://github.com/jsuereth/xsbt-ghpages-plugin/blob/refactor-plugins/src/main/scala/SitePlugin.scala)

  (Aside:  Does anyone know who stole the 'site' key?)

I think the idea of task-scoped keys completing differently in the repl is nice.  What's the full proposal for that?

currently:

{<build>}<project>/<config>:<key>(for <task-scope>)

future:

???
<task-scope>::{<build>}<project>/<config>(for <task-scope>)

Where <task-scope> has the same parsing as any setting....

Not sure that syntax buys us a lot, but it may help....

eugene yokota

unread,
Sep 26, 2011, 11:08:23 AM9/26/11
to simple-b...@googlegroups.com
1. https://github.com/harrah/xsbt/issues/202
2. Added opt-out clause in best practices regarding task-scoping until
#202 is done.
3. Given 2., I'd like to make the criterion for creating new scope in
the best practices as "If you need a different set of scala files or
jars, it's a config" and rewrite the section to reflect it.

-eugene

Doug Tangren

unread,
Sep 26, 2011, 12:05:50 PM9/26/11
to simple-b...@googlegroups.com

The :: will make all the difference for me.

Josh Suereth

unread,
Sep 26, 2011, 1:08:20 PM9/26/11
to simple-b...@googlegroups.com
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:


And let me know if I'm going insane :)

- Josh

--

Brian Clapper

unread,
Sep 26, 2011, 5:47:28 PM9/26/11
to simple-b...@googlegroups.com
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.

Josh Suereth

unread,
Sep 26, 2011, 8:44:15 PM9/26/11
to simple-b...@googlegroups.com
https://github.com/jsuereth/scala-arm/blob/master/build.sbt#L46

That's in a project build, but imagine if that line was in *your* plugin :)  The way things are set up, both plugins should be included and the default settings will ensure no explosions if the build uses the LWM plugin and not the site plugin.

- Josh

On Mon, Sep 26, 2011 at 5:47 PM, Brian Clapper <brian....@gmail.com> wrote:
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.
Reply all
Reply to author
Forward
0 new messages