Apply plugin inside a plugin

I have found the solution so I post it here for reference:

  1. in the gradle.build of the custom plugin:

    dependencies {
    runtime ‘gradle.plugin.com.bisiach.gradle.gitversion-plugin:gradle-gitversion-plugin:1.0.2’
    }

  2. In the plugin class

    package com.bisiach.gradle;

    import org.gradle.api.Plugin;
    import org.gradle.api.Project;

    class CustomPlugin implements Plugin {
    @Override
    def void apply(Project project) {
    project.getPluginManager().apply(“com.bisiach.gradle.gitversion-plugin”)
    project.version = project.GitVersion.SemVer
    }
    }

Hey thanks for sharing that! You’re quick :slight_smile: I was just about to reply with something I found in the User Guide:

Would you mind trying that approach? And sharing whether or not that also works? Thanks.

Thanks a lot for the link to guide!

Unfortunately I discovered the neither my approach or the one in the guide work.
I will use the terminology in the guide to be more clear.
In my scenario the Base Plugin (the capability plugin) is published public here:

https://plugins.gradle.org/plugin/com.bisiach.gradle.gitversion-plugin

On the other hand, the “convention” plugin will be published privately to a company maven repo. The plugin named i.e: com.acme.gradle.standard-development-plugin.
The idea is that this plug-in will be applied to all customer projects using gradle.init.

During develompent, to test com.acme.gradle.standard-development-plugin I am using composite build.
I have a project “test” that includes the build of com.acme.gradle.standard-development-plugin. In this scenario, adding the following to build.gradle of com.acme.gradle.standard-development-plugin worked. but only because in a composite build, the project “test” can see the runtime dependency:

dependencies {
runtime ‘gradle.plugin.com.bisiach.gradle.gitversion-plugin:gradle-gitversion-plugin:1.0.2’
}

After publishing my com.acme.gradle.standard-development-plugin to an internal maven repo, and including in the project “test” as a normal plugin, the plug in fails to apply the base (capability) plugin com.bisiach.gradle.gitversion-plugin.

The examples outlined in the guide always shoes capability and convention plugins coming from the same gradle build. That is why it works there I think.
I need to achieve the same between 2 plugins coming from 2 different builds/repositories

Bummer :frowning:

Here’s what I think are the actual implementations of the two plugins the User Guide refers to:

And this other one that’s also extended by the Java Plugin:

They’re in three different subprojects. Is Gradle’s structure similar to what you described for your projects.

Reading the Gradle plugin source, it all flew right over my head. I’m not even going to pretend I understand any of it Hah-hah!

But the Gradle source might give you some leads on how you might proceed.

I’m going to need to do that myself soon. What have you figured out so far? Got any pointers to share yet?

Why don’t you work with detached configs?

Conceptually I think i grasp what you mean. But I could really need a little bite more hint.
I guess that instead of

	project.buildscript.dependencies.add("classpath","gradle.plugin.com.bisiach.gradle.gitversion-plugin:gradle-gitversion-plugin:1.0.2");
	project.getPluginManager().apply("com.bisiach.gradle.gitversion-plugin");

I should do something like :

def d = dependencies.create(group: project.group, name: project.name, version: '+')
def c = configurations.detachedConfiguration(d).setTransitive(false)

I cannot see how this will help me load the com.bisiach.gradle.gitversion-plugin in the custom plug-in.
Could you point me in the right direction?

I mistook problem.

To get read of:

dependencies {
   runtime ‘gradle.plugin.com.bisiach.gradle.gitversion-plugin:gradle-gitversion-plugin:1.0.2’
}

or:

project.buildscript.dependencies.add("classpath","gradle.plugin.com.bisiach.gradle.gitversion-plugin:gradle-gitversion-plugin:1.0.2");

you need to declare transitive dependency in your plug-in because build script classpath cannot be altered programmatically.

Then I believe below will work:

project.getPluginManager().apply("com.bisiach.gradle.gitversion-plugin");

