Skip to content

Commit 1feb659

Browse files
committed
Add a Metro DSL in the Gradle plugin
This DSL allows consumer projects to enable Metro. Fixes #109
1 parent e2f7f9e commit 1feb659

File tree

5 files changed

+104
-35
lines changed

5 files changed

+104
-35
lines changed

gradle-plugin/api/gradle-plugin.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public class software/amazon/app/platform/gradle/AppPlatformExtension {
44
public final fun addPublicModuleDependencies (Z)V
55
public final fun enableComposeUi (Z)V
66
public final fun enableKotlinInject (Z)V
7+
public final fun enableMetro (Z)V
78
public final fun enableModuleStructure (Z)V
89
public final fun enableMoleculePresenters (Z)V
910
}

gradle-plugin/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ dependencies {
6262
// are guarded by checks when the plugin is applied.
6363
compileOnly libs.kotlin.multiplatform.gradle.plugin
6464

65+
// compileOnly to not force this dependency.
66+
compileOnly libs.metro.gradle.plugin
67+
6568
lintChecks libs.androidx.lint.gradle
6669
}
6770

gradle-plugin/src/main/kotlin/software/amazon/app/platform/gradle/AppPlatformExtension.kt

Lines changed: 96 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import org.gradle.api.artifacts.dsl.DependencyHandler
1313
import org.gradle.api.model.ObjectFactory
1414
import org.gradle.api.provider.Property
1515
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
16+
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
1617
import software.amazon.app.platform.gradle.ModuleStructurePlugin.Companion.testingSourceSets
1718

1819
/**
1920
* The extension to configure the App Platform. Following options are available:
2021
* ```
2122
* appPlatform {
2223
* enableKotlinInject true // false is the default
24+
* enableMetro true // false is the default
2325
*
2426
* enableMoleculePresenters true // false is the default
2527
* enableModuleStructure true // false is the default
@@ -52,6 +54,24 @@ constructor(objects: ObjectFactory, private val project: Project) {
5254

5355
internal fun isKotlinInjectEnabled(): Property<Boolean> = enableKotlinInject
5456

57+
private val enableMetro: Property<Boolean> =
58+
objects.property(Boolean::class.java).convention(false)
59+
60+
/** Adds Metro as dependency. */
61+
public fun enableMetro(enabled: Boolean) {
62+
if (enabled == enableMetro.get()) return
63+
64+
enableMetro.set(enabled)
65+
enableMetro.disallowChanges()
66+
67+
if (enabled) {
68+
addPublicModuleDependencies(true)
69+
project.enableMetro()
70+
}
71+
}
72+
73+
internal fun isMetroEnabled(): Property<Boolean> = enableMetro
74+
5575
private val enableMoleculePresenters: Property<Boolean> =
5676
objects.property(Boolean::class.java).convention(false)
5777

@@ -185,41 +205,8 @@ private fun Project.enableKotlinInject() {
185205
}
186206

187207
kmpExtension.targets.configureEach { target ->
188-
// Skip the metadata, because we want to run KSP only for the concrete platforms.
189-
if (target.name != "metadata") {
190-
target.compilations.configureEach { compilation ->
191-
fun configExists(name: String): Boolean = configurations.any { it.name == name }
192-
193-
// The implementationConfigurationName name is
194-
// 'iosSimulatorArm64CompilationImplementation', 'wasmJsTestCompileClasspath' or
195-
// 'desktopCompileClasspath'.
196-
//
197-
// E.g. 'desktopCompileClasspath' with give use 'kspDesktop'
198-
var configName =
199-
"ksp" +
200-
compilation.implementationConfigurationName
201-
.substringBefore("Compilation")
202-
.capitalize()
203-
204-
if (!configExists(configName) && target.platformType == KotlinPlatformType.androidJvm) {
205-
// Android has different naming for some reason.
206-
//
207-
// E.g. for instrumentation tests 'kspAndroidDebugAndroidTest' should actually be
208-
// 'kspAndroidAndroidTestDebug', but we will use 'kspAndroidAndroidTest'.
209-
//
210-
// For unit tests 'kspAndroidDebugUnitTest' should actually be 'kspAndroidTestDebug',
211-
// but we will use 'kspAndroidTest'.
212-
when {
213-
configName.endsWith("AndroidTest") -> configName = "kspAndroidAndroidTest"
214-
configName.endsWith("UnitTest") -> configName = "kspAndroidTest"
215-
}
216-
}
217-
218-
// Check again if the config exists.
219-
if (configExists(configName)) {
220-
dependencies.addKspProcessorDependencies(configName)
221-
}
222-
}
208+
addKspDependenciesWhenConfigExists(target) { configName ->
209+
dependencies.addKspProcessorDependencies(configName)
223210
}
224211
}
225212
}
@@ -250,6 +237,41 @@ private fun Project.enableKotlinInject() {
250237
}
251238
}
252239

