4

I try to run a very simple gradle project which uses java 9 modules, but i receive the following error.

/home/vadim/IdeaProjects/test_modules/src/main/java/module-info.java:2: error: module not found: HdrHistogram
    requires HdrHistogram;
             ^

Here is it https://github.com/vad0/test_modules. The main class does basically nothing.

package app;

import org.HdrHistogram.Histogram;

public class RunHdr {
    public static void main(String[] args) {
        final Histogram histogram = new Histogram(5);
        System.out.println(histogram);
    }
}

It uses only one dependency: HdrHistogram. I included this magic command in build.gradle according to official gradle tutorial https://docs.gradle.org/current/samples/sample_java_modules_multi_project.html.

java {
    modularity.inferModulePath = true
}

The whole build.gradle looks like this.

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

java {
    modularity.inferModulePath = true
}

dependencies {
    compile group: 'org.hdrhistogram', name: 'HdrHistogram', version: '2.1.12'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

module.info looks like this

module test.modules.main {
    requires HdrHistogram;
}

I have already read a number of tutorials on Jigsaw and a whole bunch of stackoverflow questions related to it, but still can't make this simple example work. How do i fix it?

Thank you

Naman
  • 27,789
  • 26
  • 218
  • 353
Vadim
  • 576
  • 1
  • 4
  • 13
  • Strange! I use [tag:maven] and I was able to add the module `HdrHistogram` using the same artifact version. – Naman Aug 01 '20 at 18:53
  • Switching from gradle to maven is not an option for me unfortunately – Vadim Aug 16 '20 at 09:58
  • @Naman do you happen to know what `maven` does to make this an automatic module? – Eugene Aug 17 '20 at 19:10
  • @Eugene Without diving into the actual code, from the [build logs](https://gist.github.com/namannigam/9b60f30486e0086b5b2f91c24017c1f0) what Maven seems to be doing is to first identify all the dependencies on the compile path. Then based on the artifacts/jars which are themself modular i.e. includes *module descriptor*, or declaring *Automatic-Module-Name* in META-INF, or any other artifact which is a part of the `module-info.java` of the maven module itself is treated to be on the module-path instead of the classpath. The logs shared have two components to make that difference clear. – Naman Aug 18 '20 at 04:10
  • @Naman [here](https://github.com/jjohannes/extra-java-module-info) is how gradle does it via a plugin by a gradle core author. This is a thing of beauty! The idea is not that complicated, but it's very nice. They let _you_ define `module-info.java` for non-modular jars. Have i said that I love it already? – Eugene Aug 19 '20 at 03:08
  • @Eugene On the maven side of things and in terms of plugins, [Gunnar's](https://stackoverflow.com/users/773616/gunnar) work over [moditect-maven-plugin](https://github.com/moditect/moditect#moditect---tooling-for-the-java-module-system) seems quite comparable. Though I haven't really explored it but have noticed some open-ended threads out here as well. In the longer term, I wouldn't really be looking out for a plugin to let me explicitly override a module descriptor for a library that itself doesn't choose to. (Things like these add to the reasons why people in our org don't want to migrate.) – Naman Aug 19 '20 at 04:31
  • @Naman good to know. Thx. – Eugene Aug 19 '20 at 08:40

1 Answers1

6

Unfortunately, gradle does not treat every jar as a module (in simple words). If you want to find out how exactly is gradle building the module-path (as opposed to class-path), you probably want to start from here, specifically at the isModuleJar method. It's pretty easy to understand (though it took me almost two days to set-up gradle and debug the problem out) that the dependency that you are trying to use : gradle says that it is not a module (it isn't wrong, but I am not sure it is correct either). To make it very correct, gradle will add your dependency to the CLASSPATH, but in the very next line: it will not add your dependency to the module-path, because if fails the filter in isModuleJar.

I do not know if this is a bug or not, or may be this is on purpose, but the solution is easy:

plugins.withType(JavaPlugin).configureEach {
    java {
        modularity.inferModulePath = true
    }

    tasks.withType(JavaCompile) {
        doFirst {
            options.compilerArgs = [
                '--module-path', classpath.asPath,
            ]
            classpath = files()
        }
    }
}

you add it to the path, on purpose. I will flag this as a defect and let's see what they have to say.

EDIT

Even better, use a plugin that is written by a gradle commiter:

plugins {
    id 'java'
    id 'de.jjohannes.extra-java-module-info' version "0.1"
}

And the easiest option on your case is to do :

extraJavaModuleInfo {
     automaticModule("HdrHistogram-2.1.12.jar", "HdrHistogram")
}
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • This is wonderful! Thanks a lot @Eugene. But overall it looks like very few people use modules+gradle these days. – Vadim Aug 19 '20 at 08:21