How to reference field in Gradle plugin class? - gradle

I'm using a custom Gradle plugin with a class defined like:
class CustomPlugin implements Plugin<Project> {
public static final String CONSTANT = 'value'
}
I've tried all combinations of apply plugin: 'platform-java8-fix' and import netflix.nebula.platformjava8fix.PlatformJava8FixPlugin but none of them allow me to reference CustomPlugin.CONSTANT.
I would like to do something like:
dependencies {
compile "group:module:${CustomPlugin.CONSTANT}"
}
How can this be done?
UPDATE: From within build.gradle, I'm able to access CustomPlugin.CONSTANT. I would like to access it from another file, dependencies.gradle, and have build.gradle do:
apply from: "${rootDir}/dependencies.gradle"
But when I try to perform the import, I get the error:
startup failed:
script 'dependencies.gradle': 1: unable to resolve class com.example.CustomPlugin

Related

I cannot reference a Gradle custom extension using a closure

I'm writing a custom gradle plugin that uses an extension. Here's what the relevant part look like:
class Aar2Jar implements org.gradle.api.Plugin<Project> {
void apply (Project project) {
println('applying Android Jar')
def ext = project.extensions.create('jarSpec', JarSpecExtension)
if (ext.name.get() == "") {
ext.name.set(project.name)
}
and Here's what the extension looks like:
package test.android.gradle
import org.gradle.api.provider.Property
abstract class JarSpecExtension {
abstract Property<String> getName()
abstract Property<String> getVersion()
abstract Property<String> getInclude()
abstract Property<String> getExclude()
JarSpecExtension() {
name.convention("")
version.convention("1.0.0")
include.convention("*")
exclude.convention("")
}
}
And here's what my build.gradle script that uses this custom plugin looks like:
plugins {
id 'com.android.library'
id 'test.android.jar'
}
jarSpec {
name = 'myJar'
}
The problem is that doesn't work and I get the following error:
* What went wrong:
An exception occurred applying plugin request [id: 'test.android.jar']
> Failed to apply plugin 'test.android.jar'.
> No such property: extensions for class: test.android.gradle.Aar2Jar
However, if I change the build.gradle file to this:
plugins {
id 'com.android.library'
id 'test.android.jar'
}
jarSpec.name = 'myJar'
It works perfect. The question is why can I not refer to the extension using the closure style. I'm using gradle version 7.2.

How can I use #Delegate in a custom task with Gradle 7?

I've got a custom task (specifically https://github.com/marklogic-community/ml-gradle/blob/master/src/main/groovy/com/marklogic/gradle/task/MlcpTask.groovy#L26 , though I'm going to provide a bare minimum example below) where I've been using #Delegate on a task property for many versions of Gradle, but it no longer works with Gradle 7. I've tried adding #Input/#Optional everywhere, but no luck.
My use case in the above task is that I want to reuse a Java class that has dozens of properties on it, and I want a user to be able to assign values to those properties via task properties in their build.gradle file.
Here's the easiest way to reproduce the problem I'm having - I add the following to my build.gradle file:
class MyTask extends DefaultTask {
#Delegate
MyBean myBean = new MyBean()
#TaskAction
void myAction() {
println "myBean value: " + myBean.myValue
}
}
class MyBean {
String myValue
}
task myTest(type: MyTask) {
myValue = "something"
}
And when I run "myTest" using Gradle 6.9, it runs fine and prints out "myBean value: something". When I run this on Gradle 7.1.1, I get an error of "Type 'MyTask' property 'myBean' is missing an input or output annotation.".
So I added #Input to myBean. Then I get an error of "Type 'MyTask' property 'myValue' is missing an input or output annotation."
So I add #Input to myValue in the MyBean class, and I get the same error of "Type 'MyTask' property 'myValue' is missing an input or output annotation."
Is there a way for #Delegate to still work in this fashion in Gradle, and if so, how? Or is this possibly a bug in Gradle 7? I'll file one if so. Or, is there a better way to do this - i.e. to reuse an existing Java class in a Gradle task such that the task can delegate property assignments to the Java class?

How to provide the value of a #Nested property of a gradle task?

The gradle doc describes the #Nested annotation for custom gradle tasks:
https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:task_input_output_annotations
Unfortunately, there is no complete example of this mechanism in terms of how it is used in a build.gradle file. I created a project to demonstrate a strange exception that happens whenever gradle configures the project:
https://github.com/NicolasRouquette/gradle-nested-property-test
The build.gradle has the following:
task T(type: NestedTest) {
tool = file('x')
metadata = {
a = "1"
}
}
The NestedTest custom task is in the buildSrc folder:
class NestedTest extends DefaultTask {
#InputFile
public File tool
#Nested
#Input
public Metadata metadata
#TaskAction
def run() throws IOException {
// do something...
}
}
The important bit is the #Nested property whose type is really basic:
class Mlang-groovyetadata {
String a
}
When I execute the following: ./gradlew tasks, I get this:
Build file '/opt/local/github.me/gradle-nested-property-test/build.gradle' line: 26
* What went wrong:
A problem occurred evaluating root project 'gradle-nested-property-test'.
> Cannot cast object 'build_6wy0cf8fn1e9nrlxf3vmxnl5z$_run_closure4$_closure5#2bde737' with class 'build_6wy0cf8fn1e9nrlxf3vmxnl5z$_run_closure4$_closure5' to class 'Metadata'
Can anyone explain what is happening and how to make this work?
Nicolas
Looking at the unit tests in Gradle's source code, I found that the syntax for #Nested properties requires invoking the type constructor in the build.gradle file.
That is, the following works:
task T(type: NestedTest) {
tool = file('x')
metadata = new Metadata(
a: "1"
)
}

How to Change the Package of a Maven Plug-in With Testing Harness

I used this tutorial to create a very basic setup to create and test a Maven mojo. The two classes look something like this.
Mojo:
/**
* #goal touch
* #phase process-sources
*/
public class MyMojo extends AbstractMojo {
public void execute() throws MojoExecutionException {
// stuff
}
}
Test:
public class MyMojoTest {
#Rule
public MojoRule rule = new MojoRule();
#Test
public void testSomething() throws Exception {
File pom = new File("src/test/resources/pom.xml");
MyMojo myMojo = (MyMojo) rule.lookupMojo("touch", pom);
Assert.assertNotNull(myMojo);
myMojo.execute();
}
}
It works so far. Then I moved it to my own project, to see why my tests wouldn't work. It still worked until I moved the classes into another package.
The default is org.apache.maven.plugin.my and I'm not sure if that is somehow forced for all Maven plug-ins or taken from some weird property I can't figure out.
Properties that don't contribute to this package are:
pom's artifact ID
pom's group ID
pom's version
The error message is:
org.codehaus.plexus.component.repository.exception.ComponentLookupException: java.util.NoSuchElementException
role: org.apache.maven.plugin.Mojo
roleHint: org.acme:maven-my-plugin:1.0.0-SNAPSHOT:touch
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:264)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:240)
at org.codehaus.plexus.PlexusTestCase.lookup(PlexusTestCase.java:205)
at org.apache.maven.plugin.testing.AbstractMojoTestCase.lookupMojo(AbstractMojoTestCase.java:389)
at org.apache.maven.plugin.testing.AbstractMojoTestCase.lookupMojo(AbstractMojoTestCase.java:334)
at org.apache.maven.plugin.testing.MojoRule.lookupMojo(MojoRule.java:164)
at org.apache.maven.plugin.my.MyMojoTest.testSomething(MyMojoTest.java:22)
So how do I change the package? org.apache.maven.plugin.my sounds really tutorially.
I found a bunch of stupid mistakes why my mojos (including the above one) won't work, like:
Maven mojos don't get compiled automatically like Java files, you need to start Maven manually (or use somthing like Maven -> Update Project in your IDE)
I imported org.junit.jupiter.api.Test instead of org.junit.Test, so the MojoRule was not initialized correctly

