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

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.

Related

Gradle - Multiple Jar from single project

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.

How to access version of my project in build.gradle from a Java class

I'm quite new to Gradle so the answer might be simple, so I apologize if the answer is simple: I have a testing tool that needs to fetch it's version and compare it to the version of the application it is testing. However , the version of my tool is in my build.graddle as
version '1.0'
I tried different way to access it ( such as) :
task generateSources {
File outDir
outDir = file("$buildDir/classes/test/tests/Version.java")
doFirst {
outDir.exists() || outDir.mkdirs()
new File(outDir).write("public class Version { public static final String VERSION = \"$project.version\"; }")
}
}
compileJava.dependsOn generateSources
compileJava.source generateSources.outputs.files, sourceSets.main.java
I found this piece of code to output the version to another java file, but I fail to see how I'd be able to retrieve that info afterwards ( I mean, my tests are defined in src and I would need to point to a file that doesn't exist at compilation -- correct me if I'm wrong here).
Any idea on how I could accomplish this task?
First of all, you are trying to create java source file in your build/classes (it should contain compiled classes, not sources) directory, but you have to do it in your sources, otherwise it won't be compiled. And if you need this new class to be vailable not for tests, then use src/main/java, not src/test/java/
But anyway, I suppose for your case it's much easier to use some properties file for that and replace some token within it during build. That will allow you to make some static logic to get this property value and use it yet before running the build. So all you need is:
1- to have some properties file in your resources src/main/resources (for example app.properties), where should version variable be stored, with it's value like APP_VERSION_TOKEN
version=%APP_VERSION_TOKEN%
2- configure you Gradle processResources to replace tokens, something like this:
processResources {
filesMatching('**/app.properties') {
filter {
it.replace('%APP_VERSION_TOKEN%', version)
}
}
}
3- make some method to read this file and return the value of the property and use it where you need.
And that's all. For unit tests you can have another file with the same name under src/test/resource with the unchanging value you need for testing.

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"
}

Gradle Zip task to do multiple sub-trees?

We're trying to build up a minorly complicated Zip file in Gradle from multiple filesystem source trees, but no matter how many into specifications we give, it all puts them in the same one. Is this possible to do in Gradle?
build/libs/foo.jar --> foo.jar
bar/* --> bar/*
We're getting this instead:
build/libs/foo.jar --> bar/foo.jar
bar/* --> bar/*
Using this:
task installZip(type: Zip, dependsOn: jar) {
from('build/libs/foo.jar').into('.')
from('bar').into('bar')
}
Any help would be appreciated.
EDIT: Gradle 1.0-milestone-3
Try this:
task zip(type: Zip) {
from jar.outputs.files
from('bar/') {
into('bar')
}
}
First... the jar should be in the root / of the zip (which seems to be what you want). Second, by specifying the from jar.outputs.files, there is an implicit dependsOn on the jar task, so this shows another way of accomplishing what you want. Except with this approach if the jar name changes over time it doesn't matter. Let me know if you need additional help.
Apparently the comments to an answer will not allow for a convenient way to show more code... or it isn't obvious :) I have a project which is for a client... so I can't share the full project / build file. Here is what I can share (I changed the project specific acron to XXX):
task zip(type: Zip) {
from jar.outputs.files
from('scripts/') {
fileMode = 0755
include '**/runXXX.sh'
include '**/runXXX.bat'
}
from('lib/') {
include '**/*.jar'
into('lib')
}
from('.') {
include 'xxx.config'
}
}
This creates a zip with the project jar in the root of the zip. Copies the scripts from a directory to the root, copies the config file to the root and creates a directory in the root of the zip named /lib and copies all the jars from the project /lib to the zip/lib.
This answer does not directly answer the question but I guess this would help someone who writes 'Gradle Plugins'
final Zip zipTask = project.getTasks().create(taskName, Zip.class);
final Action<? super CopySpec> cp1 = (p) -> {
p.include("**/Install_*.xml", "**/Install.xml").into(WORKING_DIR_1);
};
final Action<? super CopySpec> cp2 = (p) -> {
p.include("*Terminology*.xml").into(WORKING_DIR_2);
};
zipTask.from(projectDir + "/Release", cp1);
zipTask.from(projectDir + "/Release", cp2);

Categories

Resources