sbt-buildinfo generated object cannot be referenced - scala

I'm using the aforementioned sbt plugin to get the version of the app I'm working on.
The project has sub-modules. Here is the main build.sbt
...
lazy val abandon = (project in file(".")).
aggregate(base, cli, gui).
dependsOn(base, cli, gui).
enablePlugins(BuildInfoPlugin).
settings(commonSettings: _*).
settings(
name := "abandon",
fork in run := true,
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
buildInfoPackage := "co.uproot.abandon"
)
lazy val base = (project in file("base")).
settings(commonSettings: _*).
settings(
name := "abandon-base",
fork in run := true
)
lazy val cli = (project in file("cli")).
dependsOn(base).
settings(commonSettings: _*).
settings(
name := "abandon-cli",
fork in run := true
)
lazy val gui = (project in file("gui")).
dependsOn(base).
settings(commonSettings: _*).
settings(
name := "abandon-gui",
fork in run := true
)
The generated BuildInfo.scala is located under target/scala-2.11/src_managed/main/sbt-buildinfo/BuildInfo.scala as expected.
package co.uproot.abandon
import scala.Predef._
/** This object was generated by sbt-buildinfo. */
case object BuildInfo {
/** The value is "abandon". */
val name: String = "abandon"
/** The value is "0.3.1". */
val version: String = "0.3.1"
/** The value is "2.11.8". */
val scalaVersion: String = "2.11.8"
/** The value is "0.13.12". */
val sbtVersion: String = "0.13.12"
override val toString: String = {
"name: %s, version: %s, scalaVersion: %s, sbtVersion: %s" format (
name, version, scalaVersion, sbtVersion
)
}
}
When I go to a file inside the package co.uproot.abandon and try to reference BuildInfo.version I get
Error:(256, 42) object BuildInfo is not a member of package co.uproot.abandon
co.uproot.abandon.BuildInfo.version
I read about the problem with submodules and this plugin here and ultimately tried this workaround but that didn't help.
Any help will be appreciated!

I wanted to add an image to illustrate zaxme answer, but I can't in the comments, so I am adding another answer to add more information about it.
So,
1 - Right click on target/scala-2.11/src_managed/main:
And,
2 - Select Mark Directory as and Unmark Generated Sources Root:
Then rebuild, it should work.

Issue solved.
Just make sure to set the main part of target/scala-2.11/src_managed/main/sbt-buildinfo/BuildInfo.scala as source in the Project structure for the particular sub-module you would like to use it for and not the whole project.
Other than that the syntax highlighting is screwed so it will show up the same way as before i.e. when it wasn't compiling. To avoid that follow the link in this that I posted in the question.

Related

How can I specify a mainClass in build.sbt that resides in another module?

For some reason, our project got reorganized with the main class thrown in another module
I've specified the mainClass as below in the build.sbt but I still get a class not found error:
mainClass in Compile := Some("com.so.questions.sbt.Main")
However, this is bound to fail since it's going to look for the Main class in the src folder. However, this module lives outside of (sibling of) src:
MyScalaProject
+-MyModule
|+-src
| +-com.so.questions.sbt
| +-Main
|+-build.sbt <-- build.sbt specific to this module, currently blank
+-src
| +-<other folders>
+-build.sbt <-- build.sbt currently housing all config
How can I change the project scope in build.sbt to find and correctly load the main class?
That is, is it possible to do sbt run at the top level and have the main class be found with this structure?
It should work.
The FQCN specification for mainClass should be location independent to my understanding.
The real question that comes to mind is how you are loading your sub-module.
Here are some sbt definitions that should help point you in the right direction ( replace the <> tags with your own project Ids) :
// Define a submodule ref to be able to include it as a dependency
lazy val subModuleRef = ProjectRef(file("MyModule"),<MyModule SBT NAME>)
// Define a submodule project to be able to orchestrate it's build
lazy val subModule = Project(
id = <MyModule SBT NAME>,
base = file("MyModule"),
).addSbtFiles(file("build.sbt"))
// Define the top-level project, depending and subModule Ref for code
// inclusion and aggregating the subModule for build orchestration
lazy val scalaProject = Project(
id = <MyScalaProject NAME>,
base = file("."),
aggregate = Seq(subModule),
settings = commonSettings
).dependsOn(subModuleRef).
Let's say that you have the MyModule module/folder containing the main class and some other module called MyCoreModule (just to illustrate the whole build.sbt):
// any stuff that you want to share between modules
lazy val commonSettings = Seq(
scalaVersion := "2.12.8",
version := "1.0-SNAPSHOT"
)
lazy val root = (project in file("."))
.settings(commonSettings: _*)
.settings(
name := "parent-module"
)
.aggregate(core, app)
.dependsOn(app) // <-- here is the config that will allow you to run "sbt run" from the root project
lazy val core = project.in(file("MyCoreModule"))
.settings(commonSettings: _*)
.settings(
name := "core"
)
lazy val app = project.in(file("MyModule"))
.dependsOn(core)
.settings(commonSettings: _*)
.settings(
name := "app"
)
// define your mainClass from the "app" module
mainClass in Compile := (mainClass in Compile in app).value
Btw, sbt.version=1.2.7

