Browse Source
Set Kotlin/Native cache kind based on Kotlin version Resolves #2046 Resolves #2386pull/3502/head v1.5.0-beta02
Alexey Tsvetkov
1 year ago
committed by
GitHub
23 changed files with 439 additions and 58 deletions
@ -0,0 +1,114 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2023 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.experimental.internal |
||||||
|
|
||||||
|
import org.gradle.api.Project |
||||||
|
import org.jetbrains.compose.ComposeMultiplatformBuildService |
||||||
|
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID |
||||||
|
import org.jetbrains.compose.internal.mppExt |
||||||
|
import org.jetbrains.compose.internal.utils.KGPPropertyProvider |
||||||
|
import org.jetbrains.compose.internal.utils.configureEachWithType |
||||||
|
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion |
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget |
||||||
|
import org.jetbrains.kotlin.konan.target.presetName |
||||||
|
|
||||||
|
private const val PROJECT_CACHE_KIND_PROPERTY_NAME = "kotlin.native.cacheKind" |
||||||
|
private const val COMPOSE_NATIVE_MANAGE_CACHE_KIND = "compose.kotlin.native.manageCacheKind" |
||||||
|
private const val NONE_VALUE = "none" |
||||||
|
|
||||||
|
internal fun Project.configureNativeCompilerCaching() { |
||||||
|
if (findProperty(COMPOSE_NATIVE_MANAGE_CACHE_KIND) == "false") return |
||||||
|
|
||||||
|
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { |
||||||
|
val kotlinVersion = kotlinVersionNumbers(this) |
||||||
|
mppExt.targets.configureEachWithType<KotlinNativeTarget> { |
||||||
|
checkCacheKindUserValueIsNotNone() |
||||||
|
configureTargetCompilerCache(kotlinVersion) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun KotlinNativeTarget.checkCacheKindUserValueIsNotNone() { |
||||||
|
// To determine cache kind KGP checks kotlin.native.cacheKind.<PRESET_NAME> first, then kotlin.native.cacheKind |
||||||
|
// For each property it tries to read Project.property, then checks local.properties |
||||||
|
// See https://github.com/JetBrains/kotlin/blob/d4d30dcfcf1afb083f09279c6f1ba05031efeabb/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt#L416 |
||||||
|
val cacheKindProperties = listOf(targetCacheKindPropertyName, PROJECT_CACHE_KIND_PROPERTY_NAME) |
||||||
|
val propertyProviders = listOf( |
||||||
|
KGPPropertyProvider.GradleProperties(project), |
||||||
|
KGPPropertyProvider.LocalProperties(project) |
||||||
|
) |
||||||
|
|
||||||
|
for (cacheKindProperty in cacheKindProperties) { |
||||||
|
for (provider in propertyProviders) { |
||||||
|
val value = provider.valueOrNull(cacheKindProperty) |
||||||
|
if (value != null) { |
||||||
|
if (value.equals(NONE_VALUE, ignoreCase = true)) { |
||||||
|
ComposeMultiplatformBuildService |
||||||
|
.getInstance(project) |
||||||
|
.warnOnceAfterBuild(cacheKindPropertyWarningMessage(cacheKindProperty, provider)) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun cacheKindPropertyWarningMessage( |
||||||
|
cacheKindProperty: String, |
||||||
|
provider: KGPPropertyProvider |
||||||
|
) = """ |
||||||
|
|Warning: '$cacheKindProperty' is explicitly set to `none`. |
||||||
|
|This option significantly slows the Kotlin/Native compiler. |
||||||
|
|Compose Multiplatform Gradle plugin can set this property automatically, |
||||||
|
|when it is necessary. |
||||||
|
| * Recommended action: remove explicit '$cacheKindProperty=$NONE_VALUE' from ${provider.location}. |
||||||
|
| * Alternative action: if you are sure you need '$cacheKindProperty=$NONE_VALUE', disable |
||||||
|
|this warning by adding '$COMPOSE_NATIVE_MANAGE_CACHE_KIND=false' to your 'gradle.properties'. |
||||||
|
""".trimMargin() |
||||||
|
|
||||||
|
private fun KotlinNativeTarget.configureTargetCompilerCache(kotlinVersion: KotlinVersion) { |
||||||
|
// See comments in https://youtrack.jetbrains.com/issue/KT-57329 |
||||||
|
when { |
||||||
|
// Kotlin < 1.9.0 => disable cache |
||||||
|
kotlinVersion < KotlinVersion(1, 9, 0) -> { |
||||||
|
disableKotlinNativeCache() |
||||||
|
} |
||||||
|
// 1.9.0 <= Kotlin < 1.9.20 => add -Xlazy-ir-for-caches=disable |
||||||
|
kotlinVersion < KotlinVersion(1, 9, 20) -> { |
||||||
|
disableLazyIrForCaches() |
||||||
|
} |
||||||
|
// Kotlin >= 1.9.20 => do nothing |
||||||
|
else -> {} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private val KotlinNativeTarget.targetCacheKindPropertyName: String |
||||||
|
get() = "$PROJECT_CACHE_KIND_PROPERTY_NAME.${konanTarget.presetName}" |
||||||
|
|
||||||
|
private fun KotlinNativeTarget.disableKotlinNativeCache() { |
||||||
|
if (project.hasProperty(targetCacheKindPropertyName)) { |
||||||
|
project.setProperty(targetCacheKindPropertyName, NONE_VALUE) |
||||||
|
} else { |
||||||
|
project.extensions.extraProperties.set(targetCacheKindPropertyName, NONE_VALUE) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun KotlinNativeTarget.disableLazyIrForCaches() { |
||||||
|
compilations.configureEach { compilation -> |
||||||
|
compilation.kotlinOptions.freeCompilerArgs += listOf("-Xlazy-ir-for-caches=disable") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun kotlinVersionNumbers(project: Project): KotlinVersion { |
||||||
|
val version = project.getKotlinPluginVersion() |
||||||
|
val m = Regex("(\\d+)\\.(\\d+)\\.(\\d+)").find(version) ?: error("Kotlin version has unexpected format: '$version'") |
||||||
|
val (_, majorPart, minorPart, patchPart) = m.groupValues |
||||||
|
return KotlinVersion( |
||||||
|
major = majorPart.toIntOrNull() ?: error("Could not parse major part '$majorPart' of Kotlin plugin version: '$version'"), |
||||||
|
minor = minorPart.toIntOrNull() ?: error("Could not parse minor part '$minorPart' of Kotlin plugin version: '$version'"), |
||||||
|
patch = patchPart.toIntOrNull() ?: error("Could not parse patch part '$patchPart' of Kotlin plugin version: '$version'"), |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2023 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.internal.utils |
||||||
|
|
||||||
|
import org.gradle.api.Project |
||||||
|
import org.gradle.build.event.BuildEventsListenerRegistry |
||||||
|
import javax.inject.Inject |
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage") |
||||||
|
internal abstract class BuildEventsListenerRegistryProvider @Inject constructor(val registry: BuildEventsListenerRegistry) { |
||||||
|
companion object { |
||||||
|
fun getInstance(project: Project): BuildEventsListenerRegistry = |
||||||
|
project.objects.newInstance(BuildEventsListenerRegistryProvider::class.java).registry |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2020-2023 JetBrains s.r.o. and respective authors and developers. |
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.jetbrains.compose.internal.utils |
||||||
|
|
||||||
|
import org.gradle.api.Project |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads Kotlin Gradle plugin properties. |
||||||
|
* |
||||||
|
* Kotlin Gradle plugin supports reading property from two sources: |
||||||
|
* 1. Gradle properties. Normally located in gradle.properties file, |
||||||
|
* but can also be provided via command-line, <GRADLE_HOME>/gradle.properties |
||||||
|
* or can be set via Gradle API. |
||||||
|
* 2. local.properties file. local.properties file is not supported by Gradle out-of-the-box. |
||||||
|
* Nevertheless, it became a widespread convention. |
||||||
|
*/ |
||||||
|
internal abstract class KGPPropertyProvider { |
||||||
|
abstract fun valueOrNull(propertyName: String): String? |
||||||
|
abstract val location: String |
||||||
|
|
||||||
|
class GradleProperties(private val project: Project) : KGPPropertyProvider() { |
||||||
|
override fun valueOrNull(propertyName: String): String? = project.findProperty(propertyName)?.toString() |
||||||
|
override val location: String = "gradle.properties" |
||||||
|
} |
||||||
|
|
||||||
|
class LocalProperties(project: Project) : KGPPropertyProvider() { |
||||||
|
private val localProperties: Properties by lazyLoadProperties(project.localPropertiesFile) |
||||||
|
override fun valueOrNull(propertyName: String): String? = localProperties.getProperty(propertyName) |
||||||
|
override val location: String = "local.properties" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
plugins { |
||||||
|
id "org.jetbrains.kotlin.multiplatform" |
||||||
|
id "org.jetbrains.compose" |
||||||
|
} |
||||||
|
|
||||||
|
kotlin { |
||||||
|
iosX64 { |
||||||
|
binaries.framework { |
||||||
|
isStatic = true |
||||||
|
baseName = "shared" |
||||||
|
} |
||||||
|
} |
||||||
|
iosArm64 { |
||||||
|
binaries.framework { |
||||||
|
isStatic = true |
||||||
|
baseName = "shared" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
commonMain { |
||||||
|
dependencies { |
||||||
|
implementation(compose.runtime) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
org.jetbrains.compose.experimental.uikit.enabled=true |
@ -0,0 +1,12 @@ |
|||||||
|
pluginManagement { |
||||||
|
plugins { |
||||||
|
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER' |
||||||
|
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER' |
||||||
|
} |
||||||
|
repositories { |
||||||
|
mavenLocal() |
||||||
|
gradlePluginPortal() |
||||||
|
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } |
||||||
|
} |
||||||
|
} |
||||||
|
rootProject.name = "nativeCacheKind" |
@ -0,0 +1,10 @@ |
|||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.mutableStateOf |
||||||
|
import androidx.compose.runtime.remember |
||||||
|
import androidx.compose.runtime.setValue |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun App() { |
||||||
|
var text by remember { mutableStateOf("Hello, World!") } |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
plugins { |
||||||
|
id "org.jetbrains.kotlin.multiplatform" |
||||||
|
id "org.jetbrains.compose" |
||||||
|
} |
||||||
|
|
||||||
|
kotlin { |
||||||
|
iosX64() |
||||||
|
iosArm64() |
||||||
|
iosSimulatorArm64() |
||||||
|
macosX64() |
||||||
|
macosArm64() |
||||||
|
|
||||||
|
sourceSets { |
||||||
|
commonMain { |
||||||
|
dependencies { |
||||||
|
implementation(compose.runtime) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
org.jetbrains.compose.experimental.uikit.enabled=true |
@ -0,0 +1,12 @@ |
|||||||
|
pluginManagement { |
||||||
|
plugins { |
||||||
|
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER' |
||||||
|
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER' |
||||||
|
} |
||||||
|
repositories { |
||||||
|
mavenLocal() |
||||||
|
gradlePluginPortal() |
||||||
|
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" } |
||||||
|
} |
||||||
|
} |
||||||
|
rootProject.name = "nativeCacheKind" |
@ -0,0 +1,10 @@ |
|||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.mutableStateOf |
||||||
|
import androidx.compose.runtime.remember |
||||||
|
import androidx.compose.runtime.setValue |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun App() { |
||||||
|
var text by remember { mutableStateOf("Hello, World!") } |
||||||
|
} |
Loading…
Reference in new issue