From f00e73b691d7e4a16230aae64b29164e462dd0af Mon Sep 17 00:00:00 2001 From: Teodor Grigor Date: Mon, 15 Apr 2024 13:10:31 +0300 Subject: [PATCH 1/2] Introduce Ceres JVM Library and Android Lint Plugins --- plugin/library-convention/build.gradle.kts | 12 +++++++ .../kotlin/AndroidLintConventionPlugin.kt | 36 +++++++++++++++++++ .../main/kotlin/JvmLibraryConventionPlugin.kt | 15 ++++++++ 3 files changed, 63 insertions(+) create mode 100644 plugin/library-convention/src/main/kotlin/AndroidLintConventionPlugin.kt create mode 100644 plugin/library-convention/src/main/kotlin/JvmLibraryConventionPlugin.kt diff --git a/plugin/library-convention/build.gradle.kts b/plugin/library-convention/build.gradle.kts index fa0722d..9350b87 100644 --- a/plugin/library-convention/build.gradle.kts +++ b/plugin/library-convention/build.gradle.kts @@ -141,6 +141,12 @@ gradlePlugin { tags.set(listOf("android", "room", "android-library", "android-development")) } + register("androidLint") { + id = "dev.teogor.ceres.android.lint" + implementationClass = "AndroidLintConventionPlugin" + displayName = "Android Lint Plugin | Ceres Plugin" + } + register("androidFirebase") { id = "dev.teogor.ceres.android.application.firebase" implementationClass = "AndroidApplicationFirebaseConventionPlugin" @@ -172,6 +178,12 @@ gradlePlugin { description = "Streamline Kotlin Library Development with a Standardized Convention" tags.set(listOf("kotlin", "convention", "build-logic", "library")) } + + register("jvmLibrary") { + id = "dev.teogor.ceres.jvm.library" + implementationClass = "JvmLibraryConventionPlugin" + displayName = "JVM Library Convention" + } } } diff --git a/plugin/library-convention/src/main/kotlin/AndroidLintConventionPlugin.kt b/plugin/library-convention/src/main/kotlin/AndroidLintConventionPlugin.kt new file mode 100644 index 0000000..0599314 --- /dev/null +++ b/plugin/library-convention/src/main/kotlin/AndroidLintConventionPlugin.kt @@ -0,0 +1,36 @@ +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.dsl.LibraryExtension +import com.android.build.api.dsl.Lint +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidLintConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + when { + pluginManager.hasPlugin( + "com.android.application" + ) -> configure { + lint(Lint::configure) + } + + pluginManager.hasPlugin( + "com.android.library" + ) -> configure { + lint(Lint::configure) + } + + else -> { + pluginManager.apply("com.android.lint") + configure(Lint::configure) + } + } + } + } +} + +private fun Lint.configure() { + xmlReport = true + checkDependencies = true +} diff --git a/plugin/library-convention/src/main/kotlin/JvmLibraryConventionPlugin.kt b/plugin/library-convention/src/main/kotlin/JvmLibraryConventionPlugin.kt new file mode 100644 index 0000000..b885dfe --- /dev/null +++ b/plugin/library-convention/src/main/kotlin/JvmLibraryConventionPlugin.kt @@ -0,0 +1,15 @@ +import dev.teogor.ceres.configureKotlinJvm +import org.gradle.api.Plugin +import org.gradle.api.Project + +class JvmLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.jetbrains.kotlin.jvm") + apply("dev.teogor.ceres.android.lint") + } + configureKotlinJvm() + } + } +} From 368c4d22aab5b21f5b55056bed098d3da90d11c8 Mon Sep 17 00:00:00 2001 From: Teodor Grigor Date: Mon, 15 Apr 2024 13:11:08 +0300 Subject: [PATCH 2/2] Bump Dependencies to Latest Versions --- gradle/libs.versions.toml | 34 +++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- .../messaging/src/main/AndroidManifest.xml | 5 ++ ...roidApplicationFirebaseConventionPlugin.kt | 2 +- .../kotlin/AndroidLibraryConventionPlugin.kt | 2 +- .../kotlin/AndroidRoomConventionPlugin.kt | 2 +- .../dev/teogor/ceres/AndroidBuildConfig.kt | 2 +- .../kotlin/dev/teogor/ceres/AndroidCompose.kt | 4 +- .../kotlin/dev/teogor/ceres/CeresFlavor.kt | 2 +- .../dev/teogor/ceres/GradleManagedDevices.kt | 2 +- .../main/kotlin/dev/teogor/ceres/Jacoco.kt | 2 - .../kotlin/dev/teogor/ceres/KotlinAndroid.kt | 64 ++++++++++++------- .../dev/teogor/ceres/ProjectExtensions.kt | 9 +++ .../dev/teogor/ceres/models/DependencyType.kt | 1 + .../dev/teogor/ceres/models/LibrarySpec.kt | 7 ++ 15 files changed, 89 insertions(+), 51 deletions(-) create mode 100644 plugin/library-convention/src/main/kotlin/dev/teogor/ceres/ProjectExtensions.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 887f8bf..8094502 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,15 +1,15 @@ [versions] aboutLibraries = "10.8.3" -accompanist = "0.33.2-alpha" +accompanist = "0.34.0" androidDesugarJdkLibs = "2.0.4" -android-gradle-plugin = "8.2.2" +android-gradle-plugin = "8.3.2" androidxActivity = "1.8.2" androidxAnnotation = "1.7.1" androidxAppCompat = "1.6.1" -androidxBrowser = "1.7.0" -androidxComposeBom = "2024.02.01" -androidxComposeCompiler = "1.5.8" -androidxComposeMaterial3 = "1.2.0" +androidxBrowser = "1.8.0" +androidxComposeBom = "2024.04.00" +androidxComposeCompiler = "1.5.11" +androidxComposeMaterial3 = "1.2.1" androidxComposeRuntimeTracing = "1.0.0-beta01" androidxConstraint = "1.1.0-alpha13" androidxCore = "1.12.0" @@ -33,38 +33,38 @@ androidxWindowManager = "1.2.0" androidxWork = "2.9.0" apiValidator = "0.14.0" buildConfig = "3.1.0" -coil = "2.5.0" +coil = "2.6.0" colorMath = "3.2.0" dokka = "1.9.10" espressoCore = "3.5.1" -firebaseBom = "32.7.3" +firebaseBom = "32.8.1" firebaseCrashlyticsPlugin = "2.9.9" firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.1" googleMaterial = "1.11.0" gradlePublish = "1.1.0" gson = "2.10.1" -hilt = "2.50" +hilt = "2.51.1" hiltExt = "1.2.0" jacoco = "0.8.7" jdom2 = "2.0.6.1" jsoup = "1.16.1" junit = "4.13.2" junit4 = "4.13.2" -kotlin = "1.9.22" +kotlin = "1.9.23" kotlin-poet = "1.14.2" kotlin-xml-builder = "1.9.1" -kotlinx-serialization-core = "1.6.2" +kotlinx-serialization-core = "1.6.3" kotlinxCollections = "0.3.6" -kotlinxCoroutines = "1.7.3" +kotlinxCoroutines = "1.8.0" kotlinxDatetime = "0.5.0" -kotlinxSerializationJson = "1.6.2" -ksp = "1.9.22-1.0.17" +kotlinxSerializationJson = "1.6.3" +ksp = "1.9.23-1.0.20" landscapist = "2.3.1" lifecycleRuntimeKtx = "2.7.0" -lint = "31.2.2" +lint = "31.3.2" okhttp = "4.12.0" -protobuf = "3.24.4" +protobuf = "3.25.2" protobufPlugin = "0.9.4" querent = "1.0.0-alpha02" retrofit = "2.9.0" @@ -83,7 +83,7 @@ google-android-ump = "2.2.0" about-libraries-core = { group = "com.mikepenz", name = "aboutlibraries-core", version.ref = "aboutLibraries" } accompanist-testharness = { group = "com.google.accompanist", name = "accompanist-testharness", version.ref = "accompanist" } accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } -android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } +android-desugar-jdk-libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 53f6019..0d8dd22 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Sep 03 17:17:31 EEST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/monetisation/messaging/src/main/AndroidManifest.xml b/monetisation/messaging/src/main/AndroidManifest.xml index 9239529..11dfec0 100644 --- a/monetisation/messaging/src/main/AndroidManifest.xml +++ b/monetisation/messaging/src/main/AndroidManifest.xml @@ -22,6 +22,11 @@ android:name="dev.teogor.ceres.monetisation.admob.flag.MANUAL_ADS_SETUP" android:value="true" /> + + { apply("com.google.firebase.crashlytics") } - val libs = extensions.getByType().named("libs") dependencies { val bom = libs.findLibrary("firebase-bom").get() add("implementation", platform(bom)) diff --git a/plugin/library-convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/plugin/library-convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt index e0e1893..b1af030 100644 --- a/plugin/library-convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt +++ b/plugin/library-convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -21,6 +21,7 @@ import dev.teogor.ceres.configureGradleManagedDevices import dev.teogor.ceres.configureKotlinAndroid import dev.teogor.ceres.configurePrintApksTask import dev.teogor.ceres.disableUnnecessaryAndroidTests +import dev.teogor.ceres.libs import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.VersionCatalogsExtension @@ -48,7 +49,6 @@ class AndroidLibraryConventionPlugin : Plugin { configurePrintApksTask(this) disableUnnecessaryAndroidTests(target) } - val libs = extensions.getByType().named("libs") configurations.configureEach { resolutionStrategy { val junit4 = libs.findLibrary("junit4") diff --git a/plugin/library-convention/src/main/kotlin/AndroidRoomConventionPlugin.kt b/plugin/library-convention/src/main/kotlin/AndroidRoomConventionPlugin.kt index 47304e2..7835eef 100644 --- a/plugin/library-convention/src/main/kotlin/AndroidRoomConventionPlugin.kt +++ b/plugin/library-convention/src/main/kotlin/AndroidRoomConventionPlugin.kt @@ -15,6 +15,7 @@ */ import com.google.devtools.ksp.gradle.KspExtension +import dev.teogor.ceres.libs import dev.teogor.ceres.models.RoomOptionsExtension import dev.teogor.ceres.models.roomCompiler import dev.teogor.ceres.models.roomKtx @@ -54,7 +55,6 @@ class AndroidRoomConventionPlugin : Plugin { } } - val libs = extensions.getByType().named("libs") dependencies { add( dependencies = listOf( diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidBuildConfig.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidBuildConfig.kt index fff7146..b6f19e0 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidBuildConfig.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidBuildConfig.kt @@ -71,7 +71,7 @@ internal fun ConfigurationContainer.findSpecificDependency( * @param commonExtension The common extension of the Android project. */ internal fun Project.configureAndroidBuildConfig( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { val now = Instant.now() val buildDate = now.atOffset(ZoneOffset.UTC).toLocalDate() diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidCompose.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidCompose.kt index dd55c48..113ef41 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidCompose.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/AndroidCompose.kt @@ -31,10 +31,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile * Configure Compose-specific options */ internal fun Project.configureAndroidCompose( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { - val libs = extensions.getByType().named("libs") - commonExtension.apply { buildFeatures { compose = true diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/CeresFlavor.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/CeresFlavor.kt index 07146ad..df765ce 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/CeresFlavor.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/CeresFlavor.kt @@ -26,7 +26,7 @@ import dev.teogor.ceres.utils.getBooleanProperty import org.gradle.api.Project fun Project.configureFlavors( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, flavorConfigurationBlock: ProductFlavor.(flavor: CeresFlavor) -> Unit = {}, ) { val flavoursEnabled = getBooleanProperty( diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/GradleManagedDevices.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/GradleManagedDevices.kt index 75b075b..30dfb64 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/GradleManagedDevices.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/GradleManagedDevices.kt @@ -26,7 +26,7 @@ import org.gradle.kotlin.dsl.invoke */ @Suppress("UnstableApiUsage") internal fun configureGradleManagedDevices( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { val pixel4 = DeviceConfig("Pixel 4", 30, "aosp-atd") val pixel6 = DeviceConfig("Pixel 6", 31, "aosp") diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/Jacoco.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/Jacoco.kt index 820a428..94a94ed 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/Jacoco.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/Jacoco.kt @@ -44,8 +44,6 @@ private fun String.capitalize() = replaceFirstChar { internal fun Project.configureJacoco( androidComponentsExtension: AndroidComponentsExtension<*, *, *>, ) { - val libs = extensions.getByType().named("libs") - configure { libs.findVersion("jacoco").let { jacoco -> if (jacoco.isPresent) { diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/KotlinAndroid.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/KotlinAndroid.kt index 369106d..6b13349 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/KotlinAndroid.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/KotlinAndroid.kt @@ -17,13 +17,15 @@ package dev.teogor.ceres import com.android.build.api.dsl.CommonExtension +import dev.teogor.ceres.models.desugarJdkLibs +import dev.teogor.ceres.utils.add import dev.teogor.ceres.utils.getBooleanProperty import dev.teogor.ceres.utils.getIntProperty import org.gradle.api.JavaVersion import org.gradle.api.Project -import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -32,9 +34,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile * Configure base Kotlin with Android options */ internal fun Project.configureKotlinAndroid( - commonExtension: CommonExtension<*, *, *, *, *>, + commonExtension: CommonExtension<*, *, *, *, *, *>, ) { - commonExtension.apply { compileSdk = getIntProperty( key = "ceres.buildfeatures.sdk.compile", @@ -53,7 +54,6 @@ internal fun Project.configureKotlinAndroid( // https://developer.android.com/studio/write/java11-minimal-support-table sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 - isCoreLibraryDesugaringEnabled = getBooleanProperty( key = "ceres.buildfeatures.desugaring.enabled", defaultValue = true, @@ -61,6 +61,43 @@ internal fun Project.configureKotlinAndroid( } } + configureKotlin() + + dependencies { + if (getBooleanProperty( + key = "ceres.buildfeatures.desugaring.enabled", + defaultValue = true, + ) + ) { + add( + dependencies = listOf( + desugarJdkLibs, + ), + logger = logger, + libs = libs, + ) + } + } +} + +/** + * Configure base Kotlin options for JVM (non-Android) + */ +internal fun Project.configureKotlinJvm() { + extensions.configure { + // Up to Java 11 APIs are available through desugaring + // https://developer.android.com/studio/write/java11-minimal-support-table + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + configureKotlin() +} + +/** + * Configure base Kotlin options + */ +private fun Project.configureKotlin() { // Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947 tasks.withType().configureEach { kotlinOptions { @@ -71,26 +108,9 @@ internal fun Project.configureKotlinAndroid( val warningsAsErrors: String? by project allWarningsAsErrors = warningsAsErrors.toBoolean() freeCompilerArgs = freeCompilerArgs + listOf( - "-opt-in=kotlin.RequiresOptIn", // Enable experimental coroutines APIs, including Flow "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", - "-opt-in=kotlinx.coroutines.FlowPreview", - ) - } - } - - val libs = extensions.getByType().named("libs") - - dependencies { - if (getBooleanProperty( - key = "ceres.buildfeatures.desugaring.enabled", - defaultValue = true, ) - ) { - val desugarJdkLibs = libs.findLibrary("android.desugarJdkLibs") - if (desugarJdkLibs.isPresent) { - add("coreLibraryDesugaring", desugarJdkLibs.get()) - } } } } diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/ProjectExtensions.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/ProjectExtensions.kt new file mode 100644 index 0000000..2f4a3bf --- /dev/null +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/ProjectExtensions.kt @@ -0,0 +1,9 @@ +package dev.teogor.ceres + +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.getByType + +val Project.libs + get(): VersionCatalog = extensions.getByType().named("libs") diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/DependencyType.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/DependencyType.kt index 1f2f384..d27c020 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/DependencyType.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/DependencyType.kt @@ -8,4 +8,5 @@ enum class DependencyType(val gradleNotation: String) { ANDROID_TEST_IMPLEMENTATION("androidTestImplementation"), TEST_IMPLEMENTATION("testImplementation"), COMPILE_ONLY("compileOnly"), + CORE_LIBRARY_DESUGARING("coreLibraryDesugaring"), } diff --git a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/LibrarySpec.kt b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/LibrarySpec.kt index ea0f553..dfee515 100644 --- a/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/LibrarySpec.kt +++ b/plugin/library-convention/src/main/kotlin/dev/teogor/ceres/models/LibrarySpec.kt @@ -52,3 +52,10 @@ val androidxComposeBom = LibrarySpec( ), isBom = true, ) +val desugarJdkLibs = LibrarySpec( + name = "android.desugar.jdk.libs", + module = "com.android.tools:desugar_jdk_libs", + dependencyTypes = listOf( + DependencyType.CORE_LIBRARY_DESUGARING, + ), +)