javagradlescala-idegradle-eclipse

In Buildship: how can I substitute a Gradle project with a built jar?


I would like to be a able to get Eclipse to ignore one Gradle project, and instead use a pre-built version of it.

Background

I have a project "parser" written in Scala, and a dozen others written in Java. The weakest link in my tool-set is Scala IDE. I use this plugin to edit & compile Scala code, but unfortunately it breaks the Java (JDT) tooling quite badly in mixed-language projects*.

With Maven (m2e) I had a workaround I was quite happy with:

  1. Build as a .jar put into my local .m2 repository:

    cd parser; mvn install
    
  2. In Eclipse, close the "parser" project

"Like magic", m2e simply picked up the most recent 'installed' .jar and used it in place of the closed project.

An awesome answer would be how to get Gradle to do that!

However all I wish for is any solution that meets these...

Requirements

  1. That I can open Project parser when necessary (which is seldom), to edit and build changes via the Gradle command-line. I will close it when done.
  2. Other projects use the built .jar from my local .m2 repo. (It's fine if they always do so.)
  3. The change must not affect others who don't use Eclipse
  4. (ideally) the change can be used by other Eclipse users

Approaches

A similar question had this good answer by @lance-java with a number of general suggestions. I think I can rule out these ideas:

Something along the lines of lance-java's idea #4 sounds viable. Paraphrasing...

Questions

The main question: How can I structure a solution to this, that will actually work & avoid any major pitfalls?

Note that the project itself has a few dependencies, specifically:

dependencies {
  compile 'org.scala-lang:scala-library:2.12.4'
  compileOnly  'com.google.code.findbugs:jsr305:1.3.9'
  antlr 'org.antlr:antlr4:4.5.3'
}

So a sub-question may be: How to pull these in into the other projects without duplicating the definition? (If that doesn't work automatically.)


Solution

  • So the solution was a bit involved. After adding 'maven-publish' to create the library, I then implemented the following to force Eclipse to use the prebuilt library:

    subprojects {
        // Additional configuration to manipulate the Eclipse classpaths
        configurations {
            parserSubstitution
        }
        dependencies {
            parserSubstitution module("com.example:parser:${project.version}")
        }
    
        apply plugin: 'eclipse'
        eclipse {
            classpath {
            plusConfigurations += [ configurations.pseLangSubstitution ]
            file {
                whenMerged { cp ->
    
                    // Get Gradle to add the depedency upon
                    // parser-xxx.jar via 'plusConfigurations' above.
                    // Then this here if we have a dependency on Project(':parser')
                    //  - If so, remove it (completing the project -> jar substitution).
                    //  - If not, remove the .jar dependency: it wasn't needed.
                    def usesParser = entries.removeAll {
                        it instanceof ProjectDependency && it.path.startsWith('/parser')
                    }
                    def parserJar =
                        cp.entries.find { it instanceof Library && it.path.contains('parser-') }
                    if (usesParser) {
                        // This trick stops Buildship deleting it from the runtime classpath
                        parserJar ?. entryAttributes ?. remove("gradle_used_by_scope")
                    } else {
                        cp.entries.remove { parserJar }
                    }
                }
            }
        }
    

    So there are 2 parts to this:

    1. Using 'plusConfigurations' felt a bit round-about. I ended up doing this because I could not see how to construct class Library classpath entries directly. However it could well be that this is required to implement the 'transient dependencies' correctly anyway. (See the end of the question.)
    2. The trick to stop Buildship removing the .jar from the runtime classpath (thus deviating from a Gradle command-line launch) was provided to me by a Gradle developer in this discussion.

    Usage

    The solution works just as I hoped. Every time some code in this library is modified, I execute the following task of mine on the command line (which also does some other code & resource generation steps, in addition to building the parser jar):

    ./gradlew generateEclipse
    

    Then in Eclipse I press keyboard shortcuts for "Gradle -> Refresh Gradle Projects", Build.

    And harmony is restored. :-)