How to make Gradle put Java dependecies in different destination directories? - java

I have a project that generates a client and a server jar file. The client uses an old version of WorldWind (1.5.1) in a set of "do not touch it code", meaning I can't upgrade to a newer WorldWind. This WorldWind, in turn, depends on old versions of jogl (1.1.1a), gluegen (1.0b06), etc... The client and server both use jackson (2.6.3 is latest we have in our Artifactory) to pass JSON messages to each other, with the server using some additional jackson libraries to support another 3rd party library it needs. There is some sort of conflict between the old jogl and the jackson that makes it so that if all these libraries are together in one classpath (lib folder) they stomp on each other and running the client at startup it claims it cannot find jogl.
I had solved this in the past by generating a lib_worldwind folder that would contain only the worldwind jar, a lib_jackson folder that would contain only the server-specific jackson jars, and a lib folder that would contain everything else. I did this via ant. Then I'd tell the client its classpath was lib and lib_worldwind, and I'd tell the server it is lib and lib_jackson.
We recently have converted everything to Gradle, which in the long run will let me actually try and update some of my dependencies to never versions easier and then see what breaks (and I can then fix piecemeal). Nonetheless, it seems Gradle puts everything into one lib folder.
So my question is, how do I replicate the old behavior where stuff ends up in 3 different folders (while makeing sure Gradle also tells Eclipse where it ended up putting the stuff correctly so it can build and run)? I've been trying for a week now (I thought maybe Configurations gets me there but it either does not or I'm not doing it right).
My Gradle scripts:
root level build.gradle:
def standalone_distribution = new File(rootProject.projectDir, "standalone-distribution")
def jars_dir = new File(rootProject.projectDir, "jars")
def lib_dir = new File(rootProject.projectDir, "jars/lib")
def lib_client = new File(rootProject.projectDir, "jars/lib_worldwind")
def lib_server = new File(rootProject.projectDir, "jars/lib_jackson")
ant.importBuild 'build.xml'
buildscript {
repositories {
maven { url 'http://mylocalartifactory'}
}
dependencies {
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6'
}
}
//irrelevant sonarqube settings
allprojects {
version = 'x'
}
subprojects {
repositories {
maven { url 'http://mylocalartifactory'}
flatDir { //I have to do this since I don't have some of these old libraries in my local artifactory but instead are sitting in a lib folder of specific sub projects
dirs "${projectDir}/lib"
}
}
apply plugin: 'maven'
apply plugin: 'java'
apply plugin: 'eclipse'
sourceSets {
main.java.srcDirs = ['src']
main.resources.srcDirs = ['conf']
test.java.srcDirs = ['test']
test.resources.srcDirs = ['testConf']
}
compileJava {
options.encoding = 'UTF-8'
options.compilerArgs << "-Xilint:all"
}
javadoc.options.encoding = 'UTF-8'
configurations.all {
exclude group: 'javax.media', module: 'jai-core'
exclude group: 'com.sun.media', module: 'jai_imageio'
exclude group: 'com.sun.jdmk', module: 'jmxtools'
exclude group: 'com.sun.jmx', module: 'jmxri'
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
jar { archiveName = project.name + ".jar" }
group = 'client'
group = 'server'
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile) {
options.compilerArgs << '-Xlint:-deprecation'
options.compilerArgs << '-Xlint:-unchecked'
options.compilerArgs << '-Xlint:-rawtypes'
options.compilerArgs << '-Xlint:-varargs'
options.compilerArgs << '-Xling:-dep-ann'
}
//unit test stuff
task copyJar (type: Copy) {
from jar
into "$jars_dir"
}
task copyDeps (type: Copy) {
from configurations.runtime
into "$lib_dir"
}
task copyJarsandDeps (type: Copy, dependsOn: [copyJar, copyDeps])
}
The ant script build.xml sets up folder structure for deployment to a folder that would house this, in other words
standalone-distribution
- the primary project.name.jar jars that are my actual project + the scripts that start them
- lib
- lib_worldwind
- lib_jackson
- other folders like Resources, Docs, etc..
Then individual projects have gradle that looks like:
client build.gradle:
jar {
from('src') {
include 'resourcefolder'
}
manifest {
attributes 'Implementation-Title': 'Client', 'Implementation-Version': version
}
}
dependencies {
compile project (':some-common-code-project')
compile name: 'jogl-1.1.1a'
compile name: 'worldwind-1.5.1'
//a handful more like this name: because they are old and I don't have them in localartifactory
compile 'org.apache.commons:commons-configurations:1.10'
//a handful more that I do have
}
Then, similarly in server build.gradle:
jar {
manifest {
attributes 'Implementation-Title': 'Server', 'Implementation-Version': version
}
}
dependencies {
compile project (':some-common-code-project')
compile 'thirdpartylibrary' //not used in client, thirdpartylibrary depends on jackson-core, jackson-annotations, and jackso-databind 2.8.7 which are not compatible with WorldWind so I want them in a different directory
//a handful more that I do have in local library
}
Any idea on how to do this "split"?
edit: just inspected that old worldwind-1.5.1.jar and it contains a set of jackson packages in there! So that's probably part of the problem.
edit2: got a (previously uninvolved) coworker to look at this and he suggested trying to exclude jackson from the client build.gradle and that solved it!

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 Builds an Empty Jar File

