java 10 gradle project : automatic module not found - java

I created a java 10 project with intelliJ, using gradle.
I copied some stuff into it (some "AppFx" class using the library guava and javaFx, and a personal build.gradle file).
I also added a module-info.java file in src/main/java with this content:
module biblio5.main {
requires javafx.graphics;
requires javafx.controls;
requires javafx.base;
requires guava;
}
in which grava is an automatic module.
here is the relevant part of build.gradle:
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'com.google.guava:guava:23.0'
}
intelliJ can compile the project (using the hammer-like icon) but when I run the compileJava gradle task from intelliJ, I get an error:
13:12:46: Executing task 'compileJava'...
Task :compileJava FAILED C:\Users\lolve\Documents\gradle_java\biblio5\src\main\java\module-info.java:5:
error: module not found: guava
requires guava;
^ 1 error
I spent a lot of time on the net but did not manage to find an answer.
thank you
ps: here is the entire build.gradle:
buildscript {
dependencies {
classpath group: 'de.dynamicfiles.projects.gradle.plugins', name: 'javafx-gradle-plugin', version: '8.8.2'
classpath 'eu.appsatori:gradle-fatjar-plugin:0.3'
}
repositories {
maven {url "https://mvnrepository.com/artifact/de.dynamicfiles.projects.gradle.plugins/javafx-gradle-plugin"}
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
jcenter()
}
}
plugins {
id 'java'
id 'application'
id 'edu.sc.seis.launch4j' version '2.4.4'
}
apply plugin: 'javafx-gradle-plugin'
apply plugin: 'eu.appsatori.fatjar'
group 'lorry'
version '1'
sourceCompatibility = 1.10
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
maven {url "https://mvnrepository.com/artifact/de.dynamicfiles.projects.gradle.plugins/javafx-gradle-plugin"}
jcenter()
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'com.google.guava:guava:23.0'
}
//********************************************************************************************
launch4j {
outfile='bibliotek-v3.exe'
mainClassName = 'lorry.AppFx'
icon = "${projectDir}\\icons\\hands2.ico"
copyConfigurable = project.tasks.fatJar.outputs.files
//jar = "lib/${project.tasks.fatJar.archiveName}"
//headerType = "console"
jar = "${buildDir}\\productFatJar\\fat.jar"
}
jar {
baseName = 'executable3'
version = ''
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': 'lorry.AppFx'
)
}
}
task copyExecutable(type: Copy) {
from file("${buildDir}\\launch4j\\bibliotek-v3.exe")
into file("c:\\Users\\lolve\\Documents\\gradle_java\\produits")
}
task copyJar(type: Copy) {
from file("${buildDir}\\jfx\\app\\bibliotek-v3.jar")
into file("c:\\Users\\lolve\\Documents\\gradle_java\\produits")
}
task copyFatJar(type: Copy) {
from file("${buildDir}\\productFatJar\\fat.jar")
into file("c:\\Users\\lolve\\Documents\\gradle_java\\produits")
}
createExe.doLast{
tasks.copyExecutable.execute()
}
task createJar(){
doLast{
tasks.jfxJar.execute()
tasks.jfxNative.execute()
tasks.copyJar.execute()
}
}
jfx {
jfxMainAppJarName = "bibliotek-v3.jar"
// minimal requirement for jfxJar-task
mainClass = 'lorry.AppFx'
// minimal requirement for jfxNative-task
vendor = 'lolveley'
}
fatJar {
destinationDir=file("${buildDir}\\productFatJar")
archiveName="fat.jar"
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': 'lorry.AppFx'
)
}
}
task createFats(){
doLast{
tasks.fatJar.execute()
tasks.copyFatJar.execute()
tasks.createExe.execute()
}
}
EDIT
well, I made the change, and now I have "com.google.commons" instead guava in module-info.java, but I still get this error:
Testing started at 14:20 ... 14:20:14: Executing task 'check'...
Task :compileJava FAILED C:\Users\lolve\Documents\gradle_java\biblio5\src\main\java\module-info.java:5:
error: module not found: com.google.common
requires com.google.common;
^ 1 error
I changed gradle in intelliJ (the default option - recommended - was "default gradle wrapper") to my local gradle (v4.9), but without any effect.
What do you mean by "compatible with java"? What about try with a java 9 installation?

