Gradle - Multiple Jar from single project - java

Context: To create multiple executable jar's from a single project - multiple package gradle project
Issue: I refer to the solution provided in Link, this helps in generating the jar in build->libs folder, but when I try executing the jar nothing happens
Note: Even if I make the package name same as java file name, the generated jar does not execute.
Also I notice the file size of all the jar's generated is the same. Hope the issue faced is clear & await inputs as to where I am making a mistake.
My Project Structure (illustrative purpose):
ProjectA
-src
--main
---java
----pkg1
-----pkgCalculator
------Calculator.java
-----pkgScale
------Scale.java
----pkg2
-----pkgMusicPlayer
------MusicPlayer.java
-----pkgVideoPlayer
------VideoPlayer.java
---resources
----fxml
----css
--test
---java
---resources
Gradle file (relevant portion below, rest as per the link above ):
artifacts {
archives jarPackage("pkgCalculator", "1.0"),
jarPackage("pkgScale","1.0"),
jarPackage("pkgMusicPlayer","1.0"),
jarPackage("pkgVideoPlayer","1.0")
}

After discussion with #iCoder, first part of the issue has been solved: the jarPackage function could not be reused "as is", and had to be adapted to support generic class/package layout:
def jarPackage(String jarName, String className, artifactVersion) {
if (artifactVersion == "" || artifactVersion == null) {
artifactVersion = "1.0.0"
}
return tasks.create("jar${jarName}", Jar) {
baseName = jarName
version = artifactVersion
def String pkgName = className.substring(0, className.lastIndexOf("."))
def String pkgDir = pkgName.replaceAll("\\.", "/")
def String clazzName = className.substring( className.lastIndexOf(".") +1 )
from(sourceSets.main.output) {
include "$pkgDir//**"
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
manifest {
attributes "Implementation-Title": "$clazzName",
"Implementation-Version": "$version",
"Main-Class": "$pkgName.$clazzName"
}
}
}
artifacts {
archives jarPackage("calculator", "pkg1.pkcalculator.Calculator" , "1.0.0")
}
Another issue remains, not related to Gradle but to JDK 11 linkage problem: #iCoder you should open another question for that remaining problem, if not fixed yet.

Related

Delombok'ing source code with added jar dependencies

I am unable to delombok my Java source code, apparently due to jar dependencies that the project has, and I don't understand why. There are two jar files that have to be committed to the repo to tag along, and they are added to the project in the dependencies node of the build.gradle file by adding the line compile files('myproj1.jar'). So, the relevant part of the build.gradle file looks like this:
dependencies {
compile files('myproj1.jar')
compile files('myproj2.jar')
.....
}
When I run the delombok task I get the following error:
Execution failed for task ':delombok'.
> taskdef class lombok.delombok.ant.Tasks$Delombok cannot be found
using the classloader AntClassLoader[/path/to/repo/myproj1.jar:/path/to/repo/myproj2.jar]
Why would delombok task be using the AntClassLoader from the jar files?
I have tried the delombok'ing code from this post
Here is the task from my build.gradle file
def srcJava = 'src/main/java'
def srcDelomboked = 'build/src-delomboked'
task delombok {
// delombok task may depend on other projects already being compiled
dependsOn configurations.compile.getTaskDependencyFromProjectDependency(true, "compileJava")
// Set up incremental build, must be made in the configuration phase (not doLast)
inputs.files file(srcJava)
outputs.dir file(srcDelomboked)
doLast {
FileCollection collection = files(configurations.compile)
FileCollection sumTree = collection + fileTree(dir: 'bin')
ant.taskdef(name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok', classpath: configurations.compile.asPath)
ant.delombok(from:srcJava, to:srcDelomboked, classpath: sumTree.asPath)
}
}
I expect to be able to delombok my Java source code as part of the build process so that when the project is compiled there are no dependencies on Lombok.
So after continued trial an error, I have a working implementation. To answer my own question, the problem has nothing to do with the additional Jar files. Rather, when gradle runs the delombok task, the classes in the lombok jar are not in the classpath of the org.gradle.api.AntBuilder (ie, the ant task), and so it does not have a reference to lombok.delombok.ant.Tasks$Delombok anywhere (which seems obvious at this point, but not at the time).
The solution thus far has been to add those references in from configurations.compile
Combining code snippits from this post and this post you can do it with something like this:
def srcDelomboked = 'build/src-delomboked'
task delombok {
description 'Delomboks the entire source code tree'
def srcJava = 'src/main/java'
inputs.files files( srcJava )
outputs.dir file( srcDelomboked )
doFirst {
ClassLoader antClassLoader = org.apache.tools.ant.Project.class.classLoader
def collection = files( configurations.compile + configurations.testCompile )
def sumTree = collection + fileTree( dir: 'bin' )
sumTree.forEach { File file ->
antClassLoader.addURL(file.toURI().toURL())
}
ant.taskdef( name: 'delombok', classname: 'lombok.delombok.ant.Tasks$Delombok',
classpath: configurations.compile.asPath + configurations.testCompile.asPath )
ant.delombok( from: srcJava, to: srcDelomboked, classpath: sumTree.asPath )
}
}
sourceSets {
main {
java { srcDirs = [ srcDelomboked ] } //blow away the old source sets so that we only use the delomboked source sets
}
test {
java { srcDirs += [ srcDelomboked ] } //but add those new source sets to the tests so that their references are available at test time
}
}
compileJava.dependsOn(delombok)
bootJar {
mainClassName = 'com.myproj.MyMainClass' // you will need this if its a Spring Boot project
}
Hope this helps whomever else needs to delombok their code.

How to create JAR from specific classes and use it with Gradle?

I have few Java classes that I want to use on different projects.
I don't want to move these classes in a dedicated project for now.
So I want to build a JAR with these classes, and be able to use it in my other projects, all with Gradle.
So here my JAR task (sources) and I publish it as an artifact :
task utilitiesJar(type: Jar) {
baseName = 'utilities'
version = '0.0.1'
includeEmptyDirs = false
from sourceSets.main.allJava
include "**\\common\\exceptions\\**"
include "**\\common\\json\\**"
include "**\\common\\logging\\**"
}
publishing {
publications {
utilities(MavenPublication) {
artifact utilitiesJar
groupId group
artifactId utilitiesJar.baseName
version utilitiesJar.version
}
}
repositories {
maven {
url 'my_URL'
}
}
}
I get it back with an other project :
repositories {
mavenCentral()
maven {
url 'my_URL'
}
}
...
compile (...)
...
Seems like the JAR is correctly imported (I can see it in "External Libraries" of IntelliJ, with all its classes), but I can't use it.
Maybe because the .class files are missing ?
I'm beginner in Java, maybe I missed something.
How can I create a JAR with only some classes and then use it ?
Ok so as said in comments, I have to include the builded .class files, I can't use external .java classes like this.
So my solution :
def utilitiesName = '...'
def utilitiesVersion = '0.0.1'
task utilitiesJar(type: Jar, dependsOn: classes) {
baseName = utilitiesName
version = utilitiesVersion
includeEmptyDirs = false
from sourceSets.main.output
include ("**\\common\\exceptions\\**\\*", "**\\common\\json\\**\\*", "**\\common\\logging\\**\\*")
}
task utilitiesSourcesJar(type: Jar, dependsOn: classes) {
baseName = utilitiesName
version = utilitiesVersion
classifier = 'sources'
includeEmptyDirs = false
from sourceSets.main.allJava
include ("**\\common\\exceptions\\**\\*", "**\\common\\json\\**\\*", "**\\common\\logging\\**\\*")
}
publishing {
publications {
utilities(MavenPublication) {
artifact utilitiesJar
artifact utilitiesSourcesJar
groupId group
artifactId utilitiesName
version utilitiesVersion
}
}
repositories {
maven {
url 'myURL'
}
}
}
Now I can use it and see the classes in my IDE.
PS : doing in this way is pretty dirty. Create a sub-project / a module, it's just the way how to do it, that's finaly what I did.

Gradle : Exclude certain files from packaging (jar) using annotations

I am looking for a solution to exclude certain files marked with a particular annotation to be packaged in jar (can be compiled but not part of jar created).
I have tried the following steps
Create a ClassLoader using : sourceSets.main.output + configurations.runtime
Check recursively within the compiled classes, use ClassLoader.loadClass to load the class and check if annotation is present (Class.isAnnotationPresent)
Any pointers would be helpful.
I was able to implement this long time back but forgot I had posted the question here.
The solution I used to was actually quite simple -
Using gradle jar task -
jar {
excludes = excludedFiles(sourceSets.main.allSource.files)
baseName = artifactName
version = artifactVersion
}
And define the excludedFiles function to look up the files in the source directory provided -
def excludedFiles(Collection<File> files) {
List<String> classes = new ArrayList<>()
files.each { file ->
if (file.isDirectory()) {
excludedFiles(Arrays.asList(file.listFiles()))
}
else {
if (file.text.contains("#YourAnnotation") && file.text.contains("import foo.bar.YourAnnotation")) {
classes += getClassName(file.absolutePath)
}
}
}
return classes
}
Hope this helps.

Include specific classes in jar archive in gradle build

I have a newbie question on a gradle java project. I wish to configure the output artifact jar of this to only include certain classes from my project. I have the desired classes to be included in a file say classes.txt that is generated as a part of a separate task. How should one configure the jar task of the gradle build so that does this. This is what I have tried so far:
jar {
// reset actions
actions = []
copy {
def dependentClasses = file("classes.txt")
if (dependentClasses.exists()) {
dependentClasses.eachLine { include it }
}
from sourceSets.main.output
includeEmptyDirs = false
into "build/tmp/jar" //Some temporary location
}
// How to zip the contents?
}
I am sorry for the possibly naive question, but I haven't had luck with the other solutions seen.
Thanks!
It can be done in the following way:
apply plugin: 'java'
jar {
def excluded = project.file('classes.txt').readLines()
exclude { f ->
excluded.any { e -> f.path.replaceAll(File.separator, '.').contains(e) }
}
}
Demo can be found here, Lol1.class will be excluded.
Using Opal's answer, here is the answer for the include of files
apply plugin: 'java'
jar {
def included = project.file('classes.txt').readLines()
include { f ->
included.any { i -> f.isDirectory() ? true : f.path.replaceAll(File.separator, '.').contains(i) }
}
}
The slight trick was that the FileTreeElement f being passed to the closure also has the package directory passed and if that happens to return false, the subtree below is not traversed and hence the check on it being a directory to return true to enable processing of the subtree.
Demo for solution is here and Lol1.class will be included. Thanks for the help, Opal!

Gradle: Create a jar with no classes, only resources

I'm trying to create a group of four jars with the following pattern (each jar has its own project. helpRootDir is shared between all four jars. If somebody knows of a way to make one task that does all four, that'd be awesome)
def helpRootDir = 'runtime/datafiles/help/'
project(':schedwinclihelp') {
def helpDir = 'schedwincli'
//Include no classes. This is strictly a resource jar
sourceSets.main.java {
exclude 'com/**'
}
jar {
from '${helpRootDir}/${helpDir}'
include '**/*.*'
}
}
Anyway as you can see from the above, I want no classes in the jar, and that's working. Unfortunately, all I'm actually getting in the jar is a MANIFEST.MF file. None of the files in the jar definition are getting added. I want the full file tree in ${helpRootDir}/${helpDir} added to the root of the jar. How do I accomplish this?
Figured out I was referencing my variables incorrectly.
The correct syntax is:
def helpRootDir = 'runtime/datafiles/help/'
project(':schedwinclihelp') {
def helpDir = 'schedwincli'
//Include no classes. This is strictly a resource jar
sourceSets.main {
java {
exclude 'com/**'
}
resources {
srcDir helpRootDir + '/' + helpDir
}
}
}
Note srcDir helpRootDir + '/' + helpDir rather than '${helpRootDir}/${helpDir}'. Also, I just made my help dir a resource dir and let the java plugin do its thing automatically.
The following task will create a JAR file named resources.jar with main resource files only (those are placed under src/main/resoures directory).
Kotlin DSL:
tasks {
task<Jar>("resourcesJar") {
from(sourceSets["main"].resources)
archiveFileName.set("resources.jar")
}
}
Groovy DSL:
tasks.create("resourcesJar", Jar.class) {
from sourceSets.main.resources
archiveFileName = "resources.jar"
}

Categories

Resources