Composite build as an alternative to buildSrc in Gradle



In Gradle, an automated build system, the buildSrc approach has already become the standard for implementing custom plugins and tasks, as well as creating common configurations such as lists of dependencies and versions. But it has a significant drawback: when changing, the buildSrcassembly cache becomes invalid.



Gradle — , . , buildSrc .



Gradle



Gradle Android Android Studio. : Android- , , , Eclipse Android-. .



Gradle Groovy ( ) Stack Overflow. , build.gradle.



, - . build.gradle . , , , . , build.gradle .



// projectRoot/build.gradle

public void configureAndroid(Project project) {
  project.android {
    compileSdkVersion 9
  }
}


// projectRoot/app/build.gradle

configureAndroid(this)

android {
  // Module specific configuration
}


, . build.gradle . data, core, domain presentation, : Gradle .



// projectRoot/android.gradle

project.android {
  compileSdkVersion 9
}


// projectRoot/app/build.gradle

apply from: '../android.gradle'


IDE, . build.gradle plugins { }, .



, , , , buildSrc . buildSrc . JVM-, IDE. : ( JUnit ) , Gradle . , , Gradle?!



buildSrc



, . buildSrc . remote , . , . , Gradle , .





: compile (Java-) -> report ( ). compile JavaCompile, Java-. report — , : buildSrc build.gradle.



report , -. buildSrc- compile report , compile . build.gradle report. , - compile , . Gradle , report , , .



, compile ? buildSrc .





, . , , build.gradle. . , , Gradle-. (included) .



buildSrc , buildSrc Gradle . ( , Android Gradle plugin). Gradle .



, . , classpath. , FROM-CACHE. , .



Gradle , . , UP-TO-DATE.



buildSrc



Reaktive . :





, . , .





. buildSrc buildSrc2. buildSrc, . classpath . buildSrc, . Gradle , settings.gradle :



// projectRoot/settings.gradle.kts

pluginManagement {
    repositories {
        google()
        jcenter()
        gradlePluginPortal()
    }
}

includeBuild("buildSrc2")

// include(":module")


buildscript { repositories { } }. includeBuild Gradle buildSrc2 .





buildSrc2? .



// projectRoot/buildSrc2/build.gradle.kts

plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
}

gradlePlugin {
    //   ,        
    plugins.register("class-loader-plugin") {
        id = "class-loader-plugin"
        implementationClass = "com.example.ClassLoaderPlugin"
    }
    // ,      ,       
}


java-gradle-plugin properties, . java-gradle-plugin .



buildSrc, , Gradle-.



// ClassLoaderPlugin.kt

class ClassLoaderPlugin: Plugin<Project> {
    override fun apply(target: Project) {
        // no-op
    }
}

// Deps.kt

object Deps {
    const val kotlinStdLib = "..."
}


class-loader-plugin Deps. .



// projectRoot/app/build.gradle

plugins {
    id 'class-loader-plugin'
}

dependencies {
    implementation(Deps.kotlinStdLib)
}




build.gradle setupMultiplatformLibrary.



// projectRoot/build.gradle

void setupMultiplatformLibrary(Project project) {
    project.apply plugin: 'org.jetbrains.kotlin.multiplatform'
    project.kotlin {
        sourceSets {
            commonMain {
                dependencies {
                    implementation Deps.kotlin.stdlib.common
                }
            }

            commonTest {
                dependencies {
                    implementation Deps.kotlin.test.common
                    implementation Deps.kotlin.test.annotationsCommon
                }
            }
        }
    }
}


. Kotlin Multiplatform .



Gradle- :



// projectRoot/buildSrc2/build.gradle.kts

dependencies {
    //   Kotlin Gradle plugin,        
    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
}
gradlePlugin {
    //   
    plugins.register("mpp-configuration") {
        id = "mpp-configuration"
        implementationClass = "com.badoo.reaktive.configuration.MppConfigurationPlugin"
    }
}


// MppConfigurationPlugin.kt

class MppConfigurationPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        //       "kotlin"  "android"
        target.extensions.create("configuration", MppConfigurationExtension::class.java, target)
        //        ,    ,    Kotlin
        setupMultiplatformLibrary(target)
    }

    private fun setupMultiplatformLibrary(target: Project) {
        // project.apply plugin: 'org.jetbrains.kotlin.multiplatform'
        target.apply(plugin = "org.jetbrains.kotlin.multiplatform")
        // project.kotlin {
        target.extensions.configure(KotlinMultiplatformExtension::class.java) {
            sourceSets {
                maybeCreate("commonMain").dependencies { implementation(Deps.kotlin.stdlib.common) }
                maybeCreate("commonTest").dependencies {
                    implementation(Deps.kotlin.test.common)
                    implementation(Deps.kotlin.test.annotationsCommon)
                }
            }
        }
    }
}


setupAllTargetsWithDefaultSourceSets isLinuxArm32HfpEnabled. linuxArm32Hfp, . linuxArm32Hfp, . project.name, . , .



// MppConfigurationExtension.kt

