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

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' )
}
}
}
}
}
}

Related

Gradle how to publish a gradle-plugin to maven central

Hi I have a java project, which contains a submodule which is a gradle plugin. I want to publish this module to maven central.
I used this two plugins in my build.gradle:
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
}
and my publishing block looks something like:
publishing {
publications {
javaLibrary(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
artifactId = project.archivesBaseName
pom {
name = artifactId
description = "..."
url =
licenses {
license {
}
}
developers {
...
}
scm {
...
}
issueManagement {
...
}
ciManagement {
...
}
}
}
}
repositories { maven { url = "some local repo" } }
}
I noticed that when I build this module, the generated pom-default.xml is what I expected, but when I run gradle publishToMavenLocal and manually checked the pom.xml file in the .m2 folder, all the metadata like name description licenses are gone!
I also noticed in the .m2 folder there are 2 artifacts that are related to this single plugin, I think it's somewhat related with https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers but I don't fully understand the meaning. Both these 2 artifacts' pom are missing the pom metadata as I described above.
Could some gradle expert help me here: how to keep the metadata in the published pom?
You should not need to manually define a MavenPublication for your plugin submodule. The java-gradle-plugin reacts to the application of the maven-publish plugin and automatically configures/creates publications for the plugin artifacts. See this line.
You are correct for the (2) artifacts produced. One is the plugin marker (single pom.xml) and the other is the actual plugin JAR artifact.
As for POM customization, Gradle seemingly provides its own the POM irrespective of any POM customization(s) you have defined: https://github.com/gradle/gradle/issues/17022
Sorry for late answer, but you can do something like this:
afterEvaluate {
tasks.withType(GenerateMavenPom) { task ->
doFirst {
// Update POM here
def pom = task.pom
pom.name = ...
pom.url = ...
pom.description = ...
pom.scm {
...
}
}
}
}
This will catch the pom of the plugin marker artifact as well.

How to set packaging to pom in Gradle instead of defaulting to jar