sbt-assembly does not pick up configuration specific settings

I am updating an old 0.7.x build file from the tool sbt that thankfully removed the reference to "simple" from its name in the meantime.
Something that once worked, does not do so any longer. I had different config entries for platform specific assembly tasks. These include specific filters that for some reason are now called assemblyExcludedJars instead of excludedJars, and specific jar names that for some reason are now called assemblyJarName instead of jarName.
Basically:
val Foo = config("foo") extend Compile
lazy val assemblyFoo = TaskKey[File]("assembly-foo")
lazy val root = Project(id = "root", base = file("."))
// .configs(Foo) // needed? doesn't change anything
.settings(
inConfig(Foo)(inTask(assembly) {
assemblyJarName := "wtf.jar"
}),
scalaVersion := "2.11.7",
assemblyFoo <<= assembly in Foo
)
Now I would expect that if I run sbt assembly-foo or sbt foo:assembly, it would produce a file wtf.jar. But I am getting the default root-assembly-0.1-snapshot.jar. The same problem happens when I try to specify assemblyExcludedJars, they are simply ignored and still included.
If I remove the inConfig it works:
lazy val root = Project(id = "root", base = file("."))
.settings(
inTask(assembly) {
assemblyJarName := "wtf.jar"
},
scalaVersion := "2.11.7",
assemblyFoo <<= assembly in Foo
)
But now I cannot use different jar names for different configurations (which is the whole point).
As described in a blog post by one of sbt's authors and the author of sbt-assembly, this should work. It was also written in this Stackoverflow question. But the example requires an antique version of sbt-assembly (0.9.0 from 2013, before auto plugins etc.) and doesn't seem to apply to the current versions.
If one defines a new configuration, one has to redefine (?) all the tasks one is about to use. Apparently for sbt-assembly, this means running baseAssemblySettings:
val Foo = config("foo") extend Compile
lazy val assemblyFoo = TaskKey[File]("assembly-foo")
lazy val root = Project(id = "root", base = file("."))
.settings(
inConfig(Foo)(baseAssemblySettings /* !!! */ ++ inTask(assembly) {
jarName := "wtf.jar"
}),
scalaVersion := "2.11.7",
assemblyFoo := (assembly in Foo).value
)
Tested with sbt 0.13.9 and sbt-assembly 0.14.1.

how to declare and use object in this build.sbt?