I converted a project to gradle using gradle init from maven after updating the dependencies. gradle test works as expected. However, when I run gradle build, the jar file that's generated is entirely empty.
I've attempted to tweak the source set to make something happen, but that doesn't seem to solve the problem. The directory structure matches what gradle expects from what I can tell everything is nested in src/main/groovy
The project's full code is available on Github.
In general what causes no files to be added to a build? Is there additional configuration I need to add besides whatever gradle init creates?
Gradle build file:
plugins {
id 'java'
id 'maven-publish'
}
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
implementation 'org.codehaus.groovy:groovy-all:3.0.5'
implementation 'com.github.javafaker:javafaker:1.0.2'
testImplementation 'org.spockframework:spock-core:2.0-M3-groovy-3.0'
testCompileOnly 'org.projectlombok:lombok:1.18.12'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.12'
}
group = 'nl.topicus.overheid'
version = '0.2.0'
description = 'java-factory-bot'
sourceCompatibility = '1.8'
publishing {
publications {
maven(MavenPublication) {
from(components.java)
}
}
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
What going on now is the project is try to build as Java project and get src from src/main/java as it is default from Java project. So, you need id 'groovy' not id 'java' in plugins section to make it look into src/main/groovy and build both .java and .groovy files in there.

Clojure (clara rule) file not generated when gradle build

I have a Spring application that integrates a clara rule engine (clojure) file that is triggered from a java class. When building the app with gradle, the clojure file is not generated as part of the JAR.
So, when running ther jar, it throws the following exception:
Caused by: java.io.FileNotFoundException: Could not locate au/com/acme/mti/mec/runtime/rules/mec__init.class or au/com/acme/mti/mec/runtime/rules/mec.clj on classpath.
What is the best way (or at least a way) to make gradle generates the clj file when building/generating the jar?
I have already include a task in the build.gradle file to copy the clj file from src path to the build path. It copies the file under the build path, but it does not in the jar.
build.gradle:
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'jacoco'
}
group = 'au.com.acme.mti.mec'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
mavenLocal()
maven {
url "http://clojars.org/repo"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.clojure:clojure:1.9.0'
implementation 'com.cerner:clara-rules:0.20.0'
implementation 'org.projectlombok:lombok'
}
}
task copyRules(type: Copy){
from 'src/main/resources/au.com.acme.mti.mec.runtime.rules/'
into 'build/classes/java/main/au/com/acme/mti/mec/runtime/rules/'
}
test {
test.dependsOn copyRules
useJUnitPlatform()
finalizedBy jacocoTestReport
}
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/jacocoHtml")
}
}
Putting the Clojure files (the Clara rules) in a place, where they end up in the correct place inside the JAR, would make the copy task obsolute (which is not used for building the JAR -- at least from the part of the build.gradle we can see in the question.
Move the files from src/main/resources/au.com.acme.mti.mec.runtime.rules to src/main/resources/au/com/acme/mti/mec/runtime/rules
Get rid of the copyRules task (and it's dependsOn)
edit:
Added a repository that shows a minimal working example, that puts the clj files just into resources https://github.com/christoph-frick/try-java-clara

Java jar file won't recognize external libraries added from gradle

Yo folks basically I'm using gradle in java project and can't export the libraries in jar file that I'm using.
Tried a few solutions but nothing worked.
Do you know what I'm missing in the gradle file or I need to specify some things when I'm exporting. I'm using Eclipse
Thanks, here is my gradle file
enter code here
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
}
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:27.0.1-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
implementation "redis.clients:jedis:3.0.1"
implementation 'org.pixsee.java-fcm:Java-fcm:1.0.0'
implementation 'com.google.firebase:firebase-admin:6.10.0'
compile "org.slf4j:slf4j-api:1.6.1"
implementation 'org.slf4j:slf4j-simple:1.7.25'
implementation "com.google.maps:google-maps-services:0.9.4"
implementation 'io.vertx:vertx-core:3.8.1'
}
sourceCompatibility = 1.8
version = '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'GeofenceServer',
'Implementation-Version': version
}
}
apply plugin: "eclipse"
Finally solved it , the answer from Sterconium got me on the right track
answer but the problem was when I try to create the fatJar it says cannot find the main class ,the reason was because my files are in src/test/java instead of src/main/java and somehow when I tried to run fatJar it compiled It but could not find still the dependencies, so I change the implementation to compile in build.gradle file and now it works .So here is my final build.gradle file how it looks like .
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you started.
* For more details take a look at the Java Libraries chapter in the Gradle
* User Manual available at https://docs.gradle.org/5.4/userguide/java_library_plugin.html
*/
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
}
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
}
apply plugin: "java"
apply plugin: "eclipse"
version = '1.0'
//create a single Jar with all dependencies
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'Server.Test'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:27.0.1-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
implementation "redis.clients:jedis:3.0.1"
implementation 'com.google.firebase:firebase-admin:6.10.0'
implementation 'org.slf4j:slf4j-simple:1.7.25'
implementation 'com.google.maps:google-maps-services:0.10.0'
compile 'io.vertx:vertx-core:3.8.1'
implementation 'com.google.code.gson:gson:2.8.5'
}

