ceedubs/sbt-ctags

Support for multi-module projects

Closed this issue · 9 comments

Multi-module projects seem like they shouldn't be terribly hard to support, but currently the plugin doesn't handle them.

I made an attempt at fixing this, and I think I have it working. Well, mostly. The multi-project build seems to be working, but I am still getting a mixed bag of results in terms of how many of the dependencies I am getting tags for. I have verified the that the source jars for the missing dependencies exist in my local ivy. I will have to do some more testing and get back with a pull request or some more bugs. In the mean time, this is roughly the route I went to get the mulit-project working:

def genCtags(state: State, dependencySrcUnzipDir: File, ctagsParams: CtagsParams, ctagsGeneration: CtagsGenerationContext => Unit, ctagsSrcDirs: Seq[File], streams: TaskStreams[_]) {
    // TODO this could be pretty bad if someone overrides the install dir with an important dir
    val extracted = Project.extract(state)
    val buildStruct = extracted.structure
    val log = streams.log
    val project = Project.getProject(extracted.currentRef,buildStruct)
    val projects:Seq[ResolvedProject] = (project match {
      case Some(p) => p.aggregate.map(x => Project.getProjectForReference(x,buildStruct)) ++ Seq(project)
      case _ => Seq(project)
    }).filter(!_.isEmpty).map(_.get)

    for(proj <- projects){
      log.info(s"Grabbing all dependency source jars")
      EvaluateTask(buildStruct, Keys.updateClassifiers, state, ProjectRef(proj.base, proj.id)).fold(state)(Function.tupled { (state, result) =>
        result match {
          case Value(updateReport) =>
...
...

Any thoughts on a better/different approach?

Thanks for this; I'm really excited about the prospect of getting this to work properly for multiple modules!

Using aggregate and ProjectgetProjectForReference like you have shown looks promising. There is a line like sbt.IO.delete(dependencySrcUnzipDir) in the EvaluateTask block that probably needs to be moved above the iteration over the projects, and that just may solve the mixed bag of results issue you are seeing.

I think you could probably simplify the projects declaration to something like this:

val projects: Seq[ResolvedProject] = project.fold(Seq.empty)(p =>
  p :: p.aggregate.map(x => Project.getProjectForReference(x,buildStruct)).collect{ case Some(x) => x })

I attempted that on GitHub so it may not even compile. I think it is logically the same though; does that look right to you?

Would you be willing to try this out a bit and submit a pull request if it works? If not, I'll give it a try when I have some time, because I've wanted this feature for a long time :)

Will issue PR, thanks

@quintona Any updates on this? :-) Was about to look into adding support myself, but looks like you've beat me to it

How does this look? adelbertc@b2d1694

Tried it on some small repos locally, seems to work. Issue now is if sub-project A depends on B, the generated tags for A doesn't include B so jump-to-definition doesn't like it, though that was discussed in the original PR.

in the worst-case scenario we can throw together a shell script to do aggregation if needed.

If this all looks good, should probably put something in the README describing the behavior.

@adelbertc that looks promising. Thanks! It will probably be a little while before I get around to giving it a try. Hopefully I can make the time this weekend.

Yeah, having A not pick up B's tags is a bummer. It sounds like your commit could be a step in the right direction, though. And maybe with time we can get that bit sorted out.

If we dumped all the tag files in the root project directory w/ different names, it's easier to collect them all using something like this, as you mentioned in the PR. I experimented with this a little bit by simply prepending each project's tag file with the id.

Not sure how good/bad that approach is.

@aloiscochard @tpolecat

👍 to what @adelbertc suggest, would make things easier indeed.

Great stuff folks

New PR made here: #11

So I gave it a bit more thought and here's what I've come up with:

  1. Putting tag files in it's own project subdirectory makes things more clean organizationally, but can become annoying having to say, delete .tag files in each directory one at a time. Also, if you start the current working directory in one of the project subdirectories, then Vim (or other editors?) won't pick up other project tag files. If sub-project A depends on B, the generated tags for A doesn't include B so jump-to-definition doesn't like it. It also becomes tricky to try to gather the tag files from other subprojects.
  2. Putting all the tag files in the root project directory under different names makes things a bit messier, but it's easier to manage manually, and because it seems editors will keep going up the folder tree until it hits a .tag file, we can still start the current working directory in one of the project subdirectories while still getting the .tag files for everyone.

Not sure if those points are coherent.. having a hard time explaining at the moment.

tl;dr My current theory is that putting all the tag files in the root project directory under different names seems to be the way to go.