Setting up Gradle plugin with manual testing

Hi

I’m trying to set up my first plugin environment as a standalone project and test it as described here
https://docs.gradle.org/current/userguide/custom_plugins.html
and
https://docs.gradle.org/8.5/userguide/testing_gradle_plugins.html#manual-tests

Project has 2 subprojects ‘plugin’ and ‘plugin-test’
The top level settings.gradle

# content of settings.gradle
rootProject.name = 'my-gradle-plugin'

//includeBuild "plugin"
//includeBuild "plugin-test"

include 'plugin', 'plugin-test'

plugin has a single class

interface GreetingPluginExtension {
    Property<String> getMessage()
}

class GreetingPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        // Add the 'greeting' extension object
        def extension = project.extensions.create('greeting', GreetingPluginExtension)
        extension.message.convention('Hello from GreetingPlugin')
        // Add a task that uses configuration from the extension object
        project.task('hello') {
            doLast {
                println extension.message.get()
            }
        }
    }
}

and plugin-test has settings.gradle

pluginManagement {
    includeBuild "../plugin"
}

and its build.gradle

plugins {
    id 'java'
}

//apply plugin: GreetingPlugin

when I execute from root of the project

./gradlew :plugin-test:tasks

I don’t see the task hello
Also

./gradlew :plugin-test:hello

FAILURE: Build failed with an exception.

* What went wrong:
Cannot locate tasks that match ':plugin-test:hello' as task 'hello' not found in project ':plugin-test'. Some candidates are: 'help'.

Uncommenting apply plugin line does not change anything
What am I missing?

Are there any example projects I could check?
I’ve seen gradle itself uses includeBuild in its settings.gradle
but for me when I specify

rootProject.name = 'my-gradle-plugin'

includeBuild "plugin"
includeBuild "plugin-test"

I get

./gradlew :plugin-test:hello

FAILURE: Build failed with an exception.

* Where:
Initialization script '/Users/daniel/.gradle/init.d/taskinfo.gradle' line: 10

* What went wrong:
Failed to apply plugin class 'org.barfuin.gradle.taskinfo.GradleTaskInfoPlugin'.
> The plugin 'org.barfuin.gradle.taskinfo' does not support composite builds at this time.
  You can still run it by adding -Ptaskinfo.disableSafeguard=true to your Gradle invocation.In that case, tasks from the included builds will be executed.
./gradlew -v

------------------------------------------------------------
Gradle 8.5
------------------------------------------------------------

Build time:   2023-11-29 14:08:57 UTC
Revision:     28aca86a7180baa17117e0e5ba01d8ea9feca598

Kotlin:       1.9.20
Groovy:       3.0.17
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          17.0.9 (Eclipse Adoptium 17.0.9+9)
OS:           Mac OS X 14.2.1 aarch64

include in the settings script adds a subproject to the build.
includeBuild in the settings script includes a whole build, forming a composite build.

You should never ever ever ever ever ever include a project in multiple builds, like having a separate settings script in plugin and also include it somewhere else.

What you need to use plugin from plugin-test is a composite build as documented in the link you posted.
As the plugin it complains about with composite build is just some “show me info” plugin, I’d recommend you simply remove it, or make it dependent on some property so that you can dynamically add it when you want to use it.

Hi

Thanks for prompt reply.
That extra plugin was coming from my "~/.gradle//init.d/ and was included for all projects.
It’s disabled now

Clearly I’m confused about multi-project vs composite here

I’ve changed root settings.gradle so it looks like this now

rootProject.name = 'my-gradle-plugin'
includeBuild "plugin"
includeBuild "plugin-test"

I have build.gradle as well which is empty

I’ve added empty settings.gradle to the plugin dir, not sure if it’s needed but it’s present here
https://docs.gradle.org/8.5/userguide/testing_gradle_plugins.html#manual-tests

Dir of plugin-test has settings.gradle

pluginManagement {
    includeBuild "../plugin"
}

and build.gradle

plugins {
    id 'java'
}

tasks.register('helloFromPlugin') {
    dependsOn gradle.includedBuild('plugin').task(':hello')
}

Command ./gradlew :plugin-test:tasks
still does not show my hello task and attempt to run it shows

./gradlew :plugin-test:helloFromPlugin

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':plugin-test:helloFromPlugin'.
> Task with path ':hello' not found in project ':plugin'.

Thanks for your help so far
D.

I’ve changed root settings.gradle so it looks like this now

Actually you do not need any settings script there at all, unless you have a reason to have it.
It would actually be enough to have a build in plugin with its settings script and a build in plugin-test with its settings script and including the plugin build from plugin-test settings script.

I have build.gradle as well which is empty

If a build script is empty, you could as well just delete it, effect is the same.

I’ve added empty settings.gradle to the plugin dir, not sure if it’s needed but it’s present here

A settings script is always needed for a build, so yes you should have it.
But it should not be empty, but at least define the root project name of that build, otherwise it is defined by directory name which is sub-optimal.

and build.gradle

Why are you depending on a task hello in the plugin build?
That does not make any sense.
And besides the sense, you do not even have a task called hello in the plugin project.
What you intended to do is to apply the greeting plugin to your plugin-test project which will result in a hello task in your plugin-test project.

I’ve made the changes according to your suggestions, thanks for this

Why are you depending on a task hello in the plugin build?
My idea behind that was that I can call helloFromPlugin from the command line. I was just trying to prove the point I set it all up correctly. Obviously if plugin is included properly I can call its tasks directly as you rightly pointed out.

My other cardinal sins were

  • not including
plugins {
    id 'com.om.gradleplugin.greeting' version '1.0-SNAPSHOT'
}

I thought the plugin would be magically included if I specify it in the settings.
And it will be without specifying --include-build on the command line, providing plugin’s id is in build.xml

  • I was launching gradlew from the top level, rather than from within plugin-test

It all clicked when I looked at the example in
https://docs.gradle.org/current/samples/sample_composite_builds_plugin_development.html

Would be good if the Samples were linked in
https://docs.gradle.org/current/userguide/testing_gradle_plugins.html
Those samples are not easy to find

I appreciate your help on this.
Cheers.

My idea behind that was that I can call helloFromPlugin from the command line. I was just trying to prove the point I set it all up correctly. Obviously if plugin is included properly I can call its tasks directly as you rightly pointed out.

I’m not sure you fully got it.
I of course know what the intention was.
But the point is, that the intention made no sense.
The plugin build does not have a task called hello, so you cannot depend or execute such a task.
If the plugin is applied to a project, then it adds the hello task to that project where it is applied.

My other cardinal sins were
not including

That’s not really “other”, that is exactly the point. :slight_smile:
You need to apply the plugin so that it can do its work and add the task to the project where you applied it.

I thought the plugin would be magically included if I specify it in the settings.

It is magically available, but not magically applied.
Gradle could not guess which of the 50 plugins that build produces (once you added the other 49) should be applied, or where to apply them.
You can actually leave out the version though if it is coming from an included build.

I was launching gradlew from the top level, rather than from within plugin-test

Well, if you still have that “top-level build” where you include the two others, you could surely run from there ./gradlew plugin-test:hello, but it just for that it probably does not make too much sense to have that “top-level build”.

Actually, even without that top-level build, you could use plugin-test/gradlew -p plugin-test hello.

It all clicked when I looked at the example in

:ok_hand:

Would be good if the Samples were linked in

Feel free to open a documentation issue on GitHub, I’m just a user like you. :slight_smile: