Gradle - custom task to execute all GenerateMavenPom tasks - maven

I want to write a Gradle task to share among all my subprojects. This task finds all other tasks inside the subproject where it is called that have type "GenerateMavenPom" and executes those tasks.
By doing this, my subprojects can define any Maven publications that they wish, and I can execute gradle with a single task like "gradle generateMavenPomFiles" to create the pom.xml without knowing the individual publication types in each subproject. Why? Because the Maven plugin creates publication tasks whose names depend on the publication type.
Inside my subprojects block in the root build.gradle file I have, passing subproject as the closure delegate:
task generateMavenPomFiles << {
model {
TaskCollection<GenerateMavenPom> pomTasks = subproject.tasks.matching { t -> t.TASK_TYPE.equals("GenerateMavenPom") }
if (pomTasks != null) {
pomTasks.each { pomTask -> pomTask.execute }
}
}
}
I have accessed the Maven publishing tasks inside the model block as according to the plugin doc:
The “maven-publish” plugin leverages some experimental support for
late plugin configuration, and any GenerateMavenPom tasks will not be
constructed until the publishing extension is configured. The simplest
way to ensure that the publishing plugin is configured when you
attempt to access the GenerateMavenPom task is to place the access
inside a model block...
Inside my subprojects I have Maven publications defined like this:
publishing {
publications {
mavenCustom(MavenPublication) {
artifacts = someArtifactTask.archivePath
groupId = someGroupId
artifactId = someArtifactId
version = someVersion
}
}
}
Of course, "gradle generateMavenPomFiles" doesn't work. The task executes on each subproject but I don't see the actual Maven POM tasks created by the plugin being invoked.
I'm fairly new to Gradle + Groovy, so perhaps I have misunderstood something, or my logic is just wrong.
Any help is greatly appreciated!

you can do this with the following snippet:
task runAllGenerateMavenPomTasks {
dependsOn tasks.withType(GenerateMavenPom)
}

Related

gradle: Let the tooling api model call depend on a task from buildscript

I have a build script where a task adds dependencies to a configuration. Then compileJava depends on that task.
plugins {
`java-library`
}
repositories {
mavenCentral()
}
val setupDeps = tasks.register("setupDeps") {
doLast {
dependencies.add("implementation", "org.clojure:clojure:1.10.3")
}
}
tasks.named("compileJava") {
dependsOn(setupDeps)
}
From the shell this works. But in intellij the tooling api of gradle is used to build the dependency model. And the tooling api does not know that it has to execute this task first. Is there any way to configure this? I could of course always dynamically add the dependencies during configuration phase but i don't want to do that because to find out what needs to be added is slow.
I found the answer myself.
The task name to dependOn is :prepareKotlinBuildScriptModel
So this solves the problem:
tasks.findByPath(":prepareKotlinBuildScriptModel")?.dependsOn(setupDeps)

Excluding one or two sub-projects from publication in gradle

