Apply plugins conditionally but with configuration

Hello everyone,
I have been trying to create a company plugin where various other plugins are configured through configuration (extension).

I narrowed it down to the following code:

class CompanyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
		val config = project.extensions.create("setupPlugins", SetupPluginsExtension::class.java)
                project.afterEvaluate {
                  // apply the plugins
	        }
     }
}

I don’t mind the undeterministic order of plugins being applied in project.afterEvaluate, however there is a side-effect to this setup. Because this extension is only available after project gets evaluated & plugins are applied there, I can’t configure any of these plugins (in the project using this plugin) with their own extensions. Is there any way to go around this?

plugins {
    id("com.company.plugin") version "1.0"
}

setupPlugins {
  jacocoPlugin = true
}

jacoco {
  // jacoco config, **this doesn't compile** because plugin was not yet applied
}

I found a possible solution, but I am stuck into figuring out if/how to call it. plugins block can include a plugin but not apply it:
https://docs.gradle.org/current/userguide/plugins.html#sec:constrained_syntax

This would allow me to include all the plugins from company plugin, but only selectively apply them later (in the same fashion described above, on project.afterEvaluate). I’m also hoping this setup would create those plugins extensions. Is this even possible? I am stuck at figuring how how to use same syntax. In the source-code I am doing

class CompanyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
                project.apply<IdeaPlugin>()
                with(project.plugins) {
                   apply(DependencyCheckPlugin::class.java)
                }
     }
}

Found some similar questions, but nothing that would help me (I’m also rather noob with gradle plugins)

Actually, almost always when you use afterEvaluate, your are doing something wrong and should change your code.

And no, the type-safe accessors are only generated within build scripts and precompiled script plugins. There is no way to get them for normal plugins. You either have to write them yourself if you really want then, or configure extensions by type instead of accessor like configure<FooExtension> { ... }.

Hi,

Thanks for reply.

I am not sure what you meant by this, could you pls elaborate?

configure extensions by type instead of accessor like configure<FooExtension> { ... } .

Do you have some proposal that I could use to write this plugin? With project.afterEvaluate or not, I would still need to conditionally apply plugins.

Thanks!

Nowhere in your shown code you apply plugins conditionally.
If you mean you want to react to setupPlugins.jacocoPlugin being true and then apply the JaCoCo plugin, that’s not a really good idea.
Either make it not a property but a function, so that you can do setupPlugins { useJacocoPlugin() },
or have multiple convention plugins that you apply according to the plugins that should be present.

If you have multiple convention plugins and there is for example a com.company.plugin.jacoco that always applies the JaCoCo plugin, you also have the jacoco { ... } accessor to configure it.

If you go the function route, you could take an optional Action<JacocoPluginExtension> as argument and then forward it after applying the plugin so you can use it like setupPlugins { useJacocoPlugin { /* configuration here as if you were within "jacoco { ... }" */ } }.

With what you intend, you would have to do configure<JacocoPluginExtension> { ... } in the build script, because you only get the jacoco { ... } accessor if the plugins in the plugins block apply the plugin in question without depending on extension configuration.

Thank you Björn,
I believe I have a good idea now regarding the implementation.