androidkotlingradleandroid-gradle-pluginkotlin-gradle-plugin

Inconsistent usage of version catalog plugin references in custom Gradle Kotlin DSL setup


I'm working on a custom Gradle setup using Kotlin DSL, and while exploring the nowinandroid project—specifically the convention/build.gradle.kts and libs.versions.toml files—I noticed an inconsistency in how plugin IDs are accessed from the libs.versions.toml version catalog in the build-logic:convention module.

gradlePlugin {
    plugins {
        register("androidApplicationCompose") {
            id = libs.plugins.nowinandroid.android.application.compose.get().pluginId
            implementationClass = "AndroidApplicationComposeConventionPlugin"
        }

        register("androidApplication") {
            id = libs.plugins.nowinandroid.android.application.asProvider().get().pluginId
            implementationClass = "AndroidApplicationConventionPlugin"
        }

        // ... more plugins
    }
}

Some plugin IDs use .get() directly (e.g., compose.get()), while others use .asProvider().get() (e.g., application.asProvider().get()).

From the libs.versions.toml, these plugins are declared normally under [plugins] or indirectly referenced.

My questions are:

  1. What's the difference between plugin.get() and plugin.asProvider().get()?
  2. Is one preferred over the other in certain scenarios?
  3. Could this difference be caused by how the plugin is defined in libs.versions.toml?

Any clarification would be helpful! I'm trying to standardize how plugins are registered in the convention plugins module.


Solution

  • 1. What's the difference between plugin.get() and plugin.asProvider().get()?

    This usually occurs when you have 2 or more version catalog entries with similar prefixes, such as:

    nowinandroid-android-application-compose = "..."
    nowinandroid-android-application = "..."
    

    They both contain the same nowinandroid-android-application prefix, so Gradle will generate a version catalog parent group under application, as well as another getter method to then get the compose sub-accessor.

    For example, given the following version catalog entries:

    androidx-compose-material3-adaptive = "..."
    androidx-compose-material3-adaptive-layout = "..."
    androidx-compose-material3-adaptive-navigation = "..."
    androidx-compose-material3-adaptive-navigationSuite = "..."
    

    It would then generate the following accessors:

    public static class AndroidxComposeMaterial3AdaptiveLibraryAccessors
        extends SubDependencyFactory implements DependencyNotationSupplier {
        public AndroidxComposeMaterial3AdaptiveLibraryAccessors(
            AbstractExternalDependencyFactory owner) {
            super(owner);
        }
    
        /**
         * Dependency provider for <b>adaptive</b> with
         * <b>androidx.compose.material3.adaptive:adaptive</b> coordinates and with
         * version reference <b>androidx.compose.material3.adaptive</b> <p> This
         * dependency was declared in catalog libs.versions.toml
         */
        public Provider<MinimalExternalModuleDependency> asProvider() {
            return create("androidx.compose.material3.adaptive");
        }
    
        /**
         * Dependency provider for <b>layout</b> with
         * <b>androidx.compose.material3.adaptive:adaptive-layout</b> coordinates
         * and with version reference <b>androidx.compose.material3.adaptive</b> <p>
         * This dependency was declared in catalog libs.versions.toml
         */
        public Provider<MinimalExternalModuleDependency> getLayout() {
            return create("androidx.compose.material3.adaptive.layout");
        }
    
        /**
         * Dependency provider for <b>navigation</b> with
         * <b>androidx.compose.material3.adaptive:adaptive-navigation</b>
         * coordinates and with version reference
         * <b>androidx.compose.material3.adaptive</b> <p> This dependency was
         * declared in catalog libs.versions.toml
         */
        public Provider<MinimalExternalModuleDependency> getNavigation() {
            return create("androidx.compose.material3.adaptive.navigation");
        }
    
        /**
         * Dependency provider for <b>navigationSuite</b> with
         * <b>androidx.compose.material3:material3-adaptive-navigation-suite</b>
         * coordinates and with version reference <b>androidx.compose.material3</b>
         * <p>
         * This dependency was declared in catalog libs.versions.toml
         */
        public Provider<MinimalExternalModuleDependency> getNavigationSuite() {
            return create("androidx.compose.material3.adaptive.navigationSuite");
        }
    }
    

    The asProvider() method is then generated for the parent application accessor to be explicit that you're referring to the nowinandroid-android-application accessor and not the AndroidxComposeMaterial3AdaptiveLibraryAccessors group.


    If you were wondering where that Java code came from, you can click on the pencil/edit icon when hovering over an entry reference in a build-script to view its source code:

    Screenshot of Documentation Pop-Up in the Android Studio IDE with the Edit icon highlighted


    2. Is one preferred over the other in certain scenarios?

    The parent accessor group class appears to inherit a SubDependencyFactory which should provide access to the same version catalog accessor:

    @SuppressWarnings("unused")
    public static abstract class SubDependencyFactory
        implements ExternalModuleDependencyFactory {
        protected final AbstractExternalDependencyFactory owner;
    
        protected SubDependencyFactory(AbstractExternalDependencyFactory owner) {
            this.owner = owner;
        }
    
        @Override
        public Provider<MinimalExternalModuleDependency> create(String alias) {
            return owner.create(alias);
        }
    }
    

    There's likely no difference between having asProvider() and omitting it. I would say you should keep it if you want to be explicit that you're referencing a version catalog entry rather than the group that multiple entries under it would have, but it's up to you really.

    3. Could this difference be caused by how the plugin is defined in libs.versions.toml?

    See point 1