I have a multi-project gradle build and I have applied the maven-publish plugin to all subprojects in my master build file.
I have also defined a default publication there called mavenJava.
This is all fine and now when I run ./gradlew artifactoryPublish I deploy artifacts from all the subprojects.
However, I would like to exclude two of my subprojects from publishing this mavenJava publication.
I've tried a bunch of things such as defining a boolean (skipDefaultPublish) in the ext block in the subprojects block in my master build file and including the publication definition inside a conditional if block and overwriting this variable in the ext block of the subprojects I do not want to publish it. That doesn't work and gradle complains that it Cannot configure the 'publishing' extension after it has been accessed..
I've tried other things but nothing seems to work.
The only thing that I know will work is defining the default publication block in all subprojects except for the ones that I do not want to publish from, but I have ~20 subprojects but only two that should not publish this type of artifact so this doesn't seem like the best thing to do.
So is there any way I can configure all subprojects to publish an artifact but override that in only two of the subprojects so that they don't do that?
UPDATE
To try to clarify what my project looks like.
root build.gradle
subprojects {
apply plugin: 'maven-publish'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifactId = getCustomProjectId()
groupId = 'com.our.group.id'
}
}
}
apply plugin: "com.jfrog.artifactory"
artifactory {
contextUrl = ourContextUrl
publish {
repository {
repoKey = "ourRepoKey"
username = "ourArtifactoryUser"
password = "ourArtifactoryPass"
}
defaults {
publications('mavenJava')
publishArtifacts = true
publishPom = true
}
}
}
}
and in a subproject which should publish an artifact, just not the default one:
project(':exsub'){
publishing {
publications {
mavenSrc(MavenPublication) {
groupId "com.our.group.id"
artifactId "stuff-src"
artifact srcStuff
}
}
}
artifactoryPublish {
// publications.removeAll() //<- putting this here doesn't work
// publications.remove('mavenJava') //<- neither does this
publications ('mavenSrc') //<- this adds mavenSrc to the publication list but does not remove the mavenJava publication from it
}
}
There are several ways to publish artifacts, depending on used plugins.
To exclude particular module from publishing with modern (Gradle 4.x-7.x) publish like tasks:
tasks.withType(PublishToMavenRepository).configureEach { it.enabled = false }
Legacy uploadArchives:
uploadArchives.enabled = false
With com.jfrog.artifactory plugin:
artifactoryPublish.skip = true
Pheww... I think I finally figured out a way to do this (the aesthetics of the solution are quite debatable).
For me, it was sufficient to put a publishing.publications.remove(publishing.publications.mavenJava) line in the subproject.
However, I had to take care to put that line below the publishing{...} block in that subproject.
So now, the subprojects that should not publish the "default publication" look something like this:
project(':exsub'){
publishing {
publications {
mavenSrc(MavenPublication) {
groupId "com.our.group.id"
artifactId "stuff-src"
artifact srcStuff
}
}
}
// Remove the default publication (note: this must be located after the publishing block above)
publishing.publications.remove(publishing.publications.mavenJava)
artifactoryPublish {
publications ('mavenSrc') //<- this adds mavenSrc to the publication list but does not remove the mavenJava publication from it
}
}
Setting the following on subprojects you want to exclude worked for me:
project.tasks.publish.enabled = false
This does still require the maven-pulish plugin to be applied to the project, but no additional configuration is needed.
I found this setting here: https://discuss.gradle.org/t/disable-maven-publish-related-tasks-not-possible/13488
Let's consider the following multi-module project structure.
photos:
-> photo-client
-> photo-model
-> photo-service
In the above multimodule project, If I want to exclude the functionality of the photo-service submodule build jar (any distributive file(jar/war)) from uploading to the configured artifactory, then we have to use the below code snippet in the photo-service's build.gradle file
build.gradle
dependencies {
.....
}
project.afterEvaluate {
project.tasks.artifactoryPublish.enabled(false)
}
This question is specifically about what to do when some general configuration introduces one publication for all subprojects, but in one specific project you want to remove that "default" publication, but still have another publication. Most of the answers don't address this.
The answer by the original questioner does, but it didn't work for me.
This did:
tasks.withType(PublishToMavenRepository) {
onlyIf {
publication != publishing.publications.mavenJava
}
}
(where mavenJava is the name of the "default" publication to prevent).
This kind of solution is suggested by the Gradle documentation here: https://docs.gradle.org/current/userguide/publishing_customization.html#sec:publishing_maven:conditional_publishing

How to use gradle Artifactory plugin in gradle subproject

I have a multi-project gradle build wherein one of the subprojects is applying the Artifactory plugin (version 4.2.0), and configuring the contextUrl and resolve repoKey.
It sets up a simple configuration and dependency entry, and then has a copy task to retrieve the dependency as a zip file and extract it into a directory.
However, when the copy task runs, I get the error below. What am I doing wrong? Is this a problem with the Artifactory plugin, or gradle, or...?
The problem does not appear to be related to whether or not this is a subproject. I get the same error if I remove the multiproject configuration and run the task from the subproject directory.
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\hoobajoob\project\subproject\package.gradle' line: 36
* What went wrong:
A problem occurred evaluating project ':subproject'.
> Could not resolve all dependencies for configuration ':subproject:runtimeDep'.
> Cannot resolve external dependency company.com:artifact-id:1.0.0 because no repositories are defined.
Here are the contents of subproject/package.gradle (Artifactory url/user/password properties are in a gradle.properties file for the subproject):
plugins {
id "com.jfrog.artifactory" version "4.2.0"
}
artifactory {
contextUrl = "${artifactory_contextUrl}"
resolve {
repository {
username = "${artifactory_user}"
password = "${artifactory_password}"
repoKey = 'some-repo'
}
}
}
configurations {
runtimeDep
}
dependencies {
runtimeDep 'company.com:artifact-id:1.0.0#zip'
}
ext.destination = null
task getDependencies(type: Copy) {
from zipTree { configurations.runtimeDep.singleFile }
into ".artifacts/runtime"
}
The root project build script is empty except for the wrapper task. Below is the settings.gradle file:
include 'subproject'
rootProject.children.each { project -> project.buildFileName = "package.gradle" }
While the task setup in my question is different, this appears to be the same symptom as described in this other SO question.
The problem seems to be related to the fact that the Artifactory plugin will not perform dependency resolution until gradle's execution phase. I had assumed that defining the argument to the zipTree step in the getDependencies task with a closure would have the effect of deferring the dependency resolution until that phase.
But, for this to be deferred by the copy task, I need to define the from configuration of the getDependencies task as a closure, and include the zipTree operation in that closure.
It's the difference between:
from zipTree { configurations.runtimeDep.singleFile } // doesn't work
...and
from { zipTree( configurations.runtimeDep.singleFile ) } // works
Making this change gets the resolve working (with no required maven repositories block).
Another solution is to drop the Artifactory configuration altogether (which I can do in this case because I do not need to utilize anything unique to Artifactory) and use the traditional gradle repositories block, as described in the other SO question and by crazyjavahacking. Doing this makes the build script shorter, and I can leave the zipTree step configured as it was originally written:
repositories {
maven {
url "${artifactory_contextUrl}/repo-key"
}
}
configurations {
runtimeDep
}
dependencies {
runtimeDep 'company.com:artifact-id:1.0.0#zip'
}
ext.destination = null
task getDependencies(type: Copy) {
from zipTree { configurations.runtimeDep.singleFile }
into ".artifacts/runtime"
}
As the Gradle print to the console:
You did not define repositories{} block and so it does not know how to download declared dependency.

