package expo.modules.plugin import com.android.build.api.dsl.CommonExtension import expo.modules.plugin.text.Colors import expo.modules.plugin.text.withColor import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.VersionCatalog import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.api.plugins.ExtraPropertiesExtension import org.gradle.internal.extensions.core.extra import java.util.Optional import kotlin.jvm.optionals.getOrNull class ExpoRootProjectPlugin : Plugin { override fun apply(rootProject: Project) { val versionCatalogs = rootProject.extensions.getByType(VersionCatalogsExtension::class.java) val libs = versionCatalogs.find("expoLibs") with(rootProject) { defineDefaultProperties(libs) maybeOverrideCmakeVersion() } } } /** * Maybe override the `android.externalNativeBuild.cmake.version` for all subprojects. Set via * `android.cmakeVersion` in **gradle.properties**. */ private fun Project.maybeOverrideCmakeVersion() { val cmakeVersion = (findProperty("android.cmakeVersion") as? String)?.takeIf { it.isNotBlank() } ?: return logger.quiet( "${"[ExpoRootProject]".withColor(Colors.GREEN)} Overriding CMake version: ${cmakeVersion.withColor(Colors.GREEN)}" ) val applyCmakeVersion = { subproject: Project -> val android = subproject.extensions.getByType(CommonExtension::class.java) android.externalNativeBuild.cmake.version = cmakeVersion } subprojects { subproject -> subproject.plugins.withId("com.android.application") { applyCmakeVersion(subproject) } subproject.plugins.withId("com.android.library") { applyCmakeVersion(subproject) } } } fun Project.defineDefaultProperties(versionCatalogs: Optional) { // Android related val buildTools = extra.setIfNotExist("buildToolsVersion") { versionCatalogs.getVersionOrDefault("buildTools", "35.0.0") } val minSdk = extra.setIfNotExist("minSdkVersion") { Integer.parseInt(versionCatalogs.getVersionOrDefault("minSdk", "24")) } val compileSdk = extra.setIfNotExist("compileSdkVersion") { Integer.parseInt(versionCatalogs.getVersionOrDefault("compileSdk", "35")) } val targetSdk = extra.setIfNotExist("targetSdkVersion") { Integer.parseInt(versionCatalogs.getVersionOrDefault("targetSdk", "35")) } val ndk = extra.setIfNotExist("ndkVersion") { versionCatalogs.getVersionOrDefault("ndkVersion", "27.1.12297006") } // Kotlin related val kotlin = extra.setIfNotExist("kotlinVersion") { versionCatalogs.getVersionOrDefault("kotlin", "2.0.21") } val ksp = extra.setIfNotExist("kspVersion") { versionCatalogs.getVersionOrDefault("ksp") { val kotlinVersion = extra.get("kotlinVersion") as String KSPLookup[kotlinVersion]?.let { return@getVersionOrDefault it } if (kotlinVersion >= "2.3.0") { return@getVersionOrDefault latestKspVersion } val minSupported = KSPLookup.keys.min() throw IllegalStateException( """ Kotlin $kotlinVersion is not supported by Expo modules. The minimum supported Kotlin version is $minSupported. Update 'kotlinVersion' in your project's build.gradle to a supported version. Alternatively, you can set 'kspVersion' explicitly in build.gradle to bypass this check, but this is unsupported and may cause build failures. """.trimIndent() ) } } project.logger.quiet( """ ${"[ExpoRootProject]".withColor(Colors.GREEN)} Using the following versions: - buildTools: ${buildTools.withColor(Colors.GREEN)} - minSdk: ${minSdk.withColor(Colors.GREEN)} - compileSdk: ${compileSdk.withColor(Colors.GREEN)} - targetSdk: ${targetSdk.withColor(Colors.GREEN)} - ndk: ${ndk.withColor(Colors.GREEN)} - kotlin: ${kotlin.withColor(Colors.GREEN)} - ksp: ${ksp.withColor(Colors.GREEN)} """.trimIndent() ) } inline fun ExtraPropertiesExtension.setIfNotExist(name: String, value: () -> Any): Any? { if (!has(name)) { set(name, value()) } return get(name) } fun Optional.getVersionOrDefault(name: String, default: String): String { return getOrNull()?.findVersion(name)?.getOrNull()?.requiredVersion ?: default } fun Optional.getVersionOrDefault(name: String, default: () -> String): String { return getOrNull()?.findVersion(name)?.getOrNull()?.requiredVersion ?: default.invoke() }