How to get all projects in a SBT task? - plugins

I'm writing a SBT task, which will get all projects of the whole project, then I can run some tasks against them.
The pseudo code is like:
val projects = someTaskToGetProjects.value
val updateReports = projects.map(p => (update in p).value)
But I can't find any task or setting to get the project list, how to do it?

I think buildDependencies might suit your needs, otherwise loadedBuild has everything.
val projects = buildDependencies.value.classpath.keys
val updateReports = projects.map(p => (update in p).value)

Related

SBT & Scala AudioSystem

I have a strange problem that I've been trying to figure out for a while now.
I have an application that I am building with Scala & Spray and which uses the AudioSystem API.
I build and test the application using SBT.
I have a boot.scala which extends "App".
If I place the following code in boot.scala and run it through Eclipse(without sbt) (Run As... Scala App) it runs fine...
val stream:AudioInputStream = AudioSystem.getAudioInputStream(new File("test.wav"))
val audioFormat:AudioFormat = stream.getFormat();
val samplingRate = audioFormat.getSampleRate()
println("Sampling Rate: "+samplingRate)
The sampling rate of the file is output as expected.
I have the same code in a Specs2 Route test similar to...
"API" should {
"Respond to POST requests" in {
val stream:AudioInputStream = AudioSystem.getAudioInputStream(new File("test.wav"))
val audioFormat:AudioFormat = stream.getFormat();
val samplingRate = audioFormat.getSampleRate()
println("Sampling Rate: "+samplingRate)
...
However when I execute this from a terminal using "sbt test" I get the following error...
UnsupportedAudioFileException: : could not get audio input stream from input file
I know the file (test.wav) is ok as I can play it and executing the code through Eclipse works fine. The terminal (and its encodings) seem ok too as I put together a test file which just runs the same few lines of code and ran it from a terminal successfully.
The problem seems to only occur with SBT!
Anyone got any ideas?
Thanks
Finally found the answer after literally days of searching...
Why does AudioSystem.getMixerInfo() return different results under sbt vs Scala?
"This is a classloader issue. javax.sound does NOT like having the context classloader be anything other than the system classloader." and the fix for me was...
val cl = classOf[javax.sound.sampled.AudioSystem].getClassLoader
val old = Thread.currentThread.getContextClassLoader
var audioFormat:AudioFormat = null
try {
Thread.currentThread.setContextClassLoader(cl)
val stream:AudioInputStream =
AudioSystem.getAudioInputStream(new ByteArrayInputStream(data))
audioFormat = stream.getFormat()
} finally Thread.currentThread.setContextClassLoader(old)
Thanks

Why sbt.Extracted remove the previously defined TaskKey while append method?

There is a suitable method in the sbt.Exctracted to add the TaskKey to the current state. Assume I have inState: State:
val key1 = TaskKey[String]("key1")
Project.extract(inState).append(Seq(key1 := "key1 value"), inState)
I have faced with the strange behavior when I do it twice. I got the exception in the following example:
val key1 = TaskKey[String]("key1")
val key2 = TaskKey[String]("key2")
val st1: State = Project.extract(inState).append(Seq(key1 := "key1 value"), inState)
val st2: State = Project.extract(st1).append(Seq(key2 := "key2 value"), st1)
Project.extract(st2).runTask(key1, st2)
leads to:
java.lang.RuntimeException: */*:key1 is undefined.
The question is - why does it work like this? Is it possible to add several TaskKeys while executing the particular task by several calls to sbt.Extracted.append?
The example sbt project is sbt.Extracted append-example, to reproduce the issue just run sbt fooCmd
Josh Suereth posted the answer to sbt-dev mail list. Quote:
The append function is pretty dirty/low-level. This is probably a bug in its implementation (or the lack of documentation), but it blows away any other appended setting when used.
What you want to do, (I think) is append into the current "Session" so things will stick around and the user can remove what you've done via "sesison clear" command.
Additonally, the settings you're passing are in "raw" or "fully qualified" form. If you'd for the setting you write to work exactly the same as it would from a build.sbt file, you need to transform it first, so the Scopes match the current project, etc.
We provide a utility in sbt-server that makes it a bit easier to append settings into the current session:
https://github.com/sbt/sbt-remote-control/blob/master/server/src/main/scala/sbt/server/SettingUtil.scala#L11-L29
I have tested the proposed solution and that works like a charm.

Configuring ScalaDoc task in Gradle to generate aggregated documentation

I have a multi module scala project in Gradle. Everything works great with it, except the ScalaDoc. I would like to generate a single 'uber-scaladoc' with all of the libraries cross-linked. I'm still very new to groovy/gradle, so this is probably a 'me' problem. Any assistance getting this setup would be greatly appreciated.
build.gradle (in the root directory)
// ...
task doScaladoc(type: ScalaDoc) {
subprojects.each { p ->
// do something here? include the project's src/main/scala/*?
// it looks like I would want to call 'include' in here to include each project's
// source directory, but I'm not familiar enough with the Project type to get at
// that info.
//
// http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.scala.ScalaDoc.html
}
}
The goal here would be able to just run 'gradle doScalaDoc' at the command line and have the aggregate documentation show up.
Thanks in advance.

Define Custom Source File Dependencies in SBT

How can I cause SBT to recompile some file A whenever another non-scala file B changes?
I have defined a macro:
printMacro("path/to/file")
which creates a string literal from the file indicated by "path/to/file".
Whenever that file changes, the file that uses that macro needs to be recompiled to reflect those changes. I can use watchSources to monitor that file for changes and recompile the project when it does, but because of the incremental compiler, this recompile doesn't actually do anything.
I'll almost certainly need to write a plugin to accomplish this, but I cannot find which hooks into sbt will enable me to write such a plugin.
EDIT: Recompiling the whole project isn't desirable, since there might be multiple tracked files and the project itself might be very large.
How about this solution, which is based on FileFunction.cached.
Basically define a function, which takes:
cachedBaseDirectory - the place where it's going to keep cache metadata
inStyle - which determines how it checks for changes
action - which is invoked when the observed file is changed
The function returns another function, which takes a set of monitored files.
def cached(cacheBaseDirectory: File, inStyle: FilesInfo.Style)(action: => Unit): Set[File] => Unit = {
import Path._
lazy val inCache = Difference.inputs(cacheBaseDirectory / "in-cache", inStyle)
inputs => {
inCache(inputs) { inReport =>
if(!inReport.modified.isEmpty) action
}
}
}
This is how you can use it in build.sbt
val recompileWhenFileChanges = taskKey[Unit]("Recompiles the project when a file changes")
recompileWhenFileChanges := {
val base = baseDirectory.value
val mySpecialFile = baseDirectory.value / "path" / "to" / "file" / "test.txt"
val cache = cacheDirectory.value / "my_cache_dir"
val cachedFunction = cached(cache, FilesInfo.lastModified)(IO.delete((classDirectory in Compile).value))
cachedFunction(mySpecialFile.get.toSet)
}
compile in Compile := ((compile in Compile) dependsOn recompileWhenFileChanges).value
The task deletes classDirectory only if the file is changed. Deleting the classDirectory makes the project to recompile.
Last we make the original compile to depend on our newly created task.

sbt-idea & sub-projects

If, say, your sbt project contained 50 sub projects. Is it possible to generate an intellij project where you specify a set of the sub projects comes from your repo and for the other set of sub projects are actually contained in the project file ?
i.e. i'm looking to work on only a small subset of sub projects at a time and I know I won't need to change / compile the the other sub projects.
Thanks
You can define the following layout in project/Build.scala where you combine projects you're working on in meta project:
object MyBuild extends Build {
lazy val meta = Project(
id = "meta",
base = file(".")) aggregate(A,B) dependsOn(A,B)
lazy val A = Project(id = "A",base = file("A")))
lazy val B = Project(id = "B",base = file("B")))
... }
Then type project meta at sbt startup (if it is not meta already) and then gen-idea.
But every time you need to change projects set you're working on, you need to change meta definition and issue gen-idea.