Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a library dependency via an sbt plugin - per sub-project

Tags:

sbt

scalac

I am trying to add a library dependency through an sbt plugin. The dependency should be added to each sub-project per its binary scala version, so I iterate through each subproject.

  private def inject(): State => State = { state =>
    val extracted: Extracted = Project.extract(state)

    val enrichedLibDepSettings = extracted.structure.allProjectRefs map { projRef =>

      val projectScalaVersion = (scalaBinaryVersion in projRef)

      libraryDependencies in projRef += 
        compilerPluginOrg % (compilerPluginArtifact + "_" + projectScalaVersion.value) % compilerPluginVersion % "provided"
    }

    val newState = extracted.append(enrichedLibDepSettings, state)

    val updateAfterLibAppend = extracted.structure.allProjectRefs map { projRef => 
      println("running update: " + EvaluateTask(extracted.structure, update, newState, projRef)) }
      state
  }

However this is not working - the printed output shows no trace of a library dependency being appended through libraryDependencies in projRef +=, nor is any error issued, leaving subsequent steps to fail over the missing dependency. What might be wrong with this technique?

You will ask why is this needed in the first place? why add a library dependency through an sbt plugin like that?

Although we have in sbt addCompilerPlugin, it cannot be used for compiler plugins that have arguments (-Xplugin with a path to a jar must be specified to scalac, for it to accept compiler plugin arguments, as far as experimentation shows). Hence we need to inject the compiler plugin via -Xplugin after having it resolved as a library dependency (then fiddle its file path location inspecting the result of update). Hence we do need to add a library dependency via an sbt plugin. And we further need to do this per sub-project, as a multi-project build may house sub-projects of varying scala versions - each one must have a binary compatible compiler plugin injected, in order to maintain binary compatibility.

By the way, and this might illuminate something I'm in the dark over: When adding the library dependency in a projectSettings override for the root project - as below - the dependency seems to resolve, but that is useless, as it will apply the same binary version to all sub-projects, which is against the nature of the task at hand (some sub-projects will naturally crash over binary incompatibility). Also I think it will override the root's settings whereas the goal here is to append a setting not to override existing settings.

object Plugin extends AutoPlugin {
  override lazy val projectSettings = Seq(
    ...
}

A pair of clues?

  1. Appending scalacOptions per sub-project - using the same technique - simply works.

  2. Applying += to libraryDepenencies above, does not even affect the output of inspect libraryDependencies, unlike when using the same idiom inside a an override lazy val projectSettings block of an AutoPlugin.

like image 367
matanster Avatar asked Dec 13 '15 16:12

matanster


People also ask

How do I use sbt plugins?

If you're curious which auto plugins are enabled for a given project, just run the plugins command on the sbt console. Here, the plugins output is showing that the sbt default plugins are all enabled. sbt's default settings are provided via three plugins: CorePlugin : Provides the core parallelism controls for tasks.

Where are dependencies downloaded in sbt?

All new SBT versions (after 0.7. x ) by default put the downloaded JARS into the . ivy2 directory in your home directory. If you are using Linux, this is usually /home/<username>/.

What is provided dependency in sbt?

The “provided” keyword indicates that the dependency is provided by the runtime, so there's no need to include it in the JAR file. When using sbt-assembly, we may encounter an error caused by the default deduplicate merge strategy. In most cases, this is caused by files in the META-INF directory.

How to declare project dependencies in SBT?

Project dependencies can be declared in project/plugins.sbt (similarly to build.sbt file in a normal project) and will be available to the build definitions. The build definition classpath is searched for sbt/sbt.autoplugins descriptor files containing the names of sbt.AutoPlugin implementations.

How do I add library dependencies?

Library dependencies can be added in two ways: 1 unmanaged dependencies are jars dropped into the lib directory 2 managed dependencies are configured in the build definition and downloaded automatically from repositories More ...

What is the difference between a minimal sbt project and plugin?

It is a normal sbt project whose classpath is available to all sbt project definitions for that user as described above for per-project plugins. A minimal sbt plugin is a Scala library that is built against the version of Scala that sbt runs (currently, 2.12.16) or a Java library. Nothing special needs to be done for this type of library.

Can I use external libraries in my SBT projects?

You want to use one or more external libraries (dependencies) in your Scala /SBT projects. You can use both managed and unmanaged dependencies in your SBT projects.


1 Answers

I think you might be confused about what projectSettings is. If you extend AutoPlugin, you can define the default settings that are applied (on top of the defaults) for each project, see https://github.com/sbt/sbt/blob/v0.13.9/main/src/main/scala/sbt/Plugins.scala#L81

This means you could simply add your artefact here using the typical Setting / Task notation, e.g.

def projectSettings = Seq(
  libraryDependencies += {
    val bin = scalaBinaryVersion.value
    ...
  }
)

note that this is +=, not :=. Does that help?

like image 181
fommil Avatar answered Sep 20 '22 09:09

fommil