Include pom.xml in Jar with gradle - java

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.)

Related

How to generate all-projects-in-one Gradle project?

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.

gradle exclude external resource folder from all jars

Using gradle 5.4 with a project dependency from external folder in a netbeans project.
The external folder contains resources like images, xml and custom objects that can only be created by this netbeans project. These external assets are then used to create binary files that get packed into a separate jar by that netbeans project.
These same external resources are also used during runtime for development in the gradle project. While I need the resources for development in the gradle project, I do not need or want them to be included in any jars anywhere for any reason when using the task build command because only the binaries are needed for distribution.
How to exclude the external resources from any and all jar files in the gradle project but allow them to be used for the classPath so I can run the project?
Some code examples of failure.
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'idea'
sourceSets {
main {
resources {
srcDir '../ExternalResourceFolder/assets'
}
}
}
jar {
exclude('../ExternalResourceFolder/assets/**')
}
dependencies {
runtimeOnly files('../ExternalResourceFolder/assets')
}
jar {
exclude('../ExternalResourceFolder/assets/**')
}
distributions {
main {
contents {
exclude '../ExternalResourceFolder/assets/**'
}
}
}
Tried many more things like adding to classPath and exclude but it would just be clutter to add them. Changing from sourceSet to dependency only moves the problem around from "build/lib" folder to "build/distributions" folder.
Had to exclude per file type in the end.
sourceSets {
main {
resources {
srcDir '.'
exclude ('**/*.j3odata','**/*.mesh','**/*.skeleton',\
'**/*.mesh.xml','**/*.skeleton.xml','**/*.scene',\
'**/*.material','**/*.obj','**/*.mtl','**/*.3ds',\
'**/*.dae','**/*.blend','**/*.blend*[0-9]','**/*.bin',\
'**/*.gltf')
}
}
}

How to get sources from parent project as dependency

I want to do something like this:
dependencies {
compile project(':projectA', classifier: 'sources')
}
But I couldn't find such functionallity, I've searched in the documentation but there was nothing besides specifying the classifier on real dependencies.
So it there a way on adding the sources from an parent project inside a Gradle multimodules project as dependency?
I'm not aware that there is a feature from Gradle for that, but there is a workaround:
Add an packageSources to the project which should provide sources
// projectA/build.gradle
project.task("packageSources", type: Jar) {
classifier = 'sources'
from { project.sourceSets.main.allSource }
}
Depend the task on compileJava where you need the sources
// projectB/build.gradle
compileJava.dependsOn tasks.getByPath(':projectA:packageSources')
Add the source as dependency
// projectB/build.gradle
dependencies {
compile files(tasks.getByPath(':projectA:packageSources').archivePath)
}
projectA/build.gradle
configurations {
sources
}
dependencies {
sources sourceSets.main.allSource
}
projectB/build.gradle
dependencies {
compile project(path: ':projectA', configuration: 'sources')
}
See DependencyHandler.project(Map)

Gradle - include class files from one project in another

So I have a multi project setup that looks something like this
Root Project
--> common
--> project1
--> project2
--> 3rd_party_api
So common obviously contains a bunch of code shared across the other projects. Projects 1 and 2 are fine because they are wars and contain the common jar file as a dependency without any issues.
The problem I have is with my 3rd_party_api project. This is quite a small jar file that we will be delivering to other teams so that they can integrate with our code. Most of the java code required is contained in this project folder however there are 3 or 4 classes that are in the common project and need to be included in this library. Because it has to be standalone I need to wrap those classes in the jar file.
I have tried various iterations of srcDirs and source but I can't for the life of me figure out an easy way to do this.
As I've said I've looked at different approaches but my latest attempt looked a bit like this:
project(':api') {
defaultTasks 'jar'
apply plugin: 'java'
sourceSets {
main {
java {
srcDir 'src/main/java'
srcDir fileTree(dir: '../common/src/main/java').matching { include 'com/my/classes/**' }
}
}
dependencies {
compile project(':common')
}
}
This compiles successfully but the extra classes from common are not included in the jar.
Any help would be greatly appreciated.
Thanks Dirk,
Didn't quite work for me but got me thinking about other approaches. I eventually got it to do what I needed using a custom jar task. Something like the following:
defaultTasks 'lib'
dependencies {
compile project(':common')
}
task lib( type: Jar, dependsOn: classes) {
from sourceSets.main.output
from (project(':common').sourceSets.main.output) {
include 'com/myclasses/stuff/**'
include 'com/specificclass/MyClass.class'
}
}
maybe something like:
jar {
baseName = 'yourJarFileName'
from('path/to/your/dir/') {
include 'local/path/from/there/**/*.jar'
}
}
didn't checked this ... so don't blame me if it does not work out of the box ;)
other possibility would be to define your custom sourceSets ... but never tried this either.

Publish Android library with external dependencies

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.

Categories

Resources