Skip to content

User Stories: Conflict Warning

eugene yokota edited this page Apr 24, 2014 · 13 revisions

This is a more detailed spec on sbt#1200 shell should display all evicted libraries on start up, which was inspired by StackOverflow#22551430 In SBT 0.13, does scalaVersion still control the version of scala used for compile, run and test? Meet Alice, a build user. She is using future version of sbt.

User Story: Warn if scalaVersion is no longer effective

Alice specifies her scalaVersion to 2.10.2. She also wants to use Akka 2.3.0.

scalaVersion := "2.10.2"

libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.3.0"

When the first time Alice runs clean and compile or update, sbt detects that scalaVersion specified by Alice is no longer in effect and displays a friendly warning:

[warn] scala version was updated by one of library dependencies:
[warn] - org.scala-lang:scala-library:2.10.2 -> 2.10.3

This warning should apply to any of the libraries from "org.scala-lang" organization including reflection libraries, XML libraries etc.

User Story: Warn if direct dependencies include suspect binary incompatibilities of Java libs

Alice includes Apache commons-io 1.4 and 2.4 in a project:

id := "p1"

libraryDependencies ++= Seq(
  "commons-io" % "commons-io" % "2.4",
  "commons-io" % "commons-io" % "1.4"
)

When the first time Alice runs clean and compile or update, sbt detects that there's a potentially binary incompatible libraries in her project's direct dependencies, and displays a warning indicating that the source of the dependencies is her build:

[warn] There may be incompatibilities among your library dependencies.
[warn] Here are the libraries that were evicted:
[warn] - commons-io:commons-io:1.4 (parent: p1_2.10) -> 2.4

Here we make an assumption that first non-zero segment is different, it's a suspect for binary incompatibility.

Note

Parent info is available as "caller" in ivy resolution report:

			<revision name="1.4" evicted="latest-revision" evicted-reason="" downloaded="false" searched="false" conf="default(compile), default, compile, runtime, master" position="-1">
				<evicted-by rev="2.4"/>
				<caller organisation="p1" name="p1_2.10" conf="compile" rev="1.4" rev-constraint-default="1.4" rev-constraint-dynamic="1.4" callerrev="0.1-SNAPSHOT"/>
				<artifacts>
				</artifacts>
			</revision>

User Story: Warn if transitive dependencies include suspect binary incompatibilities of Java libs

As the project is getting bigger, Alice decides to split it up into two parts.

lazy val util = (project in file("util")).
  settings(
    libraryDependencies += "commons-io" % "commons-io" % "1.4"
  )

lazy val app = (project in file("app")).
  settings(
    libraryDependencies += "commons-io" % "commons-io" % "2.4"
  ).
  dependsOn(util)

When the first time Alice runs clean and compile or update on app project, sbt detects that there's a potentially binary incompatible libraries in her project's dependencies graph, and displays a warning indicating that the source of the dependencies is her build:

[warn] There may be incompatibilities among your library dependencies.
[warn] Here are the libraries that were evicted:
[warn] - commons-io:commons-io:1.4 (parent: util_2.10) -> 2.4

Note

Ivy resolution report includes the parent's name:

			<revision name="1.4" evicted="latest-revision" evicted-reason="" downloaded="false" searched="false" conf="default(compile)" position="-1">
				<evicted-by rev="2.4"/>
				<caller organisation="util" name="util_2.10" conf="compile" rev="1.4" rev-constraint-default="1.4" rev-constraint-dynamic="1.4" callerrev="0.1-SNAPSHOT"/>
				<artifacts>
				</artifacts>
			</revision>

User Story: Warn if transitive dependencies include suspect binary incompatibilities of Scala libs

Alice realizes that Scala libraries break binary compatibility at the minor version updates, unlike Java libraries. In fact, generally accepted naming of major.minor.patch often doesn't apply to Scala libraries at all. Here are some samplings from Top 100 Most popular Scala libraries:

  • Scala standard libraries 2.10.x are binary compatible with 2.10.0, but not with 2.9 or 2.8.
  • ScalaTest 2.1.2 It appears that ScalaTest 2.0.x is source compat, but not bincompat. "However, 2.1.2 is binary compatible with 2.1.0 and 2.1.1"
  • Play 2.2 It appears that Play 2.2 is not compat with Play 2.0 in any way.
  • Not sure about Specs2, but I suspect 2.x and 2.0 are not binary compat.
  • Akka 2.3 It appears that Akka 2.3 is not compat with Akka 2.0 in any way.

Why are they all 2.x? Let's say Alice uses Akka Actor 2.2.4 and 2.3.2.

lazy val util = (project in file("util")).
  settings(
    libraryDependencies ++= Seq(
      "commons-io" % "commons-io" % "1.4",
      "com.typesafe.akka" %% "akka-actor" % "2.2.4"
    )
  )

lazy val app = (project in file("app")).
  settings(
    libraryDependencies ++= Seq(
      "commons-io" % "commons-io" % "2.4",
      "com.typesafe.akka" %% "akka-actor" % "2.3.2"
    )
  ).
  dependsOn(util)

When the first time Alice runs clean and compile or update on app project, sbt detects that there's a potentially binary incompatible libraries in her project's dependencies graph, and displays a warning indicating that the source of the dependencies is her build:

[warn] There may be incompatibilities among your library dependencies.
[warn] Here are the libraries that were evicted:
[warn] - commons-io:commons-io:1.4 (parent: util_2.10) -> 2.4
[warn] - com.typesafe.akka:akka-actor_2.10:2.2.4 (parent: util_2.10) -> 2.3.2

The Scala libraries can be differentiated because many of them append Scala binary version, which is denoted as %% in the libraryDependencies. In those cases, the first two segments should be used regardless of zero.

User Story: Warn if transitive dependencies include suspect binary incompatibilities of cross versioned Scala libs

As a variant of the above story, sbt should be able to detect potential binary incompatibilities among the cross versioned library and non-cross versioned one as follows:

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.2.1",
  "com.typesafe.akka" % "akka-actor" % "2.0.1"
)

User Story: Customize binary incompatibility strategy

Alice realized she can utilize incompatibility report to streamline her internal organization's library usage. Alice can customize incompatibility algorithm based on ModuleID of the library.

def fullCheck(ModuleID, ModuleID): Level = ???

incompatibilityDetection := {
  val old = incompatibilityDetection.value
  {
    case ModuleID("com.example.internal", _, _, _, _, _, _, _, _, _, _) => fullCheck
    case x => old(x)
  }
}