Mark gradle defined dependencies as modular in eclipse

I’ve been attempting to migrate a project of mine that formerly built multi-release jars by making use of multiple source sets from maven to gradle. However as it defined module-infos it was a great pain to get it to compile (turns out gradle heavily dislikes MRJ) to the point that I was simply unable to use existing solutions. Which is why I am now stuck on how to actually tell eclipse that the dependencies that are fetched by gradle are to be put on the module path.

In theory (as in toying around with the .classpath myself resulted in this) it is as simple as transforming gradle’s .classpath entry to

    <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
        <attributes>
            <attribute name="module" value="true"/>
        </attributes>
    </classpathentry>

And in theory

eclipse {
    classpath {
        file {
            whenMerged {
                entries.each {
                    if (it.kind == 'con') {
                        it.entryAttributes['module'] = 'true'
                    }
                }
            }
        }
    }
}

should do that. But while it modifies the JRE declaration it doesn’t modify the gradle declaration. So how do I do this then?

It turns out that once you stare at your code for long enough you will magically find a fix, so I’ll self answer another one of my posts to prevent future pain from future adventurers:

The magic lies in that one must realize that the gradle classpath container is not added by gradle nor the gradle eclipse plugin but rather by buildship. By declaring the container through

eclipse {
    classpath {
        containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
        file {
            // <<Rest of the code goes here>>
        }
    }
}

one can freely modify the container to one’s desires.

Mind elaborating on what you mean by “turns out gradle heavily dislikes MRJ”?
I never had any major problems, neither with producing them, nor with consuming them.

It is more or less the little things in life:

  1. The java 9 source set doesn’t share the dependencies of the Java 8 source set (though this is arguably understandable and a straightforward fix exists anyways)
  2. The java 9 source set cannot reference the Java 8 source set - here too the fix is easy
  3. The java 9 module-info will not compile as it exports packages it does not define (as those are defined in the Java 8 source set) - so you’d need to add the Java 8 sources to the sources of the Java 9 source set
  4. However now javac will complain that some files collide with each other - so you need to exclude some files from the compilation sources
  5. Now Javac will emit classes for everything instead of the Java 9 sources alongside the module-info - so you have to split the module-info in it’s own source set and the Java 9 main sources in a seperate one that is not compiled with JPMS.
  6. You still have the problem that you expect that all Java 8 code is compatible with Java 9 Javac. This may not be the case.
  7. However because of all your obscenities IDEs will refuse to understand your project correctly, leaving you to resolve the issues on a case-by-case basis (this is ultimately the reason I created this post)

However they end up creating a speedbump that takes a good two days to resolve

val java9 by sourceSets.creating
tasks.named<JavaCompile>(java9.compileJavaTaskName) {
    javaCompiler = javaToolchains.compilerFor {
        languageVersion = JavaLanguageVersion.of(9)
    }
    options.compilerArgumentProviders.add(object : CommandLineArgumentProvider {
        @get:InputFiles
        @get:PathSensitive(PathSensitivity.RELATIVE)
        val mainClassesDirs = sourceSets.main.map { it.output.classesDirs }

        override fun asArguments() = mainClassesDirs
            .get()
            .files
            .map { it.absolutePath }
            .flatMap {
                listOf(
                    "--patch-module",
                    "your.module.id.here=$it"
                )
            }
    })
}

val java9Implementation by configurations.existing {
    extendsFrom(configurations.implementation.get())
}

tasks.jar {
    manifest {
        attributes("Multi-Release" to true)
    }
    from(java9.output) {
        into("META-INF/versions/9/")
    }
}

And I think all of your points should be gone. :slight_smile:

Buildship doesn’t support kotlin gradle at all so might have as well just pointed out that --patch-module exists and saved yourself some time. Needless to say I will look into the --patch-module flag.

Buildship doesn’t support kotlin gradle at all

Whether you write it in Groovy DSL or Kotlin DSL doesn’t matter, you can just port it to Groovy DSL too, but the logic stays the same.

so might have as well just pointed out that --patch-module exists and saved yourself some time

Not at all, I wrote this snippet a week ago for someone else and just copied it here. :slight_smile: