cb372/sbt-explicit-dependencies

Question: Inter-Project Dependencies

diesalbla opened this issue · 2 comments

This is an issue-discussion, but I have no inner SBT knowledge to know if this is feasible.

This is a great plugin for detecting the unused or undeclared entries in libraryDependencies, the ones that SBT has to resolve through ivy. However, there are Scala repositories that may be organised with a multi-project build, which may have scores of subprojects, which are also interconnected with interproject dependencies. This creates several challenges for explicit dependency declaration.

  • First, the same non-explicit transitive dependencies between libraries, may also appear between subprojects, for example if a subproject A declares a dependency on B, and B declares one on C, but A is invoking a function from C through its declared dependency on B.
  • Second, and also between subprojects, it may happen that A declares dependency on subproject B that is not needed.
  • Finally, there is the interaction between library dependencies and dependencies between sub-projects. One sub-project A, which declares a dependency on a sub-project B, may be using a library dependency that it nether declares, nor is it a transitive library dependency of those that A declares, but which is a library dependency (explicit of transitive) of sub-project B.

The goal of this issue would be to investigate how these problems can be solved in this plugin.

cb372 commented

I had a quick play around with what's available in sbt and zinc, and I think we could do something.

foo := {
  println("Declared dependencies:")
  println(buildDependencies.value.classpathRefs(thisProjectRef.value))
  println("allExternalDeps:")
  compile.in(Compile).value.asInstanceOf[sbt.internal.inc.Analysis].relations.allExternalDeps.foreach(println)
}
sbt:scalacache> cats-effect/foo
Declared dependencies:
List(ProjectRef(file:/Users/chris/code/scalacache/,coreJVM))
allExternalDeps:
scalacache.Async
scalacache.Mode

The first part of the output shows us that the cats-effect project (in ScalaCache) declares a dependency on the coreJVM project.

The second part shows that the project depends on 2 classes in other projects for its compilation. Both of these classes are in the coreJVM project.

So a rough implementation would look like:

  1. Get the list of projects that this project declares a dependency on

  2. Compile all projects

  3. Look in each project's target directory to build a data structure of which projects contain which classes. So a Map[ProjectRef, Set[String]] or something similar. (Maybe there's an easier way to get this info from sbt without having to traverse the target directory.)

  4. Use that data, along with the allExternalDeps, to work out which projects this project actually depends on for compilation. Let's call this result actual dependencies.

Then it's simply

undeclared dependencies = actual dependencies - declared dependencies

and

unused dependencies = declared dependencies - actual dependencies

This plugin is great to find unused dependencies on single project.
Any updates on how to find unused dependencies in multi-project using this plugin?