Skip to content

Commit

Permalink
Assorted improvements to Compose and Kotlin (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
eygraber authored Jul 30, 2023
1 parent 1679ee5 commit 00eec21
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public fun Project.configureKgp(
jvmDistribution: JvmVendorSpec? = null,
allWarningsAsErrors: Boolean = true,
explicitApiMode: ExplicitApiMode = ExplicitApiMode.Disabled,
configureJavaCompatibility: Boolean = true,
configureJavaTargetVersion: Boolean = true,
useK2: Boolean = false,
freeCompilerArgs: List<KotlinFreeCompilerArg> = emptyList(),
vararg optIns: KotlinOptIn
Expand All @@ -36,7 +36,7 @@ public fun Project.configureKgp(
jvmDistribution,
allWarningsAsErrors,
explicitApiMode,
configureJavaCompatibility,
configureJavaTargetVersion,
useK2,
freeCompilerArgs,
*optIns
Expand All @@ -49,12 +49,12 @@ public fun Project.configureKgp(
jvmDistribution: JvmVendorSpec? = null,
allWarningsAsErrors: Boolean = true,
explicitApiMode: ExplicitApiMode = ExplicitApiMode.Disabled,
configureJavaCompatibility: Boolean = true,
configureJavaTargetVersion: Boolean = true,
useK2: Boolean = false,
freeCompilerArgs: List<KotlinFreeCompilerArg> = emptyList(),
vararg optIns: KotlinOptIn
) {
if(configureJavaCompatibility && jvmTargetVersion != null) {
if(configureJavaTargetVersion && jvmTargetVersion != null) {
tasks.withType(JavaCompile::class.java) {
sourceCompatibility = jvmTargetVersion.target
targetCompatibility = jvmTargetVersion.target
Expand Down
5 changes: 5 additions & 0 deletions conventions-plugin/src/main/kotlin/ProjectExtensions.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@file:Suppress("NOTHING_TO_INLINE")

import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.android.build.gradle.AppExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.vanniktech.maven.publish.MavenPublishBaseExtension
Expand All @@ -17,6 +18,10 @@ internal inline fun Project.android(action: Action<BaseExtension>) {
action.execute(extensions.getByType(BaseExtension::class.java))
}

internal inline fun Project.androidApp(action: Action<AppExtension>) {
action.execute(extensions.getByType(AppExtension::class.java))
}

internal inline fun Project.androidLibraryComponents(action: Action<LibraryAndroidComponentsExtension>) {
action.execute(extensions.getByType(LibraryAndroidComponentsExtension::class.java))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,152 +26,143 @@ ext.kotlin.jvmTargetVersion = kotlinDefaults.jvmTargetVersion
var isAndroidPublishingConfigured = false

@Suppress("LabeledExpression")
ext.awaitKotlinConfigured { isKotlinUserConfigured ->
ext.awaitAndroidConfigured { isAndroidUserConfigured ->
val isSdkVersionsConfigured = compileSdk > 0 && minSdk > 0
if(!isSdkVersionsConfigured && !isAndroidUserConfigured) return@awaitAndroidConfigured
ext.awaitAndroidConfigured { isAndroidUserConfigured ->
val isSdkVersionsConfigured = compileSdk > 0 && minSdk > 0
if(!isSdkVersionsConfigured && !isAndroidUserConfigured) return@awaitAndroidConfigured

if(jvmTargetVersion == null && !isKotlinUserConfigured) return@awaitAndroidConfigured

check(compileSdk > 0) {
"android.compileSdk doesn't have a value set"
}
check(compileSdk > 0) {
"android.compileSdk doesn't have a value set"
}

check(minSdk > 0) {
"android.minSdk doesn't have a value set"
}
check(minSdk > 0) {
"android.minSdk doesn't have a value set"
}

val androidCompileSdk = compileSdk
val androidMinSdk = minSdk
val androidCompileSdk = compileSdk
val androidMinSdk = minSdk

androidLibrary {
compileSdk = androidCompileSdk
androidLibrary {
compileSdk = androidCompileSdk

val kmpManifestFilePath = "src/androidMain/AndroidManifest.xml"
if(layout.projectDirectory.file(kmpManifestFilePath).asFile.exists()) {
sourceSets.named("main") {
manifest.srcFile(kmpManifestFilePath)
}
val kmpManifestFilePath = "src/androidMain/AndroidManifest.xml"
if(layout.projectDirectory.file(kmpManifestFilePath).asFile.exists()) {
sourceSets.named("main") {
manifest.srcFile(kmpManifestFilePath)
}
}

val kmpResPath = "src/androidMain/res"
if(layout.projectDirectory.file(kmpResPath).asFile.exists()) {
sourceSets.named("main") {
res.srcDir(kmpResPath)
}
val kmpResPath = "src/androidMain/res"
if(layout.projectDirectory.file(kmpResPath).asFile.exists()) {
sourceSets.named("main") {
res.srcDir(kmpResPath)
}
}

val kmpResourcesPath = "src/commonMain/resources"
if(layout.projectDirectory.file(kmpResourcesPath).asFile.exists()) {
sourceSets.named("main") {
res.srcDir(kmpResourcesPath)
}
val kmpResourcesPath = "src/commonMain/resources"
if(layout.projectDirectory.file(kmpResourcesPath).asFile.exists()) {
sourceSets.named("main") {
res.srcDir(kmpResourcesPath)
}
}

defaultConfig {
consumerProguardFile(project.file("consumer-rules.pro"))
defaultConfig {
consumerProguardFile(project.file("consumer-rules.pro"))

minSdk = androidMinSdk
minSdk = androidMinSdk

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

val jvmTargetVersion = jvmTargetVersion
if(jvmTargetVersion != null) {
compileOptions {
if(coreLibraryDesugaringDependency != null) {
isCoreLibraryDesugaringEnabled = true
}
sourceCompatibility = JavaVersion.toVersion(jvmTargetVersion)
targetCompatibility = JavaVersion.toVersion(jvmTargetVersion)
}
compileOptions {
if(coreLibraryDesugaringDependency != null) {
isCoreLibraryDesugaringEnabled = true
}
}

packaging {
resources.pickFirsts += "META-INF/*"
}
packaging {
resources.pickFirsts += "META-INF/*"
}

buildTypes {
named("release") {
isMinifyEnabled = false
}
named("debug") {
isMinifyEnabled = false
}
buildTypes {
named("release") {
isMinifyEnabled = false
}
named("debug") {
isMinifyEnabled = false
}
}

for((dimension, flavorsToRegister) in flavors) {
if(dimension !in flavorDimensions) flavorDimensions += dimension
for((dimension, flavorsToRegister) in flavors) {
if(dimension !in flavorDimensions) flavorDimensions += dimension

for(flavor in flavorsToRegister) {
// register throws if the name is already registered
// and this block can be called multiple times
runCatching {
productFlavors.register(flavor.name)
}
for(flavor in flavorsToRegister) {
// register throws if the name is already registered
// and this block can be called multiple times
runCatching {
productFlavors.register(flavor.name)
}
}
}

testOptions {
unitTests {
isIncludeAndroidResources = true
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}

if(publishEverything) {
// we can't configure this twice and awaitAndroidConfigured can be called twice (default and user config)
// since publishing doesn't depend on values from the extension we can guard against this without issue
if(!isAndroidPublishingConfigured) {
publishing {
multipleVariants {
allVariants()
withJavadocJar()
withSourcesJar()
}
if(publishEverything) {
// we can't configure this twice and awaitAndroidConfigured can be called twice (default and user config)
// since publishing doesn't depend on values from the extension we can guard against this without issue
if(!isAndroidPublishingConfigured) {
publishing {
multipleVariants {
allVariants()
withJavadocJar()
withSourcesJar()
}
isAndroidPublishingConfigured = true
}
isAndroidPublishingConfigured = true
}
}

coreLibraryDesugaringDependency?.let { desugaringDependency ->
dependencies {
add("coreLibraryDesugaring", desugaringDependency)
}
coreLibraryDesugaringDependency?.let { desugaringDependency ->
dependencies {
add("coreLibraryDesugaring", desugaringDependency)
}
}
}

androidLibraryComponents {
val disabledFlavors = flavors.mapNotNull { (dimension, flavorsToRegister) ->
val disabledFlavors = flavorsToRegister.filterNot { it.enabled }
(dimension to disabledFlavors).takeIf { disabledFlavors.isNotEmpty() }
}
if(disabledFlavors.isNotEmpty()) {
val selector = selector().let {
var s = it
for((dimension, disabled) in disabledFlavors) {
for(disabledFlavor in disabled) {
s = s.withFlavor(dimension to disabledFlavor.name)
}
androidLibraryComponents {
val disabledFlavors = flavors.mapNotNull { (dimension, flavorsToRegister) ->
val disabledFlavors = flavorsToRegister.filterNot { it.enabled }
(dimension to disabledFlavors).takeIf { disabledFlavors.isNotEmpty() }
}
if(disabledFlavors.isNotEmpty()) {
val selector = selector().let {
var s = it
for((dimension, disabled) in disabledFlavors) {
for(disabledFlavor in disabled) {
s = s.withFlavor(dimension to disabledFlavor.name)
}
s
}
s
}

beforeVariants(selector) { variant ->
variant.enable = false
}
beforeVariants(selector) { variant ->
variant.enable = false
}
}

for((optIns, dependencyPredicate) in optInsToDependencyPredicate) {
onVariants { variant ->
doOnFirstMatchingIncomingDependencyBeforeResolution(
configurationName = "${variant.name}RuntimeClasspath",
dependencyPredicate = dependencyPredicate
) {
tasks.withType(KotlinCompilationTask::class.java).configureEach {
compilerOptions {
for(optIn in optIns) {
freeCompilerArgs.addAll("-opt-in=${optIn.value}")
}
for((optIns, dependencyPredicate) in optInsToDependencyPredicate) {
onVariants { variant ->
doOnFirstMatchingIncomingDependencyBeforeResolution(
configurationName = "${variant.name}RuntimeClasspath",
dependencyPredicate = dependencyPredicate
) {
tasks.withType(KotlinCompilationTask::class.java).configureEach {
compilerOptions {
for(optIn in optIns) {
freeCompilerArgs.addAll("-opt-in=${optIn.value}")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugins {
}

gradleConventionsExtension.awaitComposeConfigured {
if(ignoreNonJvmTargets) {
if(applyToAndroidAndJvmOnly) {
// only apply to android/jvm targets if we're in a multiplatform project
plugins.withId("org.jetbrains.kotlin.multiplatform") {
plugins.removeAll {
Expand All @@ -36,12 +36,13 @@ gradleConventionsExtension.awaitComposeConfigured {
)
}

plugins.withType<BasePlugin> {
android {
dependencies {
// jetbrains compose plugin rewrites compose dependencies for android to point to androidx
// if we want to use the compose BOM we need to rewrite the rewritten dependencies to not include a version
if(androidComposeDependencyBomVersion != null) {
// jetbrains compose plugin rewrites compose dependencies for android to point to androidx
// if we want to use the compose BOM we need to rewrite the rewritten dependencies to not include a version
// https://github.com/JetBrains/compose-multiplatform/issues/2502
if(androidComposeDependencyBomVersion != null) {
plugins.withType<BasePlugin> {
android {
dependencies {
components {
all {
val isCompiler = id.group.endsWith("compiler")
Expand All @@ -50,7 +51,6 @@ gradleConventionsExtension.awaitComposeConfigured {

val override = isCompose && !isCompiler && !isBom
if(override) {
// copied from Jetbrains Compose RedirectAndroidVariants - https://shorturl.at/dioY9
listOf(
"debugApiElements-published",
"debugRuntimeElements-published",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ plugins {
id("com.eygraber.conventions-compose")
}

plugins.withType<AndroidBasePlugin> {
android {
buildFeatures.compose = true
gradleConventionsExtension.awaitComposeConfigured {
plugins.withType<AndroidBasePlugin> {
android {
buildFeatures.compose = true

androidComposeCompilerVersionOverride?.let { versionOverride ->
@Suppress("UnstableApiUsage")
composeOptions.kotlinCompilerExtensionVersion = versionOverride
}
}
}
}

gradleConventionsExtension.awaitComposeConfigured {
if(enableAndroidCompilerMetrics) {
val output = project.layout.buildDirectory.dir("compose_metrics").get().asFile

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,14 @@ val composeDefaults = gradleConventionsDefaultsService.compose
ext.compose.androidComposeCompilerVersionOverride = composeDefaults.androidComposeCompilerVersionOverride
ext.compose.androidComposeDependencyBomVersion = composeDefaults.androidComposeDependencyBomVersion
ext.compose.enableAndroidCompilerMetrics = composeDefaults.enableAndroidCompilerMetrics
ext.compose.ignoreNonJvmTargets = composeDefaults.ignoreNonJvmTargets
ext.compose.applyToAndroidAndJvmOnly = composeDefaults.applyToAndroidAndJvmOnly
ext.compose.jetbrainsComposeCompilerOverride = composeDefaults.jetbrainsComposeCompilerOverride
ext.compose.useAndroidComposeCompilerVersionForJetbrainsComposeCompilerVersion =
composeDefaults.useAndroidComposeCompilerVersionForJetbrainsComposeCompilerVersion

ext.awaitComposeConfigured {
plugins.withType<AndroidBasePlugin> {
android {
androidComposeCompilerVersionOverride?.let { versionOverride ->
@Suppress("UnstableApiUsage")
composeOptions.kotlinCompilerExtensionVersion = versionOverride
}

dependencies {
androidComposeDependencyBomVersion?.let { bomVersion ->
implementation(platform("androidx.compose:compose-bom:$bomVersion"))
Expand Down
Loading

0 comments on commit 00eec21

Please sign in to comment.