9

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

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
Li Haoyi
  • 15,330
  • 17
  • 80
  • 137
  • How does your `subB/project/build.sbt` look like? You should also see [How to use plugin in sbt project when only the plugin's sources available?](http://stackoverflow.com/q/20794632/1305344). – Jacek Laskowski Feb 02 '14 at 14:04
  • It seems to me that that question does not help, for it only goes into how to add a source plugin like `subA` to the root project's build, and not to one of the subprojects like `subB`. I need to be able to add `subA` to `subB`s build and not root because if root depends on `subA` and `subA` is built by root, I have this circular dependency and nothing works – Li Haoyi Feb 02 '14 at 21:09
  • Just a quick suggestion: try to put stuff in `subB/build.sbt` instead of `subB/project/build.sbt` like you described. – yǝsʞǝla Feb 03 '14 at 00:19
  • The problem is that this stuff I'm putting in `subB/project/build.sbt` is a SBT plugin (or at least a reference to `subA`, which is an SBT plugin). AFAICT putting it in `subB/build.sbt` won't do the right thing since it the results of that plugin *need to be visible in `subB/build.sbt`* – Li Haoyi Feb 03 '14 at 11:09
  • Can you clarify why you need them to both be subprojects of the same parent project? Also does A depend on things that are directly in P or in another subproject? – nafg Feb 10 '14 at 04:50
  • I need both to be subprojects of the same parent project so I can run `test` on all of them from the same SBT console. If I was happy separating them into completely separate projects and jumping back and forth manually, I might as well use Bash for my builds (it actually seems like it might be easier than SBT at this point) – Li Haoyi Feb 10 '14 at 04:52
  • Oh and `A` depends on stuff in another subproject of `P` – Li Haoyi Feb 10 '14 at 04:54

4 Answers4

12

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.

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
  • 1
    +1 for an amazing, detailed answer that I failed to understand most of (but that's because of my sbt learning curve - when I need this, it's nice to know it's answered!) – akauppi Jul 27 '14 at 18:58
  • Just ask questions when you're in trouble or doubt. The sbt community is quite vibrant so your beginner problems should be answered in secs...hopefully. – Jacek Laskowski Jul 27 '14 at 19:01
  • This is an example of projects/plugins.sbt when you have multiple plugins: http://paste.ubuntu.com/11777916/ – Richard Gomes Jun 26 '15 at 10:44
1

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...

Community
  • 1
  • 1
Li Haoyi
  • 15,330
  • 17
  • 80
  • 137
0

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.

Community
  • 1
  • 1
elm
  • 20,117
  • 14
  • 67
  • 113
  • Looks promising, but still doesn't solve the problem of getting `B` to depend on `A` as a plugin without making `P` depend on it – Li Haoyi Feb 11 '14 at 17:25
  • This other awnser http://stackoverflow.com/a/17904023/3189923 may address the issue as to how to lighten up dependencies/aggregation between subprojects. – elm Feb 12 '14 at 07:58
0

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'.

stefan.schwetschke
  • 8,862
  • 1
  • 26
  • 30
  • It needs to be a *plugin* dependency though, not a normal dependency, so the process you're (i think) describing wouldn't work. – Li Haoyi Feb 11 '14 at 17:24