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.)
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 want to reuse the dependencies defined in this pom.xml file It's quite a long list of dependices. I want to reuse it for convenience.
so I added a line in my build.gradle file like this.:
dependencies {
// unfortunately, Gradle seems just ignore this line.
compile("org.activiti:activiti-ui-root:6.0.0")
}
But it seems that gradle just ingore this line. What's the best way of reusing a pom file if I don't want to rewrite a long list of dependencies?
Please give me some advise, many thanks.
==============================
Thanks, all you guys. finally I find "io.spring.gradle:dependency-management-plugin" helps to solve my problem.
#madhead is right. pom.xml just provide the version of jars, but will not import any file into my project.
That pom.xml does not actually define any dependencies, it only defines versions of artifacts in dependencyManagement block. Thus, depending on this artifact in Gradle does not bring any transitive dependencies in your project (neither it will in Maven, btw).
but it's OK. the version information is enough.
This segment is what I use to solve my problem.
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "io.spring.gradle:dependency-management-plugin:1.0.3.RELEASE"
} }
.......
project(':editor-image-generator') {
dependencyManagement {
imports {
// import the pom.xml from outside.
mavenBom "org.activiti:activiti-ui-root:6.0.0"
}
}
dependencies {
// It's good to see that, you don't need to specify the version here.
// with the mavenBom imported above, you can always get the right version.
compile("org.activiti:activiti-bpmn-model")
testCompile("org.activiti:activiti-bpmn-converter")
compile("org.activiti:activiti-image-generator")
compile("org.imgscalr:imgscalr-lib:4.2")
compile("org.slf4j:slf4j-api")
testCompile("commons-io:commons-io")
testCompile("junit:junit")
}
}
That pom.xml does not actually define any dependencies, it only defines versions of artifacts in dependencyManagement block. Thus, depending on this artifact in Gradle does not bring any transitive dependencies in your project (neither it will in Maven, btw).
What you can try in Spring's dependency management plugin for Gradle. It allows to reuse that dependencyManagement block definition for BOMs in Gradle.
Dear StackOverflow users
I have a gradle project of which I want to turn the artifact into an osgi bundle. In this bundle I have:
packages that I don't want to export (may not appear in manifest's Export-Package entry)
dependencies that I want to embed (may not appear in manifest's Import-Package entry)
After a bit of tinkering I have come up with the following gradle.build file which does what I intend but maybe not in the cleanest way possible, leveraging bnd...
group 'com.mycompany'
version '1.0.0'
apply plugin: 'java'
apply plugin: 'osgi'
repositories {
jcenter()
}
dependencies{
compile 'org.osgi:org.osgi.framework:1.8.0' //provided
compile 'com.google.code.gson:gson:2.8.0' //embedded
}
jar {
//embedding the gson dependency
from({
def x = configurations.compile.find({
return it.getName().contains('gson')
})
def tree = zipTree(x)
return tree
})
//explicitly building manifest entries
manifest {
instruction 'Bundle-Vendor',
'My Company'
instruction 'Bundle-Activator',
'com.mycompany.mybundle.Activator'
instruction 'Import-Package',
'!com.google.gson',
'*'
instruction 'Export-Package',
/com.mycompany.mybundle;version="${version}"/
}
}
Is it possible to accomplish this in a cleaner way? I mainly want to avoid two things:
having to manually write the import and export-package entries
having to manually copy the contents of the embedded dependencies (gson) into my jar
I thought bnd (underlying the osgi plugin) could do that for me, but with what I have tried so far (even if I add them as private package) bnd still exports everything and imports the gson package as well as it won't add the gson classes to the jar
You would be better off using the Bnd Gradle plugin for OSGi. It is written and supported by the developers of bnd who know a thing or two about OSGi.
I have an Android closed source module that will be be used as a library in other projects. It contains external dependencies.
To publish it, I'm creating a Maven artifact with the following gradle task:
apply plugin: 'maven'
def coreAarFile = file('...build\\outputs\\aar\\android-sdk-release.aar')
artifacts {
archives coreAarFile
}
uploadArchives {
repositories.mavenDeployer {
repository(url: "file://.\\mvn-repo")
pom.project {
groupId 'a.blabla'
artifactId 'blabla-sdk'
version "1.0.0"
}
}
}
It generates the .aar file, the pom.xml, etc without problems.
Then I create a project that have a dependeny to my library declared. It works until it needs to access to the external dependencies, when throws a class not found exception.
How can I edit my gradle task to include external dependencies or at least a reference to them? They are published in mvnrepository.com and github.com.
I moved uploadArchives to the build.gradle of the module and removed the artifacts element. It works!
Thanks to CommonsWare for pointing to the right direction.
I would like to control which of my dependencies in a multi-project Java build are transitive. My current solution is to set up an "export" configuration in the root project:
allprojects {
configurations {
export {
description = 'Exported classpath'
}
compile {
extendsFrom export
}
}
}
Project A has multiple file dependencies:
dependencies {
compile files('A.jar', 'B.jar')
export files('C.jar')
}
Project B has a dependency on project A, but only C.jar should be on the classpath for compilation, so add:
dependencies {
export project(path: ':A', configuration:'export')
}
This produces the desired results, A.jar and B.jar are not on the class path, but C.jar is on the classpath for compilation.
I am unsure if this is "gradle" way of doing things. To configure transitivity, I would rather specify an attribute or a configuration closure to the dependency entries in project A, instead of using a different "export" configuration.
Is this possible for file dependencies, or is there another way to achieve this?
If I understand your scenario correctly, then yes it's easy to do this. Just add an options closure to the end of the dependency declaration to prevent transitive dependencies (I've changed A,B,C .jar to X,Y,Z because I'm guessing they don't coincide with projects A and B):
// Project A build.gradle
dependencies {
compile(files('X.jar', 'Y.jar')) { transitive = false }
export files('Z.jar')
}
Which would prevent X.jar and Y.jar from being added to the classpath for project B.
Alternatively, and I don't know how well this would work for you and don't really recommend it (just want you to know of the possibilities) you could do this in project B's build.gradle:
configurations.compile.dependencies.find { it.name == "A.jar" }.exclude(jar: it)
configurations.compile.dependencies.find { it.name == "B.jar" }.exclude(jar: it)
Hope that helps.