sbt mulit-project import error - sbt

I am trying to setup a multi-project that includes a sub-project that does an import of a class that is defined in a Dependencies.scala file in its project directory. When I run sbt on the sub-project everything is fine but when I run sbt on the root project I get an error stating that Dependencies is not found. Here is my root build.sbt:
name := "sbtTest"
organization := "com.test"
version := "0.1"
lazy val foo = project
Here is foo's build.sbt:
import Dependencies._
name := "foo"
version := "0.2"
scalaVersion := "2.10.6"
Dependencies.scala is in foo/projects and here is the exact error I get:
/Users/xyz/git/sbtTest/foo/build.sbt:1: error: not found: object Dependencies
import Dependencies._
^
[error] Type error in expression
Has anyone run into this problem?

I fixed this by making my build.sbt look like this..
lazy val otherProject = RootProject(file("../otherproject"))
lazy val rootProject = (project in file("."))
// dependsOn allows the root project to use functions from
.dependsOn(otherProject)
// aggregation runs tasks of root project on aggregated projects as well
.aggregate(otherProject)

In sbt you can also define all dependencies in a separated file.
This file tends to be in /project/Dependencies.scala In the same directory than plugins.sbt.
Then import Dependencies._ can be easy imported in build.sbt file.

Related

How to set up a multi-project SBT project with inheritance and shared dependencies

I'd like to create an SBT project with inheritance and shared dependencies.
With Maven's POM files, there is the idea of Project Inheritance where you can set a parent project. I'd like to do the same thing with SBT.
The xchange-stream library uses Maven's Project Inheritance to resolve subproject dependencies when compiled from the parent project.
Here is my idea of what the file structure would look like:
sbt-project/
project/
dependencies.scala # Contains dependencies common to all projects
build.sbt # Contains definition of parent project with references
# to subprojects
subproject1/
build.sbt # Contains `subproject3` as a dependency
subproject2/
build.sbt # Contains `subproject3` as a dependency
subproject3/
build.sbt # Is a dependency for `subproject1` and `subproject2`
Where project1 and project2 can include project3 in their dependencies lists like this:
libraryDependencies ++= "tld.organization" % "project3" % "1.0.0"
Such that when subproject1 or subproject2 are compiled by invoking sbt compile from within their subdirectories, or when the parent: sbt-project is compiled from the main sbt-project directory, then subproject3 will be compiled and published locally with SBT, or otherwise be made available to the projects that need it.
Also, how would shared dependencies be specified in sbt-project/build.sbt or anywhere in the sbt-project/project directory, such that they are useable within subproject1 and subproject2, when invoking sbt compile within those subdirectories?
The following examples don't help answer either of the above points:
jbruggem/sbt-multiproject-example:
Uses recursive build.sbt files, but doesn't share dependencies among child projects.
Defining Multi-project Builds with sbt: pbassiner/sbt-multi-project-example:
Uses a single build.sbt file for the projects in their subdirectories.
sachabarber/SBT_MultiProject_Demo:
Uses a single build.sbt file.
Such that when subproject1 or subproject2 are compiled by invoking sbt compile from within their subdirectories...
Maybe Maven is meant to be used together with the shell environment and cd command, but that's not how sbt works at least as of sbt 1.x in 2019.
The sbt way is to use sbt as an interactive shell, and start it at the top level. You can then either invoke compilation as subproject1/compile, or switch into it using project subproject1, and call compile in there.
house plugin
A feature similar to parent POM would be achieved by creating a custom plugin.
package com.example
import sbt._
import Keys._
object FooProjectPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
val commonsIo = "commons-io" % "commons-io" % "2.6"
override def buildSettings: Seq[Def.Setting[_]] = Seq(
organization := "com.example"
)
override def projectSettings: Seq[Def.Setting[_]] = Seq(
libraryDependencies += commonsIo
)
}
sbt-sriracha
It's not exactly what you are asking for, but I have an experimental plugin that allows you to switch between source dependency and binary dependency. See hot source dependencies using sbt-sriracha.
Using that you could create three individual sbt builds for project1, project2, and project3, all located inside $HOME/workspace directory.
ThisBuild / scalaVersion := "2.12.8"
ThisBuild / version := "0.1.1-SNAPSHOT"
lazy val project3Ref = ProjectRef(workspaceDirectory / "project3", "project3")
lazy val project3Lib = "tld.organization" %% "project3" % "0.1.0"
lazy val project1 = (project in file("."))
.enablePlugins(FooProjectPlugin)
.sourceDependency(project3Ref, project3Lib)
.settings(
name := "project1"
)
With this setup, you can launch sbt -Dsbt.sourcemode=true and it will pick up project3 as a subproject.
You can use Mecha super-repo concept. Take a look on the setup and docs here: https://github.com/storm-enroute/mecha
The basic idea is that you can combine dependent sbt projects (with their own build.sbt) under single root super-repo sbt project:
/root
/project/plugins.sbt
repos.conf
/project1
/src/..
/project/plugins.sbt
build.sbt
/project2
/src/..
/project/plugins.sbt
build.sbt
Please, note that there is no build.sbt in the root folder!
Instead there is repos.conf file. It contains definition of the sub-repos and looks like the folowing:
root {
dir = "."
origin = ""
mirrors = []
}
project1 {
dir = "project1"
origin = "git#github.com:some_user/project1.git"
mirrors = []
}
project2 {
dir = "project2"
origin = "git#github.com:some_user/project2.git"
mirrors = []
}
Then you can specify the Inter-Project, source-level Dependencies within individual projects.
There are two approaches:
dependencies.conf file
or in the build source code
For more details, please, see the docs

