82

I'm developing several modules with dependencies among them, and would like to work with them all together in one IDEA project. I'm using sbt-idea to generate IDEA projects from the sbt build definitions, which works great for individual projects. In the multiple-module case, however, the things I've tried so far don't quite work:

Use sbt-idea to generate an IDEA .iml file for each module independently; then create a master IDEA project from scratch an add those modules to it. This makes the module sources all editable in the same window, but the dependencies among them are not tracked (so trying to navigate from some source within the foo project to something in bar takes me to the imported library version of bar, not the local sources).

Use sbt multi-project builds (aka subprojects), where the parent project's Build.scala contains things like:

lazy val foo = Project(id = "foo", base = file("foo"))
lazy val bar = Project(id = "bar", base = file("bar")) dependsOn(foo)

This almost works, in that sbt-idea generates a master IDEA project with the dependencies among the subprojects tracked. There are however two caveats:

  1. It seems to be an sbt restriction that the subprojects must live in subdirectories of the master project (i.e., file("../foo") is not allowed). This is not really what I want (what if a module--such as a "utils" or "commons" package--is used in two different master projects?) but I can live with it.
  2. One of my subprojects has its own subprojects; I'm not sure whether sbt itself deals with these nested projects correctly, but in any case they are ignored by sbt-idea. Obviously I need nested subprojects to be included recursively in the master project.

To summarize: I'd like to collect modules which may already have subprojects into one big IDEA project with tracked dependencies for convenient editing. How can I do it? Thanks!

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
David Soergel
  • 1,709
  • 1
  • 11
  • 15
  • 2
    Try setting up a meta-project that links to the others as external project references (https://github.com/harrah/xsbt/wiki/Full-Configuration). I haven't tried this myself with sbt-idea, so this is a comment rather than an answer. – retronym Jan 07 '12 at 21:56
  • Thanks for the idea-- but sadly sbt-idea ignores the external references entirely. – David Soergel Jan 09 '12 at 21:35
  • Maybe this will help in the next version of sbt-idea: https://github.com/mpeltonen/sbt-idea/commit/9f17cc8 https://github.com/mpeltonen/sbt-idea/commit/4b4adf75 – retronym Jan 10 '12 at 08:28
  • Cool, so I installed svn-idea from the latest git sources to pull in those changes, then cleaned the projects and ran sbt-idea again. There were some weird dependency resolution issues that I don't fully understand, but somehow I ended up with a working project! I did have to manually set the source dirs in IDEA for the sub-sub-projects; and in retrospect I'm not sure I couldn't have done that in the first place with the stock sbt-idea 11.1. Anyhow, thanks-- I think I can stop shaving this yak for now. – David Soergel Jan 12 '12 at 20:17
  • After writing that last comment I found that I didn't have a working project after all (again, trouble with the sub-sub-projects). So I just separated those into first-class projects, so my master project now has only one level of subprojects below it. Of course I could only do that because it this is all my own code to begin with. – David Soergel Jan 20 '12 at 21:01
  • I would if I could, but sadly I don't know any solution to the original question. The best workaround I've found is to avoid nesting projects more than one level deep. If there is just one master project and a set of child projects, with dependencies specified among them via "dependsOn" in the master Build.scala, then running sbt-idea on the master project produces a working multi-module Idea project. – David Soergel Feb 06 '12 at 04:00

2 Answers2

7

The approach with multi-project build is the correct one. You can have a nested tree of subprojects of arbitrary length, but you cannot have a module belonging to multiple parent projects. This makes absolutely sense, and in Maven happens the same.

The reason is that it would be hard to have the same module into multiple projects and keep the sources synchronized. A normal workflow is the following:

  • You have a project which the module belongs to, where you modify the module source.
  • You publish the module into your local repository
  • In other projects where you need the module, you declare it as a libraryDependency

If you want to load a module which does not belong to the current project inside Idea, this is however feasible as you can add this as an external module to the workspace:

  • SBT-IDEA generates the .iml files for your project and you import them in the workspace
  • You can add other.iml of other projects to the workspace
  • If you modify external SBT modules that you have manually added to the workspace, you should republish them to get the changes visibile on the "main" project, which sees those external modules are a "libraryDependency"
Edmondo
  • 19,559
  • 13
  • 62
  • 115
  • Thanks for the input. However the problem is that it is not the case that "You can have a nested tree of subprojects of arbitrary length". I agree that this should work with sbt, but sbt-idea apparently does not include sub-sub-projects in the resulting IDEA project file. – David Soergel May 21 '12 at 01:30
  • Does this limitation come from idea? – Edmondo Dec 28 '12 at 08:03
  • Nope, I've certainly made IDEA project hierarchies several levels deep. – David Soergel Dec 28 '12 at 23:28
7

It seems to be an sbt restriction that the subprojects must live in subdirectories of the master project (i.e., file("../foo") is not allowed). This is not really what I want (what if a module--such as a "utils" or "commons" package--is used in two different master projects?) but I can live with it.

With sbt 13.5 and intellij 13.x, you can specify inter-project dependency with relative path, using a Build.scala. Let's say you have two projects, a core project commons and another project foo, both living in a common directory code/

  1. create Build.scala under code/foo/project/
  2. put this code snippet insde Build.scala

    object ProjectDependencies {
        val commons = RootProject(file("../commons"))
    }
    
    object ProjectBuild extends Build {
        import ProjectDependencies._
    
        lazy val root = Project(id = "foo", base = file(".")).dependsOn(commons)
    }
    
  3. Generate your IntelliJ project via sbt by sbt gen-idea

Suma
  • 33,181
  • 16
  • 123
  • 191
user2829759
  • 3,372
  • 2
  • 29
  • 53