@andreasb70, I see that something similar to what @gavenkoa suggests is done in the two links I shared previously:

    ...
	
    @Override
    public void apply(Project project) {
        project.getPluginManager().apply(ComponentModelBasePlugin.class);
        project.getPluginManager().apply(JvmResourcesPlugin.class);
    }
	...
    ...
	
    @Override
    public void apply(Project project) {
        project.getPluginManager().apply(LanguageBasePlugin.class);
        project.getPluginManager().apply(BinaryBasePlugin.class);
    }
    ...	

There’s a good chance something like that could work, if you hadn’t tried it yet.

Please let us know if it does or not? Thanks.

EDIT: Actually, I see that you have tried something similar before…

Have you tried it with the class literal (.class) of the plugin you want to apply? Like it’s done in the Gradle source links? Instead of the plugin’s id string?

1 Like

Did anyone end up getting this working? I’m also trying to get this working but have had issues (the plugins are actually in completely different repos that I’m trying to incorporate)

The Issue with the above mentioned solutions is that they declared the dependency in the plugins build.gradle file as runtime. That’s why you can not apply the Plugin class (as it is missing in the compile classpath). Just change the dependency to implementation.

I don’t think that any lazy configuration will help here as one usually wants to interact with the other plugins datastructures and therefor you’ll need access to the classes on the compileClasspath and runtimeClasspath what the implementation configuration is meant for.

//build.gradle
repositories.pluginPortal()
dependencies {
    implementation ‘gradle.plugin.com.bisiach.gradle.gitversion-plugin:gradle-gitversion-plugin:1.0.2’
}
public class NewPlugin implements Plugin<Project> {
    public void apply(Project project) {
        project.getPlugins().apply(GitVersionPlugin.class);
        // customize...
    }
}

I am using this extensively with gradles internal plugins as well as externals like sonarqube or test-sets.

1 Like

Thanks @geissld, your recommendation worked in my case!

An undesired side effect (in my case anyway) is that the tasks registered by the third-party plugin are now visible from my top-level project.

I don’t really care much honestly, but I am curious as to how this could be avoided so the top-level project ends up with a cleaner interface.

Well this is by design. Even when doing it from inside your convinience plugin you technically do write apply plugin: 'includedPluginId' and this will add all Tasks, Configurations etc. to your project and thus it’s namespace. I’m pretty sure this is intentional and can not be avoided.

2 Likes

I’m not sure that this works anymore in Gradle 7. For one of my conventions, I have this line declared in my plugin project:

project.plugins.apply("org.sonarqube")
// though i've read we should prefer project.pluginManager instead of project.plugins

The plugin project’s build.gradle has the necessary dependency defined:

dependencies {
  implementation "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0"
}

But in the consuming project, I get errors like:

> Failed to apply plugin 'com.mycompany.code-quality-conventions'.
    > Plugin with id 'org.sonarqube' not found.

Similar to OP, it works with a composite build, but once I build the plugin JAR, and try to use it, I get the above error.

Does that mean I cannot dynamically choose a version for the 3rd party plugin I want to apply? I would like the user’s configuration (maybe indirectly) to affect the version of the plugin I will apply from my plugin. How can I do that?

There is very VERY shady way to do that by hacking the settings ClassLoaderScope, but I’d rather not…

The user could use a dependency constraint in the buildscript { ... } block to influence the version, or you could require the user to add the plugin in the wanted version to the buildscritp’s classpath.

Thanks, I see. Those options don’t allow me to fully encapsulate this plugin application: the user still has to know about this plugin.

Well, how can he not know about the plugin when he is expected to control its version?

The user might not be directly controlling the version of the plugin, for instance it might be that enabling some features leads to some different choice of version. But I have to admit my case is unorthodox.

I see. If this is more like an A or B situation than a 1 out of 100 situation, it might maybe be feasible to release two variants of your plugin instead of having some feature switch.