sbt aspectj with native packager

I'm attempting to use the sbt-aspectj plugin with the sbt native packager and am running into an issue where the associated -javaagent path to the aspectj load time weaver jar references an ivy cache location rather than something packaged.
That is, after running sbt stage, executing the staged application via bash -x target/universal/stage/bin/myapp/ results in this javaagent:
exec java -javaagent:/home/myuser/.ivy2/cache/org.aspectj/aspectjweaver/jars/aspectjweaver-1.8.10.jar -cp /home/myuser/myproject/target/universal/stage/lib/org.aspectj.aspectjweaver-1.8.10.jar:/home/myuser/myproject/target/universal/stage/lib/otherlibs.jar myorg.MyMainApp args
My target platform is Heroku where the artifacts are built before being effectively 'pushed' out to individual 'dynos' (very analogous to a docker setup). The issue here is that the resulting -javaagent path was valid on the machine in which the 'staged' deployable was built, but will not exist where it's ultimately run.
How can one configure the sbt-aspectj plugin to reference a packaged lib rather than one from the ivy cache?
Current configuration:
project/plugins.sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.10.6")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.5")
build.sbt (selected parts):
import com.typesafe.sbt.SbtAspectj._
lazy val root = (project in file(".")).settings(
aspectjSettings,
javaOptions in Runtime ++= { AspectjKeys.weaverOptions in Aspectj }.value,
// see: https://github.com/sbt/sbt-native-packager/issues/598#issuecomment-111584866
javaOptions in Universal ++= { AspectjKeys.weaverOptions in Aspectj }.value
.map { "-J" + _ },
fork in run := true
)
Update
I've tried several approaches including pulling the relevant output for javaOptions from existing mappings, but the result is a cyclical dependency error thrown by sbt.
I have something that technically solves my problem but feels unsatisfactory. As of now, I'm including an aspectjweaver dependency directly and using the sbt-native-packager concept of bashScriptExtraDefines to append an appropriate javaagent:
updated build.sbt:
import com.typesafe.sbt.SbtAspectj._
lazy val root = (project in file(".")).settings(
aspectjSettings,
bashScriptExtraDefines += scriptClasspath.value
.filter(_.contains("aspectjweaver"))
.headOption
.map("addJava -javaagent:${lib_dir}/" + _)
.getOrElse(""),
fork in run := true
)
You can add the following settings in your sbt config:
.settings(
retrieveManaged := true,
libraryDependencies += "org.aspectj" % "aspectjweaver" % aspectJWeaverV)
AspectJ weaver JAR will be copied to ./lib_managed/jars/org.aspectj/aspectjweaver/aspectjweaver-[aspectJWeaverV].jar in your project root.
I actually solved this by using the sbt-javaagent plugin to adding agents to the runtime

using sbt for multi project build

