How to uberjar SPECIFIC dependencies? - gradle

The typical answer for uberjar is the following code:
build.gradle for project
manifest {
attributes("Main-Class": "main.Main" )
}
jar{
//<editor-fold defaultstate="collapsed" desc="uberjar">
//uberjar start
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
//uberjar end
//</editor-fold>
}
From what I observe, this works by putting in all the jars in maven local.
How do I make it uberjar ONLY the dependencies, or only the dependencies that I choose?

You can use include or exclude configuration(or both if it suits your needs) to specify which packages you want in your uberjar.
Example:
task customJar(type: Jar) {
.....
exclude('com/somePack/excludePack1/**')
exclude('com/somePack/excludePack2/**')
include('com/somePack/**')
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
// this example will include only packages with com/somePack/
// since exclude configuration specifies com/somePack/excludePack1/ and
// com/somePack/excludePack2/ packages to be excluded.
// any other packages under com/somePack/ will be included in the jar.
If you use include it will only contain the packages that matches include definitions.
If you use exclude, it wont containt any packages that mathces exlude defitions.
You can choose to exclude your own source codes like this. I suggest creating a custom jar task and executing this task for this kind of things. (Ex: gradle yourJarTaskName)

Related

Fat Jar expands dependencies with Gradle Kotlin DSL

I am trying to build a fat jar using the following in my Kotlin based gradle file.
val fatJar = task("fatJar", type = Jar::class) {
baseName = "safescape-lib-${project.name}"
// manifest Main-Class attribute is optional.
// (Used only to provide default main class for executable jar)
from(configurations.runtimeClasspath.map({ if (it.isDirectory) it else zipTree(it) }))
with(tasks["jar"] as CopySpec)
}
tasks {
"build" {
dependsOn(fatJar)
}
}
However, the fat jar has all the dependencies expanded out. I would like to have the jars included as is in a /lib directory but I cannot work out how to achieve this.
Can anyone give any pointers as to how I can achieve this?
Thanks
Well you are using zipTree in that map part of the spec, and it behaves according to the documentation: it unzips the files that are not a directory.
If you want the jars in /lib, replace your from with:
from(configurations.runtimeClasspath) {
into("lib")
}
In case anyone is using kotlin-multiplatform plugin, the configuration is a bit different. Here's a fatJar task configuration assuming JVM application with embedded JS frontend from JS module:
afterEvaluate {
tasks {
create("jar", Jar::class).apply {
dependsOn("jvmMainClasses", "jsJar")
group = "jar"
manifest {
attributes(
mapOf(
"Implementation-Title" to rootProject.name,
"Implementation-Version" to rootProject.version,
"Timestamp" to System.currentTimeMillis(),
"Main-Class" to mainClassName
)
)
}
val dependencies = configurations["jvmRuntimeClasspath"].filter { it.name.endsWith(".jar") } +
project.tasks["jvmJar"].outputs.files +
project.tasks["jsJar"].outputs.files
dependencies.forEach { from(zipTree(it)) }
into("/lib")
}
}
}

Gradle ShadowJar jar of jars without unzipping classes

I am trying to create a jar of jars without unzipping class files from each jar. Unfortunately shadowJar unzip's all jars and resulting jars contain directories instead of jars.
My build.gradle file is:
apply plugin: 'com.github.johnrengelman.shadow'
dependencies {
compile("ldapjdk:ldapjdk:1.0"){
transitive = false
}
compile("support:activemq-core:5.3.1"){
transitive = false
}
compile("support:concurrent:0.0"){
transitive = false
}
compile("dom4j:dom4j:${dom4j_version}"){
transitive = false
}
compile("commons-lang:commons-lang:${commons_lang_version}") {
transitive = false
}
}
task copyRuntimeLibs(type: Copy) {
from(project(':server').configurations.runtime)
include 'commons-lang2*'
include 'ldapjdk*'
include 'dom4j*'
include 'concurrent*'
}
task copyFiles(dependsOn: [copyRuntimeLibs])
task copyToLib( type: Copy ) {
into "$buildDir/lib"
from configurations.runtime
}
jar { dependsOn copyToLib }
shadowJar {
zip64 true
baseName = "service"
from("$buildDir/lib") {
include '**'
}
}
I've tried from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }. This also extracts classes, MANIFEST, etc.
Is there a way to not extract classes from each jar & instead make a jar of jars?
Thanks a lot.
You may want to see this GitHub issue.
https://github.com/johnrengelman/shadow/issues/111
It is actually not solved yet in the Shadow plugin, there some workarounds are shown there.

How to specify output.classesDir for custom sourceSet in Gradle?

My build uses source code from two projects: ProjectA and ProjectB, and produces JAR with classes and resources from ProjectB. I defined custom sourceSet mainProjectB which is supposed to have output in a separate directory:
sourceSets {
mainProjectB {
output.classesDir = "$buildDir/build/classes/projectB"
output.resourcesDir = "$buildDir/build/resources/projectB"
java { srcDirs = ['src/main/java']}
resources { srcDirs = ['src/main/resources']}
}
mainProjectA {
java { srcDirs = [
'../projectA/src/main/java'
]}
resources { srcDirs = [
'../projectA/src/main/resources'
]}
}
test {
java {
srcDirs = [
'../projectA/src/test/java',
'src/test/java'
]}
resources {
srcDirs = [
'../projectA/src/test/resources',
'src/test/resources'
]}
}
}
compileJava {
source sourceSets.mainProjectB.allJava
source sourceSets.mainProjectA.allJava
}
processResources {
from sourceSets.mainProjectB.resources
from sourceSets.mainProjectA.resources
}
jar {
from sourceSets.mainProjectB.output.classesDir
from sourceSets.mainProjectB.output.resourcesDir
}
Problem: custom sourceSet mainProjectB ingores specified output directories.
The directories "$buildDir/build/classes/projectB" and "$buildDir/build/resources/projectB" are not created, and as a consequence, JAR includes files from both projects (instead of ProjectB).
UPDATE:
Projects A and B have circular dependencies. That is why they have to share source code.
I would consider to use subprojects and project to achieve your goal - gradel docs . With the following approach you can get any kind of jar file depending on your build :
group 'CoreProject'
version '1.0-SNAPSHOT'
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
}
}
project (':projectA') {
}
project (':projectB') {
def generatedResources = "$buildDir"
//in case you want resources and classes to be written to custom location where
//redefined paths are relative to projectB root folder
sourceSets {
main {
output.classesDir = 'build/classes/projectB'
output.resourcesDir = 'build/resources/projectB'
}
}
dependencies {
compile project(':projectA')
}
jar {
manifest.mainAttributes(
'Main-Class': "ProjectBClass"
)
}
//To create fat Jar that will contain classes and resources from all dependencies
task fatJar(type: Jar) {
manifest.from jar.manifest
classifier = 'all'
from {
configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "ProjectAResource" //if want to exclude resources from projectA
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
with jar
}
}
If you run jar task of projectB following jars will be created, each including only its own classes and resources : projectA/build/libs/projectA.jar , projectB/build/libs/projectB.jar('JAR with classes and resources from ProjectB' as you asked in your comment)
If you run farJar task of projectB the following jar file will be created that includes classes and resources from both projects and you can exclude any files patterns from projectA and projectB projects to create any final jar you like : projectB/build/libs/projectB-all.jar
Here is a screenshot of projects folders structure I created to mimic your scenario(as I understood it):
P.S. Also make sure none of the folders projectB/build and projectA/build are locked by any process and remove those handles if any, as otherwise Gradle will fail to run.

How to include jars from libs folder to another jar?

I have Gradle JAR (Main) project. I have other commons-io jars (dependent) that I would like to package them with the Main JAR. I tried below, but when I decompile Main JAR, I see all the dependent JARS inside libs folder with .jar extension.
jar {
from("$projectDir") {
include 'libs/**'
}
}
What I want is the class files from all the dependent JARs into Main JAR. I am doing this because the Main JAR is going to be used by multiple projects. So that way, I do not have to include all the dependent JARs.
Thanks
As far as I know You cannot pack other jars into single jar just like that. What You need to do is to extract all the jars and pack the content into single file.
Following piece of code prepares such jar for declared dependencies
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.guava:guava:16.0.1'
compile 'com.google.inject:guice:3.0'
}
task fatJar(type: Jar, dependsOn: jar) {
baseName = project.name + '-fat'
deps = configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
from(deps) {
exclude 'META-INF/MANIFEST.MF'
}
}
use this.
into(libForderName){
from {
configurations.compile.collect {
it.isDirectory() ? it : it.getAbsoluteFile()
}
}
}