I have a project that generates a Bill of Materials (BOM). When I execute gradle build it generates an empty jar, containing only a META-INF folder.
However I am able to publish the pom (BOM) to Nexus correctly, with the side effect of also uploading the empty jar.
According to the maven plugin doc https://docs.gradle.org/current/userguide/maven_plugin.html we should be able to set the packaging:
packaging archiveTask.extension
Here, uploadTask and archiveTask refer to the tasks used for uploading
and generating the archive
How can I set the packaging to pom?
Example of Gradle uploaded pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ttt.a</groupId>
<artifactId>my-bom</artifactId>
<version>Something-SNAPSHOT</version>
When I upload it with maven instead of gradle, there is an additional:
<packaging>pom</packaging>
UPDATE:
Full build.gradle config:
buildscript {
repositories {
maven {
url "http://myrepo"
}
}
dependencies {
classpath "io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.5"
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.7'
} }
apply plugin: 'java' apply plugin: 'maven' apply plugin: "io.spring.dependency-management" apply plugin: "jacoco" apply plugin: 'org.asciidoctor.convert' apply plugin: 'org.sonarqube'
group = project.properties['groupId'] version = project.properties['version'].toString()
description = """Bill of Materials"""
sourceCompatibility = 1.8 targetCompatibility = 1.8
ext {
xxx = '1.0.0'
yyy = '1.2.0'
... }
repositories {
maven {
url "http://myrepo"
} }
dependencyManagement {
dependencies {
dependency "com.myorg:xxx:${xxx}"
dependency "com.myorg:yyy:${yyy}"
...
} }
uploadArchives {
repositories {
mavenDeployer {
snapshotRepository(url: 'http://myrepo') {
authentication(userName: "$System.env.NEXUS_USER", password: "$System.env.NEXUS_PASSWORD")
}
}
} }
asciidoctor {
sourceDir = file('src/docs/asciidoc/')
sources {
include '*.adoc'
}
outputDir = file("build/docs/${version}") }
task generateDummyBom {
doLast {
project.buildDir.mkdirs()
new File("$project.buildDir/dummy.pom").write("<project></project>\n")
}
ext.bomFile = file("$project.buildDir/dummy.pom") }
artifacts {
archives(generateDummyBom.bomFile) {
builtBy generateDummyBom
} }
jar.enabled = false
I found that the maven plugin seems to ignore the packaging property. After some experimentation, I found that it sets the packaging property to the extension of the file in your artifact. So, the way to get the packaging property set to pom is to create a dummy artifact with a file having a .pom extension, as below.
// The real file that we want to publish is the pom generated implicitly by the
// maven publishing plugin.
//
// We need to generate at least one file that we can call an archive so that the
// maven plugin will actually publish anything at all. Luckily, if the file
// that we call an archive is a .pom file, it's magically discarded, and we are
// only left with the implicitly-generated .pom file.
//
// We need the extension of the file to be `.pom` so that the maven plugin will
// set the pom packaging to `pom` (i.e. `<packaging>pom</packaging>`). Otherwise,
// packaging would be set to `xml` if our only file had an `.xml` extension.
task generateDummyBom {
doLast {
// Since we don't depend on anything else, we have to create the build dir
// ourselves.
project.buildDir.mkdirs()
// The file actually has to have xml in it, or Sonatype will reject it
new File("$project.buildDir/${project.artifactId}.pom").write("<project></project>\n")
}
ext.bomFile = file("$project.buildDir/${project.artifactId}.pom")
}
artifacts {
archives(generateDummyBom.bomFile) {
builtBy generateDummyBom
}
}
jar.enabled = false
Update: If you apply the java plugin, you will need to remove the jar archive from your archives.
// Remove the default jar archive which is added by the 'java' plugin.
configurations.archives.artifacts.with { archives ->
def artifacts = []
archives.each {
if (it.file =~ 'jar') {
// We can't just call `archives.remove(it)` here because it triggers
// a `ConcurrentModificationException`, so we add matching artifacts
// to another list, then remove those elements outside of this iteration.
artifacts.add(it)
}
}
artifacts.each {
archives.remove(it)
}
}
Second update: Replaced "dummy.pom" with "${project.artifactId}" above.
I couldn't get Garrett's solution to work, but I did have success like this:
dependencies {
// ...Omitted...
}
tasks.named('generatePomFileForMavenJavaPublication') {
pom.with {
description = 'Parent BOM'
withXml {
// ...Omitted...
}
}
}
// Removing all jar artifacts from the mavenJava publication
// appears to automagically cause packaging to be set to 'pom'!
publishing.publications.named('mavenJava') {
artifacts.removeIf { artifact ->
artifact.extension == 'jar'
}
}

How to prioritize mavenLocal over artifactory repo in gradle?

I have multi-modules project and I'm using artifactory for resolving custom libraries:
build.gradle of parent project:
...
subporjects {
...
apply plugin: "com.jfrog.artifactory"
artifactory {
resolve {
contextUrl = ext.getProperty('ARTIFACTORY_URL')
repoKey = ext.getProperty('ARTIFACTORY_REPO_NAME')
username = ext.getProperty('ARTIFACTORY_USERNAME')
password = ext.getProperty('ARTIFACTORY_PASSWORD')
}
}
}
It works as expected my library is published to artifactory with gradle artifactoryPublish and then it's fetched from there. But in some cases I want to fetch my custom library from mavenLocal() repo. I have next subproject build.gradle
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
compile 'my-custom-library'
}
But as I can see it is still resolves from artifactory. Can I somehow prioritize mavenLocal() over it ?
The repository priority will be the order in which they were added to the RepositoryHandler
I'm guessing that the artifactory repository is added when the plugin is applied so you could delay this by
afterEvaluate {
subprojects {
apply plugin: "com.jfrog.artifactory"
// etc
}
}
Or maybe
evaluationDependsOnChildren()
subprojects {
apply plugin: "com.jfrog.artifactory"
// etc
}
If all you want to do is to depend on one subproject from within another, you should declare dependencies using the project notation:
dependencies {
compile project(':shared')
}
https://docs.gradle.org/current/userguide/multi_project_builds.html#sec:project_jar_dependencies

How to generate multiple jar files with gradle's java plugin

