Skip to content

RedMadRobot/gradle-infrastructure

Repository files navigation

Gradle Infrastructure

Version Build Status License

Small plugins to reduce boilerplate in Gradle build scripts.

⚠️ It is designed to use with Gradle Kotlin DSL and can't be used from Groovy DSL.


Installation

If you're planning to use android plugins, add Google repository to settings.gradle.kts:

pluginManagement {
    repositories {
        google() // Required if you use infrastructure-android
        gradlePluginPortal()
    }
}

Then you can apply any of plugins where you need:

plugins {
    id("com.redmadrobot.kotlin-library") version "0.18.1"
    id("com.redmadrobot.publish") version "0.18.1"
    id("com.redmadrobot.detekt") version "0.18.1"
    id("com.redmadrobot.application") version "0.18.1"
    id("com.redmadrobot.android-library") version "0.18.1"
}

For kotlin-library and android-related plugins, you should also add Kotlin Gradle Plugin or Android Gradle Plugin to project build dependencies.
Read more...

If you want to configure subprojects from root project, you can apply *-config plugins to root project. This way subprojects will use configs from parent projects as defaults.

./build.gradle.kts:

plugins {
    id("com.redmadrobot.android-config")
}

redmadrobot {
    android {
        minSdk.set(28)
    }
}

./app/build/build.gradle.kts:

plugins {
    id("com.redmadrobot.application")
}

// Will be used minSdk = 28 by default
android {
    // ...
}

Look at samples for quick start.

Compatibility

gradle-infrastructure Minimal Gradle version Minimal KGP version Minimal AGP version
0.18 7.5 1.7.10 7.4.0
0.17 7.2 1.7.10 7.1.0

Plugins

kotlin-library

Common configurations for pure Kotlin libraries.

publish

Common publish configurations for both Android and Kotlin libraries.

  • Applies plugin maven-publish
  • Adds sources and javadocs to publication

You should specify publishing repositories manually. You can also use predicates for publication:

publishing {
    repositories {
        // Unconditional publication
        rmrNexus()
        // Publication with conditions
        if (isRunningOnCi) githubPackages("RedMadRobot/gradle-infrastructure")
        if (isReleaseVersion && credentialsExist("ossrh")) ossrh()
    }
}

Signing

You can configure publication via extension redmadrobot.publishing:

redmadrobot {
    publishing {
        signArtifacts.set(true) // Enables artifacts signing, required for publication to OSSRH
        useGpgAgent.set(true)   // By default use gpg-agent for artifacts signing
    }
}

Read more about singing configuration in Signing Plugin docs.

Customize POM

You can configure POM properties common for all modules.

Note: there are extension-functions to simplify common configuration use-cases. All available extensions you can find here.

redmadrobot {
    publishing {
        pom {
            // Configure <url>, <scm> and <issueManagement> tags for GitHub project by it's name
            setGitHubProject("RedMadRobot/gradle-infrastructure")
            
            licenses { 
                mit() // Add MIT license
            }
            
            developers {
                // Shorthand to add a developer
                developer(id = "j.doe", name = "John Doe", email = "john@doe.com")
            }
        }
    }
}

Customize publication for module

Use publishing extension in module build script to configure publication for the single module. Take publication name from PUBLICATION_NAME constant. Read more in Maven Publish plugin docs.

publishing {
    publications {
        getByName<MavenPublication>(PUBLICATION_NAME) {
            // Configure publication here
        }
    }
}

// or even shorter
publishing.publications.getByName<MavenPublication>(PUBLICATION_NAME) {
    // Configure publication here
}

detekt

  • Adds repository mavenCentral (see Automatically added repositories)
  • Applies detekt plugin with detekt-formatting
  • Configures additional tasks:
    • detektAll - Runs Detekt over the whole codebase
    • detektBaselineAll Creates single baseline file in the config directory with issues from the detektAll task.
    • detektFormat - Reformats the whole codebase with Detekt
    • detektDiff - Runs Detekt only on changed files (see Enable Detekt checks only on changed files)
    • detekt[Variant]All - Runs Detekt checks with type resolution on specified build variant (example detektDebugAll) or main source set (detektMainAll) if project is non-android
    • detektBaseline[Variant]All Creates single baseline file in the config directory with issues from the detekt[Variant]All task.

⚠️ only detekt[Variant]All tasks are compatible with type resolution.

Android Plugins

application and android-library

Common configurations for Android libraries and application.

Both:

Library:

  • Applies plugin com.android.library
  • Adds all proguard files from proguard folder as consumerProguardFiles
  • Disables buildConfig, androidResources and resValues build-features
  • Enables explicit API mode

Application:

  • Applies plugin com.android.application
  • Adds all proguard files from proguard folder
  • Configures debug, qa and release build types
  • Adds LOCK_ORIENTATION and CRASH_REPORTS_ENABLED BuildConfig variables which false only for debug build type
  • Configures Android Lint default options

QA build type name configuration

By default, for QA builds used name "qa", but you can configure BUILD_TYPE_QA via gradle.properties:

# Override QA build type name
redmadrobot.android.build.type.qa=staging

Usage

Add KGP and AGP to build dependencies

Starting from gradle-infrastructure 0.18 KGP and AGP removed from transitive dependencies, so you should manually add it to project build dependencies.

kotlin-library plugin requires Kotlin Gradle Plugin to work. Android-related plugins requires both KGP and AGP.

You can use two different approaches to add plugin as a build dependency:

  1. Add needed plugins to top-level build.gradle.kts with apply false:

    // (root)/build.gradle.kts
    
    plugins {
        // Use `apply false` in the top-level build.gradle file to add a Gradle 
        // plugin as a build dependency but not apply it to the current (root) project.
        // Here you can specify desired AGP and KGP versions to use.
        id("com.android.application") version "7.4.2" apply false
        id("org.jetbrains.kotlin.android") version "1.8.10" apply false
    }
  2. If you have buildSrc or some other module containing build logic, you can add plugins to dependencies of this module:

    // (root)/buildSrc/build.gradle.kts
    
    dependencies {
        // Here you can specify desired AGP and KGP versions to use.
        implementation(kotlin("gradle-plugin", version = "1.8.10"))
        implementation("com.android.tools.build:gradle:7.4.2")
    }

See compatibility table to check what versions are compatible with gradle-infrastructure.

Configuration

You can configure the plugins with an extension named redmadrobot. Look for available properties with description in RedmadrobotExtension.

The extension should be configured in root project.

// root project build.gradle.kts

redmadrobot {
    configsDir.set(file("path/to/configs/"))
}

Align version of all Kotlin libraries

It is not a part of gradle-infrastructure, but it is important to know.

To align the Kotlin version for all dependencies including transitive ones, use kotlin-bom:

dependencies {
    // Align versions of all Kotlin components 
    implementation(platform(kotlin("bom", version = "1.8.10")))

    // Now you can add Kotlin components without version
    implementation(kotlin("stdlib"))
    testImplementation(kotlin("test-junit5"))
}

Another way is using kotlin.coreLibrariesVersion property:

// Set version for all Kotlin components
kotlin.coreLibrariesVersion = "1.8.10"

dependencies {
    // Now you can add Kotlin components without version
    implementation(kotlin("stdlib"))
    testImplementation(kotlin("test-junit5"))
}

Warnings as errors

By default, infrastructure plugins enable Kotlin compiler's option allWarningsAsErrors (-Werror) on CI. You can change it by defining the warningsAsErrors project property.

Read more about Gradle project properties

Share sources between build types

You can share sources between two build types.
For example, you need to use debug panel in both debug and QA builds, and don't want to write similar duplicating code for each of these build types. You can do it with one line with addSharedSourceSetRoot extension-function:

android {
    // We need to share sources between debug and QA builds
    addSharedSourceSetRoot(BUILD_TYPE_DEBUG, BUILD_TYPE_QA)

    // We can specify name for the source set root if need
    addSharedSourceSetRoot(BUILD_TYPE_DEBUG, BUILD_TYPE_QA, name = "debugPanel")
}

Enable Detekt checks only on changed files

