How can I conditionally run code if a Gradle plugin is applied? - gradle

I have a script plugin that I would like to:
Check if the ivy-publish applied (via apply plugin: ivy-publish):
If it is applied, declare publishing { repositories { ivy { } } }
If it's not applied, run some other code
However, I'm unsure of how to actually run code if the ivy-publish plugin is applied, and I couldn't find anything about that in the documentation. Is there any way to do this?

You can use the PluginManager.withPlugin(String id, Action<? super AppliedPlugin> action) method. From the Javadoc:
If a plugin with the specified ID has already been applied, the supplied action will be executed immediately. Otherwise, the action will executed immediately after a plugin with the specified ID is applied.
In your build script you could do something like:
pluginManager.withPlugin('ivy-publish') {
// Do configuration
}

You can always use findPlugin:
println project.plugins.findPlugin('ivy-publish')

Or use:
if (project.getPluginManager().hasPlugin("ivy-publish")) {
..
}

Related

How can I add/change a Maven publishing repository in a Gradle init script

I'd like an init script that lets me take arbitrary Gradle projects and change the Maven repository location that they publish artifacts to.
Adding a repository is easy enough when you edit the build file directly, just add a maven{} block inside publishing { repositories { } }. However, trying to do this generically leads to frustration and failure. I tried this:
allprojects {
beforeEvaluate {
pluginManager.withPlugin("maven-publish") {
extensions.getByType<PublishingExtension>().publications {
repositories {
maven {
url = uri("file:///my/path")
name = "myrepo"
}
}
}
}
}
}
but, no luck. No such repository appears. I suspect there is a timing issue here: although my code does run, it presumably runs after the publishing plugin has created these tasks. What I want to do is register a callback that is run before the publish plugin gets a chance to do that, but I don't know how.

Unable to resolve a plugin using the new plugin mechanism in Gradle

While trying to upgrade some of our scripts to Gradle 4.0.1 on of the plugins we are using is failing and I thought of fixing that plugin first. The plugin is a third party open source project.
So I have cloned the project and tried to compile it. However it fails with following message:
c:\source\gradle-xld-plugin>gradlew build
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\source\gradle-xld-plugin\build.gradle' line: 2
* What went wrong:
Plugin [id: 'com.gradle.plugin-publish', version: '0.9.7'] was not found in
any of the following sources:
- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- maven(https://artifactory/java-v) (Could not resolve plugin artifact 'com.gradle.plugin-publish:com.gradle.plugin-publish.gradle.plugin:0.9.7')
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --
debug option to get more log output.
BUILD FAILED in 0s
The build.gradle script for the plugin starts like this:
plugins {
id "com.gradle.plugin-publish" version "0.9.7"
id "com.github.hierynomus.license" version "0.11.0"
id 'nebula.nebula-release' version '4.0.1'
id "com.jfrog.bintray" version "1.7.3"
}
In addition to this the company policy dictates we have to go through an internal artifactory server, so following has been added to the settings.gradle file:
pluginManagement {
repositories {
maven {
url "https://artifactory/java-v"
}
}
}
The jar file exists at following location: https://artifactory/java-v/com/gradle/publish/plugin-publish-plugin/0.9.7/plugin-publish-plugin-0.9.7.jar
but when I look at the error message I am a little puzzled that it says that it cannot find com.gradle.plugin-publish:com.gradle.plugin-publish.gradle.plugin:0.9.7.
It seems to have suffixed the id with .gradle.plugin.
Does anyone know whether I am looking at the wrong location or how come it is suffixing the id with .gradle.plugin. And shouldn't it look at a location that has the GAV like this: com.gradle.plugin-publish:com.gradle.plugin-publish:0.9.7?
And does anyone know about how the resolution mechanism for the new plugin mechanism in Gradle works.
Thanks in advance
Edit
Thanks to Mateusz Chrzaszcz I was able to progress.
The only caveat I have with the solution is that it seems like a workaround rather than a solution. But it works!
In addition to his solution you had to resolve the plugins. I was able to hack my way to actually resolve the appropriate names.
In order to do so one has to do as follows:
In a webbrowser go for the plugin: id "com.github.hierynomus.license" version "0.11.0" go to following URL: https://plugins.gradle.org/api/gradle/4.0.1/plugin/use/com.github.hierynomus.license/0.11.0
The json returned contains the GAV needed in the useModule call. Use that
The following serves as an example:
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.gradle' && requested.id.name == 'plugin-publish') {
useModule('com.gradle.publish:plugin-publish-plugin:0.9.7')
} else if(requested.id.namespace == 'com.github.hierynomus' && requested.id.name == 'license') {
useModule('nl.javadude.gradle.plugins:license-gradle-plugin:0.11.0')
}
}
}
Try to implement Plugin Resolution Rules.
According to gradle documentation:
Plugin resolution rules allow you to modify plugin requests made in plugins {} blocks, e.g. changing the requested version or explicitly specifying the implementation artifact coordinates.
To add resolution rules, use the resolutionStrategy {} inside the pluginManagement {} block
like that:
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.gradle.plugin-publish') {
useModule('com.gradle.plugin-publish:0.9.7') //try a few combinations
}
}
}
repositories {
maven {
url 'https://artifactory/java-v'
}
}
}
Keep in mind this is incubating feature though.

