I want to override the default behaviour of the run task in order to preprocess the arguments passed to the sbt command and call the run task on a specific subproject with different arguments (depending on custom logic).
That's a sample of build.sbt file:
lazy val core = (project in file("core"))
lazy val bar = (project in file("bar"))
lazy val foo = (project in file("foo"))
run := Runner.buildRunTask(bar, foo).value.evaluated
The buildRunTask override the task with the new implementation. Here the code:
def buildRunTask(runnableProjects:Project*) : Initialize[InputTask[Unit]] =
Def.inputTask {
val args: Seq[String] = spaceDelimited("<arg>").parsed
val availableProjects = runnableProjects.map(_.id).mkString(",")
if (args.length == 0) throw new IllegalStateException(s"you need to specify a valid command. Available commands are: '$availableProjects'")
val command = args head
val project = runnableProjects.find(_.id == command)
.getOrElse(throw new IllegalStateException(s"invalid command '$command'. Available command are: $availableProjects"))
val newArgs = transformArgs(args.rest)
//TODO: how I can compile and execute the run task only for the project passed as argument? I want something like project.runTask(newArgs) ?
}
Do you have any suggestion?
Thanks!
Regards
Gianluca
Related
I have a sbt autoplugin and when the user runs a task I want to fork a new JVM with a -javaagent. The task should measure memory using jamm.
object SbtMemory extends AutoPlugin {
object autoImport {
val agentTest = inputKey[Unit]("Run task with javaagent")
}
def makeAgentOptions(classpath: Classpath) : String = {
val jammJar = classpath.map(_.data).filter(_.toString.contains("jamm")).head
s"-javaagent:$jammJar"
}
override lazy val projectSettings =
Seq(
agentTest := agentTask.value,
fork in agentTest := true,
javaOptions in agentTest += (dependencyClasspath in Test).map(makeAgentOptions).value
)
lazy val agentTask = Def.task {
val o = new Array[Byte](1024*1024)
val mm = new MemoryMeter()
println("Size of new Array[Byte](1024*1024): " + mm.measureDeep(o))
}
}
When I run sbt perf from the command line, I get the following exception:
java.lang.IllegalStateException: Instrumentation is not set; Jamm must be set as -javaagent
I also tried printing the javaOptions and the -javaagent option was not set.
How can I add the -javaagent javaOption inside the plugin to run the task with jamm?
Thanks!
Apparently, fork is only available for the run and test task. I added my own forking code and moved the measure code to a separate class MemoryMeasure:
val mainClass: String = "MemoryMeasure"
val forkOptions = ForkOptions(
bootJars = (fullClasspath in Test).value.files,
runJVMOptions = Seq(
(dependencyClasspath in Test).map(makeAgentOptions).value
)
)
val process = Fork.java.fork(forkOptions, mainClass +: arguments)
def cancel() = {
process.destroy()
1
}
val exitCode = try process.exitValue() catch { case e: InterruptedException => cancel() }
Does anyone knows how to configure a SBT project to run an annotation processor (APT)? I'm doing some lab on a web project, using some Java tools like QueryDSL, and i need to generate querydsl classes for my JPA model classes, in a similar way QueryDSL Maven plugin does.
Thanks in advance.
You could manually run the annotation processor (see command below) or implement an SBT task similar to the following:
lazy val processAnnotations = taskKey[Unit]("Process annotations")
processAnnotations := {
val log = streams.value.log
log.info("Processing annotations ...")
val classpath = ((products in Compile).value ++ ((dependencyClasspath in Compile).value.files)) mkString ":"
val destinationDirectory = (classDirectory in Compile).value
val processor = "com.package.PluginProcessor"
val classesToProcess = Seq("com.package.Class1", "com.package.Class2") mkString " "
val command = s"javac -cp $classpath -proc:only -processor $processor -XprintRounds -d $destinationDirectory $classesToProcess"
failIfNonZeroExitStatus(command, "Failed to process annotations.", log)
log.info("Done processing annotations.")
}
def failIfNonZeroExitStatus(command: String, message: => String, log: Logger) {
val result = command !
if (result != 0) {
log.error(message)
sys.error("Failed running command: " + command)
}
}
packageBin in Compile <<= (packageBin in Compile) dependsOn (processAnnotations in Compile)
Update destinationDirectory, processor, and classesToProcess as necessary.
You might also change the "-d" flag to "-s" depending on the type of annotation processor you have (see options for javac).
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 have to create osgi manifest for bundle created by task that uses proguard.
For osgi part I'm using sbtosgi plugin (ver 0.6)
Currently I tried something like this:
object BuilderKeys {
val pkg = TaskKey[File]("package")
val bld = TaskKey[File]("build")
val targetFile = TaskKey[File]("targetFile")
...
}
lazy val settings = ... + BuilderSettings.default(bundle) + BuilderSettings.osgi(bundle)
object BuilderSettings {
def default(configuration: Configuration) // initializes bld (task that creates one uber-jar using proguard)
def osgi(configuration: Configuration) = inConfig(configuration)(SbtOsgi.defaultOsgiSettings ++ Seq(
fullClasspath in Compile := Seq(Attributed.blank((targetFile in configuration).value)),
pkg <<= (OsgiKeys.bundle dependsOn bld) map { f: File =>
f
}
))
}
Running bundle:package creates jar with manifest but not based on jar created by proguard. Is it possible to force it in some way?
My projects are still using sbt 0.7.7 and I find it very convenient to have utility classes that I can run from the sbt prompt. I can also combine this with properties that are separately maintained - typically for environment related values that changes from hosts to hosts. This is an example of my project definition under the project/build directory:
class MyProject(info: ProjectInfo) extends DefaultProject(info) {
//...
lazy val extraProps = new BasicEnvironment {
// use the project's Logger for any properties-related logging
def log = MyProject.this.log
def envBackingPath = path("paths.properties")
// define some properties that will go in paths.properties
lazy val inputFile = property[String]
}
lazy val myTask = task { args =>
runTask(Some("foo.bar.MyTask"),
runClasspath, extraProps.inputFile.value :: args.toList).dependsOn(compile)
describedAs "my-task [options]"
}
}
I can then use my task as my-task option1 option2 under the sbt shell.
I've read the new sbt 0.11 documentation at https://github.com/harrah/xsbt/wiki including the sections on Tasks and TaskInputs and frankly I'm still struggling on how to accomplish what I did on 0.7.7.
It seems the extra properties could simply be replaced a separate environment.sbt, that tasks have to be defined in project/build.scala before being set in build.sbt. It also looks like there is completion support, which looks very interesting.
Beyond that I'm somewhat overwhelmed. How do I accomplish what I did with the new sbt?
You can define a task like this :
val myTask = InputKey[Unit]("my-task")
And your setting :
val inputFile = SettingKey[String]("input-file", "input file description")
You can also define a new configuration like :
lazy val ExtraProps = config("extra-props") extend(Compile)
add this config to your project and use it to set settings for this configuration :
lazy val root = Project("root", file(".")).config( ExtraProps ).settings(
inputFile in ExtraProps := ...
...
myTask in ExtraPops <<= inputTask { (argTask:TaskKey[Seq[String]]) =>
(argTask, inputFile) map { (args:Seq[String], iFile[String]) =>
...
}
}
).dependsOn(compile)
then launch your task with extra-props:my-task