This is a minimal example to show a very weird thing when using sbt run. Trying to make the example any smaller, then the problem does not arises any more. Am I missing something or is this a bug or sbt?
build.sbt
name := "testsbt"
version := "1.0"
scalaVersion := "2.11.8"
project/build.properties
sbt.version = 0.13.13
src/main/scala/application/Test.scala
package application
import java.io._
case class Page(items: List[Item])
case class Item(text: String)
object Test extends App {
val page = Page(List(Item("item1")))
val filename = "./page.obj"
val oos = new ObjectOutputStream(new FileOutputStream(filename))
try oos.writeObject(page)
finally oos.close()
println("read: " + new ObjectInputStream(new FileInputStream(filename)).readObject())
}
executing this with sbt run fails:
$ sbt run
[error] (run-main-0) java.lang.ClassCastException: cannot assign instance of scala.collection.immutable.List$SerializationProxy to field application.Page.items of type scala.collection.immutable.List in instance of application.Page
however, executing sbt package and running from it works:
$ sbt package
$ scala -cp target/scala-2.11/testsbt_2.11-1.0.jar application.Test
read: Page(List(Item(item1)))
Related
In a sbt project, how to get full list of dependencies(including transitive dependencies) with scope?
For Example:
xxx.jar(Compile)
yyy.jar(Provided)
zzz.jar(Test)
...
From the sbt shell you can execute one or all of the following commands:
Test / fullClasspath
Runtime / fullClasspath
Compile / fullClasspath
Which will output the jars associated with the scope (Test/Runtime/Compile).
If you want to get a bit more fancy, sbt provides a number of ways of interacting with the outputs generated through the dependency management system. The documentation is here.
For example, you could add this to your build.sbt file:
lazy val printReport = taskKey[Unit]("Report which jars are in each scope.")
printReport := {
val updateReport: UpdateReport = update.value
val jarFilter: ArtifactFilter = artifactFilter(`type` = "jar")
val testFilter = configurationFilter(name = "test")
val compileFilter = configurationFilter(name = "compile")
val testJars = updateReport.matching(jarFilter && testFilter)
val compileJars = updateReport.matching(jarFilter && compileFilter)
println("Test jars:\n===")
for (jar <- testJars.sorted) yield {println(jar.getName)}
println("\n\n******\n\n")
println("compile jars:\n===")
for (jar <- compileJars.sorted) yield {println(jar.getName)}
}
It creates a new task printReport which can be executed like a normal sbt command with sbt printReport. It takes the value of the UpdateReport which is generated by the update task, and then filters for jar files in the respective test/compile scopes before printing the results.
I would like to get system property from command line and print it in sbt.
I used followiing code snippet
Depend.scala
val myVar = Option(System.getProperty("myVar")).getOrElse("default")
Build.sbt
val showMesg = settingKey[Unit]("Show message")
showMesg := {
sLog.value.info(myVar)
}
It works well when I use following command:
sbt -DmyVar=abc compile
[info] abc
But if I want to output it in debug log level. It can't get the system property correctly.
val showMesg = settingKey[Unit]("Show message")
showMesg := {
sLog.value.debug(myVar)
}
sbt -DmyVar=abc compile -debug
[debug] default
I just curious why I can't get the property when log level is debug.
The sbt in Action book introduces a concept of Key in Configuration
It then lists the default configurations:
Compile
Test
Runtime
IntegrationTest
Q1) Is it possible to print out a list of all Configurations from a sbt session? If not, can I find information on Configurations in the sbt documentation?
Q2) For a particular Configuration, e.g. 'Compile', is it possible to print out a list of Keys for the Configuration from a sbt session? If not, can I find information on a Configuration's Keys in the sbt documentation?
List of all configurations
For this you can use a setting like so:
val allConfs = settingKey[List[String]]("Returns all configurations for the current project")
val root = (project in file("."))
.settings(
name := "scala-tests",
allConfs := {
configuration.all(ScopeFilter(inAnyProject, inAnyConfiguration)).value.toList
.map(_.name)
}
This shows the name of all configurations. You can access more details about each configuration inside the map.
Output from the interactive sbt console:
> allConfs
[info] * provided
[info] * test
[info] * compile
[info] * runtime
[info] * optional
If all you want is to print them you can have a settingKey[Unit] and use println inside the setting definition.
List of all the keys in a configuration
For this we need a task (there might be other ways, but I haven't explored, in sbt I'm satisfied if something works... ) and a parser to parse user input.
All join the above setting in this snippet:
import sbt._
import sbt.Keys._
import complete.DefaultParsers._
val allConfs = settingKey[List[String]]("Returns all configurations for the current project")
val allKeys = inputKey[List[String]]("Prints all keys of a given configuration")
val root = (project in file("."))
.settings(
name := "scala-tests",
allConfs := {
configuration.all(ScopeFilter(inAnyProject, inAnyConfiguration)).value.toList
.map(_.name)
},
allKeys := {
val configHints = s"One of: ${
configuration.all(ScopeFilter(inAnyProject, inAnyConfiguration)).value.toList.mkString(" ")
}"
val configs = spaceDelimited(configHints).parsed.map(_.toLowerCase).toSet
val extracted: Extracted = Project.extract(state.value)
val l = extracted.session.original.toList
.filter(set => set.key.scope.config.toOption.map(_.name.toLowerCase)
.exists(configs.contains))
.map(_.key.key.label)
l
}
)
Now you can use it like:
$ sbt "allKeys compile"
If you are in interactive mode you can press tab after allKeys to see the prompt:
> allKeys
One of: provided test compile runtime optional
Since allKeys is a task it's output won't appear on the sbt console if you just "return it" but you can print it.
I wish to set my version numbers externally across several build.sbt files through a single include file.
Within build.sbt I can do this
val base = "1.1"
version := base + ".8-SNAPSHOT"
This works fine as a first step.
According the the online help I should be able to create a file global.sbt in my ~/.sbt/0.13 folder
I created the file global.sbt with single line
val base = "1.1"
and removed the corresponding line from build.sbt
But when I start up my sbt I get "error: not found: value base"
So either it's not finding the global sbt or this form of global setting doesn't work.
Any suggestions as to how I can resolve this?
Can I make an explicit include command in my build.sbt files?
It seems from your test that vals in global ~/.sbt/0.13/*.sbt files don't propagate to local *.sbt files.
Here's a setup that works:
~/.sbt/0.13/plugins/VersionBasePlugin.scala
import sbt._, Keys._
object VersionBasePlugin extends AutoPlugin {
override def requires = plugins.CorePlugin
override def trigger = allRequirements
object autoImport {
val versionBase = settingKey[String]("version base")
}
import autoImport._
override def projectSettings = Seq(versionBase := "1.1")
}
and then in your build.sbt:
version := (versionBase.value + ".8-SNAPSHOT")
Does that work for you?
I'm having an issue with getting SBT Subprojects to recognize commands provided by plugins. I have the following plugin source:
object DemoPlugin extends AutoPlugin {
override lazy val projectSettings = Seq(commands += demoCommand)
lazy val demoCommand =
Command.command("demo") { (state: State) =>
println("Demo Plugin!")
state
}
}
Which is used by a project configured as follows:
lazy val root = project in file(".")
lazy val sub = (project in file("sub")).
enablePlugins(DemoPlugin).
settings(
//...
)
The plugin is, of course, listed in project/plugins.sbt. However, when I open up sbt in the project, I see the following:
> sub/commands
[info] List(sbt.SimpleCommand#413d2cd1)
> sub/demo
[error] Expected ':' (if selecting a configuration)
[error] Not a valid key: demo (similar: doc)
[error] sub/demo
Even stranger, using consoleProject, I can see that the command in the project is the one defined by DemoPlugin!
scala> (commands in sub).eval.map { c => c.getClass.getMethod("name").invoke(c) }
res0: Seq[Object] = List(demo)
I'm looking to be able to type sub/demo, and have it perform the demo command. Any help would be much appreciated!
Commands aren't per-project. They only work for the top-level project.
It's also recommended to try and use tasks, or if needed input tasks where you might want to use a command.
If you really need a command, there's a way to have a sort of "holder" task, see the answer to Can you access a SBT SettingKey inside a Command?