Configure a custom gradle plugin task based on a condition that is a plugin property

I am writing a custom plugin that contains a copy task. The task is supposed to fetch either a wsdl file or a zip containing wsdl and xsds from a maven repository. In the case of the zip file it will use ziptree to expand the files.
When this is done, another task will use wsimport to create a client jar from these files.
My current attempt is to use a plugin boolean property to differentiate. The following code is what I'm trying to achieve:
void addTasks(Project project) {
project.task('downloadWSDL', type: Copy) {
if (project.wsimport.zipped) {
from { project.configurations.wsdl.collect { project.zipTree(it) }}
into { project.wsimport.wsdlDir }
} else {
from { project.configurations.wsdl }
into { project.wsimport.wsdlDir }
}
}
}
The problem here is that project.wsimport.zipped is not set (I suppose false) in the configuration phase. I have tried to put it in a closure, but it seems to then still do the zipped path even though the property is set to false.
How can I configure a task like this based on a condition?
Will a working solution function as expected when checking if it is up-to-date?
I have thought an alternative approach might be to have 2 different tasks and modify the dependsOn of a depending class dynamically, but I suspect it will run into a similar problem.
Thank you.

How can I make Gradle extensions lazily evaluate properties that are set dynamically by tasks?

I'm pretty new to working with Gradle and I'm trying to develop a plugin that helps manage version numbering. This plugin defines a task that sets the project.version property of the project it's applied to.
What I'm trying to do is make it so that this property is set at the start of every Gradle build. Using Peter's answer to another Gradle question, I've managed to get my task to execute before any other by adding gradle.startParameter.taskNames = [":setProjectVersionNumber"] + gradle.startParameter.taskNames within my plugin's apply method.
However, other plugins (notably 'Maven-publish') rely on the version being specified during the configuration phase:
publishing {
publications {
somePublication(MavenPublication) {
version = project.version
}
}
}
What I'd like to know is if there's a way that I can make the evaluation of properties like version within these extensions as lazy as possible - such that they're not evaluated until a task that depends upon them is called, which in this case might be :publishToMavenLocal.
Below is an SSCCE that demonstrates what I'm hoping to achieve:
// This would be included within the plugin
class SetProjectVersionNumber extends DefaultTask {
#TaskAction
void start() {
// This will set project.version during execution phase
project.version = "1.2.3"
logger.info "Set project version number: $project.version"
}
}
task setProjectVersionNumber(type: SetProjectVersionNumber)
// Imagine this block being replaced by a maven 'publishing' block (or something similar)
ext {
version = project.version
// This will print 'unspecified', as it's evaluated during configuration phase
println "In extension, setting version=$project.version"
}
If you can provide a way to make ext.version equal 1.2.3 in the example above, I believe you've resolved my issue.
If this is asking too much, it may be possible for me to make my plugin generate the version string at configuration-time rather than execution-time. It would be nice to know if I could do it this way, though.
EDIT
In an experimental branch, I tried moving all the version string assignment logic to the configuration-phase (by making it all happen during plugin application rather than during task execution), but I don't believe this will work as the plugin extension has not yet been processed and trying to refer to properties defined in it fail.
EDIT 2
Wrapping the version string assignment logic in a project.afterEvaluate closure seems to have worked:
#Override
public void apply(Project project) {
logger = project.logger
project.extensions.create(EXTENSION_NAME, SemVerPluginExtension)
project.afterEvaluate {
setVersionProjectNumber(project)
addTasks(project)
}
}
In a mock project, I implement build.gradle as follows:
apply plugin: 'semver'
apply plugin: 'maven-publish'
group = 'temp'
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath 'com.github.tagc:semver-plugin:0.2.2'
}
}
semver {
versionFilePath = 'version.properties'
}
publishing {
publications {
testPublication(MavenPublication) {
version = project.version
assert version
println "Set publication version to $version"
}
}
}
For some reason, this seems to work. Although the version string assignment logic is wrapped in an 'afterEvaluate' closure and the test publication version assignment isn't, the former still occurs before the latter:
Compiling build file '/Users/davidfallah/Documents/semver/TestSemver2/build.gradle' using StatementExtractingScriptTransformer.
Compiling build file '/Users/davidfallah/Documents/semver/TestSemver2/build.gradle' using BuildScriptTransformer.
VERSION FILE PATH=version.properties
Current Git branch: develop
Set project version to 0.2.1-SNAPSHOT
Set publication version to 0.2.1-SNAPSHOT
All projects evaluated.
I'm leaving this question open and unresolved since I'd still like to know if it's possible to do it the way I originally intended. Additionally, I'd appreciate any explanation about why the publication version is assigned after the project version is set, and whether I can depend on that always being the case or whether that's just happening now by accident.
You can use lazy instantiation of GStrings to evaluate properties at run time:
project.tasks.create("example_task", Exec.class, {
commandLine 'echo', "${-> project.someproperty}"
})
Note that you have to use quotation marks and not apostrophes - "${...}" works, but '${...}' does not.