open class MppConfigurationExtension @Inject constructor(
    private val project: Project
) {
    var isLinuxArm32HfpEnabled: Boolean = false
        private set

    //    ,      ARM32
    fun enableLinuxArm32Hfp() {
        if (isLinuxArm32HfpEnabled) return
        project.plugins.findPlugin(MppConfigurationPlugin::class.java)?.setupLinuxArm32HfpTarget(project)
        isLinuxArm32HfpEnabled = true
    }
}


// MppConfigurationPlugin.kt

class MppConfigurationPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        target.extensions.create("configuration", MppConfigurationExtension::class.java, target)
        ...
    }

    fun setupLinuxArm32HfpTarget(project: Project) {
        if (!Target.shouldDefineTarget(project, Target.LINUX)) return
        project.kotlin {
            linuxArm32Hfp()
            sourceSets {
                maybeCreate("linuxArm32HfpMain").dependsOn(getByName("linuxCommonMain"))
                maybeCreate("linuxArm32HfpTest").dependsOn(getByName("linuxCommonTest"))
            }
        }
    }
}


, (disableLinuxArm32Hfp() ), Kotlin ( ). mpp-configuration configuration.



// projectRoot/reaktive/build.gradle

plugins {
    id 'mpp-configuration'
}

// 
configuration {
    enableLinuxArm32Hfp()
}


.







Binary compatibility validator, . binary-compatibility.gradle build.gradle. , .



// projectRoot/binary-compatibility.gradle

if (Target.shouldDefineTarget(target, Target.ALL_LINUX_HOSTED)) {
    apply plugin: kotlinx.validation.BinaryCompatibilityValidatorPlugin

    apiValidation {
        ignoredProjects += [
                'benchmarks',
                'jmh',
                'sample-mpp-module',
                'sample-android-app',
                'sample-js-browser-app',
                'sample-linuxx64-app',
                'sample-ios-app',
                'sample-macos-app'
        ]
    }
}


.



// projectRoot/buildSrc2/build.gradle.kts

dependencies {
    //    Binary Compatibility Plugin
    implementation("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.3")
}
gradlePlugin {
    //   
    plugins.register("binary-compatibility-configuration") {
        id = "binary-compatibility-configuration"
        implementationClass = "com.badoo.reaktive.compatibility.BinaryCompatibilityConfigurationPlugin"
    }
}


// BinaryCompatibilityConfigurationPlugin.kt

class BinaryCompatibilityConfigurationPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        if (Target.shouldDefineTarget(target, Target.ALL_LINUX_HOSTED)) {
            target.apply(plugin = "binary-compatibility-validator")
            target.extensions.configure(ApiValidationExtension::class) {
                ignoredProjects.addAll(
                    listOf(
                        "benchmarks",
                        "jmh",
                        "sample-mpp-module",
                        "sample-android-app",
                        "sample-js-browser-app",
                        "sample-linuxx64-app",
                        "sample-ios-app",
                        "sample-macos-app"
                    )
                )
            }
        }
    }
}


build.gradle.



// projectRoot/build.gradle

plugins {
    id 'binary-compatibility-configuration'
}




. , , , . . .



// rootProject/dependencies/build.gradle.kts

plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
}

//   
group = "com.badoo.reaktive.dependencies"
version = "SNAPSHOT"

repositories {
    jcenter()
}

gradlePlugin {
    //   ,     
    plugins.register("dependencies") {
        id = "dependencies"
        implementationClass = "com.badoo.reaktive.dependencies.DependenciesPlugin"
    }
}


Deps . . settings.gradle includeBuild("dependencies"). dependencies Deps.



// projectRoot/buildSrc2/build.gradle.kts

import com.badoo.reaktive.dependencies.Deps

plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
    id("dependencies")
}

dependencies {
    //        Deps
    implementation(Deps.kotlin.plugin)
    // implementation(implementation("com.badoo.reaktive.dependencies:dependencies:SNAPSHOT"))
}

//    ,   implementation("com.badoo.reaktive.dependencies:dependencies:SNAPSHOT"),    
kotlin.sourceSets.getByName("main").kotlin.srcDir("../dependencies/src/main/kotlin")


, , IDEA ( ). , Deps buildSrc2, , buildSrc2. , , . implementation("com.badoo.reaktive.dependencies:dependencies:SNAPSHOT").



dependencies .





buildSrc . , plugins { } apply plugin: 'id' . plugins , Groovy- . , . , .



, :



apply plugin: 'android-library'

android {
    compileSdkVersion 30
}

apply plugin: 'custom-plugin'


class CustomPlugin: Plugin<Project> {
    override fun apply(target: Project) {
        target.logger.warn(
            target.extentions.getByType(BaseExtension::class)
                .compileSdkVersion.toString()
        )
    }
}


compileSdkVersion. 30. plugins { }.



plugins {
    id 'android-library'
    id 'custom-plugin'
}

android {
    compileSdkVersion 30
}


null, custom-plugin android. :



  1. apply plugin: 'custom-plugin' . , -.
  2. project.afterEvaluate { }. : , afterEvaluate afterEvaluate .
  3. API, . Gradle API, .




buildSrc, Gradle. . Groovy-, plugins { }. , .




All Articles