Access property from settings.gradle in Gradle plugin

I am in the process of building a custom Gradle plugin using the following guide. I want to define a property in the settings.gradle file and read it in the plugin class, but every attempt failed so far.
Here is the error I am getting:
Could not find property 'MyProperty' on root Project 'MyProject'
Here is my settings.gradle file:
gradle.ext.MyProperty = "The Value"
and here is the file containing my plugin:
package myplugin.gradle
import org.gradle.api.Project
import org.gradle.api.Plugin
class FirstPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('myTask') << {
def instance = project.MyProperty
println "VALUE : " + instance
}
}
}
I can access MyProperty if I put project.ext.set("MyProperty", "Property Value") in build.gradle, but I can't find a way to set it in the settings.gradle file.
I guess one way to fix this issue would be to read the content of settings.gradle in build.gradle and send it to the plugin using project.ext.set(...), but is there a more direct way?
Apparently, the way to go (which is better anyways) is to use an extension object. This extension object allows to send data from the build.gradle file to the plugin. The build.gradle file reads the content of the variable in the settings.gradle file and puts it in the extension object.
In the file containing the plugin:
package myplugin.gradle
import org.gradle.api.Project
import org.gradle.api.Plugin
class MyExtension {
String myProperty
}
class FirstPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myExtensionName', MyExtension)
project.task('myTask') << {
def instance = project.myExtensionName.myProperty
println "VALUE : " + instance
}
}
}
In the build.gradle file:
apply plugin: 'myPluginName'
...
myExtensionName {
myProperty = gradle.ext.myPropertyInSettings
}
And finally, in the settings.gradle file :
gradle.ext.myPropertyInSettings = "The Value"
Also, I recommend this tutorial instead of the one provided by Gradle.
I have a gradle project where a property set in settings.gradle is accessed from 3 places:
settings.gradle itself
build.gradle (including subprojects {} )
a gradle plugin in ./buildSrc
Accessing from settings.gradle and build.gradle:
gradle.ext.MY_PROPERTY_NAME
Accessing from a gradle plugin's apply() method:
(plugin is applied from subprojects {} closure)
project.rootProject.gradle.ext.MY_PROPERTY_NAME
This seems to work fine in Gradle 4.7 and Gradle 5.2

Resources