How to provide generated data-binding classes in a Gradle Javadoc task? - java

With this Gradle task I've used to extract AAR, in order to generate Javadoc:
task javadoc(type: Javadoc) {
doFirst {
configurations.implementation.filter { it.name.endsWith('.aar') }.each { aar ->
copy {
from zipTree(aar)
include "**/classes.jar"
into "$buildDir/tmp/aarsToJars/${aar.name.replace('.aar', '')}/"
}
}
}
failOnError false
options.linkSource true
options.links("https://docs.oracle.com/en/java/javase/11/docs/api/")
options.links("https://developer.android.com/reference/")
title = "Colorpicker Library ${versionName} API"
source = android.sourceSets.main.java.srcDirs
classpath = files(new File("${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"))
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += fileTree(dir: "$buildDir/tmp/aarsToJars/")
configurations.implementation.setCanBeResolved(true)
classpath += configurations.implementation
destinationDir = file("${project.buildDir}/outputs/javadoc/")
exclude "**/BuildConfig.java"
exclude "**/R.java"
}
It fails since I've enabled androidx.databinding 7.2.1 inside the library module:
> Task :library:javadoc
...\ColorPickerDialogFragment.java:24: error: package com.acme.databinding does not exist
import com.acme.databinding.DialogColorPickerBinding;
^
...\ColorPickerDialogFragment.java:46: error: cannot find symbol
DialogColorPickerBinding mDataBinding;
^
symbol: class DialogColorPickerBinding
location: class ColorPickerDialogFragment
2 errors
How can I add these generated sources to classpath? Ignoring the class import doesn't seem to be an option. Or does javadoc have to depend on the task, which generates these (bad timing)? In general, exclude "**/ColorPickerDialogFragment.java" is not the answer I'm looking for.

Extracting the built AAR and putting it on classpath provides the generated classes -
but it's a whole lot more elegant to reference the intermediate classes.jar already:
task javadoc(type: Javadoc) {
...
doFirst {
...
def aar_main = new File("$buildDir/intermediates/aar_main_jar")
if (aar_main.exists()) {
copy {
from aar_main
include "**/classes.jar"
into "$buildDir/tmp/aarsToJars/aar_main_jar/"
}
}
}
}
One can also check .exists() before already:
javadoc.onlyIf {
new File("$buildDir/intermediates/aar_main_jar").exists()
}

Related

Task in gradle to include java class before compile

I need to create Gradle task which can replace .java files before Gradle build.
Gradle build package app in .war file. And I need to have replaced bytecode there after build.
I tried sourceSets Gradle task but it can only exclude files.
sourceSets {
main {
java {
exclude 'com/myapp/example/resource/impl/ResourceBundleImpl.java'
}
}
}
But I need to also include file in the same place. How I can do it with Gradle?
The directory to file that I need to exclude: com/myapp/example/resource/impl/ResourceBundleImpl.java
The directory to file that I need to include: src/main/webapp/WEB-INF/my/ResourceBundleImpl.java
To copy file content it is also posible solution.But How can I do it before compile time?
The below task didn't helped, becouse in build file have .java files instead of .classe file.
task prepareSources(type: Copy) {
from('src/main/webapp/WEB-INF/my')
into('build/classes/java/main/com/myapp/example/resource/impl')com/myapp/example
}
// Prepare sources, before compile
compileJava {
dependsOn prepareSources
}
The below task throws :
Task :cdx-war:compileJava FAILED
error: package com.myapp.example.util does not exist
sourceSets {
main {
java {
srcDir "$projectDir"
exclude 'com/medtronic/diabetes/carelink/rbps/resource/impl/ResourceBundleImpl.java'
include 'src/main/webapp/WEB-INF/my/ResourceBundleImpl.java'
}
}
I put classes that I need to replace together and add suffix to one of them and replace this suffix during compile time :
sourceSets {
main {
java {
srcDirs = ["$buildDir\\generated-src"]
}
}
}
task copySourceSet(type: Copy) {
println 'Change java class in patient-svc-war'
from "$rootDir\\patient-svc-war\\src\\main\\java"
into "$buildDir\\generated-src"
from file("$rootDir\\patient-svc-war\\**_suffix.java")
into "$buildDir\\generated-src"
rename { String fileName ->
fileName.replace("_suffix", "")
}
filter { line -> line.replaceAll('_suffix', '')}
}
compileJava.source "$buildDir\\generated-src"
compileJava {
dependsOn copySourceSet
}
compileJava.doLast {
delete "$buildDir\\generated-src"
}

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!

Categories

Resources