How to fix the dependency graph to resolve deduplicate errors? - sbt

I'm facing some deduplicate errors like below, which indicate that some of my dependencies import other dependencies which contains files with the same path name. Since they have the same path, they cannot be included together in the jar file that I'm trying to create with sbt-assembly.
I understand that the clean way to fix it is by fixing my dependencies. The conflicting dependencies from the example below seem to be reactive-streams and reactive-streams-flow-adapters, but I'm not sure what they are and where they come from. How can I find which of my dependencies are importing them?
If I can figure that out, how can I fix it? Is there a way other than just removing one of them?
An example of deduplicate errors:
[error] (assembly) deduplicate: different file contents found in the following:
[error] /home/guillaume/.cache/coursier/v1/https/repo1.maven.org/maven2/org/reactivestreams/reactive-streams-flow-adapters/1.0.2/reactive-streams-flow-adapters-1.0.2.jar:org/reactivestreams/FlowAdapters$FlowPublisherFromReactive.class
[error] /home/guillaume/.cache/coursier/v1/https/repo1.maven.org/maven2/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar:org/reactivestreams/FlowAdapters$FlowPublisherFromReactive.class
Here is a list of my dependencies, if it helps:
libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.12.2",
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
libraryDependencies += "com.softwaremill.sttp.client3" %% "core" % "3.3.6",
libraryDependencies += "com.softwaremill.sttp.client3" %% "httpclient-backend-zio" % "3.3.6",
libraryDependencies += "dev.zio" %% "zio" % "1.0.9",
// libraryDependencies += "dev.zio" %% "zio-streams" % "1.0.9",
libraryDependencies += "org.apache.commons" % "commons-compress" % "1.20",
libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.9",
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.9" % "test",
libraryDependencies += ("tech.sparse" %% "toml-scala" % "0.2.2").cross(CrossVersion.for3Use2_13),

I'm not familiar with Reactive Streams, but it looks like they made some changes during the patch version and moved classes around in 1.0.3. See Reactive Streams 1.0.3 is here!
When we released 1.0.2, we shipped a compatibility/conversion library to seamlessly convert between the java.util.concurrent.Flow and the org.reactivestreams namespaces—in 1.0.3 these adapters are instead included in the main 1.0.3 jar.
Investigate transitive dependencies using dependencyTree
How can I find which of my dependencies are importing them?
If you're using the latest sbt 1.5.4, it has Johannes's dependency graph plugin built-in by default, so you can run dependencyTree from sbt shell:
sbt:root> dependencyTree
[info] root:root_2.13:0.1.0-SNAPSHOT [S]
....
[info] +-com.softwaremill.sttp.client3:httpclient-backend-zio_2.13:3.3.6 [S]
[info] | +-com.softwaremill.sttp.client3:httpclient-backend_2.13:3.3.6 [S]
[info] | | +-com.softwaremill.sttp.client3:core_2.13:3.3.6 [S]
[info] | | | +-com.softwaremill.sttp.model:core_2.13:1.4.7 [S]
[info] | | | +-com.softwaremill.sttp.shared:core_2.13:1.2.5 [S]
[info] | | | +-com.softwaremill.sttp.shared:ws_2.13:1.2.5 [S]
[info] | | | +-com.softwaremill.sttp.model:core_2.13:1.4.7 [S]
[info] | | | +-com.softwaremill.sttp.shared:core_2.13:1.2.5 [S]
[info] | | |
[info] | | +-org.reactivestreams:reactive-streams-flow-adapters:1.0.2
[info] | | +-org.reactivestreams:reactive-streams:1.0.2 (evicted by: 1.0.3)
[info] | | +-org.reactivestreams:reactive-streams:1.0.3
....
This tells us that org.reactivestreams:reactive-streams-flow-adapters:1.0.2 was pulled in from com.softwaremill.sttp.client3:httpclient-backend_2.13:3.3.6.
Excluding reactive-streams-flow-adapters
If I can figure that out, how can I fix it? Is there a way other than just removing one of them?
In this case, reactive-streams-flow-adapters is subsumed by reactive-streams post-1.0.3, so I think we can exclude it from the dependency as follows:
libraryDependencies += ("com.softwaremill.sttp.client3" %% "httpclient-backend-zio" % "3.3.6")
.exclude("org.reactivestreams", "reactive-streams-flow-adapters"),
Ignoring module-info.class
To ignore module-info.class we can add this to build.sbt:
ThisBuild / assemblyMergeStrategy := {
case "module-info.class" => MergeStrategy.discard
case x =>
val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
oldStrategy(x)
}
Result
sbt:root> assembly
[info] Strategy 'discard' was applied to 10 files (Run the task at debug level to see details)
[success] Total time: 3 s, completed Jun 26, 2021 6:02:04 PM