Update: Gradle 6.4 added basic support for Jigsaw modules. See this sample in the documentation (which also links to other related documentation). Note that the Building Java 9 Modules article linked to in this answer has changed significantly since this answer was posted.
The issue is Gradle still (as of 4.10-rc-2) doesn't have first-class support for Jigsaw modules. All the tasks will use the classpath, not the modulepath, when executing. This obviously will cause issues when trying to create a modular library/application (with module-info.java).
If you want to use Jigsaw modules in your project you should read Building Java 9 Modules. Your scenario, as #nullpointer mentions, is best covered by this section of the linked document. The takeaway is to add the following to your build.gradle file:
ext.moduleName = 'your.module'
compileJava {
inputs.property('moduleName', moduleName)
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath
]
classpath = files()
}
}
They also have sections for modifying the compileTestJava task (here) and the test task (here). Personally, I tend to not modify those tasks as testing often requires a lot of reflection which in turn requires a lot of --add-opens arguments. If you find out that's not true (haven't tried it in a while) or there's a better way, please let me know.
If your Gradle project is an application you also want to read the section covering the run and assemble tasks.
There is an experimental Gradle plugin that does all this for you: experimental-jigsaw. The plugin is limited, however, and there is a fork on GitHub, named chainsaw, that adds more features. Note: I don't know how maintained either plugin is.
Another Gradle plugin is available: Gradle Modules Plugin.
If you want to watch for updates regarding Jigsaw support in Gradle they maintain an epic on GitHub.
Also, to include what #nullpointer commented, you should be using a version of Guava that includes an Automatic-Module-Name entry in its manifest. Without this entry (combined with no module-info) the name of the module is subject to the name of the jar file; which may change unexpectedly. In other words, the Automatic-Module-Name entry makes for a better contract regarding the name of an automatic module. The first version that Guava added this entry is 23.2:
Changelog
Added JPMS module name com.google.common for Guava.
...
However, the most recent version (as of writing this answer) is 26.0.
More information about automatic modules can be found:
in the Javadoc of ModuleFinder.of(Path...)
this section of The State of the Module System
and this Stack Overflow question

Related

How can I force Gradle to use --module-path instead of -cp?

