Cleanup and rework build logic (#3113)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.samWithReceiver)
|
||||
alias(libs.plugins.spotless)
|
||||
`java-gradle-plugin`
|
||||
}
|
||||
|
||||
// Configuration should be synced with [/gradle/build-logic/src/main/kotlin/PluginSpotless.kt]
|
||||
val ktlintVersion = libs.ktlint.bom.get().version
|
||||
val editorConfigFile = rootProject.file("../../.editorconfig")
|
||||
spotless {
|
||||
kotlin {
|
||||
target("src/**/*.kt")
|
||||
ktlint(ktlintVersion).setEditorConfigPath(editorConfigFile)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
target("*.kts")
|
||||
ktlint(ktlintVersion).setEditorConfigPath(editorConfigFile)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(gradleKotlinDsl())
|
||||
compileOnly(libs.android.gradle)
|
||||
compileOnly(libs.kotlin.compose.compiler.gradle)
|
||||
compileOnly(libs.kotlin.gradle)
|
||||
implementation(libs.spotless.gradle)
|
||||
implementation(libs.tapmoc.gradle)
|
||||
|
||||
// These allow us to reference the dependency catalog inside our compiled plugins
|
||||
compileOnly(files(libs::class.java.superclass.protectionDomain.codeSource.location))
|
||||
compileOnly(files(mihonx::class.java.superclass.protectionDomain.codeSource.location))
|
||||
}
|
||||
|
||||
samWithReceiver {
|
||||
annotation("org.gradle.api.HasImplicitReceiver")
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("android-application") {
|
||||
id = mihonx.plugins.android.application.get().pluginId
|
||||
implementationClass = "PluginAndroidApplication"
|
||||
}
|
||||
register("android-base") {
|
||||
id = mihonx.plugins.android.base.get().pluginId
|
||||
implementationClass = "PluginAndroidBase"
|
||||
}
|
||||
register("android-library") {
|
||||
id = mihonx.plugins.android.library.get().pluginId
|
||||
implementationClass = "PluginAndroidLibrary"
|
||||
}
|
||||
register("android-test") {
|
||||
id = mihonx.plugins.android.test.get().pluginId
|
||||
implementationClass = "PluginAndroidTest"
|
||||
}
|
||||
register("compose-android") {
|
||||
id = mihonx.plugins.compose.get().pluginId
|
||||
implementationClass = "PluginComposeAndroid"
|
||||
}
|
||||
register("kotlin-multiplatform") {
|
||||
id = mihonx.plugins.kotlin.multiplatform.get().pluginId
|
||||
implementationClass = "PluginKotlinMultiplatform"
|
||||
}
|
||||
register("spotless") {
|
||||
id = mihonx.plugins.spotless.get().pluginId
|
||||
implementationClass = "PluginSpotless"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
dependencyResolutionManagement {
|
||||
@Suppress("UnstableApiUsage")
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../libs.versions.toml"))
|
||||
}
|
||||
create("mihonx") {
|
||||
from(files("../mihon.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "build-logic"
|
||||
@@ -0,0 +1,16 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidApplication : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(mihonx.plugins.android.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import mihon.gradle.configurations.configureAndroid
|
||||
import mihon.gradle.configurations.configureKotlin
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.configureTest
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidBase : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
configureKotlin()
|
||||
configureTest()
|
||||
configureAndroid()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidLibrary : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(mihonx.plugins.android.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginAndroidTest : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.test)
|
||||
alias(mihonx.plugins.android.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.android
|
||||
import mihon.gradle.extensions.api
|
||||
import mihon.gradle.extensions.debugApi
|
||||
import mihon.gradle.extensions.implementation
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginComposeAndroid : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.compose.compiler)
|
||||
}
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
|
||||
// Compose @Preview tooling
|
||||
api(libs.androidx.compose.uiToolingPreview)
|
||||
debugApi(libs.androidx.compose.uiTooling)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import mihon.gradle.configurations.configureAndroid
|
||||
import mihon.gradle.configurations.configureKotlin
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.configureTest
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginKotlinMultiplatform : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
}
|
||||
|
||||
configureKotlin()
|
||||
configureTest()
|
||||
configureAndroid()
|
||||
|
||||
kotlin {
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
applyDefaultHierarchyTemplate()
|
||||
|
||||
androidTarget()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.kotlin(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
extensions.configure(block)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import mihon.gradle.extensions.alias
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.plugins
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
@Suppress("UNUSED")
|
||||
class PluginSpotless : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = with(target) {
|
||||
plugins {
|
||||
alias(libs.plugins.spotless)
|
||||
}
|
||||
|
||||
// Configuration should be synced with [/gradle/build-logic/build.gradle.kts]
|
||||
val ktlintVersion = libs.ktlint.bom.get().version
|
||||
spotless {
|
||||
kotlin {
|
||||
target("src/**/*.kt")
|
||||
ktlint(ktlintVersion)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
|
||||
kotlinGradle {
|
||||
target("*.kts")
|
||||
ktlint(ktlintVersion)
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
|
||||
format("xml") {
|
||||
target("src/**/*.xml")
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.spotless(block: SpotlessExtension.() -> Unit) {
|
||||
extensions.configure(block)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package mihon.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
interface BuildConfig {
|
||||
val includeTelemetry: Boolean
|
||||
val enableUpdater: Boolean
|
||||
val enableCodeShrink: Boolean
|
||||
val includeDependencyInfo: Boolean
|
||||
}
|
||||
|
||||
val Project.Config: BuildConfig get() = object : BuildConfig {
|
||||
override val includeTelemetry: Boolean = project.hasProperty("include-telemetry")
|
||||
override val enableUpdater: Boolean = project.hasProperty("enable-updater")
|
||||
override val enableCodeShrink: Boolean = !project.hasProperty("disable-code-shrink")
|
||||
override val includeDependencyInfo: Boolean = project.hasProperty("include-dependency-info")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package mihon.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
// Git is needed in your system PATH for these commands to work.
|
||||
// If it's not installed, you can return a random value as a workaround
|
||||
fun Project.getLatestCommitCount(): String {
|
||||
return exec("git rev-list --count HEAD")
|
||||
// return "1"
|
||||
}
|
||||
|
||||
fun Project.getLatestCommitSha(): String {
|
||||
return exec("git rev-parse --short HEAD")
|
||||
// return "1"
|
||||
}
|
||||
|
||||
private val BUILD_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
|
||||
/**
|
||||
* @param useLatestCommitTime If `true`, the build time is based on the timestamp of the last Git commit;
|
||||
* otherwise, the current time is used. Both are in UTC.
|
||||
* @return A formatted string representing the build time. The format used is defined by [BUILD_TIME_FORMATTER].
|
||||
*/
|
||||
fun Project.getBuildTime(useLatestCommitTime: Boolean): String {
|
||||
return if (useLatestCommitTime) {
|
||||
val epoch = exec("git log -1 --format=%ct").toLong()
|
||||
Instant.ofEpochSecond(epoch).atOffset(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER)
|
||||
} else {
|
||||
LocalDateTime.now(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.exec(command: String): String {
|
||||
return providers.exec {
|
||||
commandLine = command.split(" ")
|
||||
}
|
||||
.standardOutput
|
||||
.asText
|
||||
.get()
|
||||
.trim()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mihon.gradle.configurations
|
||||
|
||||
import com.android.build.api.dsl.ApplicationDefaultConfig
|
||||
import mihon.gradle.extensions.android
|
||||
import mihon.gradle.extensions.coreLibraryDesugaring
|
||||
import mihon.gradle.extensions.libs
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
fun Project.configureAndroid() {
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = mihonx.versions.android.sdk.min.get().toInt()
|
||||
if (this is ApplicationDefaultConfig) {
|
||||
targetSdk = mihonx.versions.android.sdk.target.get().toInt()
|
||||
}
|
||||
|
||||
ndkVersion = mihonx.versions.android.ndk.get()
|
||||
}
|
||||
|
||||
compileSdk = mihonx.versions.android.sdk.compile.get().toInt()
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.android.desugar)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package mihon.gradle.configurations
|
||||
|
||||
import mihon.gradle.extensions.mihonx
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.dsl.HasConfigurableKotlinCompilerOptions
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions
|
||||
import tapmoc.configureJavaCompatibility
|
||||
|
||||
fun Project.configureKotlin() {
|
||||
configureJavaCompatibility(mihonx.versions.java.get().toInt())
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs.add("-Xcontext-parameters")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.kotlin(block: KotlinBaseExtension.() -> Unit) {
|
||||
extensions.configure(block)
|
||||
}
|
||||
|
||||
private fun KotlinBaseExtension.compilerOptions(block: KotlinCommonCompilerOptions.() -> Unit) {
|
||||
if (this is HasConfigurableKotlinCompilerOptions<*>) compilerOptions(block)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mihon.gradle.extensions
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ExternalModuleDependencyBundle
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||
|
||||
fun DependencyHandlerScope.api(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("api", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.coreLibraryDesugaring(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("coreLibraryDesugaring", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.debugApi(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("debugApi", dependencyNotation)
|
||||
}
|
||||
|
||||
@JvmName("implementationBundle")
|
||||
fun DependencyHandlerScope.implementation(dependencyNotation: Provider<ExternalModuleDependencyBundle>) {
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.implementation(dependencyNotation: Provider<MinimalExternalModuleDependency>) {
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.implementation(dependencyNotation: Project) {
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package mihon.gradle.extensions
|
||||
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.plugin.use.PluginDependency
|
||||
|
||||
fun PluginManager.alias(notation: Provider<PluginDependency>) {
|
||||
apply(notation.get().pluginId)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package mihon.gradle.extensions
|
||||
|
||||
import com.android.build.api.dsl.CommonExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.gradle.accessors.dm.LibrariesForMihonx
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
internal val Project.libs get() = the<LibrariesForLibs>()
|
||||
internal val Project.mihonx get() = the<LibrariesForMihonx>()
|
||||
|
||||
internal fun Project.plugins(block: PluginManager.() -> Unit) {
|
||||
pluginManager.apply(block)
|
||||
}
|
||||
|
||||
internal fun Project.android(block: CommonExtension<*, *, *, *, *, *>.() -> Unit) {
|
||||
extensions.configure<BaseExtension> {
|
||||
if (this is CommonExtension<*, *, *, *, *, *>) apply(block)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureTest() {
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package mihon.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class GenerateLocalesConfigTask : DefaultTask() {
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun action() {
|
||||
val locales = project.fileTree("src/commonMain/moko-resources") {
|
||||
matching { include("**/strings.xml") }
|
||||
}
|
||||
.asSequence()
|
||||
.filterNot { it.readText().contains(emptyResourcesElement) }
|
||||
.map {
|
||||
it.parentFile.name
|
||||
.replace("base", "en")
|
||||
.replace("-r", "-")
|
||||
.replace("+", "-")
|
||||
}
|
||||
.distinct()
|
||||
.sorted()
|
||||
.joinToString("\n") { "| <locale android:name=\"$it\"/>" }
|
||||
|
||||
val content = """
|
||||
|<?xml version="1.0" encoding="utf-8"?>
|
||||
|<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
$locales
|
||||
|</locale-config>
|
||||
""".trimMargin()
|
||||
|
||||
outputDir.get().file("xml/locales_config.xml").asFile.apply {
|
||||
parentFile.mkdirs()
|
||||
writeText(content)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val emptyResourcesElement = "<resources>\\s*</resources>|<resources\\s*/>".toRegex()
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package mihon.gradle.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class ReplaceShortcutsPlaceholderTask : DefaultTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val applicationId: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val shortcutsFile: RegularFileProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@TaskAction
|
||||
fun action() {
|
||||
val content = shortcutsFile.asFile.get()
|
||||
.readText()
|
||||
.replace($$"${applicationId}", applicationId.get())
|
||||
|
||||
outputDir.get().file("xml/shortcuts.xml").asFile.apply {
|
||||
parentFile.mkdirs()
|
||||
writeText(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user