Main contents of this paper:

  1. Dependency management: Gradle plug-in 7.0 and above uses version Catalog for dependency management
  2. General build extended property management: configures common extension properties, plug-ins, and dependencies for each subproject in the plug-in project
  3. Plug-in engineering: Hybrid compilation of the plug-in project and the main project, and how gradle plug-ins 7.0 and above are published to the Jitpack.

Now the project is basically multi-module structure, each new module will copy the minSdk, targetSdk and other general extension attributes will be more troublesome, should use the general plug-in to manage.

1 Use of Plug-ins

Use case: Plugin /sample

Plugin source: feature/plugin

1.1 Adding a Dependency

A:

// Project root directory build.gradle.kts
buildscript {
    repositories {
        maven { setUrl("https://jitpack.io") }
    }

    dependencies {
        classpath("Com.github.Jadyli.com posing: config - plugin: 0.1.2.")}}Copy the code

Method 2 (Recommended) :

// The project root directory settings.gradle.kts
pluginManagement {
    repositories {
        maven { setUrl("https://jitpack.io") }
    }
    resolutionStrategy {
        eachPlugin {
            when (requested.id.id) {
                "com.jady.lib.config-plugin" -> {
                    useModule("Com.github.Jadyli.com posing: config - plugin: 0.1.2.")}}}}}Copy the code

1.2 Configuring the SDK Version:

Method 1: Configure it in the gradle.properties file in the root directory of the project

minSdk=21
targetSdk=31
compileSdk=31
javaMajor=11
javaVersion=11
compose=1.1. 0-beta04
Copy the code

Method 2 (recommended) : In the project root directory build.gradle. KTS

// Here is the version catalog. You can also directly replace it with the corresponding version number, refer to Method 1
ext {
    set("minSdk", libs.versions.minSdk.get())
    set("targetSdk", libs.versions.targetSdk.get())
    set("compileSdk", libs.versions.compileSdk.get())
    set("javaMajor", libs.versions.java.major.get())
    set("javaVersion", libs.versions.java.asProvider().get())
    set("compose", libs.versions.compose.get()}Copy the code

For compose, you can add the compose dependency and configuration by default. If you don’t need it, you can add it and leave a comment.

1.3 Application in module

/ / way (recommended), with the version catalog can be written as alias (libs. Plugins. Config. The plugin)
plugins {
    id("com.jady.lib.config-plugin")}2 / / way
apply(from = "com.jady.lib.config-plugin")
Copy the code

Once added, a Library configuration file will be very concise, with just a few plug-ins and dependencies needed for modules.

@file:Suppress("UnstableApiUsage")

@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    alias(libs.plugins.android.library)
    alias(libs.plugins.hilt.android)
    alias(libs.plugins.config.plugin)
}

dependencies {
    // official library
    implementation(libs.bundles.compose.core)
    implementation(libs.hilt.runtime)
    kapt(libs.bundles.hilt.compiler)

    // other module
    api(projects.framework.utils)
}
Copy the code

2 Dependency Management

Gradle sharing depends on the official document: docs.gradle.org/current/use…

Toml official website: Toml. IO /en/

2.1 version of the catalog

In the past, there has been no uniform way to manage dependencies. Either by setting dependencies in the project properties file (such as creating a dependencies. Gradle file), or by creating a plug-in project to put all dependencies in the plug-in. The advantage of this is that the plug-in can be shared by multiple projects and used as a variable when retrieved. The Version Catalog is similar to this plug-in, except that the Version Catalog is a generic TOML format file for defining dependencies.

[versions]
# build config
compileSdk = "31"
# official library
kotlin = "1.6.0"
compose = "1.1.0 - beta04"
androidx-appcompat = "1.4.0"

[libraries]
# official library
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-activity-compose = "Androidx. Activity: activity - compose: 1.4.0." "
compose-material = { module = "androidx.compose.material3:material3", version = "1.0.0 - alpha02" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0 [", prefer="3.9" } }

[plugins]
android-application = { id = "com.android.application", version.ref = "android-plugin" }

[bundles]
compose-core = ["androidx-activity-compose"."compose-uiTooling"."compose-material"]
Copy the code

To sum up:

  1. Gradle supports four types of nodes:
    1. [versions]Used to define the version number, which must be a string.
    2. [libraries]For defining dependencies, refer to the example above for the supported format
    3. [plugins]Used to define plug-ins
    4. [bundles]Used to define a dependency group
  2. Version number You can specify a single version and a range of versions. You can refer to the specific rulesVersionConstraintClass comments are also available for referenceDeclaring Rich Versions, here is a brief explanation:
    1. Version ranges are represented by ranges.(,)Represents the open interval,[,]Represents a closed interval.[I put it on the right-hand side of the interval), such as[1, 2 [The equivalent of[1, 2).]I put it on the left-hand side of the interval(, such as] 1, 2]The equivalent of(1, 2].
    2. There are four version matching modes that can be specified:strictly,require,prefer,reject.
    3. require(When no mode is specified,requireIs the default mode) version or version range corresponding toVersionConstraintIn thegetRequiredVersion()Required Version Indicates the lowest supported version, which can be higher but not lower.
    4. strictlyThe strict version, literally,prefer,rejectAlso literally.

The dependencies defined can be used locally or after publication.

2.2 Local Use

There is no rule for the location of the toml file. You can customize the location. In general, you can save the toml file in the gradle directory or your own configuration file directory, as long as it can be referenced in the script file. Now you can define the Version Catalog container, refer to:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("${rootDir.path}/.config/dependencies-common.toml"))}}}Copy the code

After sync, Java code is generated from the toml file. You can use libs variables in build.gradle. Here’s an example:

