diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3101b6a0e..3d091029a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,11 +1,14 @@ +import mihon.buildlogic.getBuildTime +import mihon.buildlogic.getCommitCount +import mihon.buildlogic.getGitSha import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - id("com.android.application") + id("mihon.android.application") + id("mihon.android.application.compose") id("com.mikepenz.aboutlibraries.plugin") - kotlin("android") - kotlin("plugin.serialization") id("com.github.zellius.shortcut-helper") + kotlin("plugin.serialization") } if (gradle.startParameter.taskRequests.toString().contains("Standard")) { @@ -119,7 +122,6 @@ android { buildFeatures { viewBinding = true - compose = true buildConfig = true // Disable some unused things @@ -132,10 +134,6 @@ android { abortOnError = false checkReleaseBuilds = false } - - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } } dependencies { @@ -150,7 +148,6 @@ dependencies { implementation(projects.presentationWidget) // Compose - implementation(platform(compose.bom)) implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) @@ -295,25 +292,6 @@ tasks { "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", ) - - if (project.findProperty("tachiyomi.enableComposeCompilerMetrics") == "true") { - kotlinOptions.freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + - project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath, - ) - kotlinOptions.freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + - project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath, - ) - } - - // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 - kotlinOptions.freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", - ) } } diff --git a/build.gradle.kts b/build.gradle.kts index dfaf0816e..93146c0a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,3 @@ -import com.android.build.gradle.BaseExtension -import com.android.build.gradle.BasePlugin -import org.gradle.api.tasks.testing.logging.TestLogEvent -import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile - buildscript { dependencies { classpath(libs.android.shortcut.gradle) @@ -17,45 +12,6 @@ plugins { alias(kotlinx.plugins.serialization) apply false } -subprojects { - tasks.withType { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - } - - tasks.withType { - useJUnitPlatform() - testLogging { - events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) - } - } - - plugins.withType { - plugins.apply("detekt") - configure { - compileSdkVersion(AndroidConfig.compileSdk) - defaultConfig { - minSdk = AndroidConfig.minSdk - targetSdk = AndroidConfig.targetSdk - ndk { - version = AndroidConfig.ndk - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - isCoreLibraryDesugaringEnabled = true - } - - dependencies { - add("coreLibraryDesugaring", libs.desugar) - } - } - } -} - tasks.register("clean") { delete(rootProject.layout.buildDirectory) } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a948efcc7..0ce5d68de 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -4,8 +4,11 @@ plugins { dependencies { implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) - implementation(androidxLibs.gradle) - implementation(kotlinLibs.gradle) + implementation(files(androidx.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(files(compose.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(files(kotlinx.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(androidx.gradle) + implementation(kotlinx.gradle) implementation(libs.detekt.gradlePlugin) implementation(gradleApi()) } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 05a279df0..1b058e0d8 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -3,10 +3,13 @@ dependencyResolutionManagement { create("libs") { from(files("../gradle/libs.versions.toml")) } - create("androidxLibs") { + create("androidx") { from(files("../gradle/androidx.versions.toml")) } - create("kotlinLibs") { + create("compose") { + from(files("../gradle/compose.versions.toml")) + } + create("kotlinx") { from(files("../gradle/kotlinx.versions.toml")) } } diff --git a/buildSrc/src/main/kotlin/AndroidConfig.kt b/buildSrc/src/main/kotlin/AndroidConfig.kt deleted file mode 100644 index ba608c7e5..000000000 --- a/buildSrc/src/main/kotlin/AndroidConfig.kt +++ /dev/null @@ -1,6 +0,0 @@ -object AndroidConfig { - const val compileSdk = 34 - const val minSdk = 26 - const val targetSdk = 34 - const val ndk = "26.1.10909125" -} diff --git a/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt b/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt deleted file mode 100644 index a641ef5c5..000000000 --- a/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.tasks.TaskProvider -import org.gradle.kotlin.dsl.TaskContainerScope - -private val emptyResourcesElement = "\\s*|".toRegex() - -fun TaskContainerScope.registerLocalesConfigTask(project: Project): TaskProvider { - return with(project) { - register("generateLocalesConfig") { - val languages = fileTree("$projectDir/src/commonMain/resources/MR/") - .matching { include("**/strings.xml") } - .filterNot { it.readText().contains(emptyResourcesElement) } - .map { - it.parentFile.name - .replace("base", "en") - .replace("-r", "-") - .replace("+", "-") - .takeIf(String::isNotBlank) ?: "en" - } - .sorted() - .joinToString(separator = "\n") { - " " - } - - val content = """ - - -$languages - - """.trimIndent() - - val localeFile = file("$projectDir/src/androidMain/res/xml/locales_config.xml") - localeFile.parentFile.mkdirs() - localeFile.writeText(content) - } - } -} - diff --git a/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts b/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts new file mode 100644 index 000000000..eda5fb22e --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts @@ -0,0 +1,11 @@ +import mihon.buildlogic.AndroidConfig +import mihon.buildlogic.configureCompose + +plugins { + id("com.android.application") + kotlin("android") +} + +android { + configureCompose(this) +} diff --git a/buildSrc/src/main/kotlin/mihon.android.application.gradle.kts b/buildSrc/src/main/kotlin/mihon.android.application.gradle.kts new file mode 100644 index 000000000..3c4c00ac3 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.android.application.gradle.kts @@ -0,0 +1,17 @@ +import mihon.buildlogic.AndroidConfig +import mihon.buildlogic.configureAndroid +import mihon.buildlogic.configureTest + +plugins { + id("mihon.code.detekt") + id("com.android.application") + kotlin("android") +} + +android { + defaultConfig { + targetSdk = AndroidConfig.TARGET_SDK + } + configureAndroid(this) + configureTest() +} diff --git a/buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts b/buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts new file mode 100644 index 000000000..b857c4343 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts @@ -0,0 +1,13 @@ +import mihon.buildlogic.configureAndroid +import mihon.buildlogic.configureTest + +plugins { + id("mihon.code.detekt") + id("com.android.test") + kotlin("android") +} + +android { + configureAndroid(this) + configureTest() +} diff --git a/buildSrc/src/main/kotlin/detekt.gradle.kts b/buildSrc/src/main/kotlin/mihon.code.detekt.gradle.kts similarity index 100% rename from buildSrc/src/main/kotlin/detekt.gradle.kts rename to buildSrc/src/main/kotlin/mihon.code.detekt.gradle.kts diff --git a/buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts b/buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts new file mode 100644 index 000000000..9f39d7737 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts @@ -0,0 +1,10 @@ +import mihon.buildlogic.configureCompose + +plugins { + id("mihon.code.detekt") + id("com.android.library") +} + +android { + configureCompose(this) +} diff --git a/buildSrc/src/main/kotlin/mihon.library.gradle.kts b/buildSrc/src/main/kotlin/mihon.library.gradle.kts new file mode 100644 index 000000000..0ea496172 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.library.gradle.kts @@ -0,0 +1,12 @@ +import mihon.buildlogic.configureAndroid +import mihon.buildlogic.configureTest + +plugins { + id("mihon.code.detekt") + id("com.android.library") +} + +android { + configureAndroid(this) + configureTest() +} diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt new file mode 100644 index 000000000..afea120de --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt @@ -0,0 +1,11 @@ +package mihon.buildlogic + +import org.gradle.api.JavaVersion as GradleJavaVersion + +object AndroidConfig { + const val COMPILE_SDK = 34 + const val TARGET_SDK = 34 + const val MIN_SDK = 26 + const val NDK = "26.1.10909125" + val JavaVersion = GradleJavaVersion.VERSION_17 +} diff --git a/buildSrc/src/main/kotlin/Commands.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/Commands.kt similarity index 90% rename from buildSrc/src/main/kotlin/Commands.kt rename to buildSrc/src/main/kotlin/mihon/buildlogic/Commands.kt index 5592c4eef..faa75e2b4 100644 --- a/buildSrc/src/main/kotlin/Commands.kt +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/Commands.kt @@ -1,3 +1,5 @@ +package mihon.buildlogic + import org.gradle.api.Project import java.io.ByteArrayOutputStream import java.time.LocalDateTime @@ -23,9 +25,9 @@ fun Project.getBuildTime(): String { return LocalDateTime.now(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER) } -fun Project.runCommand(command: String): String { +private fun Project.runCommand(command: String): String { val byteOut = ByteArrayOutputStream() - project.exec { + exec { commandLine = command.split(" ") standardOutput = byteOut } diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt new file mode 100644 index 000000000..874e556a2 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt @@ -0,0 +1,112 @@ +package mihon.buildlogic + +import com.android.build.api.dsl.CommonExtension +import org.gradle.accessors.dm.LibrariesForAndroidx +import org.gradle.accessors.dm.LibrariesForCompose +import org.gradle.accessors.dm.LibrariesForKotlinx +import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.Project +import org.gradle.api.tasks.testing.Test +import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +val Project.androidx get() = the() +val Project.compose get() = the() +val Project.kotlinx get() = the() +val Project.libs get() = the() + +internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, *, *, *>) { + commonExtension.apply { + compileSdk = AndroidConfig.COMPILE_SDK + + defaultConfig { + minSdk = AndroidConfig.MIN_SDK + ndk { + version = AndroidConfig.NDK + } + } + + compileOptions { + sourceCompatibility = AndroidConfig.JavaVersion + targetCompatibility = AndroidConfig.JavaVersion + isCoreLibraryDesugaringEnabled = true + } + } + + tasks.withType().configureEach { + kotlinOptions { + jvmTarget = AndroidConfig.JavaVersion.toString() + // freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" + // freeCompilerArgs += "-Xcontext-receivers" + + // Treat all Kotlin warnings as errors (disabled by default) + // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties + // val warningsAsErrors: String? by project + // allWarningsAsErrors = warningsAsErrors.toBoolean() + } + } + + dependencies { + "coreLibraryDesugaring"(libs.desugar) + } +} + +internal fun Project.configureCompose(commonExtension: CommonExtension<*, *, *, *, *, *>) { + commonExtension.apply { + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = compose.versions.compiler.get() + } + + dependencies { + "implementation"(platform(compose.bom)) + } + } + + tasks.withType().configureEach { + kotlinOptions { + freeCompilerArgs += buildComposeMetricsParameters() + + // Enable experimental compiler opts + // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 + freeCompilerArgs += listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", + ) + } + } +} + +private fun Project.buildComposeMetricsParameters(): List { + val rootProjectDir = rootProject.layout.buildDirectory.asFile.get() + val relativePath = projectDir.relativeTo(rootDir) + + val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean() + val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean() + + return listOfNotNull( + ("metricsDestination" to "compose-metrics").takeIf { enableMetrics }, + ("reportsDestination" to "compose-reports").takeIf { enableReports }, + ).flatMap { (flag, dirName) -> + val buildDirPath = rootProjectDir.resolve(dirName).resolve(relativePath).absolutePath + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:$flag=$buildDirPath" + ) + } +} + +internal fun Project.configureTest() { + tasks.withType { + useJUnitPlatform() + testLogging { + events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) + } + } +} diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt new file mode 100644 index 000000000..961a3b751 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt @@ -0,0 +1,37 @@ +package mihon.buildlogic.tasks + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider + +private val emptyResourcesElement = "\\s*|".toRegex() + +fun Project.getLocalesConfigTask(): TaskProvider { + return tasks.register("generateLocalesConfig") { + val locales = fileTree("$projectDir/src/commonMain/resources/MR/") + .matching { include("**/strings.xml") } + .filterNot { it.readText().contains(emptyResourcesElement) } + .map { + it.parentFile.name + .replace("base", "en") + .replace("-r", "-") + .replace("+", "-") + .takeIf(String::isNotBlank) ?: "en" + } + .sorted() + .joinToString("\n") { "| " } + + val content = """ + | + | + $locales + | + """.trimMargin() + + file("$projectDir/src/androidMain/res/xml/locales_config.xml").apply { + parentFile.mkdirs() + writeText(content) + } + } +} + diff --git a/core-metadata/build.gradle.kts b/core-metadata/build.gradle.kts index 5c8605879..dfb3c1943 100644 --- a/core-metadata/build.gradle.kts +++ b/core-metadata/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.library") + id("mihon.library") kotlin("android") kotlin("plugin.serialization") } diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index aaf0f794b..e31015dc4 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.library") + id("mihon.library") kotlin("android") kotlin("plugin.serialization") } diff --git a/data/build.gradle.kts b/data/build.gradle.kts index f4ce0d039..e3f9d9004 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -1,8 +1,8 @@ plugins { - id("com.android.library") + id("mihon.library") + id("app.cash.sqldelight") kotlin("android") kotlin("plugin.serialization") - id("app.cash.sqldelight") } android { diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 63f2b9df9..c49c5bc51 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.library") + id("mihon.library") kotlin("android") kotlin("plugin.serialization") } diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index 9ed5a6cbf..c782eac0f 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -1,7 +1,10 @@ +import mihon.buildlogic.tasks.getLocalesConfigTask +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { - kotlin("multiplatform") - id("com.android.library") + id("mihon.library") id("dev.icerock.mobile.multiplatform-resources") + kotlin("multiplatform") } kotlin { @@ -41,12 +44,12 @@ multiplatformResources { } tasks { - val localesConfigTask = registerLocalesConfigTask(project) + val localesConfigTask = project.getLocalesConfigTask() preBuild { dependsOn(localesConfigTask) } - withType { + withType { kotlinOptions.freeCompilerArgs += listOf( "-Xexpect-actual-classes", ) diff --git a/macrobenchmark/build.gradle.kts b/macrobenchmark/build.gradle.kts index e8a41c403..ec2d049a5 100644 --- a/macrobenchmark/build.gradle.kts +++ b/macrobenchmark/build.gradle.kts @@ -1,6 +1,5 @@ plugins { - id("com.android.test") - kotlin("android") + id("mihon.benchmark") } android { diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index e30c5869c..c7b64ad42 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("com.android.library") + id("mihon.library") + id("mihon.library.compose") kotlin("android") } @@ -10,14 +11,6 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } } dependencies { @@ -25,7 +18,6 @@ dependencies { api(projects.i18n) // Compose - implementation(platform(compose.bom)) implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) diff --git a/presentation-widget/build.gradle.kts b/presentation-widget/build.gradle.kts index 896979d98..292b67240 100644 --- a/presentation-widget/build.gradle.kts +++ b/presentation-widget/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("com.android.library") + id("mihon.library") + id("mihon.library.compose") kotlin("android") } @@ -10,14 +11,6 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } } dependencies { diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index 209136e48..ae28caa14 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -1,7 +1,7 @@ plugins { + id("mihon.library") kotlin("multiplatform") kotlin("plugin.serialization") - id("com.android.library") } kotlin { diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index 46de484ce..b63ae8fab 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -1,6 +1,6 @@ plugins { + id("mihon.library") kotlin("multiplatform") - id("com.android.library") } kotlin {