Removing transitive 'project' dependencies in sbt - scala

I was wondering If it is possible to exclude local transitive 'project' dependencies in an sbt build.
given 3 modules: module1, module2 and module3
module 1 depends on module 2
module 2 depends on module 3
in this scenario I want to exclude all all package references from module 3 in module 1, please see below an illustrative example which I tried!
lazy val module1: Project =
(project in file("module1"))
.dependsOn(module2)
.settings(
excludeDependencies := Seq(ExclusionRule("com.x", "module3"))
)
lazy val module2: Project =
(project in file("module2"))
.dependsOn(module3)
lazy val module3: Project =
(project in file("module3"))
.settings(
organization := "com.x",
name := "module3"
)
I expected module 1 to not have any reference to module3 packages, but unfortunately it did, can anybody help?

Related

Root project version is not set for subprojects

I'm packaging multi-project sbt build with sbt package and have the following version property set in the build.sbt root:
version := "1.0.0"
But unfortunately the aggregated subprojects jar are all have 0.1.0-SNAPSHOT suffix unless I specify a version := for each of the subproject specifically. Is there a way to propagate the version := "1.0.0" set in the build.sbt root? Or any other way to set a version for all the aggregated subprojects?
I tried
lazy val root = project
.in(file("."))
.aggregate(
//...
)
.settings(
version := "1.0.0",
//...
)
But it didn't work.
From Examples of scoped key notation in the sbt shell:
ThisBuild / version sets the subproject axis to “entire build” where the build is ThisBuild, with the default configuration.
Therefore, as #LuisMiguelMejíaSuárez mentioned in the comment, the following should do that:
ThisBuild / version := "1.0.0"
lazy val root = project
.in(file("."))
.aggregate(
//...
)
.settings(
//...
)
In general, I'd recommend reading about Scopes in sbt.

sbt multimodule project importing trait from another module

I have an sbt project with multiple modules, each with their own build.sbt file.
In the root project, I have the following:
lazy val commonSettings = Seq(
organization := "com.game.scala",
sourcesInBase := false,
fork in run := true,
scalaVersion := "2.12.1"
)
lazy val common = project.settings(commonSettings)
lazy val original = project.settings(commonSettings).dependsOn(common)
lazy val functional = project.settings(commonSettings).dependsOn(common)
lazy val root = (project in file("."))
.aggregate(original, functional)
.settings(commonSettings)
The build.sbt in all the submodules are pretty much the same:
lazy val module = (project in file("."))
.settings(
name := "Game subpart",
version := "0.1.0-SNAPSHOT"
)
And the layout of the project looks something like:
root
|__ common
|__ original
|__ functional
The problem is that from within the functional module, if I try importing a trait declared in common module, I get an error that it is not available:
Error:(1, 12) object game is not a member of package com
import com.game.scala
What am I missing?
This is because you never compiled your common project. The dependsOn method only add a dependency to the other project, but do no action on it, unless explicitly asked to. If you want your dependency to be re-compiled whenever the functional module is compiled, you should do both dependsOn(common) and aggregate(common).

Intertwined dependencies between sbt plugin and projects within multi-project build that uses the plugin itself

I'm developing a library that includes an sbt plugin. Naturally, I'm using sbt to build this (multi-project) library. My (simplified) project looks as follows:
myProject/ # Top level of library
-> models # One project in the multi-project sbt build.
-> src/main/scala/... # Defines common models for both sbt-plugin and framework
-> sbt-plugin # The sbt plugin build
-> src/main/scala/...
-> framework # The framework. Ideally, the sbt plugin is run as part of
-> src/main/scala/... # compiling this directory.
-> project/ # Multi-project build configuration
Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build?
Note: similar (but simpler) question: How to develop sbt plugin in multi-project build with projects that use it?
Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build?
I have a working example on Github eed3si9n/plugin-bootstrap. It's not super pretty, but it kind of works. We can take advantage of the fact that sbt is recursive.
The project directory is another build inside your build, which knows how to build your build. To distinguish the builds, we sometimes use the term proper build to refer to your build, and meta-build to refer to the build in project. The projects inside the metabuild can do anything any other project can do. Your build definition is an sbt project.
By extension, we can think of the sbt plugins to be library- or inter-project dependencies to the root project of your metabuild.
meta build definition (project/plugins.sbt)
In this example, think of the metabuild as a parallel universe or shadow world that has parallel multi-build structure as the proper build (root, model, sbt-plugin).
To reuse the source code from model and sbt-plugin subprojects in the proper build, we can re-create the multi-project build in the metabuild. This way we don't need to get into the circular dependency.
addSbtPlugin("com.eed3si9n" % "sbt-doge" % "0.1.5")
lazy val metaroot = (project in file(".")).
dependsOn(metaSbtSomething)
lazy val metaModel = (project in file("model")).
settings(
sbtPlugin := true,
scalaVersion := "2.10.6",
unmanagedSourceDirectories in Compile :=
mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "model")
)
lazy val metaSbtSomething = (project in file("sbt-plugin")).
dependsOn(metaModel).
settings(
sbtPlugin := true,
scalaVersion := "2.10.6",
unmanagedSourceDirectories in Compile :=
mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "sbt-plugin")
)
def mirrorScalaSource(baseDirectory: File): Seq[File] = {
val scalaSourceDir = baseDirectory / "src" / "main" / "scala"
if (scalaSourceDir.exists) scalaSourceDir :: Nil
else sys.error(s"Missing source directory: $scalaSourceDir")
}
When sbt loads up, it will build metaModel and metaSbtSomething first, and use metaSbtSomething as a plugin to your proper build.
If you have any other plugins you need you can just add it to project/plugins.sbt normally as I've added sbt-doge.
proper build (build.sbt)
The proper build looks like a normal multi-project build.
As you can see framework subproject uses SomethingPlugin. Important thing is that they share the source code, but the target directory is completely separated, so there are no interference once the proper build is loaded, and you are changing code around.
import Dependencies._
lazy val root = (project in file(".")).
aggregate(model, framework, sbtSomething).
settings(inThisBuild(List(
scalaVersion := scala210,
organization := "com.example"
)),
name := "Something Root"
)
// Defines common models for both sbt-plugin and framework
lazy val model = (project in file("model")).
settings(
name := "Something Model",
crossScalaVersions := Seq(scala211, scala210)
)
// The framework. Ideally, the sbt plugin is run as part of building this.
lazy val framework = (project in file("framework")).
enablePlugins(SomethingPlugin).
dependsOn(model).
settings(
name := "Something Framework",
crossScalaVersions := Seq(scala211, scala210),
// using sbt-something
somethingX := "a"
)
lazy val sbtSomething = (project in file("sbt-plugin")).
dependsOn(model).
settings(
sbtPlugin := true,
name := "sbt-something",
crossScalaVersions := Seq(scala210)
)
demo
In the SomethingPlugin example, I'm defining something task that uses foo.Model.x.
package foo
import sbt._
object SomethingPlugin extends AutoPlugin {
def requries = sbt.plugins.JvmPlugin
object autoImport {
lazy val something = taskKey[Unit]("")
lazy val somethingX = settingKey[String]("")
}
import autoImport._
override def projectSettings = Seq(
something := { println(s"something! ${Model.x}") }
)
}
Here's how we can invoke something task from the build:
Something Root> framework/something
something! 1
[success] Total time: 0 s, completed May 29, 2016 3:01:07 PM
1 comes from foo.Model.x, so this demonstrates that we are using the sbt-something plugin in framework subproject, and that the plugin is using metaModel.