Related

Explaining the Difference between Expressions defining libraryDependencies

I'm unable to understand the difference between the following expressions. The expressions a reduced -- the real world case has more settings distributed in seperate objects.
specified directly as sequence
Seq(libraryDependencies +=
"org.openjdk.jmh" % "jmh-core" % "1.6.2" % "compile")
wrapped in inConfig
inConfig(Compile)(libraryDependencies +=
"org.openjdk.jmh" % "jmh-core" % "1.6.2" % "compile")
In both cases show compile:libraryDependencies shows the same
[info] List(org.scala-lang:scala-library:2.10.4, org.openjdk.jmh:jmh-core:1.6.2:compile)
but for show compile:managedClasspath the dependency towards JMH is only shown in the first case. As a result the normal compiler run fails due to unresolvable classes.
Please explain or point the logical difference between both cases.
TLDR: Use % "compile" not Compile when declaring libraryDependencies
What you're seeing is a gap, possibly a bug, in how sbt tries to use Ivy's module configurations (such as compile) as one of its setting scopes.
For reference:
Official docs about Scopes
Blog post about sbt "dimensions"
The problem lies that, currently, you can declare the configuration at both the value level:
"org.openjdk.jmh" % "jmh-core" % "1.6.2" % "compile"
and the key level:
inConfig(Compile)(libraryDependencies += xyz)
or alternatively:
libraryDependencies in Compile += xyz
As you say in both examples show compile:libraryDependencies shows the same sequence, but show libraryDependencies demonstrates that you only added jmh-core in the Compile axes of libraryDependencies:
show libraryDependencies
[info] List(org.scala-lang:scala-library:2.10.4, org.openjdk.jmh:jmh-core:1.6.2:compile)
show libraryDependencies
[info] List(org.scala-lang:scala-library:2.10.4)
This then leads as to why show compile:managedClasspath is different.
Have a look at what inspect actual compile:managedClasspath outputs:
[info] Task: scala.collection.Seq[sbt.Attributed[java.io.File]]
[info] Description:
[info] The classpath consisting of external, managed library dependencies.
[info] Provided by:
[info] {file:/Users/dnw/Desktop/t-2015-04-08.0540/}t-2015-04-08-0540/compile:managedClasspath
[info] Defined at:
[info] (sbt.Classpaths) Defaults.scala:991
[info] Dependencies:
[info] *:update
[info] */*:classpathTypes
[info] compile:classpathConfiguration
[info] compile:managedClasspath::streams
[info] Reverse dependencies:
[info] compile:externalDependencyClasspath
[info] Delegates:
[info] compile:managedClasspath
[info] *:managedClasspath
[info] {.}/compile:managedClasspath
[info] {.}/*:managedClasspath
[info] */compile:managedClasspath
[info] */*:managedClasspath
[info] Related:
[info] test:managedClasspath
[info] runtime:managedClasspath
The thing to note is its dependency on *:update, which isn't scoped to compile. From there it eventually leads to *:libraryDependencies which in your second example doesn't include jmh-core.

How to add custom resource directory to test classpath without copying the files inside?

When I run my tests, the contents of my special resources directory special-resources are copied to the target/classes directory. I have something like this
unmanagedResourceDirectories in Compile += baseDirectory.value / "special-resources",
But I do not want to copy these resources into the target directory, but I want them to be on the classpath of forked java processes (e.g. for testing).
I have tried using
unmanagedClasspath in Compile += baseDirectory.value / "special-resources",
but the resources are not available.
How can I add a resource directory to the classpath without having to copy the files? Or, alternatively, how can I setup sbt to not copy resources to the target directory?
To have the contents of the special-resources directory included in the classpath for tests and runMain task, do the following:
unmanagedClasspath in Test += baseDirectory.value / "special-resources"
unmanagedClasspath in (Compile, runMain) += baseDirectory.value / "special-resources"
Check that the setting is set properly with show:
> show test:unmanagedClasspath
[info] List(Attributed(C:\dev\sandbox\runtime-assembly\special-resources))
With the following Specs2 tests I'm convinced the setup worked fine:
import org.specs2.mutable._
class HelloWorldSpec extends Specification {
"Hello world" should {
"find the file on classpath" in {
val text = io.Source.fromInputStream(getClass.getResourceAsStream("hello.txt")).mkString
text must have size(11)
}
}
}
hello.txt is in the special-resources directory with a hello world string inside.
> ; clean ; test
[success] Total time: 0 s, completed 2014-08-06 20:00:02
[info] Updating {file:/C:/dev/sandbox/runtime-assembly/}runtime-assembly...
[info] Resolving org.jacoco#org.jacoco.agent;0.7.1.201405082137 ...
[info] Done updating.
[info] Compiling 1 Scala source to C:\dev\sandbox\runtime-assembly\target\scala-2.10\test-classes...
[info] HelloWorldSpec
[info]
[info] Hello world should
[info] + find the file on classpath
[info]
[info] Total for specification HelloWorldSpec
[info] Finished in 17 ms
[info] 1 example, 0 failure, 0 error
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
And build.sbt:
unmanagedClasspath in Test += baseDirectory.value / "special-resources"
libraryDependencies += "org.specs2" %% "specs2" % "2.4" % "test"
fork in Test := true

How to set up sbt-assembly to include jars with runtime scope?

How to set up sbt-assembly to include jars with runtime scope? What's the rationale for not doing it by default?
This is now being discussed under sbt/sbt-assembly#120.
sbt-assembly is intended to wire full classpath from Runtime configuration. And it's coded as follows:
fullClasspath in assembly <<= fullClasspath or (fullClasspath in Runtime),
For Spark, for whatever reason fullClasspath is wired, so that's likely interfering. The reason sbt-assembly included fullClasspath I think is now historical, so I might get rid of "fullClasspath or" part. As noted in the issue, the current workaround is likely:
fullClasspath in assembly := (fullClasspath in Runtime).value
FYI for those who learn sbt the above sbt-0.13+ format with := corresponds to the older pre-sbt-0.13 format with := as follows:
fullClasspath in assembly <<= (fullClasspath in Runtime)
My tests show that the plugin does include dependencies with runtime scope as evidenced with the following example.
project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
build.sbt
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.2" % "runtime"
assemblySettings
This is a sample session:
> about
[info] This is sbt 0.13.6-SNAPSHOT
[info] The current project is {file:/C:/dev/sandbox/runtime-assembly/}runtime-assembly 0.1-SNAPSHOT
[info] The current project is built against Scala 2.10.4
[info] Available Plugins: sbt.plugins.IvyPlugin, sbt.plugins.JvmPlugin, sbt.plugins.CorePlugin, sbt.plugins.JUnitXmlReportPlugin, org.sbtidea.SbtIdeaPlugin, de.johoop.jacoco4sbt.JacocoPlugin, com.timushev.sbt.updates.UpdatesPlugin, sbtassembly.Plugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.10.4
> assembly
[info] Updating {file:/C:/dev/sandbox/runtime-assembly/}runtime-assembly...
[info] Resolving org.jacoco#org.jacoco.agent;0.6.4.201312101107 ...
[info] downloading http://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.1.2/logback-classic-1.1.2.jar ...
[info] [SUCCESSFUL ] ch.qos.logback#logback-classic;1.1.2!logback-classic.jar (1122ms)
[info] downloading http://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.1.2/logback-core-1.1.2.jar ...
[info] [SUCCESSFUL ] ch.qos.logback#logback-core;1.1.2!logback-core.jar (1313ms)
[info] Done updating.
[info] Including: slf4j-api-1.7.6.jar
[info] Including: logback-classic-1.1.2.jar
[info] Including: logback-core-1.1.2.jar
[info] Including: scala-library.jar
[info] Checking every *.class/*.jar file's SHA-1.
[info] Merging files...
[warn] Merging 'META-INF\MANIFEST.MF' with strategy 'discard'
[warn] Strategy 'discard' was applied to a file
[info] SHA-1: 0f520cdfc30de8ced8408a3c25b326b3ff5afda8
[info] Packaging C:\dev\sandbox\runtime-assembly\target\scala-2.10\runtime-assembly-assembly-0.1-SNAPSHOT.jar ...
[info] Done packaging.
Jar verification:
/cygdrive/c/dev/sandbox/runtime-assembly
$ jar -tf target/scala-2.10/runtime-assembly-assembly-0.1-SNAPSHOT.jar | grep logback | wc -l
592

In SBT, how does one `addCompilerPlugin` from Git?

Normally, when not using Git, you can just write:
addCompilerPlugin("something" % "blah" ...)
scalacOptions += "-P:blah:..."
This addCompilerPlugin takes a ModuleID. But here... I've tried adding:
lazy val root = project in file(".") dependsOn
uri("git://github.com/puffnfresh/wartremover.git#master")
to the project/plugins.sbt as well as:
lazy val wartRemover = RootProject(
uri("git://github.com/puffnfresh/wartremover.git#master"))
lazy val root = Project(...).settings(
...
scalacOptions += "-P:wartremover:...",
...
) depends on wartRemover
Both result in:
[error] (root/*:update) sbt.ResolveException: unresolved dependency:
org.brianmckenna#wartremover_2.10.3;0.5-SNAPSHOT: not found
tl;dr The project wartremover has not been published for Scala version 2.10.3. Downgrade yours with the following in build.sbt among the other necessary settings:
scalaVersion := "2.10.2"
Detailed procedure focusing on Scala 2.10.3
The following in build.sbt
addCompilerPlugin("org.brianmckenna" % "wartremover" % "0.5" cross CrossVersion.full)
adds org.brianmckenna:wartremover:0.5:plugin->default(compile) to libraryDependencies.
[sbt-0-13-2]> show libraryDependencies
[info] List(org.scala-lang:scala-library:2.10.3, org.brianmckenna:wartremover:0.5:plugin->default(compile))
So to use a RootProject that points at the project wartremover at GitHub I had to use the following in build.sbt (this is the complete file):
scalacOptions in root += "-P:wartremover:traverser:org.brianmckenna.wartremover.warts.Unsafe"
lazy val root = project in file(".") dependsOn wartRemover % "plugin->default(compile)"
lazy val wartRemover = RootProject(
uri("git://github.com/puffnfresh/wartremover.git#master"))
Since the project wartremover is not published for 2.10.3 I followed the steps below:
Show available projects
[root]> projects
[info] In file:/Users/jacek/sandbox/so/sbt-0.13.2/
[info] * root
[info] In git://github.com/puffnfresh/wartremover.git#master
[info] wartremover
Switch to wartremover and publishLocal it for scalaVersion set to 2.10.3.
[wartremover]> set scalaVersion := "2.10.3"
[info] Defining wartremover/*:scalaVersion
[info] The new value will be used by wartremover/*:allDependencies, wartremover/*:assemblyPackageScala::assemblyDefaultJarName and 12 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] Set current project to wartremover (in build git://github.com/puffnfresh/wartremover.git#master)
[wartremover]> publishLocal
[info] Packaging /Users/jacek/.sbt/0.13/staging/d6dd3d2e3d818e69943a/wartremover/target/scala-2.10/wartremover_2.10.3-0.6-SNAPSHOT-sources.jar ...
[info] Updating {git://github.com/puffnfresh/wartremover.git#master}wartremover...
...
[info] published ivy to /Users/jacek/.ivy2/local/org.brianmckenna/wartremover_2.10.3/0.6-SNAPSHOT/ivys/ivy.xml
[success] Total time: 7 s, completed Jan 18, 2014 11:34:07 PM
Switch to the project root and do update. It should now work fine.
[wartremover]> project {file:/Users/jacek/sandbox/so/sbt-0.13.2/}
[info] Set current project to root (in build file:/Users/jacek/sandbox/so/sbt-0.13.2/)
[root]> update
[info] Updating {file:/Users/jacek/sandbox/so/sbt-0.13.2/}root...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[success] Total time: 0 s, completed Jan 18, 2014 11:36:24 PM
console should work fine now, too.
[root]> console
[info] Starting scala interpreter...
[info]
<console>:5: error: var is disabled
var value: scala.tools.nsc.interpreter.IMain = _
^
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
The error message worries me, though. I don't know the plugin and neither do I know how to get rid of it. It also happens when I follow the steps described in Compiler plugin when scalaVersion := "2.10.2" is set in build.sbt (so the compiler plugin's available in the repo).

SBT, Jetty and Servlet 3.0

I have a really tiny problem.
I have the following build.sbt file:
name := "Tueet"
libraryDependencies += "org.eclipse.jetty" % "jetty-webapp" % "8.1.2.v20120308"
After invoking sbt update, I get the following:
[info] Set current project to Tueet (in build file:/C:/dev/tueet/)
[info] Updating {file:/C:/dev/tueet/}default-d5e982...
[info] Resolving org.scala-lang#scala-library;2.9.1 ...
[info] Resolving org.eclipse.jetty#jetty-webapp;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-xml;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-util;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-servlet;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-security;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-server;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty.orbit#javax.servlet;3.0.0.v201112011016 ...
[info] Resolving org.eclipse.jetty#jetty-continuation;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-http;8.1.2.v20120308 ...
[info] Resolving org.eclipse.jetty#jetty-io;8.1.2.v20120308 ...
[warn] [NOT FOUND ] org.eclipse.jetty.orbit#javax.servlet;3.0.0.v201112011016!javax.servlet.orbit (603ms)
[warn] ==== public: tried
[warn] http://repo1.maven.org/maven2/org/eclipse/jetty/orbit/javax.servlet/3.0.0.v201112011016/javax.servlet-3.0.0.v201112011016.orbit
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: FAILED DOWNLOADS ::
[warn] :: ^ see resolution messages for details ^ ::
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: org.eclipse.jetty.orbit#javax.servlet;3.0.0.v201112011016!javax.servlet.orbit
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[error] {file:/C:/dev/tueet/}default-d5e982/*:update: sbt.ResolveException: download failed: org.eclipse.jetty.orbit#javax.servlet;3.0.0.v201112011016!javax.servlet.orbit
[error] Total time: 1 s, completed 2012-03-27 14:33:34
This is silly, as it works in Maven no prob. I found out that this is because Orbit does something with packaging (they set it to orbit apparently).
I tried doing exclude("org.eclipse.jetty.orbit", "javax.servlet") but nothing happened and it still needed that dependency.
I couldn't find any info on how to actually fix this, maybe someone will help me here :)
Update: the presented bug provides a workaround, so to fix this problem I actually changed build.sbt to
name := "Tueet"
libraryDependencies += "org.eclipse.jetty" % "jetty-server" % "8.1.2.v20120308"
ivyXML :=
<dependency org="org.eclipse.jetty.orbit" name="javax.servlet" rev="3.0.0.v201112011016">
<artifact name="javax.servlet" type="orbit" ext="jar"/>
</dependency>
See this bug: https://jira.codehaus.org/browse/JETTY-1493
The crux of the issue is that ivy doesn't support the orbit extension and needs to map the orbit packaging type to jar. Not sure if you're using ivy or not there, but the fundamental reason is the same, you can see that by looking at the url it is downloading from maven central.
This bug has a bit more of the background on the reason we switched to these dependencies in the first place.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=371954
I found a workaround. Further information can be found here: SBT, Jetty and Servlet 3.0.
classpathTypes ~= (_ + "orbit")
libraryDependencies ++= Seq(
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container" artifacts (Artifact("javax.servlet", "jar", "jar")
)
)
libraryDependencies ++= Seq(
"org.eclipse.jetty" % "jetty-webapp" % "8.1.4.v20120524" % "container" artifacts (Artifact("jetty-webapp", "jar", "jar"))
)
with sbt 0.12+ (from:https://github.com/sbt/sbt/issues/499)
libraryDependencies ++= Seq(
"org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "container",
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container" artifacts Artifact("javax.servlet", "jar", "jar")
)

Resources