How can plugin programmatically configure maven-publish publishing and allow build.gradle to modify it

I have project wide settings in a plugin, called parent, that attempts to apply the maven-publish plugin and then programmatically configure the publishing extension. This seems to work but when I apply this plugin in a build.gradle script I can not configure publishing extension to set the project specific publications.
I receive the error:
Cannot configure the 'publishing' extension after it has been accessed.
My intent was to set up the publishing repository in the parent plugin and then let each build.gradle script add the appropriate publications.
Is there a way to do this?
Currently ParentPlugin.groovy looks like:
def void apply(Project project) {
project.getProject().apply plugin: 'maven-publish'
def publishingExtension = project.extensions.findByName('publishing')
publishingExtension.with {
repositories {
maven {
mavenLocal()
credentials {
username getPropertyWithDefault(project.getProject(), 'publishUserName', 'dummy')
password getPropertyWithDefault(project.getProject(), 'publishPassword', 'dummy')
}
}
}
}
}
My client build.gradle fails when it tries to configure the publishing extension.
apply plugin: 'parent'
publishing {
publications {
mavenJava(MavenPublication) {
groupId 'agroup'
artifactId 'anartifactid'
version '1.0.0-SNAPSHOT'
from components.java
}
}
}
Is this possible? Is there another way I should be approaching this?
NOTE regarding repositories{} and publications{} for plugin maven-publish:
Topic: How to workaround this perplexing gradle fatal error message:
Cannot configure the 'publishing' extension after it has been accessed
First thing to try (deep magic):
(note "project." prefix is optional)
-- Configure publications and repositories not like this:
project.publishing {publications {...}}
project.publishing {repositories {...}}
but instead like this recommended style:
project.publishing.publications {...}
project.publishing.repositories {...}
It would be instructive for a gradle guru to explain why this trick works.
Another known workaround is to make sure that each apply of plugin
maven-publish is in the same project code block as
project.publishing.repositories and project.publishing.publications.
But that is more complex and harder to do than the first thing to try,
since by default the CBF applies maven-publish and a second apply of it
may itself cause the same error.
maven-publish is normally applied in pub/scripts/publish-maven.gradle,
unless PUB_PUBLISH_MAVEN is set to override that file location,
in which case the caller should apply plugin maven-publish.
See https://orareview.us.oracle.com/29516818 for how this not-preferred
workaround can be done (for project emcapms) while still using the CBF.
P.S. Someday I'll write this up with minimal code examples. But I'm putting this hard-won knowedge out there now to save other folks from wasting days on this common maven-publish issue.
To deal with this, I wrote another plugin, which can delay modifications to the publication while also avoid a "reading" of the extension, which would put it in the "configured" state. The plugin is called nebula-publishing-plugin, the code for the "lazy" block can be found in the github repo. It looks like this:
/**
* All Maven Publications
*/
def withMavenPublication(Closure withPubClosure) {
// New publish plugin way to specify artifacts in resulting publication
def addArtifactClosure = {
// Wait for our plugin to be applied.
project.plugins.withType(PublishingPlugin) { PublishingPlugin publishingPlugin ->
DefaultPublishingExtension publishingExtension = project.getExtensions().getByType(DefaultPublishingExtension)
publishingExtension.publications.withType(MavenPublication, withPubClosure)
}
}
// It's possible that we're running in someone else's afterEvaluate, which means we need to run this immediately
if (project.getState().executed) {
addArtifactClosure.call()
} else {
project.afterEvaluate addArtifactClosure
}
}
You would then call it like this:
withMavenPublication { MavenPublication t ->
def webComponent = project.components.getByName('web')
// TODO Include deps somehow
t.from(webComponent)
}
The plugin is available in jcenter() as 'com.netflix.nebula:nebula-publishing-plugin:1.9.1'.
A little bit late, but I found a solution that does not require an additional plugin:
(This has been taken from one of my internal plugins, that can work with old and new publishing, thus the ...withType... stuff.
instead of:
project.plugins.withType(MavenPublishPlugin) {
project.publishsing {
publications {
myPub(MavenPublication) {
artifact myJar
}
}
}
}
do this:
project.plugins.withType(MavenPublishPlugin) {
project.extensions.configure PublishingExtension, new ClosureBackedAction( {
publications {
myPub(MavenPublication) {
artifact myJar
}
}
})
}
This will not resolve the Extension immediately, but will apply the configuration at the time when it gets first resolved by someone.
Of course it would perfectly make sense to use this style of configuration in your project-wide plugin to configure the repositories and use the publication extension in the build scripts as usual. This would avoid confusion for buildscript authors.

Resources