From bc1b9ae9b3a7c521f5f854a9c7a1f5fab902a817 Mon Sep 17 00:00:00 2001 From: Stepan Goncharov Date: Sun, 7 May 2023 00:58:01 -0700 Subject: [PATCH] POC#1 Enhanced version of catalog API's - Standardized names generator - Strict versions enforced - Bundle API automatically adds libs - new deps api's for version catalog synthetic accessors --- .../extensions/android-util/build.gradle.kts | 28 +++--- .../core/mvvm/library/build.gradle.kts | 3 +- .../characters/detail/impl/build.gradle.kts | 3 +- application/settings.gradle.kts | 41 +++++++++ .../kotlin/tools/forma/depgen/DepgenPlugin.kt | 8 -- .../deps-core/src/main/java/dependencies.kt | 92 ++++++++++++------- plugins/validation/build.gradle.kts | 2 +- 7 files changed, 120 insertions(+), 57 deletions(-) diff --git a/application/common/extensions/android-util/build.gradle.kts b/application/common/extensions/android-util/build.gradle.kts index ee340c18..4e246afd 100644 --- a/application/common/extensions/android-util/build.gradle.kts +++ b/application/common/extensions/android-util/build.gradle.kts @@ -1,18 +1,16 @@ androidUtil( packageName = "com.stepango.blockme.common.extensions.android.util", owner = Teams.core, - dependencies = deps( - androidx.constraintlayout, - androidx.core_ktx, - androidx.constraintlayout, - androidx.fragment, - androidx.viewmodel, - androidx.navigation, - androidx.recyclerview, - androidx.paging, - google.material, - io.coil - ) + deps( - target(":common:placeholder:res") - ) -) \ No newline at end of file + dependencies = + deps( + androidx.constraintlayout, + androidx.core_ktx, + androidx.constraintlayout, + androidx.fragment, + androidx.viewmodel, + androidx.navigation, + androidx.recyclerview, + androidx.paging, + google.material, + ) + deps(libs.bundles.coil) + deps(target(":common:placeholder:res")) +) diff --git a/application/core/mvvm/library/build.gradle.kts b/application/core/mvvm/library/build.gradle.kts index 488dd5d7..751bbc04 100644 --- a/application/core/mvvm/library/build.gradle.kts +++ b/application/core/mvvm/library/build.gradle.kts @@ -12,7 +12,8 @@ androidLibrary( androidx.paging, dataBinding.runtime, google.dagger, - jakewharton.timber + )+ deps( + libs.jakewharton.timber, ), testDependencies = deps( diff --git a/application/feature/characters/detail/impl/build.gradle.kts b/application/feature/characters/detail/impl/build.gradle.kts index dd8dafcd..0c9ddff0 100644 --- a/application/feature/characters/detail/impl/build.gradle.kts +++ b/application/feature/characters/detail/impl/build.gradle.kts @@ -11,9 +11,10 @@ impl( androidx.paging, google.material, google.dagger, - jakewharton.timber, kotlinx.coroutines_core, viewbinding.viewpropertydelegate + ) + deps( + libs.jakewharton.timber, ) + deps( target(":feature:characters:core:api"), target(":feature:characters:favorite:api"), diff --git a/application/settings.gradle.kts b/application/settings.gradle.kts index a8257a10..dca1c8c7 100644 --- a/application/settings.gradle.kts +++ b/application/settings.gradle.kts @@ -16,6 +16,47 @@ plugins { rootProject.name = "application" +val filteredTokens = listOf("com", "io", "net", "org") +val coilVersion = "2.1.0" + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + addLibrary("com.jakewharton.timber:timber:4.7.1") + addBundle("coil", "io.coil-kt:coil:$coilVersion", "io.coil-kt:coil-base:$coilVersion") + } + } +} + +fun VersionCatalogBuilder.addBundle( + name: String, + vararg groupArtifactVersion: String, + nameGenerator: (String) -> String = ::defaultNameGenerator +): String { + bundle(name, groupArtifactVersion.map { addLibrary(it, nameGenerator) }) + return name +} + +fun VersionCatalogBuilder.addLibrary( + groupArtifactVersion: String, + nameGenerator: (String) -> String = ::defaultNameGenerator +): String { + val (group, artifact, version) = groupArtifactVersion.split(":") + return nameGenerator(groupArtifactVersion).apply { + library(this, group, artifact).version { strictly(version) } + } +} + +fun defaultNameGenerator(groupArtifactVersion: String) = + groupArtifactVersion + .split(":") + .dropLast(1) + .fold(emptyList()) { acc, s -> acc + s.split(".", "-") } + .filter { it !in filteredTokens } + .distinct() + .joinToString(".") + .also { println("Generated name $it for $groupArtifactVersion") } + // refer to this issue https://github.com/gradle/gradle/issues/18536 // tools.forma.dependencies are applied in buildscript {} block includeBuild("../build-dependencies") diff --git a/depgen/plugin/src/main/kotlin/tools/forma/depgen/DepgenPlugin.kt b/depgen/plugin/src/main/kotlin/tools/forma/depgen/DepgenPlugin.kt index 251a5d1d..7d30273c 100644 --- a/depgen/plugin/src/main/kotlin/tools/forma/depgen/DepgenPlugin.kt +++ b/depgen/plugin/src/main/kotlin/tools/forma/depgen/DepgenPlugin.kt @@ -9,13 +9,5 @@ import org.gradle.kotlin.dsl.embeddedKotlinVersion */ class DepgenPlugin : Plugin { override fun apply(settings: Settings) { - settings.dependencyResolutionManagement { - versionCatalogs { - create("libs") { - // For test purposes only - library("kotlin-stdlib", "org.jetbrains.kotlin:kotlin-stdlib:$embeddedKotlinVersion") - } - } - } } } diff --git a/plugins/deps-core/src/main/java/dependencies.kt b/plugins/deps-core/src/main/java/dependencies.kt index 686f41dd..7bc4b3e5 100644 --- a/plugins/deps-core/src/main/java/dependencies.kt +++ b/plugins/deps-core/src/main/java/dependencies.kt @@ -1,4 +1,9 @@ +import java.io.File import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ExternalModuleDependencyBundle +import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.provider.Provider import tools.forma.deps.ConfigurationType import tools.forma.deps.DepType import tools.forma.deps.EmptyDependency @@ -15,7 +20,6 @@ import tools.forma.deps.PlatformSpec import tools.forma.deps.TargetDependency import tools.forma.deps.TargetSpec import tools.forma.target.FormaTarget -import java.io.File val DepType.names: List get(): List = filterIsInstance(NameSpec::class.java) @@ -26,20 +30,34 @@ val DepType.targets: List val DepType.files: List get(): List = filterIsInstance(FileSpec::class.java) -infix operator fun FormaDependency.plus(dep: FormaDependency): MixedDependency = MixedDependency( - dependency.names + dep.dependency.names, - dependency.targets + dep.dependency.targets, - dependency.files + dep.dependency.files -) - -inline fun emptyDependency(): T = when(T::class) { - FormaDependency::class -> EmptyDependency as T - NamedDependency::class -> NamedDependency() as T - FileDependency::class -> FileDependency() as T - TargetDependency::class -> TargetDependency() as T - MixedDependency::class -> MixedDependency() as T - else -> throw IllegalArgumentException("Illegal Empty dependency, expected ${T::class.simpleName}") -} +val Provider.dep: NameSpec + get() = with(get()) { NameSpec("$group:$name:$version", Implementation) } + +val Dependency.dep: NameSpec + get() = NameSpec("$group:$name:$version", Implementation) + +val Provider.dep: List + get() = get().map { it.dep } + +infix operator fun FormaDependency.plus(dep: FormaDependency): MixedDependency = + MixedDependency( + dependency.names + dep.dependency.names, + dependency.targets + dep.dependency.targets, + dependency.files + dep.dependency.files + ) + +inline fun emptyDependency(): T = + when (T::class) { + FormaDependency::class -> EmptyDependency as T + NamedDependency::class -> NamedDependency() as T + FileDependency::class -> FileDependency() as T + TargetDependency::class -> TargetDependency() as T + MixedDependency::class -> MixedDependency() as T + else -> + throw IllegalArgumentException( + "Illegal Empty dependency, expected ${T::class.simpleName}" + ) + } fun FormaDependency.forEach( nameAction: (NameSpec) -> Unit = {}, @@ -58,15 +76,14 @@ fun FormaDependency.forEach( } internal fun FormaDependency.hasConfigType(configType: ConfigurationType): Boolean { - dependency.forEach { dep -> - if (dep.config == configType) return true - } + dependency.forEach { dep -> if (dep.config == configType) return true } return false } -fun deps(vararg names: String): NamedDependency = transitiveDeps(names = *names, transitive = false) +fun deps(vararg names: String): NamedDependency = transitiveDeps(names = names, transitive = false) -fun platform(vararg names: String): PlatformDependency = transitivePlatform(*names, transitive = false) +fun platform(vararg names: String): PlatformDependency = + transitivePlatform(*names, transitive = false) fun transitivePlatform(vararg names: String, transitive: Boolean = true): PlatformDependency = PlatformDependency(names.toList().map { PlatformSpec(it, Implementation, transitive) }) @@ -76,32 +93,45 @@ fun transitiveDeps(vararg names: String, transitive: Boolean = true): NamedDepen @Suppress("DeprecatedCallableAddReplaceWith") @Deprecated( - "Deprecated in favor of targets version of this function:\n" + - "deps(target(\":name\"))" + "Deprecated in favor of targets version of this function:\n" + "deps(target(\":name\"))" ) fun deps(vararg projects: Project): TargetDependency = - TargetDependency(projects.toList().map { TargetSpec(it.target, Implementation) }) + TargetDependency(projects.map { TargetSpec(it.target, Implementation) }) fun deps(vararg targets: FormaTarget): TargetDependency = - TargetDependency(targets.toList().map { TargetSpec(it, Implementation) }) + TargetDependency(targets.map { TargetSpec(it, Implementation) }) fun deps(vararg files: File): FileDependency = - FileDependency(files.toList().map { FileSpec(it, Implementation) }) + FileDependency(files.map { FileSpec(it, Implementation) }) fun deps(vararg dependencies: NamedDependency): NamedDependency = dependencies.flatMap { it.names }.let(::NamedDependency) +@Suppress("UNCHECKED_CAST") +inline fun deps(vararg dependencies: Provider): NamedDependency = + when (T::class) { + Dependency::class, + MinimalExternalModuleDependency::class -> + dependencies.map { (it as Provider).dep } + ExternalModuleDependencyBundle::class -> + dependencies.flatMap { (it as Provider).dep } + else -> throw IllegalArgumentException("Unsupported dependency type ${T::class.simpleName}") + }.let(::NamedDependency) + fun deps(vararg dependencies: TargetDependency): TargetDependency = dependencies.flatMap { it.targets }.let(::TargetDependency) fun kapt(vararg names: String): NamedDependency = - NamedDependency(names.toList().map { NameSpec(it, Kapt, true) }) - -val String.dep: NamedDependency get() = deps(this) + NamedDependency(names.map { NameSpec(it, Kapt, true) }) -val String.kapt: NamedDependency get() = kapt(this) +val String.dep: NamedDependency + get() = deps(this) -val Project.target: FormaTarget get() = FormaTarget(this) +val String.kapt: NamedDependency + get() = kapt(this) -fun Project.target(name: String): FormaTarget = FormaTarget(project(":" + name.substring(1).replace(":", "-"))) +val Project.target: FormaTarget + get() = FormaTarget(this) +fun Project.target(name: String): FormaTarget = + FormaTarget(project(":" + name.substring(1).replace(":", "-"))) diff --git a/plugins/validation/build.gradle.kts b/plugins/validation/build.gradle.kts index a451fe7e..a0f4e64d 100644 --- a/plugins/validation/build.gradle.kts +++ b/plugins/validation/build.gradle.kts @@ -8,4 +8,4 @@ version = "0.0.1" dependencies { implementation(project(":target")) -} \ No newline at end of file +}