How to detect if a plugin was NOT applied?

The opposite question is easy: plugins.withType or plugins.withId, but how do I detect if a plugin is not applied?

For example: in my custom plugin I want to execute code only in modules the Android plugin is not applied:
Let’s say the “Android” plugin is
id = "com.android.base"
type = com.android.build.gradle.api.AndroidBasePlugin

So I have

project.plugins.withId("com.android.base") {
    // code only valid when it's an Android module
}

how do I implement an “else” for this?

I was thinking

project.afterEvaluate {
    if (project.plugins.findPlugin("com.android.base") == null) {
        // no Android plugin in this project (submodule)
    }
}

but afterEvaluate is too late since I’m registering tasks via TaskContainer.register in that block. Since the tasks would be added in afterEvaluate the users who want to configure the task would need to put their code in afterEvaluate too to be able to use for example TaskContainer.named(...).configure { ... }. That’s not a nice build.gradle :frowning:

Am I missing a concept in this process somewhere?

It’s not what you are looking for, but have you considered adding your logic to your own plugin that you apply to the non-android projects? If necessary, you could have this plugin throw an exception if it is used alongside the android plugin.

Yeah, I though about splitting, but it’s a plugin that concerns many/all modules, and I want to keep the config to a minimum: e.g. allprojects { apply plugin: '...' } in root build.gradle. The tricky bit is that the root’s allprojects block is applied first, so at this point findPlugin would give me a valid answer yet.

I’m playing with the idea of just simply doing project == project.rootProject, because that module is usually not Android and if it is I can detect it.

There’s probably no APIs for this, because the first point it’s known if all plugins are applied is afterEvaluate, or maybe it’s not even guaranteed then.

Is there a callback that’s called when project.plugins gets sealed or it’s supported to even apply a plugin in Gradle.buildFinished? (latter means I’m going to have to pick an alternative like @Chris_Dore suggested.)

Thanks @TWiStErRob for this question. There is no way to finalize the plugin container. This is something we are actively working on with the Provider API work, however, we aren’t at the level of locking containers as well as providing finalizing callback yet.

Unless your project has several thousands of subprojects, I would simply over-configure by registering all the tasks you require and disable the tasks if the android plugins get added. If performance is a concern, I would then profile the build in order to arrive at a quantifiable performance hit that we can then look at improving inside Gradle.

1 Like

Am I missing a concept in this process somewhere?

registering all the tasks you require and disable the tasks

That’s it! Nice workaround @Daniel_L, thanks!

Reviving this thread as I needed something in my init script to apply a plugin if it wasn’t already applied, I discovered there is now the plugins.hasPlugin API. E.g. with the taskinfo plugin in my ~/.gradle/init.d

initscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath("gradle.plugin.org.barfuin.gradle.taskinfo:gradle-taskinfo:1.3.1")
    }
}
// don't use rootProject as it may not be ready
allprojects {
    afterEvaluate {
        if (parent == null) {
            // using a class name there somwhow mkaes Gradle complain about 'taskinfo'
            // extension being already registered, instead let's use the plugin id
            if(!plugins.hasPlugin("org.barfuin.gradle.taskinfo")) {
                apply<org.barfuin.gradle.taskinfo.GradleTaskInfoPlugin>()
            }
        }
    }
}
  1. Don’t use project.plugins, according to its JavaDoc you should avoid using it, but instead use either project.pluginManager or PluginAware methods on project directly.
  2. Don’t check whether it is already applied, just apply it. Gradle takes care that applying a plugin multiple times is just a no-op, remove the if-hasPlugin and remove the afterEvaluate.
1 Like

Don’t check whether it is already applied, just apply it. Gradle takes care that applying a plugin multiple times is just a no-op, remove the if-hasPlugin and remove the afterEvaluate .

@Vampire Actually that is quite true, that’s why I need to check if the plugin has been applied, and do it at the end of the evaluation.

...

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/brice.dutheil/.../repo/build.gradle.kts' line: 9

* What went wrong:
An exception occurred applying plugin request [id: 'org.barfuin.gradle.taskinfo', version: '1.3.1']
> Failed to apply plugin 'org.barfuin.gradle.taskinfo'.
   > Cannot add extension with name 'taskinfo', as there is an extension already registered with that name.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s

Moreover the plugin version may differ, and I prefer this init script to honor the project’s plugin version.

Ah, seems to be a class loading problem then.
I thought the no-op check might also use the id due to such problems, but it seems to work on the class.
And it seems the init script class loader is not a parent to the project class loader, so the plugin classes come from different class loaders and thus are considered different to the Gradle “no-op if reapplied” check and also to the hasPlugin check with class, which is why you had to use the id instead and get that error in both cases.

The “prefer project version” argument is an important point of course, so yeah, in that case you probably need the afterEvaluate and manual plugin check, but you should probably still use getPluginManager().hasPlugin(...). :slight_smile:

getPluginManager().hasPlugin(...)

That I did.

Thanks a lot for the feedback, it’s much appreciated !

1 Like