How to set up multi-module build with reference to external local sbt project?

I just started with Scala and Play and I'm trying to set up a multi build with sbt 0.13.5
My project structure is the following:
/AnormCypher
-> /src
->/main
->/scala
->org.anormcypher[package]
->[Some classes]
-> [other dirs/files]
-> build.sbt
/sample
-> /src
->/main
->/scala
->/controllers[package]
->Application.scala
->[Some classes]
-> [other dirs/files]
-> build.sbt
The sample project depends on the AnormCypher project. I tried to set up the dependency following this SO post. My build.sbt in sample looks like this:
name := """sample"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.11.1"
libraryDependencies ++= Seq(
jdbc,
anorm,
cache,
ws
)
lazy val core = ProjectRef(file("../AnormCypher"), "anormcypher")
val main = root.dependsOn(core)
When I go into my console and type
activator
sbt is able to load the project. But when I try to compile the sources and try to use classes from the org.anormcypher package, they can't be resolved:
object anormcypher is not a member of package org
[error] import org.anormcypher._
[error] ^
Running a clean compile also brought no results.
Change
lazy val root = (project in file(".")).enablePlugins(PlayScala)
to
lazy val root = (project in file(".")).enablePlugins(PlayScala).dependsOn(core)
and remove
val main = root.dependsOn(core)
reload and the project should work fine.

How do I publish a fat JAR (JAR with dependencies) using sbt and sbt-release?

I need to build a single jar, including dependencies, for one of my sub-projects so that it can be used as a javaagent.
I have a multi-module sbt project and this particular module is the lowest level one (it's also pure Java).
Can I (e.g. with sbt-onejar, sbt-proguard or sbt assembly) override how the lowest level module is packaged?
It looks like these tools are really designed to be a post-publish step, but I really need a (replacement or additional) published artefact to include the dependencies (but only for this one module).
UPDATE: Publishing for sbt-assembly are instructions for a single project, and doesn't easily translate into multi-project.
Publishing for sbt-assembly are instructions for a single project, and doesn't easily translate into multi-project.
People have been publishing fat JAR using sbt-assembly & sbt-release without issues. Here's a blog article from 2011: Publishing fat jar created by sbt-assembly. It boils down to adding addArtifact(Artifact(projectName, "assembly"), sbtassembly.AssemblyKeys.assembly) to your build.sbt (note that the blog is a little out of date AssemblyKeys is now a member of sbtassembly directly).
For sbt 0.13 and above, I prefer to use build.sbt for multi-projects too, so I'd write it like:
import AssemblyKeys._
lazy val commonSettings = Seq(
version := "0.1-SNAPSHOT",
organization := "com.example",
scalaVersion := "2.10.1"
)
val app = (project in file("app")).
settings(commonSettings: _*).
settings(assemblySettings: _*).
settings(
artifact in (Compile, assembly) ~= { art =>
art.copy(`classifier` = Some("assembly"))
}
).
settings(addArtifact(artifact in (Compile, assembly), assembly).settings: _*)
See Defining custom artifacts:
addArtifact returns a sequence of settings (wrapped in a SettingsDefinition). In a full build configuration, usage looks like:
...
lazy val proj = Project(...)
.settings( addArtifact(...).settings : _* )
...