2

I'm writing an SBT Plugin that adds a Command and would like users to be able to configure this Command by setting variables in their build.sbt. What is the simplest way to achieve this?

Here is an simplified example of what the Plugin looks like:

import sbt.Keys._
import sbt._

object MyPlugin extends Plugin {

  override lazy val settings = Seq(commands += Command.args("mycommand", "myarg")(myCommand))

  def myCommand = (state: State, args: Seq[String]) => {

    //Logic for command...

    state
  }
}

I would like someone to be able to add the follow to their build.sbt file:

newSetting := "light"

How do I make this available as a String variable from inside the myCommand Command above?

theon
  • 14,170
  • 5
  • 51
  • 74

2 Answers2

6

Take a look at the example here: http://www.scala-sbt.org/release/docs/Extending/Plugins.html#example-plugin

In this example, a task and setting are defined:

val newTask = TaskKey[Unit]("new-task")
val newSetting = SettingKey[String]("new-setting")

val newSettings = Seq(
  newSetting := "test",
  newTask <<= newSetting map { str => println(str) }
)

A user of your plugin could then provide their own value for the newSetting setting in their build.sbt:

newSetting := "light"

EDIT

Here's another example, closer to what you're going for:

Build.scala:

import sbt._                                                
import Keys._                                               

object HelloBuild extends Build {                           

    val newSetting = SettingKey[String]("new-setting", "a new setting!")

    val myTask = TaskKey[State]("my-task")                  

    val mySettings = Seq(                                   
      newSetting := "default",                              
      myTask <<= (state, newSetting) map { (state, newSetting) =>  
        println("newSetting: " + newSetting)                
        state
      }
    )

    lazy val root =
      Project(id = "hello",
              base = file("."),
              settings = Project.defaultSettings ++ mySettings)            
}

With this configuration, you can run my-task at the sbt prompt, and you'll see newSetting: default printed to the console.

You can override this setting in build.sbt:

newSetting := "modified"

Now, when you run my-task at the sbt prompt, you'll see newSetting: modified printed to the console.

EDIT 2

Here's a stand-alone version of the example above: https://earldouglas.com/ext/stackoverflow.com/questions/17038663/

earldouglas
  • 13,265
  • 5
  • 41
  • 50
  • I tried this out, but didn't have much joy. TBH, I'm not sure what a `TaskKey` is so think I need to read up some more on that. I've edited the question to be more specific about my use case, which is an SBT plugin that adds an SBT `Command` and added a code example of what I currently have. Let me know if you have any ideas where I could be going wrong. – theon Jun 17 '13 at 21:04
  • Found some quite good documentation [here](http://www.scala-sbt.org/release/docs/Detailed-Topics/Tasks.html#define-the-key) and Tasks and Commands look pretty similar. (Also mentioned [here](https://groups.google.com/forum/#!msg/simple-build-tool/vgxkDSgOnlc/BQyKT7nOYnQJ)) I think tomorrow I'll try migrating away from using `Command`s to using `Task`s. – theon Jun 17 '13 at 22:15
  • I updated my answer with an example a bit closer to what you're looking for. – earldouglas Jun 17 '13 at 23:45
  • I've got this working inside a plugin - thanks for your help. I'll add some more to your answer later about some other problems I faced, if that is ok? – theon Jun 18 '13 at 13:33
  • Sure thing, feel free to add to my answer. – earldouglas Jun 19 '13 at 16:33
  • @James I'm a little confused about the example link you gave. How are the build.sbt and My.plugin connected? I don't see any common key or naming between them. Would you please explain? – jpswain Jul 01 '13 at 05:31
  • @orange80 I'm not sure what you refer to by *My.plugin*, but I put together a minimal example that requires only a Build.scala and a build.sbt. You can check it out here: https://github.com/JamesEarlDouglas/sbt-custom-task – earldouglas Jul 01 '13 at 16:47
  • @James Sorry it's MyPlugin.scala, that's how they have it at the scala-sbt link at the top of this answer. I'll check out the one you just posted, much appreciated :-) – jpswain Jul 01 '13 at 17:39
2

I've accepted @James's answer as it really helped me out. I moved away from using a Commands in favour of a Task (see this mailing list thread). In the end my plugin looked something like this:

package packge.to.my.plugin

import sbt.Keys._
import sbt._

object MyPlugin extends Plugin {

  import MyKeys._

  object MyKeys {
    val myTask = TaskKey[Unit]("runme", "This means you can run 'runme' in the SBT console")
    val newSetting = SettingKey[String]("newSetting")
  }

  override lazy val settings = Seq (
    newSetting := "light",
    myTask <<= (state, newSetting) map myCommand
  )

  def myCommand(state: State, newSetting: String) {
    //This code runs when the user types the "runme" command in the SBT console
    //newSetting is "light" here unless the user overrides in their build.sbt (see below)
    state.log.info(newSetting)
  }
}

To override the newSetting in the build.sbt of a project that uses this plugin:

import packge.to.my.plugin.MyKeys._

newSetting := "Something else"

The missing import statement had me stuck for a while!

theon
  • 14,170
  • 5
  • 51
  • 74