I have a multi-project gradle build using the java plugin setup as follows:
myProj/
settings.gradle
build.gradle
util/
build.gradle
In my util project, I would like to generate 2 jars... one for packageA and one for packageB. I'm a noob with gradle so any help here would be much appreciated. Here are my settings and gradle files:
myProj/settings.gradle
include 'util'
myProj/build.gradle
subprojects {
apply plugin: 'java'
repositories {
maven {
url "http://mymavenurl"
}
}
sourceSets {
main {
java {
srcDir 'src/java'
}
}
}
}
myProj/util/build.gradle
dependencies {
.
.
.
}
jar {
baseName = 'packageA'
includes = ['com/mycomp/packageA']
}
task packageBJar(type: Jar) {
dependsOn classes
includes = ['com/mycomp/packageB']
baseName = 'packageB'
}
When I try to build my project here is the output:
:util:compileJava
:util:processResources UP-TO-DATE
:util:classes
:util:jar
:util:assemble
:util:compileTestJava UP-TO-DATE
:util:processTestResources UP-TO-DATE
:util:testClasses UP-TO-DATE
:util:test
:util:check
:util:build
I would hope to see :util:packageBJar after classes, but I'm not having any luck.
One way is to declare packageBJar as an artifact of, say, the archives configuration:
artifacts {
archives packageBJar
}
Now gradle assemble, and therefore also gradle build, will produce packageBJar.

How to upload an existing collection of 3rd-party Jars to a Maven server in Gradle?

How can I upload a collection of existing Jars to a Maven repository? The Jars are built from an ANT Task imported to Gradle, and used as a dependency to my task... The Jars don't have version tag, so they should ALL receive the same version number when they are uploaded...
apply plugin: 'java'
apply plugin: 'maven'
version = "6.1.1"
group = "com.oahu"
ant.importBuild "$projectDir/tools/ant/package.xml"
uploadArchives(dependsOn: ["oahu-jar", "client-sdk-jar", "common-jar"]) << {
// the dependencies will generate "oahu.jar", "oahu_client_sdk.jar", "common.jar"
// UPLOAD THE DEPENDENCIES LISTED ABOVE LOCATED AT the subdirectory "build/"
description = "Uploads the generated jar ${archivesBaseName}-${version}.jar to ${cn_mvn_serverUrl}"
repositories.mavenDeployer {
repository(url: "${cn_mvn_releaseUrl}") {
authentication(userName: "${cn_mvn_username}", password: "${cn_mvn_password}")
}
}
}
The tasks "oahu-jar", "client-sdk-jar", "common-jar" are the ones imported from ANT... I have the Maven repositories configuration already working from another project... But the Maven plugin uploads the Jar generated by the Jar task from the Java plugin... Considering the imported ANT tasks generates:
build.gradle
src
build
|-"oahu.jar"
|-"oahu_client_sdk.jar"
|-"common.jar"
The result of this should be the upload of those Jars with the given version...
"oahu-6.1.1.jar", "oahu_client_sdk-6.1.1.jar", "common-6.1.1.jar"... all uploaded to the Maven repository...
Add sourceSets? Configuration? Artifacts?
currently that is not explicitly supported by gradle so you have to do some scripting for that. Based on your snippet above, I've created a sample snippet, that should be easy to adapt:
apply plugin:'java'
apply plugin:'maven'
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
version = "6.1.1"
group = "com.oahu"
ant.importBuild "$projectDir/tools/ant/package.xml"
// a list of the ant tasks that create a jar
// I assumed the following convention:
// ant task named "SampleAntJar-jar" creates the jar "build/SampleAntJar.jar"
def antJarTasks = ["SampleAntJar-jar", "SecondSampleAntJar-jar"]
artifacts{
//for each ant task add a defaultpublishArtifact to the archives configuration
antJarTasks.each{ taskName ->
def artifactName = taskName - '-jar'
archives new DefaultPublishArtifact(artifactName, "jar", "jar", null, new
Date(), new File("$buildDir", "${artifactName}.jar"))
}
}
uploadArchives(){
dependsOn: antJarTasks
repositories {
mavenDeployer {
repository(url: "file://{'/Users/Rene/.m2/repository/'}")
antJarTasks.each{ antJarTask ->
antJarName = antJarTask - "-jar"
addFilter(antJarName) {artifact, file ->
artifact.name == antJarName
}
pom(antJarName).artifactId = antJarName
}
}
}
}
regards,
René

Resources