1

I'm trying to write a custom Gradle task class and use it in another subproject. I'm having problems tying the build together.

In this example, I have named the subprojects "a" (for application) and "p" (for plugin, although I'm not using a plugin object but just providing a task class to the build script).

settings.gradle

include 'p'
include 'a'

p/build.gradle

apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'maven-publish'

group 'test'
version '1.0'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}

dependencies {
    implementation gradleApi()
    implementation localGroovy()
}

p/src/main/groovy/p/MyTask.groovy

package p

import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;

class MyTask extends DefaultTask {
    @TaskAction
    void run() {
        System.out.println('yay!');
    }
}

a/build.gradle

buildscript {

    repositories {
        mavenLocal()
    }

    dependencies {
        classpath 'test:p:1.0'
    }

}

group = 'test'
version = '1.0'

apply plugin: 'java'

task myTask(type: p.MyTask) {
}

The "plugin" is built by running, inside the p folder:

../gradlew clean build publishToMavenLocal

Inside the a folder:

../gradlew myTask

prints "yay!"

However, while developing, bugs happen. When I simulate a bug in MyTask:

MyTask() {
    throw new RuntimeException("an error");
}

and build the plugin (in folder p):

../gradlew clean build publishToMavenLocal

it fails as expected.

Now I "fix" the bug by removing the broken constructor again, and rebuild in folder p:

../gradlew clean build publishToMavenLocal

but this command fails with the same error.

As far as I understand, the reason is that:

  • the broken plugin is in my local maven repo
  • trying to build the plugin detects the settings.gradle in the parent folder
  • gradle tries to configure all projects referenced from settings.gradle
  • this loads the broken plugin
  • the build fails

To verify, I comment out the include line for a in settings.gradle, and it works again. Reverting settings.gradle, and it still works, because now the "fixed" plugin is in my maven repo, and rebuilding the plugin will just overwrite it again with the working version.

The bottom line is that bugs in my custom task class (or custom plugin, or any other buildscript code) have the potential to make themself un-compilable, with the workaround being to edit or temporarily rename settings.gradle. The more complex the project gets, the more cumbersome this becomes: Renaming does not work if the plugin code itself contains multiple subprojects, and even commenting out becomes "commenting out the right lines".

What is the intended way to fix this problem?

Martin Geisse
  • 1,189
  • 1
  • 9
  • 22

1 Answers1

1

Complex logic for a single (multi-) project is best organized in buildSrc. You can for the most part regard it as a normal sub-project but just for the build classpath instead. Plugins and tasks that you create here are automatically available for all projects in the multi-project.

If, for some reason, you rather continue to work with a local Maven repository, you could think about publishing stable releases of the plugin with a version number so it is easier to roll back.

Bjørn Vester
  • 6,851
  • 1
  • 20
  • 20
  • The problem occurs while developing the plugin, not for stable releases. buildSrc has the problem that it is just like a single sub-project, but the plugin itself is complex enough to consist of multiple sub-projects. – Martin Geisse Aug 18 '20 at 04:22
  • You can have sub-projects in `buildSrc` (though the root has to depend on them). You can also extract the build logic to a stand-alone project and bring it in as an composite (included) build, which is what `buildSrc` is under the hood. I usually do that to dog-food my own plugins. The point is that if you want to publish it to a repository, you should use a version number so you can always roll back in case of failure. If you can't roll back to the latest stable, you can use timestamps on the snapshots. But I still find the first `buildSrc`/composite build more maintainable. – Bjørn Vester Aug 18 '20 at 08:00
  • I suspected that a composite build is what fits my situation best. Your hint that buildSrc does the same in a more ad-hoc way helped me better search for resources on this, so this combination of answer+comment solves my problem. – Martin Geisse Aug 18 '20 at 08:31