Gradle: Task does not execute another task based on the configuration it uses

I've got a very simple multiproject build like below:
module1, which generates a public API jar and exposes it through "publicAPI" configuration:
configurations {
publicAPI
}
task generatePublicAPI(type: Jar) {
outputs.upToDateWhen { false }
baseName 'public-api'
from sourceSets.main.output
}
artifacts {
publicAPI generatePublicAPI
}
module2, which uses the public API jar (by referencing 'publicAPI' configuration defined in module1) to generate a application jar:
configurations {
generateApplication
}
dependencies {
generateApplication project(path: ':module1', configuration: 'publicAPI')
}
task jarApp(type: Jar) {
baseName 'app'
from configurations.generateApplication.collect {
it.isDirectory() ? it : zipTree(it)
}
}
Now, when I execute 'gradle :module2:jarApp" task, I got the following error:
Cannot expand ZIP
'/home/picasso/Documents/GradlePlayground/module1/build/libs/public-api.jar'
as it does not exist
and I can see that gradle was not trying to execute 'generatePublicAPI' of module1.
However, if I make "jarApp" task depends on "generatePublicAPI" task explicitly,
task jarApp(dependsOn: 'module1:generatePublicAPI', type: Jar) {...}
then everything's fine.
BUT, wouldn't this approach against one of the purpose of using dependency configuration so that I don't have to worry about the details of how module1 is built, e.g. which task generates the jar and what artifacts it produces?
I thought gradle is able to work out the tasks it needs to execute by following along the "route" of the referenced dependency configuration.
Am I missing something here so that "generatePublicAPI" task can be executed automatically without have to declare "dependsOn" explicitly for "createApp" task?
I asked the same question on Gradleware's forum and got the answer from one of the core developer, here's the link.
Basically, the issue is that collect method returns a new collection but gradle has no way to know that this new collection was generated from the configuration, therefore it couldn't infer which task to execute.
The solution is instead of declaring a dependency on a task, declare a dependency on the actual configuration instead like the following:
task jarApp(dependsOn: configurations.generateApplication, type: Jar)

Gradle Release System Task - Calling uploadArchives on subprojects

In order to release my system, I am creating a releaser project that calls uploadArchives for a number of other projects.
In my project I have the following files:
settings.gradle
include '../proj-a', '../proj-b'
build.gradle
task releaseSystem() {
// what goes here?
}
What should the contents of the releaseSystem task be such that I can call gradle releaseSystem and have it run uploadArchives for each of my sub projects?
I've tried a number of options, but none have been successful thus far.
Thank you for any insights you can provide.
ANSWER
I'm continually impressed with the graceful solutions gradle provides to my problems. Here's my final solution (as Peter pointed out below):
settings.gradle
include 'proj-a', 'proj-b'
project (':proj-a').projectDir = new File(settingsDir, '../proj-a')
project (':proj-b').projectDir = new File(settingsDir, '../proj-b')
build.gradle
task releaseSystem() {
dependsOn {
[
subprojects.build,
subprojects.uploadArchives
]
}
}
Note, that since my repository is a maven repository, when I originally included '../proj-a' in my settings.gradle, it produced poms with artifactId ../proj-a which was invalid. I had to change my settings.gradle to the format above for the system to put the poms together correctly and for the uploadArchives task to complete successfully.
Assuming all subprojects have an uploadArchives task:
task releaseSystem {
dependsOn { subprojects.uploadArchives }
}
Note that this won't run the subprojects' tests.

Resources