Plugin com.redmadrobot.detekt adds task detektDiff to check the only files changed comparing to the base branch. To enable this feature, you should specify base branch name:

redmadrobot {
    detekt {
        checkOnlyDiffWithBranch("develop")
    }
}

The plugin then adds a detektDiff task that allows you to check only changed files comparing to the specified base branch. The modified files are provided by Git.

Task detektDiff is incompatible with type resolution. It means some configured rules will not work.

The detektDiff task includes the '.kt' and '.kts' files. You can change it by providing a set of extensions in the configuration block to checkOnlyDiffWithBranch:

redmadrobot {
    detekt {
        checkOnlyDiffWithBranch("develop") {
            fileExtensions = setOf(".kt")
        }
    }
}

Configure JUnit test execution options

By default, the plugin uses the JUnit Platform to run tests. If you want to configure it, for example include an engine, you can do it using the test extension for both JVM and android.

redmadrobot {
    test {
        useJunitPlatform {
            includeEngines("spek2")
        }
    }
    android {
        test {
            useJunitPlatform {
                includeEngines("spek2")
            }
        }
    }
}

If you want to use JUnit 4 framework to run tests, you need to specify useJunit() in test block.

redmadrobot {
    test {
        useJunit()
    }
}

Automatically added repositories

Infrastructure plugins automatically add repositories required to make project work:

  • kotlin plugin adds mavenCentral repo
  • detekt plugin adds mavenCentral repo
  • android plugins add mavenCentral and google repos

In the case you don't want these repositories to be added automatically, you can disable this behavior via flag in gradle.properties:

redmadrobot.add.repositories=false

Samples

Look for samples in samples package.

If you need closer to life samples, check these projects:

Troubleshooting

Tests failed - No value has been specified for property 'localResourcesFile'

A problem was found with the configuration of task ':mylib:generateReleaseUnitTestStubRFile' (type 'GenerateLibraryRFileTask').
> No value has been specified for property 'localResourcesFile'.

It is a known bug in AGP 4.1.0 caused when androidResources is disabled. As workaround, you can enable this build feature for module:

android {
    // TODO: Remove when bug in AGP will be fixed.
    //  https://issuetracker.google.com/issues/161586464
    buildFeatures.androidResources = true
}

Android resource linking failed or Unresolved reference: R

Execution failed for task ':app:processDebugResources'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > Android resource linking failed
     AAPT: error: resource style/TextAppearance.App.Headline4 (aka com.example.app.debug:style/TextAppearance.App.Headline4) not found.
     error: resource style/TextAppearance.App.Body2 (aka com.example.app.debug:style/TextAppearance.App.Body2) not found.
     error: resource style/Theme.App (aka com.example.app.debug:style/Theme.App) not found.
     error: resource style/Theme.App (aka com.example.app.debug:style/Theme.App) not found.
     error: failed linking references.

Build feature androidResources is disabled by default for android libraries. If you get these errors you should enable it:

android {
    buildFeatures.androidResources = true
}

Build failed on CI - No version of NDK matched the requested version

Execution failed for task ':app:stripDebugDebugSymbols'.
> No version of NDK matched the requested version 21.0.6113669. Versions available locally: 21.1.6352462

It is because NDK version on CI differs from a requested version. You can change requested version by setting android.ndkVersion.

Plugins com.redmadrobot.android-library and com.redmadrobot.application by default apply NDK version from env variable ANDROID_NDK_VERSION if it set.

Could not resolve or Could not find dependencies

> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
   > Could not find com.xwray:groupie:2.7.2
     Searched in the following locations:
       - ...
     Required by:
         project :app > com.xwray:groupie:2.7.2

It may be because of gradle-infrastructure uses mavenCentral instead of jcenter by default. JCenter is at the end of life and should not be used anymore. Unfortunately not all libraries migrated to Maven Central yet. To avoid these errors, declare jcenter repository in your build script and configure it to be used only for missing dependencies.

repositories {
    jcenter {
        content {
            // It is useful to add a link to the issue about migration from JCenter
            // https://github.com/lisawray/groupie/issues/384
            includeModule("com.xwray", "groupie")
        }
    }
}

Contributing

Merge requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

MIT