Gradle: 'clone' original jar task to create a new task for a jar including dependencies

I would like to create a new task in my project that creates a jar archive with the class files of my project and the class files of the dependencies (also called 'shaded jar' or 'fat jar').
The solution proposed by the Gradle cookbook modifies the standard jar task of the JavaPlugin:
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
However, I would like to keep the original jar taks as it is and have an additional task for the shaeded jar, i.e. a task that behaves exactly like the jar task, but includes the additional files according to
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
and has another classifier ('shaded').
I tried to take over the configuration of the jar task by copying properties like this:
task shadedJar(type: Jar, dependsOn: configurations.compile) {
dependencies = tasks.jar.taskDependencies
source = tasks.jar.source
manifest = tasks.jar.manifest
includes = tasks.jar.includes
classifier = 'shaded'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
But the resulting tasks does not take over the dependencies of 'jar' and the resulting jar does not include the project's class files. Additionally, this approach seems to cumbersome to be the recommended way of using an existing task as a template for a new one.
What would a recommendable approach to my specific need (the seperate shadedJar task) and for 'cloning' tasks to use them as templates for additional tasks in general?
(I am currently still on Gradle 1.3,but solutions for the current Gradle version are also welcome)
There is no built-in way to clone tasks. However, it's easy to configure the fatJar task to include the same files as the java plugin's jar task:
task fatJar(type: Jar) {
appendix = "fat"
from sourceSets.main.output // that's it
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
Task autowiring will automatically establish the necessary task dependencies.
If the build script goes on to customize the jar task, you can apply the customizations to both tasks simultaneously:
configure([jar, fatJar]) {
version = "2.0"
entryCompression = "STORED"
}
If, unlike in the jar task's case, you are defining the "template" yourself, you can "instantiate" it with a factory method:
def getMeAnotherOne(String name) {
task(name, type: Jar) {
version = "2.0"
entryCompression = "STORED"
}
}
getMeAnotherOne("jar1")
getMeAnotherOne("jar2")

Resources