I have a relatively straightforward modular project, and I've already set all of the options that I've found in the documentation as well as in different answers or articles such as How to force gradle to add dependencies to the module path instead of the class path in eclipse? and others.
I'm getting a module not found exception:
java.lang.module.FindException: Module gdx.backend.lwjgl not found, required by com.myproject.client
I only get this exception when running my application through gradle's application:run task. When I run it in IntelliJ or using the runtime image generated by the Badass JLink plugin, everything runs fine. After hours of debugging and building command lines by hand I was able to figure out that the difference between a correctly started program and what gradle does, is that everything else starts the program using the --module-path option while Gradle INSISTS on producing the following command line, no matter what I do in my build.gradle files:
Starting process 'command 'C:\Tools\java\bin\java.exe''. Working directory: C:\repos\pv-core\master\pv-client Command: C:\Tools\java\bin\java.exe -Dfile.encoding=windows-1252 -Duser.country=FR -Duser.language=fr -Duser.variant -cp C:\Users\accou\.gradle\caches\modules-2\files-2.1\com.badlogicgames.gdx\gdx-backend-lwjgl\1.9.11\3c094feb74e2aef14e30e988326612ee75387c8f\gdx-backend-lwjgl-1.9.11.jar;[...] --module com.myproject.client/com.myproject.client.ClientStarter
Successfully started process 'command 'C:\Tools\java\bin\java.exe''
Error occurred during initialization of boot layer
java.lang.module.FindException: Module gdx not found, required by com.myproject.client
My gradle files contain the following:
Root file:
subprojects {
apply plugin: "java"
java {
modularity.inferModulePath = true
}
targetCompatibility = '11'
sourceCompatibility = '11'
repositories {
mavenLocal()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
jcenter()
google()
}
test {
useJUnitPlatform()
testLogging {
events 'PASSED', 'FAILED', 'SKIPPED'
}
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}
version = '0.0.1'
ext {
appName = 'MyProject'
gdxVersion = '1.9.11'
box2DLightsVersion = '1.4'
aiVersion = '1.8.0'
artemisVersion = '2.3.0'
junitVersion = '5.4.2'
slf4jVersion = '1.8.0-beta2'
}
}
project(':pv-core') {
dependencies {
}
}
Client project (application):
plugins {
id 'org.beryx.jlink' version '2.22.3'
id 'application'
}
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher{
name = 'hello'
jvmArgs = ['-Dlog4j.configurationFile=./log4j2.xml']
}
}
ext.moduleName = 'com.myproject.client'
group = 'com.myproject.client'
//mainClassName = 'com.myproject.client.PVClientStarter'
application {
mainModule = 'com.myproject.client'
mainClass = 'com.myproject.client.ClientStarter'
}
apply plugin: 'java-library'
dependencies {
api "com.badlogicgames.gdx:gdx:$gdxVersion"
api "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
api "com.esotericsoftware.spine:spine-libgdx:3.6.53.1"
api "com.underwaterapps.overlap2druntime:overlap2d-runtime-libgdx:0.1.0"
api "com.kotcrab.vis:vis-ui:1.3.0"
api "net.dermetfan.libgdx-utils:libgdx-utils:0.13.4"
//api "de.tomgrill.gdxfacebook:gdx-facebook-core:1.4.1"
//api "de.tomgrill.gdxdialogs:gdx-dialogs-core:1.2.5"
api "com.github.czyzby:gdx-kiwi:1.9.1.9.6"
api "com.github.czyzby:gdx-lml:1.9.1.9.6"
api "com.github.czyzby:gdx-lml-vis:1.9.1.9.6"
api "de.golfgl.gdxgamesvcs:gdx-gamesvcs-core:1.0.2"
compile "com.badlogicgames.gdx:gdx-ai:$aiVersion"
compile "com.badlogicgames.gdx:gdx-controllers:$gdxVersion"
//compile "com.esotericsoftware:kryo:5.0.0"
compile "kryonet:kryonet:2.21"
testCompile 'org.testng:testng:7.3.0'
}
test {
useTestNG()
//testLogging.showStandardStreams = true
testLogging {
events "passed", "skipped", "failed"
}
}
compileJava {
inputs.property("moduleName", moduleName)
doFirst {
options.compilerArgs += [
'--module-path', classpath.asPath
]
classpath = files()
}
}
Any idea as to how I can possibly (FINALLY) tell gradle to go with --module-path rather than -cp ?!
I think I just figured it out randomly. I kept messing with the gradle files and eventually removed the java blocks containing the statements modularity.inferModulePath = true (despite this being the main advice given in the gradle docs....!), moved up the compileJava block above dependencies and the jlink plugin configuration and it ended up working somehow.
Lots of black magic involved in all this stuff, is the feeling I'm left with after a couple of days of fighting vs modules & gradle tbh..
EDIT: actually I didn't address the root cause of the problem, gradle is still using -cp over --module-path, but the changes I just described above here eventually lead to the gradle successfully launching my application, yet STILL with the -cp option. So I'm still very much interested in having gradle do a proper, JPMS-compliant, module launch (instead of a classpath launch).
The model Gradle introduced for dealing with modules in Java is based on inferring whether or not the current project code is a module or not.
This means that your application needs to have either a module-info or a Automatic-Module-Name entry in its manifest.
Further details in the documentation.

Gradle custom plugin jar with dependencies

