If I define a SBT task key outside of my build.sbt file as a Scala class in the project folder, how can I import that task
So in ./project/MyTask.scala I have;
import sbt.Keys._
import sbt._
object MyTask {
lazy val uname = settingKey[String]("Your name")
lazy val printHi = taskKey[Unit]("print Hi")
printHi := { println(s"hi ${name.value}") }
}
Then in ./build.sbt I have;
import MyTask._
uname := "Joe"
Then when I run sbt printHi I get an error that the task cannot be found. Running show uname also works. When I define printHi in build.sbt directly without the object import everything works as expected.
I need so somehow add this task to the build.sbt file. How can I do this?
The issue is that your expression printHi := { println(s"hi ${name.value}") } isn't associated to anything.
First off, everything in sbt is a transformation, in this case (:=) overrides any previous setting of printHi to the definition you give (println(s"hi ${name.value}")). But by not associating that expression (which is a Setting[Task[Unit]]) to anything (for instance to a project, or as a value that then gets attached to a project) it just gets evaluated in the construction of the MyTask object and then thrown away.
One way to do this is to put that setting (printHi := println(s"hi ${name.value}")), in a Seq[Setting[_]] that you then pull into build.sbt:
project/MyTask.scala
import sbt._, Keys._
object MyTask {
val printHi = taskKey[Unit]("prints Hi")
val myTaskSettings = Seq[Setting[_]](
printHi := println(s"hi ${name.value}")
)
}
build.sbt
import MyTask._
myTaskSettings
Another way is to define MyTask to be a mini plugin that lives in project/. You can see an example of this in PgpCommonSettings.
Related
I am migrating a Build.scala file to a build.sbt file.
In the Build.scala file, there are print statements that print out vals (of type String) defined in the body of the Build.scala file.
project/Build.scala:
import sbt._
import Keys._
object HelloBuild extends Build {
val foo = System.getProperty("foo")
println(foo)
}
How do I migrate these print statements to the build.sbt file?
You can't just print it. You are declaring your build, but when it will be printed is different story. Probably it should be within a task for instance. From docs:
myTask := {
val log = streams.value.log
val propertyFoo = System.getProperty("foo")
log.info(s"property foo = $propertyFoo")
}
I'm trying to create a task in sbt that will output the full classpath of a custom Configuration, but I get an undefined setting error when sbt tries to load the project definition. I can't figure out which setting has to be defined:
import sbt.Keys._
import sbt._
object FoobarBuild extends Build {
lazy val ZK = config("zk")
lazy val fcp = TaskKey[String]("fcp", "create formatted classpath")
lazy val fcpTask = fcp <<= (fullClasspath in ZK) map { cp =>
println(cp.files.absString)
cp.files.absString
}
lazy val project = Project("foobar", file(".")).
configs(ZK).
settings(
name := "foobar",
version := "1.0",
scalaVersion := "2.11.7"
).
settings(fcpTask)
}
Error:
[info] Loading project definition from foobar/project
Reference to undefined setting:
zk:fullClasspath from *:fcp (/Users/gaston/mesosphere/foobar/project/Build.scala:7)
zk:fullClasspath on the 7th line of this file is, of course, fullClasspath in ZK. It's undefined because it isn't set or inherited from any other config, I believe.
I am new to SBT and I have been trying to build a custom task for this build.
I have a simple build project:
import sbt._
import Keys._
object JsonBuild extends Build{
lazy val barTask = taskKey[Unit]("some simple task")
val afterTestTask1 = barTask := { println("tests ran!") }
val afterTestTask2 = barTask <<= barTask.dependsOn(test in Test)
lazy val myBarTask = taskKey[Unit]("some simple task")
//val afterMyBarTask1 = myBarTask := { println("tests ran!") }
lazy val afterMyBarTask2 = myBarTask <<= (myBarTask).dependsOn(test in Test) map { _ => println("tests ran!") }
//settings ++ Seq(afterMyBarTask2)
override lazy val settings = super.settings ++ Seq(afterMyBarTask2)
}
I keep getting the error:
References to undefined settings:
{.}/*:myBarTask from {.}/*:myBarTask (C:\Users\haques\Documents\workspace\SBT\jsonParser\project\Build.scala:13)
{.}/test:test from {.}/*:myBarTask (C:\Users\haques\Documents\workspace\SBT\jsonParser\project\Build.scala:13)
Did you mean test:test ?
I have googled around and I cannot find a solution.
Can you explain why it is not working?
lazy val myBarTask = taskKey[Unit]("some simple task")
override lazy val settings = super.settings ++ Seq(myBarTask := { (test in Test).value; println("tests ran!") } )
myBarTask is undefined when you call dependsOn. you should define it before using dependsOn. also value call on key (task/setting) is now preferred way to depend on other keys. you can still use your version, but define myBarTask
This has been bothering.
I did a bit more reading.
I think I know why the above code does not work.
lazy val afterMyBarTask2 = myBarTask <<= (myBarTask).dependsOn(test in Test) map { _ => println("tests ran!") }
When I write (myBarTask).dependsOn(test in Test), the project scope for test is chosen by SBT as ThisBuild.
{.}/test:test from {.}/*:myBarTask (C:\Users\haques\Documents\workspace\SBT\jsonParser\project\Build.scala:13)
ThisBuild project scope does not have the setting test in configuration Test.
Only projects have the setting test.
The key I think that setting is added by some default SBT plugin to the projects settings.
You check what scopes settings exist in SBT by using the inspect command.
If you type in the SBT REPL:
{.}/test:test
The output is:
inspect {.}/test:test
[info] No entry for key.
SBT correctly suggests:
test:test which is:
{file:/C:/Users/haques/Documents/workspace/SBT/jsonParser/}jsonparser/test:test
If the project is not specified in the project scope axis, SBT chooses the current project by default.
Every SBT project if not specified has its own project settings.
I need a way to update a SettingKey[String] in my sbt build each time the compile task executes. A SettingKey is necessary so it can be injected into application via sbt-buildinfo. Creating a command is one way to accomplish this per this question, but I want it to happen when the compile task executes, which can be caused by any number of other tasks. Any ideas how to accomplish this in sbt 0.12?
You cannot use SettingKey[String] as the SettingKeys are only computed once - during the project load, which is specified in the documentation:
For a setting, the value will be computed once at project load time.
For a task, the computation will be re-run each time the task is
executed.
How about following Build.scala
import sbt._
import Keys._
import sbtbuildinfo.Plugin._
object HelloBuild extends Build {
val helloSettings = Defaults.defaultSettings ++
buildInfoSettings ++ Seq (
sourceGenerators in Compile <+= buildInfo,
buildInfoKeys := Seq[BuildInfoKey](
name,
version,
scalaVersion,
// Here is the interesting part
BuildInfoKey.action("buildTime") {
System.currentTimeMillis
}
),
buildInfoPackage := "org.myorg.myapp"
)
lazy val project = Project (
id = "project",
base = file ("."),
settings = helloSettings
)
}
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?
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/
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!