240+
private fun Project.enableMetro() {
241+
plugins.apply(PluginIds.METRO)
242+
243+
// Enable KSP for our custom extensions.
244+
plugins.apply(PluginIds.KSP)
245+
246+
fun DependencyHandler.addKspProcessorDependencies(kspConfigurationName: String) {
247+
add(
248+
kspConfigurationName,
249+
"$APP_PLATFORM_GROUP:metro-contribute-impl-code-generators:$APP_PLATFORM_VERSION",
250+
)
251+
}
252+
253+
plugins.withId(PluginIds.KOTLIN_MULTIPLATFORM) {
254+
kmpExtension.sourceSets.getByName("commonMain").dependencies {
255+
implementation("$APP_PLATFORM_GROUP:di-common-public:$APP_PLATFORM_VERSION")
256+
implementation("$APP_PLATFORM_GROUP:metro-public:$APP_PLATFORM_VERSION")
257+
258+
kmpExtension.targets.configureEach { target ->
259+
addKspDependenciesWhenConfigExists(target) { configName ->
260+
dependencies.addKspProcessorDependencies(configName)
261+
}
262+
}
263+
}
264+
}
265+
266+
plugins.withIds(PluginIds.KOTLIN_ANDROID, PluginIds.KOTLIN_JVM) {
267+
dependencies.add("implementation", "$APP_PLATFORM_GROUP:di-common-public:$APP_PLATFORM_VERSION")
268+
269+
dependencies.add("implementation", "$APP_PLATFORM_GROUP:metro-public:$APP_PLATFORM_VERSION")
270+
271+
dependencies.addKspProcessorDependencies("ksp")
272+
}
273+
}
274+
253275
private fun Project.enableMoleculePresenters() {
254276
plugins.apply(PluginIds.COMPOSE_COMPILER)
255277

@@ -335,3 +357,42 @@ private fun Project.enableComposeUi() {
335357
}
336358
}
337359
}
360+
361+
private fun Project.addKspDependenciesWhenConfigExists(
362+
target: KotlinTarget,
363+
block: (String) -> Unit,
364+
) {
365+
if (target.name != "metadata") {
366+
target.compilations.configureEach { compilation ->
367+
fun configExists(name: String): Boolean = configurations.any { it.name == name }
368+
369+
// The implementationConfigurationName name is
370+
// 'iosSimulatorArm64CompilationImplementation', 'wasmJsTestCompileClasspath' or
371+
// 'desktopCompileClasspath'.
372+
//
373+
// E.g. 'desktopCompileClasspath' with give use 'kspDesktop'
374+
var configName =
375+
"ksp" +
376+
compilation.implementationConfigurationName.substringBefore("Compilation").capitalize()
377+
378+
if (!configExists(configName) && target.platformType == KotlinPlatformType.androidJvm) {
379+
// Android has different naming for some reason.
380+
//
381+
// E.g. for instrumentation tests 'kspAndroidDebugAndroidTest' should actually be
382+
// 'kspAndroidAndroidTestDebug', but we will use 'kspAndroidAndroidTest'.
383+
//
384+
// For unit tests 'kspAndroidDebugUnitTest' should actually be 'kspAndroidTestDebug',
385+
// but we will use 'kspAndroidTest'.
386+
when {
387+
configName.endsWith("AndroidTest") -> configName = "kspAndroidAndroidTest"
388+
configName.endsWith("UnitTest") -> configName = "kspAndroidTest"
389+
}
390+
}
391+
392+
// Check again if the config exists.
393+
if (configExists(configName)) {
394+
block(configName)
395+
}
396+
}
397+
}
398+
}

gradle-plugin/src/main/kotlin/software/amazon/app/platform/gradle/AppPlatformPlugin.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ public open class AppPlatformPlugin : Plugin<Project> {
9292
if (appPlatform.isKotlinInjectEnabled().get()) {
9393
add("$APP_PLATFORM_GROUP:kotlin-inject-impl:$APP_PLATFORM_VERSION")
9494
}
95+
if (appPlatform.isMetroEnabled().get()) {
96+
add("$APP_PLATFORM_GROUP:metro-impl:$APP_PLATFORM_VERSION")
97+
}
9598
}
9699

97100
plugins.withId(PluginIds.KOTLIN_MULTIPLATFORM) {

gradle-plugin/src/main/kotlin/software/amazon/app/platform/gradle/PluginIds.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ internal object PluginIds {
1010
const val KOTLIN_JVM = "org.jetbrains.kotlin.jvm"
1111
const val KOTLIN_ANDROID = "org.jetbrains.kotlin.android"
1212
const val KSP = "com.google.devtools.ksp"
13+
const val METRO = "dev.zacsweers.metro"
1314
}

0 commit comments

Comments
 (0)