I'm trying to build a jar for a custom gradle plugin to be used by other gradle projects. I'm using java to write the plugin. I'm having a problem including dependencies in my jar. If I build the jar using the below build.gradle
plugins {
id 'groovy'
}
repositories{
mavenCentral()
}
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.google.guava:guava:27.0-jre'
testCompile 'junit:junit:4.12'
//compile 'org.apache.commons:commons-lang3:3.8.1'
}
group = 'com.mine'
version = '1.0'
I get a NoClassDefFound exception for guava classes when applying the plugin on a project. If I include a task to create a jar with dependencies like below in the build.gradle
jar {
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it)}
}
}
It says Plugin with Id 'my-plugin' not found. How do I include dependencies in a gradle plugin jar?
Your plugin project should be configured as a standalone Plugin project and then published to a maven repository, which will make dependencies resolution work; there is good documentation about writing custom plugin here, specially the following part : using Gradle plugin development plugin
There is also a good example of writing/publishing/consuming a custom Plugin in the Gradle examples here : https://github.com/gradle/gradle/tree/master/subprojects/docs/src/samples/plugins (see the two subprojects publishing and consuming )
And here is a working example with a plugin that has dependency on external library (commons-lang for example):
Plugin project
build.gradle
plugins {
id 'java-gradle-plugin'
id 'groovy'
id 'maven-publish'
}
group 'org.gradle.sample.plugin'
version '0.1'
// pugin metadata configuration
gradlePlugin {
plugins {
myplugin {
id = "org.gradle.sample.plugin.myplugin"
implementationClass = "org.gradle.sample.plugin.MyPlugin"
}
}
}
// publish to local maven repo for testing
publishing {
repositories {
maven {
url "../repos/maven-repo"
}
}
}
// repo for dependences resolution
repositories{
jcenter()
}
// dependencies of this plugin
dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1'
}
Plugin implementation : src/main/groovy/org/gradle/sample/plugin/MyPLugin.groovy
package org.gradle.sample.plugin
import org.apache.commons.lang3.StringUtils
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin implements Plugin<Project> {
#Override
void apply(final Project project) {
println "Applying custom plugin... "
project.tasks.create('testPlugin'){
doLast{
println " custom plugin task executing."
println "Result: " + StringUtils.capitalize("stringtotest")
}
}
}
}
Build and publish this plugin ./gradlew publish : the plugin jar and "plugin marker artefacts" will be published to local maven repo in ../repos/maven-repo
Consumer project
build.gradle
plugins {
id 'java'
// import/apply your custom plugin
id 'org.gradle.sample.plugin.myplugin' version '0.1'
}
group 'org.gradle.sample.plugin'
version '0.1'
repositories{
maven {
url "../repos/maven-repo"
}
jcenter()
}
To test the plugin, try to execute the plugin task testPlugin
> Task :testPlugin
custom plugin task executing.
Result: Stringtotest
Sorry to add this as an answer but I don't have enough points to comment (yes it is a bit late in coming but I found this in a search and it came so close, maybe this will help someone else).
The answer by #M.Ricciuti is correct, just missing one file, namely a settings.gradle in the referencing project (not the plugin) directory:
pluginManagement {
repositories {
maven {
url '../repos/maven-repo'
}
gradlePluginPortal()
ivy {
url '../repos/ivy-repo'
}
}
}
Many thanks, I have tried many things that didn't work before finding this, even the examples by gradle didn't work (or more likely I didn't run them correctly). Anyway I merged what I saw in the answers with M. Ricciuti's answer and saw that file in the sample.
My complete project is at https://github.com/reddierocket/sampleGradlePlugin
The readme has instructions to run it. (Note I did not include the wrapper but I am using gradle version 5.3.1.)

Compile zip with gradle including libraries implemented by a sibling subproject

I have a java project made by some sibling modules. One of these modules is a library and I'm applying java-library plugin on it. All the other modules depend on it.
What I need to do is to automate the creation of a zip for each module, containing all the classes and all the dependencies needed for it to work (I'm deploying the zip as aws-lambda functions).
So far this is what I have achieved, but the resulting zip only contains module's classes. I thought that the problem might be the type of dependency I'm using (implementation) and I tried switching to the default one but gradle doesn't even success in building.
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
task buildZip(type: Zip) {
from compileJava
from processResources
from configurations.runtime
}
dependencies {
implementation project(':utils')
testCompile group: 'junit', name: 'junit', version: '4.12'
}
After some testing, I think I have the answer.
First: the order you declared the task and the dependencies is fine! It's OK to use a configuration before it's populated. I only say this because I indicated in my comments that it might be an issue.
Second, the issue here is the use of configurations.runtime. This does not extend implementation and api, so those dependencies are not included. runtime has been superseded by runtimeOnly, which hopefully makes the behaviour clear.
The following task definition should work:
task buildZip(type: Zip) {
from compileJava
from processResources
from configurations.runtimeClasspath
}

Successful Gradle project build produces NoClassDefFoundError at runtime

I am using Gradle to build my Java project in Eclipse. gradle.build is as follows
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.0'
compile group: 'org.reflections', name: 'reflections', version: '0.9.10'
}
All libraries are functioning properly when run through Eclipse. But sometimes it is useful to work on the command line. When run on the command line, the runtime error Exception in thread "main" java.lang.NoClassDefFoundError: com/flowpowered/nbt/regionfile/SimpleRegionFileReader occurs, even though the build is successful and the code contains imports from those libraries. I have tried cleans and rebuilds, along with gradlew build --refresh-dependencies, but I still encounter the same runtime error.
I would assume that the libraries are just never actually imported? Or that they are not being stored where the java project thinks they are? I'm unfamiliar with Gradle, so any advice on this is welcome.
Based on the posted build.gradle file you are not packaging the application as an executable JAR.
First apply the application plugin. But this will not be enough as you won't be able to run the executable as a single JAR without all of its dependencies. Apply the shadow plugin too.
These two plugins will give you access to the following tasks:
run: execute the application from gradle's command line.
runShadow: execute the application but with all dependencies packaged in a single JAR, alongside your compiled classes and resources.
shadowJar: create a single "fat JAR" with compiled classes and all dependencies.
Thus your build.gradle may look like this
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '1.2.4'
}
mainClassName = 'com.acme.YourMainClassName'
repositories {
mavenCentral()
}
dependencies {
compile group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.0'
compile group: 'org.reflections', name: 'reflections', version: '0.9.10'
}
Plugin documentation:
https://github.com/johnrengelman/shadow
https://docs.gradle.org/3.4/userguide/application_plugin.html#useApplicationPlugin
Another solution without using any plugins and still end up with a runnable fat jar
jar {
archiveName = 'NameOfYourApp.jar'
manifest {
attributes 'Main-Class': 'uk.co.cdl.Main',
'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' '),
'Implementation-Version': project.version
}
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
include/exclude anything if need to if not take the curlys off
}
}

