Gradle: package multi-project into a single jar - gradle

I have a gradle multi-project and want to create a single jar (library) containing all the classes of my subprojects and external dependencies.
I have the following project structure. Each project has its own 3rd party dependencies. Common dependencies are included in the root project. The two modules A and B are dependent on the core.
+ root-project (only build.gradle and settings.gradle)
- core (src/main/java, src/main/resources, ..)
- module-A (src/main/java, src/main/resources, ..)
- module-B (src/main/java, src/main/resources, ..)
To export a single jar, I added the following task to the build.gradle of the root project:
apply plugin: "java"
subprojects.each { subproject -> evaluationDependsOn(subproject.path)}
task allJar(type: Jar, dependsOn: subprojects.jar) {
baseName = 'multiproject-test'
subprojects.each { subproject ->
from subproject.configurations.archives.allArtifacts.files.collect {
zipTree(it)
}
}
}
artifacts {
archives allJar
}
This approach works, but does only collect the project source files. The 3rd party dependencies are ignored. So I tried out the Shadow Plugin (http://imperceptiblethoughts.com/shadow/) which should also include external dependencies.
Unfortunately the plugin does not collect anything at all. This is most probably due to missing dependencies between the root project and its sub projects. How can I tell the shadow plugin, that it should collect the sources of the subprojects? Or is there a better approach to export a single library out of multiple projects?
complete build.gradle using the shadow plugin:
/****************************************
* instructions for all projects
****************************************/
allprojects {
apply plugin: 'idea'
apply plugin: 'eclipse'
group = 'com.test.multi-project'
version = '1.0'
}
/****************************************
* instructions for each sub project
****************************************/
subprojects {
apply plugin: "java"
sourceCompatibility = 1.9
targetCompatibility = 1.9
repositories {
mavenCentral()
}
dependencies {
compile "org.slf4j:slf4j-api:1+"
compile "ch.qos.logback:logback-core:1+"
compile "ch.qos.logback:logback-classic:1+"
testCompile "junit:junit:4+"
}
}
/****************************************
* Single jar out of all sub projects
****************************************/
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
}
}
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'java'
shadowJar {
baseName = 'multiproject-test'
}
The submodules are included in the settings.gradle of the root project
rootProject.name = 'myproject-root'
// submodules
include ":core"
include ":module-A"
include ":module-B"
Thanks for your help!

I solve my problem with the solution explained here: https://discuss.gradle.org/t/how-to-get-gradle-install-to-actually-bundle-all-project-subproject-classes-resources-etc/12070/4
My build.gradle looks now like this:
/****************************************
* instructions for all projects
****************************************/
allprojects {
apply plugin: 'idea'
apply plugin: 'java'
repositories {
mavenCentral()
}
group = 'com.test.multiproject'
version = '1.0'
sourceCompatibility = 1.9
targetCompatibility = 1.9
}
/****************************************
* instructions for each sub project
****************************************/
subprojects {
// common dependencies
dependencies {
compile "org.slf4j:slf4j-api:1+"
compile "ch.qos.logback:logback-core:1+"
compile "ch.qos.logback:logback-classic:1+"
testCompile "junit:junit:4+"
}
}
/****************************************
* Single library jar containing all sub projects and 3rd party dependencies
****************************************/
configurations {
childJars
}
dependencies {
subprojects.each {
childJars project(it.path)
}
}
jar {
dependsOn configurations.childJars
from { configurations.childJars.collect { zipTree(it) } }
}

How about getting all runtime libs while building jar itself
jar {
archiveName 'Some.jar'
manifest {
attributes 'Implementation-Title': 'Some',
'Plugin-Class': 'main'
}
from {configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it)}}
}