I am totally new to SBT. Suppose I have three Scala projects: project_a, project_b, project_c. How should I go about building all three projects into one jar file? Suppose I use project_a as the root project. The directory structure is like
--project_a
--build.sbt
--project_b
--project_c
Following the instructions on sbt webiste, I created a build.sbt file, which looks something like
lazy val root = (project.in(file("."))).aggregate(project_b, project_c)
lazy val project_b = project
lazy val project_c = project
I put the build.sbt under the project_a. When I run sbt clean compile under project_a, a new (kinda of empty) project_b and project_c folders are created under the folder project_a. However, in the build.sbt file, I meant project_b and project_c to refer to the original folders I already created which contains the source and test code, and which are outside project_a.
Can someone let me know what I did wrong?
Thanks
First, your multi-project setup is not right.
Getting Started guide says:
Aggregation means that running a task on the aggregate project will also run it on the aggregated projects.
If you have project_a that uses project_b and project_c, then you need root in addition to project_a, project_b, and project_c.
Root can aggregate all three (a, b, and c), but it only aggregates commands given to sbt shell, for instance for compiling all three at the same time.
project_a should be set up to depend on project_b and project_c.
Here's an example:
lazy val commonSettings = Seq(
scalaVersion := "2.11.4",
organization := "com.example"
)
lazy val root = (project in file(".")).
aggregate(project_a, project_b, project_c).
settings(commonSettings: _*)
lazy val project_a = project.
dependsOn(project_b, project_c).
settings(commonSettings: _*).
settings(
// your settings here
)
lazy val project_b = project.
settings(commonSettings: _*)
lazy val project_c = project.
settings(commonSettings: _*)
How should I go about building all three projects into one jar file?
If you just want *.class files from your own projects, you can see an example on Macro Projects.
If you want *.class files and library dependencies, you need sbt-assembly.

Why can't sbt find my custom command with build.scala build definition?

Why does sbt not able to find a command in a multiproject build?
My plugin resembles
object MyPlugin extends Plugin {
lazy val plug = Seq( commands ++= Seq(versionWriteSnapshotRelease) )
def versionWriteSnapshotRelease = Command.command(
"versionWriteSnapshotRelease",
"Writes the release format of the snapshot version. This is used to preserve the actual snapshot version in a release commit.",
""
) { state => .... }
}
I have my project/Build.scala file as follows:
lazy val app = Project(id = "app-subproject", base = file("app"))
.settings(MyPlug.plug :_*)
.settings(...)
lazy val common = Project(id = "library-subproject", base = file("common"))
.settings(MyPlug.plug :_*)
.settings(...)
With files laid out like
root
|_ common
|_ src
|_ app
|_ src
This configurations fails with the error:
[error] Not a valid command: versionWriteSnapshotRelease
[error] Not a valid project ID: versionWriteSnapshotRelease
[error] Expected ':' (if selecting a configuration)
[error] Not a valid key: versionWriteSnapshotRelease (similar: version, ...)
[error] versionWriteSnapshotRelease
However if I restructure to something like
lazy val app = Project(id = "app-subproject", base = file("."))
.settings(MyPlug.plug :_*)
.settings(...)
lazy val common = Project(id = "library-subproject", base = file("common"))
.settings(MyPlug.plug :_*)
.settings(...)
With files laid out like
root
|_ common
|_ src
|_ src
Then it works. Note that my change is to put the app project /src in the basedir and set the app project to have base ".".
This plugin is used across multiple projects and has no issue when the file layout is in the second form. So I know it isn't an issue with the plugin per-se.
The reason for the error is that you define two submodules common and app with the third default root project without the command set.
You should add yet another definition of a project for the default root project so the command is also defined for it as follows:
lazy val root = Project(id = "root", base = file("."))
.settings(MyPlug.plug :_*)
.settings(...)
You've confirmed that thinking with your test when you turned app into the default root project.
As an additional check before you add the third module definition, do projects and see what projects are defined. For every project in the list, execute [projectName]/versionWriteSnapshotRelease. I'm sure it's going to be fine for app and common, but not for the default root module.

Specifying rootProject in build.sbt

In my Build.scala, I have:
override def rootProject = Some(frontendProject)
I'm trying to convert to the newer build.sbt format, but don't know the equivalent of this line. How do I set the project for sbt to load by default when using build.sbt?
I'm still not sure that I understood you right, but you said about multi-project build, so I assume that you want to define a root project which aggregates subprojects. Here is how you can do that (in your root build.sbt):
lazy val root = project.in( file(".") ).aggregate(subProject1, subProject2)
lazy val subProject1 = project in file("subProject1")
lazy val subProject2 = project in file("subProject2")
See sbt documentation about multi-projects.
Then if you want to set the default project to load on sbt startup to a sub-project, in addition to your answer to this SO question, I can suggest
run sbt with sbt "project XXX" shell command
or adding this line to your build.sbt:
onLoad in Global := { Command.process("project XXX", _: State) } compose (onLoad in Global).value
In both cases sbt first loads the root project and then the subproject.
I've found the following script to be helpful:
#!/usr/bin/env bash
exec "sbt" "project mysubproject" "shell"
exit $?

Resources