[versions]
kotlin = "1.6.0"
kotlin-coroutines = "1.6.0 - RC2"
[libraries]
kotlin-stdlib-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
[bundles]
compose-core = ["androidx-activity-compose"."compose-uiTooling"."compose-material"]
[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin"} the corresponding code call is:val kotlinVersion = libs.versions.kotlin.asProvider().get(a)val kotlinCoroutinesVersion = libs.versions.kotlin.coroutines.get()
implementation(libs.kotlin.stdlib.jdk7)
implementation(libs.bundles.compose.core)

// The current version of AS does not support the version catalog. Add @suppress.
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    alias(libs.plugins.kotlin.android)
}
Copy the code

2.3 Publishing to the Warehouse

Release code:

@file:Suppress("UnstableApiUsage")

@Suppress("DSL_SCOPE_VIOLATION")
plugins {
    `maven-publish`
    `version-catalog`
}

catalog {
    versionCatalog {
        from(files("${rootDir.path}/.config/dependencies-common.toml"))
    }
}

publishing {
    publications {
        create<MavenPublication>("maven") {
            groupId = "com.jady.lib"
            artifactId = "dependencies"
            version = "1.0.0"
            from(components["versionCatalog"])
        }
    }
    repositories {
        maven {
            setUrl("repo.url")
            credentials {
                username = project.property("repo.username").toString()
                password = project.property("repo.password").toString()
            }
        }
    }
}

Copy the code

After the release, change the dependencyResolutionManagement mentioned above. Other projects can also share dependencies in this way.

dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            from("Com. Mycompany: catalog: 1.0")}}}Copy the code

3 Plug-in Engineering

Here the extension attribute refers to something like this (BaseAppModuleExtension) :

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.jady.sample"
        minSdk 21
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'}}Copy the code

With multiple modules, it’s not too much trouble to configure properties like buildTypes and compileOptions for each module. The trouble is that every module needs to be changed every time it changes, which is very maintenance unfriendly. It is a good idea to dispose of these attributes in the plug-in.

Let’s start by creating a plug-in project. In my opinion, Composing builds have been available for a few years but do not appear to be widely used in the country. In my opinion, Composing build is my preference to be called mixed build, although some of it can be translated into composite build or combination build, I don’t care, my name is mixed build 🧐.

In short, the main function of hybrid compilation is that small modules can be compiled and run as independent projects or as part of a larger project. At present, the basic idea of compiling acceleration in China is module AAR. For a project of dozens of modules, aar compilation is used for modules that have not been changed, and source code compilation is used for modules that have been changed. In fact, if the module is relatively independent, it can also be a little faster, is mixed compilation, a single module as an independent project development and debugging, after the development of nothing need to change, with a big project package.

You can see our pluginThe master branch.

Note that the plugin is a separate project and can be compiled and run separately. You can check the plugin source code in the feature/ Plugin branch.

So the plugin project is a hybrid compilation, includeBuild(“plugins”) in the settings.gradle file of the main project, and then use everything else as described in section 1. Isn’t that amazing? Even if we set the dependency address for the plugin ID in pluginManagement, the project will still use our plugin source code! When we’re done, we just publish the new version and comment out includeBuild(“plugins”).

Let’s configure build.gradle for the plugin.

plugins {
    `kotlin-dsl`
    `maven-publish`
}

group = "com.jady.lib"
version = "0.1.2"

gradlePlugin {
    plugins.register("config") {
        id = "com.jady.lib.config-plugin"
        implementationClass = "com.jady.lib.config.ConfigPlugin"}}Copy the code

Import the plug-in, set the group and version, define the plug-in ID and entry, and you’re done. Regardless of how the plug-in is implemented, let’s see how it is published to the Jitpack. The jitpack is compiled using Java 1.8 by default. We are using Java 11, so we need to configure the jitpack.


jdk:
  - openjdk11
Copy the code

And then commit, add a tag to the commit, and then run on the Jitpack (jitpack address), and hit Get It to do the compilation.

See the output at the end of the log file:

Build artifacts:
com.github.Jadyli.composing:config-plugin:0.12.
com.github.Jadyli.composing:com.jady.lib.config-plugin.gradle.plugin:0.12.
Copy the code

You’ll notice there are two of them, so let’s just use the first one, refer to the first one.

Let’s write the plug-in:

@Suppress("UnstableApiUsage")
class ConfigPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.run {
            plugins.all {
                when (this) {
                    is LibraryPlugin -> configureLibraryPlugin()
                    is AppPlugin -> configureAppPlugin()
                }
            }
        }
    }

    private fun Project.configureLibraryPlugin(a) {
        configCommonPlugin()
        configCommonDependencies()
        extensions.getByType<LibraryExtension>().configCommonExtension(this@configureLibraryPlugin)}private fun Project.configureAppPlugin(a) {
        configCommonPlugin()
        configCommonDependencies()
        extensions.getByType<BaseAppModuleExtension>().run {
            configCommonExtension(this@configureAppPlugin)

            defaultConfig {
                splits {
                    abi {
                        isEnable = true
                        reset()
                        include("x86"."armeabi-v7a"."arm64-v8a")
                        isUniversalApk = false}}}... }}private fun BaseExtension.configCommonExtension(project: Project) {
        setCompileSdkVersion(project.property("compileSdk").toString().toInt())
        
       	...
    }

    private fun Project.configCommonPlugin(a) {
        plugins.apply("org.jetbrains.kotlin.android")... }private fun Project.configCommonDependencies(a){... }}Copy the code

Plug-ins are very simple, you can configure attributes, plug-ins, dependencies, see the code to understand, nothing to say, we have requirements can comment, the above split should be some projects can not be used, but also according to their own needs fork a copy out at will.