What about something simple like that :
task fatJar(type: Jar) {
subprojects.each { subproject ->
from subproject.configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}

Related

Trying to import PersistentCookieJar library on Gradle project

I'm doing a project with Gradle and I'm currently using okhttp3 library in order to make requests to a webpage. A need a cookie handler so I'm trying to import this library on GitHub. I don't know why the dependency is not recognized on IntelliJ.
This is my build.gradle:
apply plugin: 'java'
apply plugin: 'application'
compileJava.options.encoding = 'UTF-8'
mainClassName = 'com.Main'
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.0'
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.Main'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
allprojects {
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
}
}
dependencies {
compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
compile 'com.squareup.okhttp3:okhttp:3.10.0'
}
EDIT: On Netbeans I got the same thing, the library isn't recognized.
This library is an Android Library (see the library's gradle file and the aar extension), you cannot use it as a Pure Java Library because it refers to several Android classpaths.

Gradle: create .jar from .class files and include it in EAR_dir/lib when EAR is built

You'd think this'd be easy enough. Gradle/Maven were designed specifically to get rid of build nightmares. And yet... I have scoured the web, including SO. I would prefer to be using Maven but alas this is not in my control.
My master build.gradle file looks like this:
buildscript {
repositories {
maven { url "https://aaa.com/xxxx/aaa-mvn" }
}
dependencies {
{redacted}
classpath "com.aaa.plugin.gradle:module-plugin:1.+"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.4.4"
}
}
apply plugin: 'maven-publish'
apply plugin: {redacted}
apply plugin: {redacted}
apply plugin: 'build-defaults'
apply plugin: 'module-plugin'
apply plugin: 'war'
description = 'xxxxxx'
defaultTasks 'build','install'
dependencies {
jbossModule(group: 'com.aaa.bbbb', name: 'inf-jdbc', version: '3.0.2')
}
artifactory {
publish {
repoKey=version.endsWith("SNAPSHOT") ? 'aaa-mvn-dist-snapshots' : 'aaa-mvn-dist'
defaults {
publications('mavenJava')
}
}
}
publishing {
publications {
mavenJava(MavenPublication) {
artifact project(':xxxEAR').ear
}
}
}
repositories {
maven { url "https://xxxx.aaa.com/artifactory/aaa-mvn" }
}
This is gradle.build for my .war. I have a .war inside an .ear.
description = 'Pricing'
buildscript {
repositories {
maven { url "https://xxx.aaa.com/artifactory/aaa-mvn" }
}
dependencies {
classpath "com.aaa.plugin.gradle:ucd-publish-plugin:1.+"
classpath "com.aaa.plugin.gradle:build-defaults-plugin:1.+"
classpath {redacted}
classpath {redacted}
}
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jacoco'
apply plugin: {redacted}
apply plugin: 'ucd-publish'
apply plugin: 'build-defaults'
apply plugin: {redacted}
apply plugin: 'maven'
apply plugin: 'maven-publish'
repositories {
maven { url "https://xxxx.aaa.com/artifactory/aaa-mvn" }
}
war {
archiveName 'xxxx.war'
}
dependencies {
compile {redacted}
compileOnly group: 'javax', name: 'javaee-api', version:'7.0'
testCompile "junit:junit:4.12"
compile 'io.swagger:swagger-annotations:1.5.10'
compile(group: 'com.aaa.inf', name: 'inf-jdbc', version: '3.0.2', classifier: 'sources')
compile(group: 'com.aaa.inf', name: 'inf-throttle', version: '3.0.1')
compile(group: 'com.ibm.db2', name: 'db2jcc', version: '3.64.133')
compile {redacted}
}
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.0
}
}
}
}
The build.gradle file for my .ear is:
description = 'xxxxx'
buildscript {
repositories {
maven { url "https://xxxx.aaa.com/artifactory/aaa-mvn" }
}
dependencies {
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.4.5"
classpath "com.aaa.plugin.gradle:build-defaults-plugin:1.+"
classpath "com.aaa.plugin.gradle:ucd-publish-plugin:1.+"
classpath "com.aaa.plugin.gradle:openapi-generator-gradle-plugin:1.+"
}
}
apply plugin: 'ear'
apply plugin: 'maven'
apply from: 'build.local-jboss.gradle'
apply plugin: 'maven-publish'
apply plugin: {redacted}
apply plugin: 'ucd-publish'
apply plugin: 'build-defaults'
apply plugin: {redacted}
configurations{
gen
}
dependencies {
deploy project (path: ":pricingWAR", configuration: 'archives')
earlib 'com.aaa.inf:inf-api-auth:1.4.+'
earlib {redacted}
earlib group: 'javax.security.enterprise', name: 'javax.security.enterprise-api', version: '1.0'
earlib (group: {redacted}) {
exclude group: 'javax.security.dddddd'
}
gen "io.swagger:swagger-codegen:2.2.2"
}
artifacts{
archives ear
}
repositories {
maven {
url "https://xxxx.aaa.com/artifactory/aaa-mvn"
}
}
task cleanVolumes(type: Delete) {
delete fileTree(dir: "./volumes/deployments/")
}
task copyEar(type: Copy) {
tasks.cleanVolumes.execute()
from "build/libs"
into "${project.projectDir}/volumes/deployments"
fileMode = 0644
}
build.finalizedBy(generateSwagger)
swaggerConfig {
archive = ear.archivePath
outputFormat = "JSON"
outputPath = project.buildDir.toString() + "/swagger"
}
Just looking to do this: .jar up the .class files compiled in my business classes and add them to EAR_file\lib.
I've gone so far as to try to use Gradle's native Groovy nature to write code that builds the .jar and moves it into EAR_file\lib. This of course is ridiculous. And worse still, it does not quite work. Paired with auto-deploy there are timing issues.
Some of you no doubt are wincing.
To save me from such insanity, please let me know what you know about this. I have of course tried various permutations of:
apply plugin: 'java'
...
jar {
...
}
to no avail.
Thank you in advance.
Finally have it working after helpful input from coworkers. For others' benefit here is the whole thing it ended up as:
In build.gradle in the EAR project level:
// My project is named: xxx-svc and the .war subproject is named pricing-theAPI
dependencies {
deploy project (path: ":xxx-theAPI", configuration: 'archives') // This was here already
earlib project(path: ":xxx-theAPI", configuration: 'customJar') // This is new
}
In build.gradle in the WAR project level:
configurations {
customJar
}
task doJar(dependsOn:classes, type: Jar){
from sourceSets.main.output
include 'com/xyz/xxxxx/yyyyyy/zzzz/**' // See Note 1
}
artifacts {
customJar doJar
}
Note 1: I added this modifier b/c in my case I needed only the .class files at the given location in the .jar and in fact had to exclude all others. Note the Gradle docs have the modifier "include" misidentified. They call it "includes" but in fact it is "include".

