4

Let's say I'm writing a Scala library L that depends on some dependency D and is consumed by a program P and another program Q. P depends on version 3.2 of D directly while Q depends on version 3.3 directly.

Between those two versions D's API was shuffled so that to get the same function I use in L, I must write different import statements in L. Likewise P relies on 3.2-specific behavior whereas Q relies on 3.3-specific behavior.

Now, normally what will happen is that the most recent version of D will be chosen when compiling P and Q, but this will cause either P to break if L depends on version 3.3 of the library or L to break when compiling Q if L depends on version 3.2 of D.

I would ideally like the same version of L to be used by both P and Q since L's public API does not change. Is this possible?

The general method that comes to mind is conditional compilation of L based on dependency resolution. This seems unachievable though in the JVM world since we don't transitively compile a project's dependencies and instead rely on precompiled artifacts.

I can do this right now with SBT if D is Scala itself (i.e. cross-compiling with different Scala versions and having version specific code live in its own directories), but this is something of a hack from the viewpoint of dependency resolution since SBT changes the names of the artifacts to allow this cross-compilation to work.

badcook
  • 3,699
  • 14
  • 25

1 Answers1

4

You can tell sbt to handle dependencies as intransitive:

libraryDependencies ++= Seq(
  "org.some.id" % "some-lib" % "1.0.foobar" intransitive()
)

would add some-lib to the dependencies but would not follow the dependencies of that lib. So if some-lib had a dependency to some-other-lib that other lib would not be downloaded.

Maybe a bit more specific. Say, you need several libraries all using SLF4J as a logging interface. Maybe, some of the libraries require a slightly different version, but without any API differences. So they could all work with the same SLF4J version. You would then mark all theses libraryDependencies as intransitive and add SLF4J itself once as top level library dependency.

libraryDependencies ++= Seq(
 "org.slf4j" % "slf4j-api" % 1.7.6,
 "com.typesage.slick" %% "slick" % "2.0.0" intransitive(),
 "com.typesafe.akka" %% "akka-slf4j" % "2.2.0" intransitive()
)

Am I making sense?

Sascha Kolberg
  • 7,092
  • 1
  • 31
  • 37
  • That's not quite the problem I'm facing. To take your SLF4J example, the problem I'm running into is akin to if Slick used version 1.7 of SLF4J, but could also use version 1.6 with some small modifications to its code. The application using Slick must use version 1.6 and breaks on 1.7. Can the Slick authors accommodate this use case by allowing the piece of adapter code needed to accommodate 1.6 be compiled if 1.6 is present and have another piece of code compiled if 1.7 is present? – badcook Jul 22 '15 at 19:03
  • For reference, in other language ecosystems, this is often accomplished via preprocessor directives that result in one code path if certain dependencies are detected and another if other dependencies are detected. – badcook Jul 22 '15 at 19:05