Li Haoyi
Li Haoyi

Reputation: 15802

Defining plugin dependency between subprojects in SBT?

EDIT:

Since I put up the bounty, I thought I should restate the question

How can a SBT project P, with two sub-projects A and B, set up B to have a plugin dependency on A, which is a SBT plugin?

  • Giving P a plugin dependency on A does not work, since A depends on other things in P, which results in a circular dependency graph
  • It has to be a plugin dependency, for A is a plugin needed to run Bs test suite.
  • dependsOn doesn't work, because, well, it has to be a plugin dependency

I'd like to know either of

  • How to do this, or
  • Why this is impossible, and what the next best alternatives are.

EDIT: clarified that it's a plugin-dependency, since build-dependency is ambiguous

Upvotes: 9

Views: 2345

Answers (4)

Li Haoyi
Li Haoyi

Reputation: 15802

As Jacek said, it cannot be done as I would like, as a subproject cannot have a SBT plugin that the root project does not. On the other hand, this discussion on the mailing list contains several alternatives, and would no doubt be useful to anyone who comes across this question in the future.

EDIT: Well, in the end the alternatives mentioned (sbt scripted, etc) were hard and clunky to use. My final solution was to just have a separate project (not subproject) inside the repo that depends on the original project via it's ivy coordinates, and using bash to publishLocal the first project, going into the second project and running its tests

sbt publishLocal; cd test; sbt test; cd ..

I always thought the point of something like SBT was to avoid doing this kind of bash gymnastics, but desperate times call for desperate measures...

Upvotes: 1

Jacek Laskowski
Jacek Laskowski

Reputation: 74669

When you have a multi-project build configuration with "project P and two sub-projects A and B" it boils down to the following configuration:

build.sbt

lazy val A, B = project

As per design, "If a project is not defined for the root directory in the build, sbt creates a default one that aggregates all other projects in the build." It means that you will have an implicit root project, say P (but the name is arbitrary):

[plugin-project-and-another]> projects
[info] In file:/Users/jacek/sandbox/so/plugin-project-and-another/
[info]     A
[info]     B
[info]   * plugin-project-and-another

That gives us the expected project structure. On to defining plugin dependency between B and A.

The only way to define a plugin in a SBT project is to use project directory that's the plugins project's build definition - "A plugin definition is a project in <main-project>/project/." It means that the only way to define a plugin dependency on the project A is to use the following:

project/plugins.sbt

addSbtPlugin("org.example" % "example-plugin" % "1.0")

lazy val plugins = project in file(".") dependsOn(file("../A"))

In this build configuration, the plugins project depends on another SBT project that happens to be our A that's in turn a plugin project.

A/build.sbt

// http://www.scala-sbt.org/release/docs/Extending/Plugins.html#example-plugin
sbtPlugin := true

name := "example-plugin"

organization := "org.example"

version := "1.0"

A/MyPlugin.scala

import sbt._

object MyPlugin extends Plugin
{
    // configuration points, like the built in `version`, `libraryDependencies`, or `compile`
    // by implementing Plugin, these are automatically imported in a user's `build.sbt`
    val newTask = taskKey[Unit]("A new task.")
    val newSetting = settingKey[String]("A new setting.")

    // a group of settings ready to be added to a Project
    // to automatically add them, do
    val newSettings = Seq(
        newSetting := "Hello from plugin",
        newTask := println(newSetting.value)
    )

    // alternatively, by overriding `settings`, they could be automatically added to a Project
    // override val settings = Seq(...)
}

The two files - build.sbt and MyPlugin.scala in the directory A - make up the plugin project.

The only missing piece is to define the plugin A's settings for the project B.

B/build.sbt

MyPlugin.newSettings

That's pretty much it what you can do in SBT. If you want to have multi-project build configuration and have a plugin dependency between (sub)projects, you don't have much choice other than what described above.

With that said, let's see if the plugin from the project A is accessible.

[plugin-project-and-another]> newTask
Hello from plugin
[success] Total time: 0 s, completed Feb 13, 2014 2:29:31 AM
[plugin-project-and-another]> B/newTask
Hello from plugin
[success] Total time: 0 s, completed Feb 13, 2014 2:29:36 AM
[plugin-project-and-another]> A/newTask
[error] No such setting/task
[error] A/newTask
[error]          ^

As you may have noticed, newTask (that comes from the plugin from the project A) is available in the (default) root project and the project B, but not in A.

Upvotes: 12

stefan.schwetschke
stefan.schwetschke

Reputation: 8932

Leaving the facts about plugins by side, you have a parent project P with sub-projects A and B. And then you state that A depends on P. But P is a aggregate of A and B and hence depends on A. So you already have a circular dependency between A and P. This can never work.

You have to split P in two parts: The part where A depends on (let's call this part A') and the rest (let's call this P_rest). Then you throw away P and make a new project P_rest consisting of A', A and B. And A depends on A'.

Upvotes: 0

elm
elm

Reputation: 20415

This answer may include the solution https://stackoverflow.com/a/12754868/3189923 .

From that link, in short, set exportJars := true and to obtain jar file paths for a (sub)project exportedProducts in Compile.

Upvotes: 0

Related Questions