I am trying to understand how to write a well-written build.sbt file, and followed a tutorial on youtube. In that tutorial an object is created similar as below, but what I have written gives the shown error.
What am I doing wrong?
I have tried to remove blank lines between imports and the object declaration without any change.
import sbt._
import sbt.Keys._
object BuildScript extends Build {
lazy val commonSettings = Seq(
organization := "me",
version := "0.1.0",
scalaVersion := "2.11.4"
)
lazy val root = (project in file(".")).
settings(commonSettings: _*).
settings(
name := "deepLearning",
libraryDependencies += "org.deeplearning4j" % "deeplearning4j-core" % "0.4-rc3.4"
)}
error message:
error: illegal start of simple expression
object BuildScript extends Build {
^
[error] Error parsing expression. Ensure that there are no blank lines within a setting.
I think this thread actually explains it: What is the difference between build.sbt and build.scala?
I feel that the edit made by chris martin on this post was unnecessary, but can't reject it.
I think your tutorial was out of date. Using a recent version of sbt (0.13+ or so) you really want to do this:
lazy val commonSettings = Seq(
organization := "me",
version := "0.1.0",
scalaVersion := "2.11.4"
)
lazy val root = (project in file(".")).
settings(commonSettings).
settings(
name := "deepLearning",
libraryDependencies += "org.deeplearning4j" % "deeplearning4j-core" % "0.4-rc3.4"
)
If your project doesn't have any subprojects, though, the commonSettings val is somewhat superfluous, and you can just inline it:
lazy val root = (project in file(".")).
settings(
organization := "me",
name := "deepLearning",
version := "0.1.0",
scalaVersion := "2.11.4",
libraryDependencies += "org.deeplearning4j" % "deeplearning4j-core" % "0.4-rc3.4"
)
If you do have subprojects, and wind up with a lot of common settings, you may want to pull them out into an autoplugin, but that's a more advanced concept.
There are two ways to fix this:
Do object BuildScript extends Build { ... } and use .scala extension for the build file. I think this way is the recommended long term Sbt build file style.
Change build definition to gregsymons's answer and keep the .sbt extension.

Project aggregation in sbt

I am just starting to learn sbt to build scala projects.
Here is my build.sbt file
lazy val commonSettings = Seq(
organization := "com.example",
version := "0.1.0",
scalaVersion := "2.11.7"
)
lazy val task = taskKey[Unit]("An example task")
lazy val root = project.in(file(".")).
aggregate(core).
settings(commonSettings: _*).
settings(
task := { println("Hello!") },
name := "hello",
version := "1.0"
)
lazy val core = project.in( file("SbtScalaProjectFoo") )
My project structure is as follows
SbtScalaProject
|--SbtScalaProjectFoo
|--build.sbt
|--build.sbt
When I try to run "sbt" inside SbtScalaProject I get the following
No project 'core' in 'file:/Users/asattar/Dev/work/SbtScalaProject/'
What am I missing?
Having multiple build.sbt's has proven to make problems for me, too.
I would recommend aggregating all project data into one build.sbt. If you want to modularize the build, consider moving parts into .scala-Files in (the root project's) project/ directory.

Can I access my Scala app's name and version (as set in SBT) from code?

I am building an app with SBT (0.11.0) using a Scala build definition like so:
object MyAppBuild extends Build {
import Dependencies._
lazy val basicSettings = Seq[Setting[_]](
organization := "com.my",
version := "0.1",
description := "Blah",
scalaVersion := "2.9.1",
scalacOptions := Seq("-deprecation", "-encoding", "utf8"),
resolvers ++= Dependencies.resolutionRepos
)
lazy val myAppProject = Project("my-app-name", file("."))
.settings(basicSettings: _*)
[...]
I'm packaging a .jar at the end of the process.
My question is a simple one: is there a way of accessing the application's name ("my-app-name") and version ("0.1") programmatically from my Scala code? I don't want to repeat them in two places if I can help it.
Any guidance greatly appreciated!
sbt-buildinfo
I just wrote sbt-buildinfo.
After installing the plugin:
lazy val root = (project in file(".")).
enablePlugins(BuildInfoPlugin).
settings(
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion),
buildInfoPackage := "foo"
)
Edit: The above snippet has been updated to reflect more recent version of sbt-buildinfo.
It generates foo.BuildInfo object with any setting you want by customizing buildInfoKeys.
Ad-hoc approach
I've been meaning to make a plugin for this, (I wrote it) but here's a quick script to generate a file:
sourceGenerators in Compile <+= (sourceManaged in Compile, version, name) map { (d, v, n) =>
val file = d / "info.scala"
IO.write(file, """package foo
|object Info {
| val version = "%s"
| val name = "%s"
|}
|""".stripMargin.format(v, n))
Seq(file)
}
You can get your version as foo.Info.version.
Name and version are inserted into manifest. You can access them using java reflection from Package class.
val p = getClass.getPackage
val name = p.getImplementationTitle
val version = p.getImplementationVersion
You can also generate a dynamic config file, and read it from scala.
// generate config (to pass values from build.sbt to scala)
Compile / resourceGenerators += Def.task {
val file = baseDirectory.value / "conf" / "generated.conf"
val contents = "app.version=%s".format(version.value)
IO.write(file, contents)
Seq(file)
}.taskValue
When you run sbt universal:packageBin the file will be there.