I have a front end application from the chain of three modules A, B and C. B depends on A. C depends on B and A. Each module has own sbt file for building the js file. Some application pages use only A, other A and B, other A, B and C. SBT file of module C looks like this.
name := "C"
version := "1.0"
scalaVersion := "2.12.1"
scalaSource in Compile := baseDirectory.value / "src"
lazy val A = RootProject(file("../A"))
lazy val B = RootProject(file("../B"))
val main = Project(id = "C", base = file(".")).dependsOn(A).dependsOn(B)
enablePlugins(ScalaJSPlugin)
The problem is that the "sbt fullOptJS" creates a js file in which the definitions from A and B are duplicated. I want the file created for module C to use the files already loaded to the browser for modules A and B, and not to repeat them.
I'm afraid that's not possible. See https://github.com/scala-js/scala-js/issues/2681 and https://github.com/scala-js/scala-js/issues/2833 for some context.
Related
In a multiproject build, I have two projects, A and B, that are cross-compiled to Scala 2.12 and Scala 2.13. I would like to add a task to project A that depends on B's Scala 2.12 classpath (B / Compile / fullClasspath) regardless of the scala version used in A:
ThisBuild / scalaVersion := "2.13.2"
ThisBuild / crossScalaVersions := Seq("2.13.2", "2.12.10")
val foo = taskKey[Unit]("foo-task")
lazy val B = project.in(file("B"))
lazy val A = project.in(file("A")).settings(
foo := {
println((B / Compile / fullClasspath).value)
}
)
As is, running A/foo would print the B's scala-2.13 classpath. How can this be changed so that B's Scala 2.12 classpath is printed?
Context: during source generation for A, I would like to execute code from B inside SBT, so I needed the 2.12 classes of B, regardless of the version of A being compiled.
You can use sbt-cross instead of crossScalaVersions, then you'll have a separate subproject for each scala version.
https://github.com/lucidsoftware/sbt-cross
I have a project, called Main, and 2 subprojects: One, Two. Here's what my build.sbt looks like:
name := "Main"
version := "0.1"
scalaVersion := "2.12.7"
lazy val root = Project(id = "root", base = file(".")) aggregate(one, two) dependsOn(one, two)
lazy val one = Project(id = "one", base = file("One"))
lazy val two = Project(id = "two", base = file("Two"))
when I run sbt compile package I only get a .jar for Main (Main.jar), but I want to get a .jar for every subproject and not Main: One.jar, Two.jar.
How do I achieve this?
Also I have no idea what aggregate(one, two) dependsOn(one, two) means, do I even need that?
I also want every subproject to build into a fat jar with sbt-assembly.
It actually works now. I was encountering this behavior, where sbt was telling me that my assembly was not packaged, because it was up-to-date, but then I started doing sbt clean assembly, instead of sbt assembly and it works like a charm now.
I'm building a scala applications with these module and dependencies :
a shared lib "common"
module "A" depends on "common" and can be built in scala 2.10 only
module "B" depends on "common" and can be built in scala 2.11+ only
I'm trying to have all the 3 modules in a single sbt build :
import sbt._
import Keys._
lazy val root = (project in file("."))
.aggregate(common, A, B)
lazy val common = (project in file("common"))
lazy val A = (project in file("A"))
.dependsOn(common)
lazy val B = (project in file("B"))
.dependsOn(common)
I've read things about crossScalaVersions, but whatever combination I try in root build, or in common, etc, I can't manage to make this work properly.
Any clue ?
I'm using sbt 0.13.8 by the way.
Using bare sbt this might not be possible.
An example build.sbt file representing such situation:
lazy val common = (project in file("common"))
.settings(crossScalaVersions := Seq("2.10.6", "2.11.8"))
lazy val A = (project in file("A"))
.settings(scalaVersion := "2.10.6")
.dependsOn(common)
lazy val B = (project in file("B"))
.settings(scalaVersion := "2.11.8")
.dependsOn(common)
This will work just fine.
Now. A compilation of any project leads to a creation of a package. Even for the root. If you follow the console, at some point it says:
Packaging /project/com.github.atais/target/scala-2.10/root_2.10-0.1.jar
So as you see sbt needs to decide on some Scala version, just to build this jar! That being said your projects A and B must have a common Scala version so they can be aggregated in a common project root.
Therefore, you can't have:
lazy val root = (project in file("."))
.aggregate(common, A, B)
if they do not share any Scala version they could be built with.
But... sbt-cross to the rescue
You can use sbt-cross plugin to help you out.
In project/plugins.sbt, add
addSbtPlugin("com.lucidchart" % "sbt-cross" % "3.2")
And define your build.sbt in a following way:
lazy val common = (project in file("common")).cross
lazy val common_2_11 = common("2.11.8")
lazy val common_2_10 = common("2.10.6")
lazy val A = (project in file("A"))
.settings(scalaVersion := "2.10.6")
.dependsOn(common_2_10)
lazy val B = (project in file("B"))
.settings(scalaVersion := "2.11.8")
.dependsOn(common_2_11)
lazy val root = (project in file("."))
.aggregate(common, A, B)
And then it works :-)!
In my experience sbt multi-module builds are quite finicky to get to work reliably if you require any extra hoops to jump through such as this requirement.
Have you considered the simpler way to achieve this:
publish your common dependency (sbt publish-local if you only need to access it yourself)
make two projects A and B
make both A and B import common as a dependency
I have a multi-SBT-project in IntellJ Idea. My SBT file in the root dir looks like this:
name := "PlayRoot"
version := "1.0"
lazy val shapeless_learn = project.in(file("shapeless_learn")).dependsOn(common)
lazy val scalaz_learn = project.in(file("scalaz_learn")).dependsOn(common)
lazy val common = project.in(file("common"))
lazy val root = project.in(file(".")).aggregate(common, shapeless_learn, scalaz_learn)
scalaVersion := "2.11.7"
Then I have folders for each of the projects: ./common, ./shapeless_learn, ./scalaz_learn and each has its own build.sbt there. But for some reason I require to put in each of the subproject build.sbt files the line scalaVersion := "2.11.7".
If I forget to do that, the build fails with the message:
Error:Unresolved dependencies: common#common_2.10;0.1-SNAPSHOT: not found
See complete log in ...
For some reason if I do not specify that my scala version is 2.11.7, sbt falls back to 2.10 and tries to find common project that is built for 2.10 which I do not have.
I always keep forgetting adding scalaVersion := "2.11.7" to the newly created project and it keeps bugging me. I also would prefer sbt generating build.sbt with some default data, but instead it requires me not to forget to create it manually.
Is there any way I could set the single scala version for all projects and sub-projects in a single place? I figured that I could add a separate lazy val commonSettings = Seq { scalaVersion := "2.11.7" } in a root definition. And for each lazy val project definition I should add in the end .settings(commonSettings). This is nice, but still doesn't look beautiful enough - I should do this for every project definition. Is there a better way?
Is there any way I could create a template for a newly created project, so when I just put line lazy val newProject = ..., it will put an appropriate build.sbt file there with the contents I want?
Use
scalaVersion in ThisBuild := "2.11.7"
in the root build.sbt.
I just started with SBT's Multi-Project-Builds and I ran into an interesting problem that I have not seen a good example for in the scala-sbt docs.
In my build.sbt, Project B and C are dependent on A, but B is also dependent on C (at least dependent on B's classes in C's testing scope):
(Common is referenced to an Object in root/project/Common.scala)
root/build.sbt:
lazy val prjA: Project = project.in(file("Project-A")).
settings(
name := "Project-A",
version := Common.prjVersion,
scalaVersion := Common.scalaVersion,
libraryDependencies ++= Common.Imports.compileDependencies,
libraryDependencies ++= Common.Imports.testDependencies,
)
lazy val prjB: Project = project.in(file("Project-B")).
settings(
name := "Project-B",
version := Common.prjVersion,
scalaVersion := Common.scalaVersion,
libraryDependencies ++= Common.Imports.compileDependencies,
libraryDependencies ++= Common.Imports.testDependencies,
).dependsOn(prjA)//.dependsOn(prjC % "test->compile")
lazy val prjC: Project = project.in(file("Project-C")).
settings(
name := "Project-C",
version := Common.prjVersion,
scalaVersion := Common.scalaVersion,
libraryDependencies ++= Common.Imports.compileDependencies,
libraryDependencies ++= Common.Imports.testDependencies,
).dependsOn(prjA).dependsOn(prjB)
This build.sbt, as it is written here, runs successful (via sbt clean update compile) but for sure, I cannot start the test-cases in prjB. Once I establish the .dependsOn(prjC % "test->compile") on prjB in my build.sbt, the output is a StackOverflowError - this makes perfectly sense to me, as the cross-dependency between prjB and prjC can not be solved.
However, is there a practical way to solve this endless recursion? I am thinking about one more step in the building process (1 & 2 are done by the actual build.sbt, as you can see), but I don't know how to do that.
First compile prjB with prjA dependency,
Then compile prjC with prjA and prjB dependency
at last include the builded prjC's classes in prjB for testing purposes. <- is this a valid approach?
Best regards and thanks in advance!
This isn't really an SBT problem, but a module dependency problem.
Ignoring A for a moment, since B needs C to compile and C needs B to compile, this cycle cannot be resolved by any build system.
The only way to solve this is to change the structure of the modules themselves. For example, if possible, you could create a D project that contains the common classes and have them both rely on it. Or, use the Dependency Inversion Principle.