I am having two project(ProjectA and projectB). ProjectA have 2 tasks of creating jarA1.jar and jarA2.jar. This is configured with ProjectA's build.gradle. Now I want to add these two created jars as dependency to ProjectB , by just mentioning the project and not specifying the jar's file path separate.
Suppose in future if I add multiple jars at ProjectA , all the jars must be added as dependency to ProjectB without any changes in projectB's build.gradle.
ProjectA => build.gradle
task jarA1(type: Jar, dependsOn: compileGrpcJava) {
...
}
task jarA2(type: Jar, dependsOn: compileGrpcJava) {
...
}
jar {
dependsOn jarA1
dependsOn jarA2
}
ProjectB => build.gradle
Approach1:
dependencies {
implementation project (:ProjectA)
}
is not importing the Jars. But this works.
Approach2:
dependencies {
implementation project (:ProjectA)
implementation files('<path_to_jarA1.jar')
implementation files('<path_to_jarA2.jar')
}
Can anyone say why approach 1 is not working. Why adding the complete project as dependency not taking up all the jars of that project by default?
Related
I have a gradle monolithic project with too many dependencies.
I'd like to explode it into many sub-projects and publish all sub-projects (build + sources + javadoc) + an extra project being the merge of all sub-projects.
This extra project should be like a virtual artifact with all my projects in a single jar like it is today because I don't want a too big change for my users.
The jar must not include dependencies (it is not an uber-jar) but the resulted pom.xml must contain the dependencies of all sub-projects (the generated pom.xml of the maven artifact must contain all dependencies).
The virtual artifact will include the merge of javadoc and sources too in order to respect Maven Central conventions.
Current state:
Project Main, generate
pom.xml
main.jar
main-sources.jar
main-javadoc.jar
Expected state:
Subproject A, generate
A-pom.xml
A.jar
A-sources.jar
A-javadoc.jar
Subproject B, generate
B-pom.xml
B.jar
B-sources.jar
B-javadoc.jar
virtal-Project Main, generate
pom.xml=A-pom.xml+B-pom.xml
main.jar=A.jar+B.jar
main-sources.jar=A-sources.jar+B-sources.jar
main-javadoc.jar=A-javadoc.jar+B-javadoc.jar
How can I manage it?
We have been in exactly the same situation for some time now. We want to publish a single artifact for our clients to depend on, although internally the product is developed through a few separate component projects. I got it done eventually (with compromises), and here is what I learned:
Merging jars is not as straightforward as it looks like because there could be things like resource files within a jar that are not
always namespace-ed. It is possible that two of your jars have a
resource file with the same name, in which case you will have to
merge the content of those files.
Javadoc is very hard to merge without accessing the original source
files because it has summary pages (index pages).
So my advice would be:
Think twice, maybe what you really want is NOT a single jar, but a single dependency for your clients? These are different. You can easily have a pom only artifact. Depending on this pom only artifact will simply translates transitively into depending on individual artifacts of your component sub projects. To your client, practically, nothing is changed. Spring Boot takes this approach. To do it, you can create an empty java-library project, make all your component projects its api dependency. You don't even need any source code in this project.
If you really want to merge into a single jar, you can try building a fat jar with customization. The customization is not to pull in 3rd party dependencies.
We use the Gradle Shadow plugin for merging jars. Its original purpose was to build a fat jar, which will include all the transitive dependencies. But it also has a special "shadow" configuration, to which you can add dependencies if you want the dependencies to be exported into POM rather than bundled. So what you need to do:
Define a non-transitive configuration (say bundler) to which you will add your sub-project as dependencies. This is going to be the target configuration for the Gradle Shadow plugin.
Define a transitive configuration (bundlerTransitive) that extends from your non-transitive one. This will be manually resolved in order to find the 3rd party dependencies
in your build.gradle, register an afterEvaluate closure, where you find the level two dependencies of the resolved transitive configuration, add them to the shadow configuration. The reason for level-two is that level one dependencies will be your sub-project artifacts.
After all the above, the artifact produced by shadowJar task is the one to be uploaded to maven. You will need to configure the shadowJar task to remove the classifier (which is shadow by default)
Here is a complete example (build.gradle) of bundling vertx-web and all its dependencies within the io.vertx group:
plugins {
id 'java'
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '5.2.0'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
configurations {
bundler {
transitive = false
}
bundlerTansitive {
extendsFrom bundler
transitive = true
}
}
dependencies {
bundler "io.vertx:vertx-web:4.0.0"
bundler "io.vertx:vertx-web-common:4.0.0"
bundler "io.vertx:vertx-core:4.0.0"
bundler "io.vertx:vertx-auth-common:4.0.0"
bundler "io.vertx:vertx-bridge-common:4.0.0"
}
shadowJar {
configurations = [project.configurations.bundler]
classifier ''
}
publishing {
publications {
shadow(MavenPublication) { publication ->
project.shadow.component(publication)
}
}
}
project.afterEvaluate {
// this is needed because your sub-projects might have inter-dependencies
def isBundled = { ResolvedDependency dep ->
return configurations.bundler.dependencies.any {
dep.moduleGroup == it.group && dep.moduleName == it.name
}
}
logger.lifecycle '\nBundled artifacts and their 1st level dependencies:'
// level one dependencies
configurations.bundlerTansitive.resolvedConfiguration.firstLevelModuleDependencies.forEach {
logger.lifecycle "+--- ${it.getName()}"
// level two dependencies
it.children.findAll({ ResolvedDependency dep -> !isBundled(dep) })
.forEach { ResolvedDependency dep ->
logger.lifecycle "| +--- ${dep.name}"
project.dependencies.add('shadow', [group: dep.moduleGroup, name: dep.moduleName, version: dep.moduleVersion])
}
}
logger.lifecycle '\nExported Dependencies:'
configurations.shadow.getResolvedConfiguration().getFirstLevelModuleDependencies().forEach {
project.logger.lifecycle "+--- ${it.getName()}"
}
}
For javadoc if you don't care about the index (compromise, as I said), then it is just a jar task with a copy spec:
configurations {
javadoc {
transitive = false
}
}
dependencies {
javadoc 'com.my:component-a:1.1.0:javadoc'
javadoc 'com.my:component-b:1.1.0:javadoc'
javadoc 'com.my:component-c:1.1.0:javadoc'
javadoc 'com.my:component-d:1.1.0:javadoc'
}
task javadocFatJar(type: Jar) {
archiveClassifier.set('javadoc')
from {
configurations.javadoc.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}
This cannot be done with maven-publish directly, but one can add individual java-library modules and package each of them with sources and docs. With Gradle this would be a simple jar task, but when the artifacts are publicly available ...such transitive dependencies should better be provided by a meta package; nothing but Maven (Local/Central) dependencies, instead of embedded JARS. In this case, this would be just another module (which obviously would only build after having published the others).
And concerning the concept, that it would require any "merged" JavaDocs ...
https://central.sonatype.org/pages/requirements.html#supply-javadoc-and-sources
While they're referenced (Maven Central) in *.pom, Gradle will be able to find them.
Just use repository mavenLocal() instead of mavenCentral() for testing purposes.
I'm attempting to include a generated pom.xml in the jar that I'm creating with gradle.
So far, in my parent project, I have
subprojects {
apply plugin: 'maven-publish'
publishing {
publications {
maven(MavenPublication) {
from(components.java)
}
}
}
}
and in the sub-project I have:
tasks.build.dependsOn install
sourceSets {
main {
resources {
srcDirs = [ "src/main/resources", "build/poms" ]
}
}
}
This will generate ./build/poms/pom-default.xml, but it will not add it to the JAR.
Creating a dependency on an earlier phase than build creates circular dependencies (and I don't know whether this is the problem anyway).
Also, I'd like the pom.xml to show up inside META-INF with name pom.xml (not pom-default.xml), so this may not be the right approach anyway.
Somehow I'm thinking it can't be as complicated as this looks?
You should be able to include the POM in your JAR by adding the following to your subprojects closure:
jar {
into("META-INF/maven/${project.group}/${project.name}") {
from generatePomFileForMavenPublication
rename { it.replace('pom-default.xml', 'pom.xml') }
}
}
If you already have a jar closure, you can add it there. This automatically creates a task dependency on the generatePomFileForMavenPublication task, so that the POM file is there when the JAR is created.
The sourceSets part from your question would not be required for this.
(Side note: It would not be strictly necessary to do this at all, because the Maven publish process will publish the POM as an individual artifact anyway.)
I'm trying to generate a pom.xml file for my multi-module Gradle project.
My settings.gradle file looks as follows:
include 'moduleOne'
include 'moduleTwo'
include 'moduleThree'
Each one of the submodules may or may not declare its own dependencies, and I would like for all of them to be included into the result "pom.xml" file.
When using maven Gradle plugin as follows:
/* my buildscript... */
task createPom {
pom {
project {
groupId 'org.test'
artifactId 'test'
version '1.0.0-SNAPSHOT'
}
}.writeTo('pom.xml')
}
in the build.gradle of the root project a "pom.xml" with no dependencies is generated, which is no surprise - the root project itself declares no dependencies.
When creating the createPom task in the subprojects closure, many pom files that contain dependencies are generated, but I would like to have 1 "pom.xml" with all of the dependencies.
Is there a way I can automatically merge all of my dependencies into one super "pom.xml" file?
I have two different java projects, projectA and projectB. I'm at a stage where projectB depends on projectA. How do I pull in the jars created by building projectA and all of its dependencies?
In essence, I would like to mirror the effect of adding a projectA to the build path of projectB in eclipse,effectively pulling in jars on the classpath projectA.
If the two projects are closely related you can make Project A and Project B part of the same Multi-project build, then:
//ProjectB build.gradle
dependencies {
compile project(':ProjectA')
}
If they're not closely related you can publish the output of Project A to a repository e.g. your local Maven, Artificatory, Bintray etc. Then in Project B you'll get it like any other dependency. E.g.
//ProjectB build.gradle
repositories {
mavenLocal()
//Or something like this:
//maven { url "http://urltoyourartifactory.com/repo/" }
}
dependencies {
compile 'com.yourname:project-a:1.0.0'
}
Gradle 2.3; shadow plugin 1.2.1.
In my build.gradle, I use the shadow plugin in order to repackage a dependency, like such:
shadowJar {
relocate("com.google.common", "r.com.google.common");
}
I also add the shadow jar to the list of artifacts to publish:
artifacts {
archives jar;
archives sourcesJar;
archives javadocJar;
archives shadowJar;
}
However the list of dependencies of the shadow jar still contains all the dependencies of the "normal" jar, even though it has every dependency builtin.
Is this the intended behavior? How can I make the shadow jar exclude this or that dependency?
Here at work we had the same problem and we just put this in a build.gradle of one of our projects:
def installer = install.repositories.mavenInstaller
def deployer = uploadArchives.repositories.mavenDeployer
[installer, deployer]*.pom*.whenConfigured { pom ->
pom.dependencies.retainAll {
it.groupId == 'our.group.id' && it.artifactId == 'some-api'
}
}
This removes all dependencies from the pom.xml except for the dependency on one of our API projects.
(And it is a pretty verbatim copy of an example from the official Gradle documentation.)