Configure an eclipse project with gradle

I've a single gradle file where I'm applying eclipse plugin:
plugins {
id 'java'
id 'eclipse'
}
repositories {
mavenCentral()
}
dependencies {
compile 'javax.servlet:javax.servlet-api:3.1.0'
compile 'org.codehaus.jettison:jettison:1.3.7'
compile 'org.apache.directory.api:api-all:1.0.0-M30'
compile 'com.whalin:Memcached-Java-Client:3.0.2'
compile 'org.mongodb:mongo-java-driver:2.14.3'
compile 'commons-configuration:commons-configuration:1.10'
}
task wrapper(type: Wrapper) {
gradleVersion = '3.1'
}
manifest {
attributes 'Implementation-Title': '---', 'Implementation-Version': 0.1
}
All my source files are under src/main/java. When I perform gradle eclipse it generates me eclipse artifacts. Then, I import this project using Import > General > Existing project into workspace. Nevertheless, my source folders are not set such as source folder into imported project.
Porject structure:
workspace
└───project
└───src
└───main
├───java
└───resources
I'd also like to set other parameters like output folder, java compliance compiler...
Any ideas?
On the official site there is a solution of setting the output folder.
apply plugin: 'java'
sourceSets {
main {
//if you truly want to override the defaults:
output.resourcesDir = 'out/res'
output.classesDir = 'out/bin'
}
}
I hope it helps.

Spring Boot Tutorial doesn't work with multi project gradle setup

i get a strange behaviour (at least for me :D) when I switch from the gradle file located in https://spring.io/guides/gs/serving-web-content/ to a multi project gradle file setup.
build.gradle in root directory
//Applied to all projects.
allprojects {
apply plugin: 'eclipse'
apply plugin: 'idea'
group = 'de.test.platform'
version = '0.1'
}
subprojects {
//Currently all subprojects are also java projects. If this changes we
//need to move it into the corresponding projects
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
idea {
module {
downloadSources = true
downloadJavadoc = false
}
}
}
idea {
project {
jdkName = '1.8'
languageLevel = '1.8'
}
}
build.gradle in sub directory frontend (thus sub project called :frontend)
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE")
}
}
apply plugin: 'war'
apply plugin: 'spring-boot'
jar {
baseName = 'crowdio-frontend'
version = '0.1.0'
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("junit:junit")
}
when I run gradle bootRun and navigate to http://localhost:8080/greeting as in the tutorial i get a infinite loop error. If i change the template from greeting.html to hello.html and return hello instead of greeting in the controller greeting() action i get an 404 Error.
The template is stored in project_root/frontend/src/main/resources/templates/greeting.html
It seems like that for whatever Reason spring boot can decide on thymeleaf with the exact structure of the gradle build file like in the tutorial. However if you switch to a multi project setup you need to add
compile("org.thymeleaf:thymeleaf-spring4")
as a dependency.

Publish (rootproject) pom without (rootproject) publishing artifact / packaging = pom

I'm migrating one of our projects from maven to gradle: it's a gradle multi-project & all subprojects are publishing artifacts to artifactory. So far so good.
The legacy (maven-based) build environment however also expects the root project to publish a pom file with the "packaging" node equal to "pom" (common maven behaviour, so it seems)
So now, I'm trying to have this generated by Gradle, but only find ways to customize an automatically generated pom for each artifact, I can't find a way to generate/upload a pom without publishing an actual artifact.
Workaround for now is to have the root project use the java plugin, generate/install an empty jar and manipulate the generated pom to conform to maven expectations (packaging=pom), but that's a hack.
Is there a way to have this root pom file generated with gradle ?
Example project:
settings.gradle
rootProject.name = 'MultiProject'
include 'child01', 'child02'
rootProject.children.each { it.name = rootProject.name + "-" + it.name }
build.gradle
subprojects {
apply plugin: 'java'
}
allprojects {
apply plugin: 'maven'
group = 'my_group'
version = '0.0.1-SNAPSHOT'
}
EDIT (current workaround), addition to build.gradle
// workaround to generate pom
apply plugin: 'java'
configurations {
pomCreation
}
task createPom {
ext.newPomFile = "${buildDir}/blabla.pom"
doLast {
pom {
project {
packaging 'pom'
}
}.writeTo(newPomFile)
}
}
install.dependsOn(createPom)
artifacts {
pomCreation file(createPom.newPomFile)
}
I would use the gradle maven-publish plugin for that. With that plugin you can define your specific pom and don't have to upload other artifacts. Here an example:
publishing {
publications {
maven(MavenPublication) {
pom.withXml{
def xml = asNode()
xml.children().last() + {
delegate.dependencies {
delegate.dependency {
delegate.groupId 'org.springframework'
delegate.artifactId 'spring-context'
delegate.version( '3.2.8.RELEASE' )
}
}
}
}
}
}

Resources