This message was deleted.
# community-support
s
This message was deleted.
z
my concern is that I change an ID and my code silently breaks
c
yea. it’s annoying that the ‘preferred’ way (pluginManager) doesn’t offer type safety. Using these extension functions to normalize all that, hide the plugins/pluginManager.
Copy code
fun Project.prohibitPlugin(pluginId: String, message: String) {
    withPlugin(pluginId) {
        throw GradleException("Plugin prohibited: $pluginId; $message")
    }
}

fun Project.withPlugin(pluginId: String, action: Action<AppliedPlugin>) {
    pluginManager.withPlugin(pluginId, action)
}

inline fun <reified S : Plugin<Project>> Project.withPlugin(noinline configuration: S.() -> Unit) {
    plugins.withType<S>(configuration)
}

override fun apply(project: Project): Unit = project.run {

    // shortcut for plugins.withType<>  
    withPlugin<MavenPublishPlugin> {
        configure<PublishingExtension> {
            publications {
                create<MavenPublication>("javaLibrary") {
                    from(components["java"])
                }
            }
        }
    }
}
z
what makes it the preferred way? why would I prefer
pluginManager.withId(..)
over
plugins.withType<PluginClass>()
?
c
i wouldn’t say the stringly-typed way is preferred. There are documentation references that pluginManager is preferred over plugins, but that’s a weak statement until Gradle has a better story on symmetry between the two.
👍 1
really, if you are getting a plugin, its almost invariable to then configure it - which requires it’s classes anyhow, so let’s just go all in on type-safety…
❤️ 1
t
I don't think the type safety story is all it's cracked up to be. given how complex Gradle's classloader hierarchy is, I worry the type check could fail because you have the same class coming from multiple classloaders.
👍 1
z
I guess maybe leveraging an ID from an alias generated from a version catalog would be the right way to do this?
t
and you could also argue that the plugin type is an implementation detail. the id is what really matters.
change the type, keep the ID, your other plugins still work
👍 1
c
the plugin class is what Gradle uses internally to not only instantiate the plugin, but to keep a reference to having applied that plugin (such that a given is only applied once). The id is merely a reference to the class. While this could be done differently, that’s the currently class-centric implementation. The classloader won’t be an issue for plugins as Gradle manages the resolution of those, though (as with anywhere in Java-land) you could end up with janky situations where two different modules have the same classes, subject to class loader ordering as to which one wins…
leveraging an ID from a version catalog is a good practice, but orthogonal to looking up / using a plugin.
t
The plugin class is indeed an implementation detail (as Tony said), given that the instance itself is not meant to be configurable, but rather register extensions and tasks for configurability. So actually you only need a way to identify your plugin, and everywhere in Gradle we're using the plugin ID:
plugins { id("…") }
,
apply(plugin = "…")
. There's no real reason to use the class as an identifier.
c
it’s a cleaner plugin reference than a string id, affording type-safety at compile time vs “did I get that ID right… in multiple places…”
e
the string ID is the public interface to a plugin, the plugin implementation class is not. I would only use a reference to the plugin class for internal plugins.
👍 1
c
While the ID is necessary at the edge (to apply the plugin when there’s nothing else available), the plugin class is also part of the plugin API (it’s exposed in the published construct). Within plugin code, it seems nonsensical to do
pluginManager.withPlugin("org.something.somePlugin")
when invariably you need the various API classes from the plugin anyhow. Sure, you could define a constant for that - but that’s not only extra work, it needs to be repeated across multiple places (have dozens of plugins that use MavenPublishPlugin, bringing in consistency of naming that constant, etc.) Use the plugin class and embrace the type safety.
Gradle isn’t really doing us any favors here, allowing both the ID & class for plugins: -plugins can be applied by id or class (in limited cases); -plugins can be referenced by id or class, interchangeably; -Gradle internals store the list of applied plugin classes AND a derived map of id -> class, to support looking up either way
t
I think you're simply wrong here. it's one thing to have type safety to reference registered extensions and tasks, but there's no particular reason to care about the type for external plugins. and really, even needing the task types exposes a flaw in the plugin's design
c
it seems like we’re stuck with a model that allows two Plugin identifiers, id and class. IMO, when referencing a plugin (within other plugin code) it’s cleaner to reference the class and have the code completion, click-thru, and compile time safety. Stringly-typed stuff reads poorly, requires extra work to create constants (yet-another-identifier-for-a-plugin) and adds deployment time failure modes.
t
Fwiw, I'd have to test but I don't think the plugin class needs to be public. Neither do the tasks and extensions actual types, as long as they at least expose an interface (or abstract class) for their public API (e.g. Android Gradle Plugin's DSL is all interfaces; I think the classes are publicly accessible, most of them at least, but you really shouldn't use them).
1