Gradle and Lwjgl 3 Natives

I'm new to gradle and I'm trying to configure gradle with lwjgl3. Because I didn't found a repo where lwjgl3 is hosted i decided that everybody who use this project has to define the path to the lwjgl lib. I created a user.gradle file with contains the paths to the jar and to the natives.
My build.gradle looks like this at the moment.
apply plugin: 'java'
apply from: 'user.gradle'
apply plugin: 'application'
sourceCompatibility = 1.8
targetCompatibility = 1.8
mainClassName = "mp.Main"
println("LWJGL jar path is configured to: ${config.lwjgl3Jar}")
println("LWJGL natives path is configured to: ${config.lwjgl3Natives}")
repositories {
mavenCentral()
flatDir {
dir config.lwjgl3Jar
}
}
dependencies {
compile 'com.google.code.gson:gson:2.3.1'
compile 'net.java.dev.jna:jna:4.1.0'
testCompile 'junit:junit:4.+'
testCompile 'com.carrotsearch:junit-benchmarks:0.7.2'
compile name: 'lwjgl'
}
tasks.withType(Test) {
scanForTestClasses = false
include "**/*Test.class" // whatever Ant pattern matches your test class files
}
sourceSets{
main {
java {
srcDir 'src'
exclude 'mp/graphics/gl/scene/Mesh.java'
exclude 'test'
}
}
test{
java {
srcDir 'src/test'
exclude '**/UnsafeTest.java'
exclude '**/DispatchTests/*'
exclude '**/MemoryTest.java'
exclude '**/SuperFastListTest.java'
exclude '**/MatrixTest.java'
exclude '**/SimulationTest.java'
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
How to set the natives? I tried it different ways. Google didn't helped me out this time. All results are related to older versions of this lib and all are using repositories. Maybe I'm missing the forest for the trees in between. Any ideas?
Best regards!
PS: Not sure if it is important: We are using different IDE's like intelliJ and Eclipse on Windows, Linux, and Mac.
I have run into the same problem and wrote a plugin for handling the natives associated with Java jar files.
http://cjstehno.github.io/gradle-natives/
It will unpack them from the jar files so that you can use them and deploy them in your project.
I solved the problem for me. The problem for was that I didn't knew how to configure gradle to use the natives. Normally I set the the classpath in the run config. However:
The very simple solution how to set the classpath with gradle:
Apply the java plugin and use the function:
run {
systemProperty 'java.library.path', 'path to ur natives')
}
The simply run your application via gradle and it should work.
There were so many solutions by searching for "lwjgl gradle natives" that I didn't found the right one :-)
Hope the solution helps somebody.

Categories