Browse Source

Manage Kotlin native cache kind automatically based on Kotlin version (#3477)

Set Kotlin/Native cache kind based on Kotlin version

Resolves #2046
Resolves #2386
pull/3502/head v1.5.0-beta02
Alexey Tsvetkov 1 year ago committed by GitHub
parent
commit
950d5a991c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt
  2. 78
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeMultiplatformBuildService.kt
  3. 6
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt
  4. 22
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/ComposeProjectProperties.kt
  5. 114
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/internal/configureNativeCompilerCaching.kt
  6. 4
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/utils/IosGradleProperties.kt
  7. 18
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/BuildEventsListenerRegistryProvider.kt
  8. 35
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/KGPPropertyProvider.kt
  9. 13
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/fileUtils.kt
  10. 11
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/gradleUtils.kt
  11. 2
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/providerUtils.kt
  12. 55
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/GradlePluginTest.kt
  13. 2
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestKotlinVersions.kt
  14. 2
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestProjects.kt
  15. 22
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt
  16. 27
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/build.gradle
  17. 1
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/gradle.properties
  18. 12
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/settings.gradle
  19. 10
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/src/commonMain/kotlin/App.kt
  20. 20
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/build.gradle
  21. 1
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/gradle.properties
  22. 12
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/settings.gradle
  23. 10
      gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/src/commonMain/kotlin/App.kt

20
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt

@ -13,7 +13,6 @@ import org.jetbrains.compose.internal.mppExtOrNull
import org.jetbrains.compose.internal.webExt
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import java.lang.ClassCastException
import javax.inject.Inject
@Suppress("UnstableApiUsage")
@ -48,24 +47,7 @@ class ComposeCompilerKotlinSupportPlugin @Inject constructor(
return !groupId.startsWith("org.jetbrains.compose.compiler")
}
val service = ComposeMultiplatformBuildService.provider(target)
buildEventsListenerRegistry.onTaskCompletion(service)
val providedService = try {
service.get()
} catch (e: ClassCastException) {
// Compose Gradle plugin was probably loaded more than once
// See https://github.com/JetBrains/compose-multiplatform/issues/3459
throw IllegalStateException(
"Failed to get ComposeMultiplatformBuildService instance." +
" Compose Gradle plugin was probably loaded more than once." +
" Consider declaring it in the root build.gradle.kts",
e
)
}
providedService.parameters.unsupportedCompilerPlugins.add(
ComposeMultiplatformBuildService.getInstance(target).unsupportedCompilerPlugins.add(
target.provider {
composeCompilerArtifactProvider.compilerArtifact.takeIf {
target.hasNonJvmTargets() && it.isNonJBComposeCompiler()

78
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeMultiplatformBuildService.kt

@ -6,27 +6,32 @@ import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.services.BuildServiceRegistry
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.jetbrains.compose.internal.utils.BuildEventsListenerRegistryProvider
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
// The service implements OperationCompletionListener just so Gradle would use the service
// even if the service is not used by any task or transformation
abstract class ComposeMultiplatformBuildService : BuildService<ComposeMultiplatformBuildService.Parameters>,
abstract class ComposeMultiplatformBuildService : BuildService<BuildServiceParameters.None>,
OperationCompletionListener, AutoCloseable {
interface Parameters : BuildServiceParameters {
val unsupportedCompilerPlugins: SetProperty<Provider<SubpluginArtifact?>>
}
private val log = Logging.getLogger(this.javaClass)
internal abstract val unsupportedCompilerPlugins: SetProperty<Provider<SubpluginArtifact?>>
internal abstract val delayedWarnings: SetProperty<String>
fun warnOnceAfterBuild(message: String) {
delayedWarnings.add(message)
}
override fun close() {
notifyAboutUnsupportedCompilerPlugin()
logDelayedWarnings()
}
private fun notifyAboutUnsupportedCompilerPlugin() {
val unsupportedCompilerPlugin = parameters.unsupportedCompilerPlugins.orNull
val unsupportedCompilerPlugin = unsupportedCompilerPlugins.orNull
?.firstOrNull()
?.orNull
@ -35,28 +40,55 @@ abstract class ComposeMultiplatformBuildService : BuildService<ComposeMultiplatf
}
}
private fun logDelayedWarnings() {
for (warning in delayedWarnings.get()) {
log.warn(warning)
}
}
override fun onFinish(event: FinishEvent) {}
companion object {
fun configure(project: Project, fn: Parameters.() -> Unit): Provider<ComposeMultiplatformBuildService> =
project.gradle.sharedServices.registerOrConfigure<Parameters, ComposeMultiplatformBuildService> {
fn()
private val COMPOSE_SERVICE_FQ_NAME = ComposeMultiplatformBuildService::class.java.canonicalName
private fun findExistingComposeService(project: Project): ComposeMultiplatformBuildService? {
val registration = project.gradle.sharedServices.registrations.findByName(COMPOSE_SERVICE_FQ_NAME)
val service = registration?.service?.orNull
if (service != null) {
if (service !is ComposeMultiplatformBuildService) {
// Compose Gradle plugin was probably loaded more than once
// See https://github.com/JetBrains/compose-multiplatform/issues/3459
if (service.javaClass.canonicalName == ComposeMultiplatformBuildService::class.java.canonicalName) {
val rootScript = project.rootProject.buildFile
error("""
Compose Multiplatform Gradle plugin has been loaded in multiple classloaders.
To avoid classloading issues, declare Compose Gradle Plugin in root build file $rootScript.
""".trimIndent())
} else {
error("Shared build service '$COMPOSE_SERVICE_FQ_NAME' has unexpected type: ${service.javaClass.canonicalName}")
}
}
return service
}
fun provider(project: Project): Provider<ComposeMultiplatformBuildService> = configure(project) {}
return null
}
@Suppress("UnstableApiUsage")
fun init(project: Project) {
val existingService = findExistingComposeService(project)
if (existingService != null) {
return
}
val newService = project.gradle.sharedServices.registerIfAbsent(COMPOSE_SERVICE_FQ_NAME, ComposeMultiplatformBuildService::class.java) {
}
// workaround to instanciate a service even if it not binded to a task
BuildEventsListenerRegistryProvider.getInstance(project).onTaskCompletion(newService)
}
fun getInstance(project: Project): ComposeMultiplatformBuildService =
findExistingComposeService(project) ?: error("ComposeMultiplatformBuildService was not initialized!")
}
}
inline fun <reified P : BuildServiceParameters, reified S : BuildService<P>> BuildServiceRegistry.registerOrConfigure(
crossinline fn: P.() -> Unit
): Provider<S> {
val serviceClass = S::class.java
val serviceFqName = serviceClass.canonicalName
val existingService = registrations.findByName(serviceFqName)
?.apply { (parameters as? P)?.fn() }
?.service
return (existingService as? Provider<S>)
?: registerIfAbsent(serviceFqName, serviceClass) {
it.parameters.fn()
}
}

6
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt

@ -21,6 +21,7 @@ import org.jetbrains.compose.desktop.preview.internal.initializePreview
import org.jetbrains.compose.experimental.dsl.ExperimentalExtension
import org.jetbrains.compose.experimental.internal.configureExperimentalTargetsFlagsCheck
import org.jetbrains.compose.experimental.internal.configureExperimental
import org.jetbrains.compose.experimental.internal.configureNativeCompilerCaching
import org.jetbrains.compose.experimental.uikit.internal.resources.configureSyncTask
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.internal.mppExt
@ -35,8 +36,10 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
internal val composeVersion get() = ComposeBuildConfig.composeVersion
class ComposePlugin : Plugin<Project> {
abstract class ComposePlugin : Plugin<Project> {
override fun apply(project: Project) {
ComposeMultiplatformBuildService.init(project)
val composeExtension = project.extensions.create("compose", ComposeExtension::class.java, project)
val desktopExtension = composeExtension.extensions.create("desktop", DesktopExtension::class.java)
val androidExtension = composeExtension.extensions.create("android", AndroidExtension::class.java)
@ -52,6 +55,7 @@ class ComposePlugin : Plugin<Project> {
composeExtension.extensions.create("web", WebExtension::class.java)
project.plugins.apply(ComposeCompilerKotlinSupportPlugin::class.java)
project.configureNativeCompilerCaching()
project.afterEvaluate {
configureDesktop(project, desktopExtension)

22
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/internal/ComposeProjectProperties.kt

@ -7,7 +7,7 @@ package org.jetbrains.compose.desktop.application.internal
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.jetbrains.compose.internal.utils.findProperty
import org.jetbrains.compose.internal.utils.valueOrNull
import org.jetbrains.compose.internal.utils.toBooleanProvider
internal object ComposeProperties {
@ -23,32 +23,32 @@ internal object ComposeProperties {
internal const val CHECK_JDK_VENDOR = "compose.desktop.packaging.checkJdkVendor"
fun isVerbose(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(VERBOSE).toBooleanProvider(false)
providers.valueOrNull(VERBOSE).toBooleanProvider(false)
fun preserveWorkingDir(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(PRESERVE_WD).toBooleanProvider(false)
providers.valueOrNull(PRESERVE_WD).toBooleanProvider(false)
fun macSign(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(MAC_SIGN).toBooleanProvider(false)
providers.valueOrNull(MAC_SIGN).toBooleanProvider(false)
fun macSignIdentity(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_SIGN_ID)
providers.valueOrNull(MAC_SIGN_ID)
fun macSignKeychain(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_SIGN_KEYCHAIN)
providers.valueOrNull(MAC_SIGN_KEYCHAIN)
fun macSignPrefix(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_SIGN_PREFIX)
providers.valueOrNull(MAC_SIGN_PREFIX)
fun macNotarizationAppleID(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_NOTARIZATION_APPLE_ID)
providers.valueOrNull(MAC_NOTARIZATION_APPLE_ID)
fun macNotarizationPassword(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_NOTARIZATION_PASSWORD)
providers.valueOrNull(MAC_NOTARIZATION_PASSWORD)
fun macNotarizationAscProvider(providers: ProviderFactory): Provider<String?> =
providers.findProperty(MAC_NOTARIZATION_ASC_PROVIDER)
providers.valueOrNull(MAC_NOTARIZATION_ASC_PROVIDER)
fun checkJdkVendor(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(CHECK_JDK_VENDOR).toBooleanProvider(true)
providers.valueOrNull(CHECK_JDK_VENDOR).toBooleanProvider(true)
}

114
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/internal/configureNativeCompilerCaching.kt

@ -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'"),
)
}

4
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/uikit/internal/utils/IosGradleProperties.kt

@ -7,12 +7,12 @@ package org.jetbrains.compose.experimental.uikit.internal.utils
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.jetbrains.compose.internal.utils.findProperty
import org.jetbrains.compose.internal.utils.valueOrNull
import org.jetbrains.compose.internal.utils.toBooleanProvider
internal object IosGradleProperties {
const val SYNC_RESOURCES_PROPERTY = "org.jetbrains.compose.ios.resources.sync"
fun syncResources(providers: ProviderFactory): Provider<Boolean> =
providers.findProperty(SYNC_RESOURCES_PROPERTY).toBooleanProvider(true)
providers.valueOrNull(SYNC_RESOURCES_PROPERTY).toBooleanProvider(true)
}

18
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/BuildEventsListenerRegistryProvider.kt

@ -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
}
}

35
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/KGPPropertyProvider.kt

@ -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"
}
}

13
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/fileUtils.kt

@ -12,6 +12,7 @@ import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Provider
import java.io.File
import java.util.*
internal fun Provider<String>.toDir(project: Project): Provider<Directory> =
project.layout.dir(map { File(it) })
@ -55,4 +56,14 @@ internal fun FileSystemOperations.clearDirs(vararg dirs: Provider<out FileSystem
}
private fun Array<out Provider<out FileSystemLocation>>.ioFiles(): Array<File> =
let { providers -> Array(size) { i -> providers[i].ioFile } }
let { providers -> Array(size) { i -> providers[i].ioFile } }
internal fun lazyLoadProperties(propertiesFile: File): Lazy<Properties> = lazy {
Properties().apply {
if (propertiesFile.isFile) {
propertiesFile.inputStream().use {
load(it)
}
}
}
}

11
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/gradleUtils.kt

@ -5,6 +5,7 @@
package org.jetbrains.compose.internal.utils
import org.gradle.api.DomainObjectCollection
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.logging.Logger
@ -61,3 +62,13 @@ internal fun Project.detachedDependency(
internal fun Configuration.excludeTransitiveDependencies(): Configuration =
apply { isTransitive = false }
internal inline fun <reified SubT> DomainObjectCollection<*>.configureEachWithType(
crossinline fn: SubT.() -> Unit
) {
configureEach {
if (it is SubT) {
it.fn()
}
}
}

2
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/providerUtils.kt

@ -30,7 +30,7 @@ internal inline fun <reified T> Provider<T>.toProperty(objects: ObjectFactory):
internal inline fun <reified T> Task.provider(noinline fn: () -> T): Provider<T> =
project.provider(fn)
internal fun ProviderFactory.findProperty(prop: String): Provider<String?> =
internal fun ProviderFactory.valueOrNull(prop: String): Provider<String?> =
provider {
gradleProperty(prop).forUseAtConfigurationTimeSafe().orNull
}

55
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/GradlePluginTest.kt

@ -108,6 +108,61 @@ class GradlePluginTest : GradlePluginTestBase() {
}
}
@Test
fun nativeCacheKind() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
fun nativeCacheKindProject(kotlinVersion: String) = testProject(
TestProjects.nativeCacheKind,
defaultTestEnvironment.copy(kotlinVersion = kotlinVersion, useGradleConfigurationCache = false)
)
val task = ":linkDebugFrameworkIosX64"
with(nativeCacheKindProject(kotlinVersion = TestKotlinVersions.v1_8_20)) {
gradle(task, "--info").checks {
check.taskSuccessful(task)
check.logDoesntContain("-Xauto-cache-from=")
}
}
testWorkDir.deleteRecursively()
testWorkDir.mkdirs()
with(nativeCacheKindProject(kotlinVersion = TestKotlinVersions.v1_9_0) ) {
gradle(task, "--info").checks {
check.taskSuccessful(task)
check.logContains("-Xauto-cache-from=")
check.logContains("-Xlazy-ir-for-caches=disable")
}
}
}
@Test
fun nativeCacheKindWarning() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
fun nativeCacheKindWarningProject(kotlinVersion: String) = testProject(
TestProjects.nativeCacheKindWarning,
defaultTestEnvironment.copy(kotlinVersion = kotlinVersion)
)
val cacheKindWarning = "Warning: 'kotlin.native.cacheKind' is explicitly set to `none`"
val args = arrayOf("build", "--dry-run", "-Pkotlin.native.cacheKind=none")
with(nativeCacheKindWarningProject(kotlinVersion = TestKotlinVersions.v1_8_20)) {
gradle(*args).checks {
check.logContainsOnce(cacheKindWarning)
}
// check that the warning is shown even when the configuration is loaded from cache
gradle(*args).checks {
check.logContainsOnce(cacheKindWarning)
}
}
testWorkDir.deleteRecursively()
testWorkDir.mkdirs()
with(nativeCacheKindWarningProject(kotlinVersion = TestKotlinVersions.v1_9_0) ) {
gradle(*args).checks {
check.logContainsOnce(cacheKindWarning)
}
}
}
@Test
fun skikoWasm() = with(
testProject(

2
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestKotlinVersions.kt

@ -7,4 +7,6 @@ package org.jetbrains.compose.test.utils
object TestKotlinVersions {
val Default = TestProperties.composeCompilerCompatibleKotlinVersion
val v1_8_20 = "1.8.20"
val v1_9_0 = "1.9.0"
}

2
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/TestProjects.kt

@ -26,4 +26,6 @@ object TestProjects {
const val jvmPreview = "misc/jvmPreview"
const val iosResources = "misc/iosResources"
const val iosMokoResources = "misc/iosMokoResources"
const val nativeCacheKind = "misc/nativeCacheKind"
const val nativeCacheKindWarning = "misc/nativeCacheKindWarning"
}

22
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt

@ -29,6 +29,14 @@ internal class BuildResultChecks(private val result: BuildResult) {
val log: String
get() = result.output
fun logContainsOnce(substring: String) {
val actualCount = log.countOccurrencesOf(substring)
if (actualCount != 1) throw AssertionError(
"Test output must contain substring '$substring' exactly once. " +
"Actual number of occurrences: $actualCount"
)
}
fun logContains(substring: String) {
if (!result.output.contains(substring)) {
throw AssertionError("Test output does not contain the expected string: '$substring'")
@ -96,3 +104,17 @@ internal fun assertNotEqualTextFiles(actual: File, expected: File) {
private fun File.normalizedText() =
readLines().joinToString("\n") { it.trim() }
private fun String.countOccurrencesOf(substring: String): Int {
var count = 0
var i = 0
while (i >= 0 && i < length) {
i = indexOf(substring, startIndex = i)
if (i == -1) break
i++
count++
}
return count
}

27
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/build.gradle

@ -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)
}
}
}
}

1
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/gradle.properties

@ -0,0 +1 @@
org.jetbrains.compose.experimental.uikit.enabled=true

12
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/settings.gradle

@ -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"

10
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKind/src/commonMain/kotlin/App.kt

@ -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!") }
}

20
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/build.gradle

@ -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)
}
}
}
}

1
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/gradle.properties

@ -0,0 +1 @@
org.jetbrains.compose.experimental.uikit.enabled=true

12
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/settings.gradle

@ -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"

10
gradle-plugins/compose/src/test/test-projects/misc/nativeCacheKindWarning/src/commonMain/kotlin/App.kt

@ -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…
Cancel
Save