How do I include a single dependency in my JAR with Gradle?

I'm starting with Gradle and I was wondering how do I include a single dependency (TeamSpeak API in my case) into my JAR so that it could be available at the runtime.
Here is a part of my build.gradle :
apply plugin: 'java'
compileJava {
sourceCompatibility = '1.8'
options.encoding = 'UTF-8'
}
jar {
manifest {
attributes 'Class-Path': '.......'
}
from {
* What should I put here ? *
}
}
dependencies {
compile group: 'org.hibernate', name: 'hibernate-core', version: '4.3.7.Final'
compile group: 'org.spigotmc', name: 'spigot', version: '1.8-R0.1-RELEASE'
// Many other dependencies, all available at runtime...
// This one isn't. So I need to include it into my JAR :
compile group: 'com.github.theholywaffle', name: 'teamspeak3-api', version: '+'
}
Thanks for your help :)
The easiest way is to start with a separate configuration for the dependencies you want to include. I know you only asked about a single jar but this solution will work if you add more dependencies to your new configuration. Maven has a well known name for this sort of thing called provided, so that is what we will use.
configurations {
provided
// Make compile extend from our provided configuration so that things added to bundled end up on the compile classpath
compile.extendsFrom(provided)
}
dependencies {
provided group: 'org.spigotmc', name: 'spigot', version: '1.8-R0.1-RELEASE'
}
jar {
// Include all of the jars from the bundled configuration in our jar
from configurations.provided.asFileTree.files.collect { zipTree(it) }
}
Using provided as the name of the configuration is also important because when the jar gets published, any dependencies you have in the providedconfiguration will show up as provided in the POM.xml that gets published with the JAR. Maven dependency resolvers will not pull down provided dependencies and users of your jar will not end up with duplicate copies of classes on the classpath. See Maven Dependency Scopes

Categories