Browse Source

Delete outdated build services (#4959)

We had two build services:
 1) to check unsupported compose compiler plugins were applied
 2) to check a native cache kind configuration

Both of them are outdated and we may get rid of them.

Fixes https://github.com/JetBrains/compose-multiplatform/issues/4815

## Release Notes
### Fixes - Gradle Plugin
- Delete outdated build services
pull/4963/head
Konstantin 5 months ago committed by GitHub
parent
commit
a73ed61ea5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 38
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposeCompilerKotlinSupportPlugin.kt
  2. 10
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/ComposePlugin.kt
  3. 127
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/experimental/internal/configureNativeCompilerCaching.kt
  4. 81
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/AbstractComposeMultiplatformBuildService.kt
  5. 19
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/BuildEventsListenerRegistryProvider.kt
  6. 69
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/ConfigurationProblemReporterService.kt
  7. 52
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/GradlePropertySnapshotService.kt
  8. 36
      gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/utils/KGPPropertyProvider.kt
  9. 110
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/GradlePluginTest.kt
  10. 52
      gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/UnsupportedCompilerPluginWarningTest.kt

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

@ -14,8 +14,6 @@ import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID
import org.jetbrains.compose.internal.Version import org.jetbrains.compose.internal.Version
import org.jetbrains.compose.internal.ideaIsInSyncProvider import org.jetbrains.compose.internal.ideaIsInSyncProvider
import org.jetbrains.compose.internal.mppExtOrNull
import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService
import org.jetbrains.compose.internal.webExt import org.jetbrains.compose.internal.webExt
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
@ -26,7 +24,6 @@ import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
internal fun Project.configureComposeCompilerPlugin() { internal fun Project.configureComposeCompilerPlugin() {
//only one of them can be applied to the project //only one of them can be applied to the project
@ -84,33 +81,9 @@ class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
} }
applicableForPlatformTypes = composeExt.platformTypes applicableForPlatformTypes = composeExt.platformTypes
collectUnsupportedCompilerPluginUsages(target)
} }
} }
private fun collectUnsupportedCompilerPluginUsages(project: Project) {
fun Project.hasNonJvmTargets(): Boolean {
val nonJvmTargets = setOf(KotlinPlatformType.native, KotlinPlatformType.js, KotlinPlatformType.wasm)
return mppExtOrNull?.targets?.any {
it.platformType in nonJvmTargets
} ?: false
}
fun SubpluginArtifact.isNonJBComposeCompiler(): Boolean {
return !groupId.startsWith("org.jetbrains.compose.compiler")
}
ConfigurationProblemReporterService.registerUnsupportedPluginProvider(
project,
project.provider {
composeCompilerArtifactProvider.compilerArtifact.takeIf {
project.hasNonJvmTargets() && it.isNonJBComposeCompiler()
}
}
)
}
override fun getCompilerPluginId(): String = override fun getCompilerPluginId(): String =
"androidx.compose.compiler.plugins.kotlin" "androidx.compose.compiler.plugins.kotlin"
@ -153,14 +126,3 @@ class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
private fun options(vararg options: Pair<String, String>): List<SubpluginOption> = private fun options(vararg options: Pair<String, String>): List<SubpluginOption> =
options.map { SubpluginOption(it.first, it.second) } options.map { SubpluginOption(it.first, it.second) }
} }
private const val COMPOSE_COMPILER_COMPATIBILITY_LINK =
"https://github.com/JetBrains/compose-jb/blob/master/VERSIONING.md#using-compose-multiplatform-compiler"
internal fun createWarningAboutNonCompatibleCompiler(currentCompilerPluginGroupId: String): String {
return """
WARNING: Usage of the Custom Compose Compiler plugin ('$currentCompilerPluginGroupId')
with non-JVM targets (Kotlin/Native, Kotlin/JS, Kotlin/WASM) is not supported.
For more information, please visit: $COMPOSE_COMPILER_COMPATIBILITY_LINK
""".trimMargin()
}

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

@ -21,8 +21,6 @@ import org.jetbrains.compose.desktop.preview.internal.initializePreview
import org.jetbrains.compose.experimental.dsl.ExperimentalExtension import org.jetbrains.compose.experimental.dsl.ExperimentalExtension
import org.jetbrains.compose.experimental.internal.* import org.jetbrains.compose.experimental.internal.*
import org.jetbrains.compose.internal.* import org.jetbrains.compose.internal.*
import org.jetbrains.compose.internal.service.ConfigurationProblemReporterService
import org.jetbrains.compose.internal.service.GradlePropertySnapshotService
import org.jetbrains.compose.internal.utils.currentTarget import org.jetbrains.compose.internal.utils.currentTarget
import org.jetbrains.compose.resources.ResourcesExtension import org.jetbrains.compose.resources.ResourcesExtension
import org.jetbrains.compose.resources.configureComposeResources import org.jetbrains.compose.resources.configureComposeResources
@ -35,15 +33,8 @@ import org.jetbrains.kotlin.gradle.plugin.*
internal val composeVersion get() = ComposeBuildConfig.composeVersion internal val composeVersion get() = ComposeBuildConfig.composeVersion
private fun initBuildServices(project: Project) {
ConfigurationProblemReporterService.init(project)
GradlePropertySnapshotService.init(project)
}
abstract class ComposePlugin : Plugin<Project> { abstract class ComposePlugin : Plugin<Project> {
override fun apply(project: Project) { override fun apply(project: Project) {
initBuildServices(project)
val composeExtension = project.extensions.create("compose", ComposeExtension::class.java, project) val composeExtension = project.extensions.create("compose", ComposeExtension::class.java, project)
val desktopExtension = composeExtension.extensions.create("desktop", DesktopExtension::class.java) val desktopExtension = composeExtension.extensions.create("desktop", DesktopExtension::class.java)
val androidExtension = composeExtension.extensions.create("android", AndroidExtension::class.java) val androidExtension = composeExtension.extensions.create("android", AndroidExtension::class.java)
@ -60,7 +51,6 @@ abstract class ComposePlugin : Plugin<Project> {
composeExtension.extensions.create("web", WebExtension::class.java) composeExtension.extensions.create("web", WebExtension::class.java)
project.configureComposeCompilerPlugin() project.configureComposeCompilerPlugin()
project.configureNativeCompilerCaching()
project.configureComposeResources(resourcesExtension) project.configureComposeResources(resourcesExtension)

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

@ -1,127 +0,0 @@
/*
* 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.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.KonanTarget
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"
// Compose runtime supports these k/native targets:
// https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/compose/runtime/runtime/build.gradle#L75
private val SUPPORTED_NATIVE_TARGETS = setOf(
// ios
KonanTarget.IOS_X64,
KonanTarget.IOS_ARM64,
KonanTarget.IOS_SIMULATOR_ARM64,
// macos
KonanTarget.MACOS_X64,
KonanTarget.MACOS_ARM64,
// tvos
KonanTarget.TVOS_X64,
KonanTarget.TVOS_ARM64,
KonanTarget.TVOS_SIMULATOR_ARM64,
// watchOS
KonanTarget.WATCHOS_ARM64,
KonanTarget.WATCHOS_ARM32,
KonanTarget.WATCHOS_X64,
KonanTarget.WATCHOS_SIMULATOR_ARM64,
// mingw
KonanTarget.MINGW_X64,
// linux
KonanTarget.LINUX_X64,
)
internal val SUPPORTED_NATIVE_CACHE_KIND_PROPERTIES =
SUPPORTED_NATIVE_TARGETS.map { it.targetCacheKindPropertyName } +
PROJECT_CACHE_KIND_PROPERTY_NAME
internal fun Project.configureNativeCompilerCaching() {
if (findProperty(COMPOSE_NATIVE_MANAGE_CACHE_KIND) == "false") return
plugins.withId(KOTLIN_MPP_PLUGIN_ID) {
val kotlinPluginVersion = kotlinVersionNumbers(project.getKotlinPluginVersion())
mppExt.targets.configureEachWithType<KotlinNativeTarget> {
if (konanTarget in SUPPORTED_NATIVE_TARGETS) {
checkExplicitCacheKind()
if (kotlinPluginVersion < KotlinVersion(1, 9, 20)) {
// Pre-1.9.20 Kotlin compiler caches have known compatibility issues
// See KT-57329, KT-61270
disableKotlinNativeCache()
}
}
}
}
}
private fun KotlinNativeTarget.checkExplicitCacheKind() {
// 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) {
error(explicitCacheKindErrorMessage(cacheKindProperty, value, provider))
}
}
}
}
private fun explicitCacheKindErrorMessage(
cacheKindProperty: String,
value: String,
provider: KGPPropertyProvider
) = """
|Error: '$cacheKindProperty' is explicitly set to '$value'.
|This option significantly slows the Kotlin/Native compiler.
|Compose Multiplatform Gradle plugin manages this property automatically based on a Kotlin compiler version being used.
| * Recommended action: remove explicit '$cacheKindProperty=$value' from ${provider.location}.
| * Alternative action: disable cache kind management by adding '$COMPOSE_NATIVE_MANAGE_CACHE_KIND=false' to your 'gradle.properties'.
""".trimMargin()
private val KotlinNativeTarget.targetCacheKindPropertyName: String
get() = konanTarget.targetCacheKindPropertyName
private val KonanTarget.targetCacheKindPropertyName: String
get() = "$PROJECT_CACHE_KIND_PROPERTY_NAME.${presetName}"
private fun KotlinNativeTarget.disableKotlinNativeCache() {
val existingValue = project.findProperty(targetCacheKindPropertyName)?.toString()
if (NONE_VALUE.equals(existingValue, ignoreCase = true)) return
if (targetCacheKindPropertyName in project.properties) {
project.setProperty(targetCacheKindPropertyName, NONE_VALUE)
} else {
project.extensions.extraProperties.set(targetCacheKindPropertyName, NONE_VALUE)
}
}
internal fun kotlinVersionNumbers(version: String): KotlinVersion {
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'"),
)
}

81
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/AbstractComposeMultiplatformBuildService.kt

@ -1,81 +0,0 @@
/*
* 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.service
import org.gradle.api.Project
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.services.BuildServiceRegistration
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
// The service implements OperationCompletionListener just so Gradle would materialize the service even if the service is not used by any task or transformation
abstract class AbstractComposeMultiplatformBuildService<P : BuildServiceParameters> : BuildService<P> , OperationCompletionListener, AutoCloseable {
override fun onFinish(event: FinishEvent) {}
override fun close() {}
}
internal inline fun <reified Service : BuildService<*>> serviceName(instance: Service? = null): String =
fqName(instance)
internal inline fun <reified Service : AbstractComposeMultiplatformBuildService<Params>, reified Params : BuildServiceParameters> registerServiceIfAbsent(
project: Project,
crossinline initParams: Params.() -> Unit = {}
): BuildServiceRegistration<Service, Params> {
if (findRegistration<Service, Params>(project) == null) {
val newService = project.gradle.sharedServices.registerIfAbsent(fqName<Service>(), Service::class.java) {
it.parameters.initParams()
}
// Workaround to materialize a service even if it is not bound to a task
BuildEventsListenerRegistryProvider.getInstance(project).onTaskCompletion(newService)
}
return getExistingServiceRegistration(project)
}
internal inline fun <reified Service : BuildService<Params>, reified Params : BuildServiceParameters> getExistingServiceRegistration(
project: Project
): BuildServiceRegistration<Service, Params> {
val registration = findRegistration<Service, Params>(project)
?: error("Service '${serviceName<Service>()}' was not initialized")
return registration.verified(project)
}
private inline fun <reified Service : BuildService<Params>, reified Params : BuildServiceParameters> BuildServiceRegistration<*, *>.verified(
project: Project
): BuildServiceRegistration<Service, Params> {
val parameters = parameters
// We are checking the type of parameters instead of the type of service
// to avoid materializing the service.
// After a service instance is created all changes made to its parameters won't be visible to
// that particular service instance.
// This is undesirable in some cases. For example, when reporting configuration problems,
// we want to collect all configuration issues from all projects first, then report issues all at once
// in execution phase.
if (parameters !is Params) {
// Compose Gradle plugin was probably loaded more than once
// See https://github.com/JetBrains/compose-multiplatform/issues/3459
if (fqName(parameters) == fqName<Params>()) {
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 '${serviceName<Service>()}' parameters have unexpected type: ${fqName(parameters)}")
}
}
@Suppress("UNCHECKED_CAST")
return this as BuildServiceRegistration<Service, Params>
}
private inline fun <reified S : BuildService<P>, reified P : BuildServiceParameters> findRegistration(
project: Project
): BuildServiceRegistration<*, *>? =
project.gradle.sharedServices.registrations.findByName(fqName<S>())
private inline fun <reified T : Any> fqName(instance: T? = null) = T::class.java.canonicalName

19
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/BuildEventsListenerRegistryProvider.kt

@ -1,19 +0,0 @@
/*
* 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.service
import org.gradle.api.Project
import org.gradle.build.event.BuildEventsListenerRegistry
import javax.inject.Inject
// a hack to get BuildEventsListenerRegistry conveniently, which can only be injected by Gradle
@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
}
}

69
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/ConfigurationProblemReporterService.kt

@ -1,69 +0,0 @@
/*
* 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.service
import org.gradle.api.Project
import org.gradle.api.logging.Logging
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.compose.createWarningAboutNonCompatibleCompiler
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
abstract class ConfigurationProblemReporterService : AbstractComposeMultiplatformBuildService<ConfigurationProblemReporterService.Parameters>() {
interface Parameters : BuildServiceParameters {
val unsupportedPluginWarningProviders: ListProperty<Provider<String?>>
val warnings: SetProperty<String>
}
private val log = Logging.getLogger(this.javaClass)
override fun close() {
warnAboutUnsupportedCompilerPlugin()
logWarnings()
}
private fun warnAboutUnsupportedCompilerPlugin() {
for (warningProvider in parameters.unsupportedPluginWarningProviders.get()) {
val warning = warningProvider.orNull
if (warning != null) {
log.warn(warning)
}
}
}
private fun logWarnings() {
for (warning in parameters.warnings.get()) {
log.warn(warning)
}
}
companion object {
fun init(project: Project) {
registerServiceIfAbsent<ConfigurationProblemReporterService, Parameters>(project) {
// WORKAROUND! Call getter at least once, because of Issue: https://github.com/gradle/gradle/issues/27099
warnings
}
}
private inline fun configureParameters(project: Project, fn: Parameters.() -> Unit) {
getExistingServiceRegistration<ConfigurationProblemReporterService, Parameters>(project)
.parameters.fn()
}
fun reportWarning(project: Project, message: String) {
configureParameters(project) { warnings.add(message) }
}
fun registerUnsupportedPluginProvider(project: Project, unsupportedPlugin: Provider<SubpluginArtifact?>) {
configureParameters(project) {
unsupportedPluginWarningProviders.add(unsupportedPlugin.map { unsupportedCompiler ->
unsupportedCompiler?.groupId?.let { createWarningAboutNonCompatibleCompiler(it) }
})
}
}
}
}

52
gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/internal/service/GradlePropertySnapshotService.kt

@ -1,52 +0,0 @@
/*
* 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.service
import org.gradle.api.Project
import org.gradle.api.provider.MapProperty
import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.compose.experimental.internal.SUPPORTED_NATIVE_CACHE_KIND_PROPERTIES
import org.jetbrains.compose.internal.utils.loadProperties
import org.jetbrains.compose.internal.utils.localPropertiesFile
internal abstract class GradlePropertySnapshotService : AbstractComposeMultiplatformBuildService<GradlePropertySnapshotService.Parameters>() {
interface Parameters : BuildServiceParameters {
val gradlePropertiesCacheKindSnapshot: MapProperty<String, String>
val localPropertiesCacheKindSnapshot: MapProperty<String, String>
}
internal val gradleProperties: Map<String, String> = parameters.gradlePropertiesCacheKindSnapshot.get()
internal val localProperties: Map<String, String> = parameters.localPropertiesCacheKindSnapshot.get()
companion object {
fun init(project: Project) {
registerServiceIfAbsent<GradlePropertySnapshotService, Parameters>(project) {
// WORKAROUND! Call getter at least once, because of Issue: https://github.com/gradle/gradle/issues/27099
gradlePropertiesCacheKindSnapshot
localPropertiesCacheKindSnapshot
initParams(project)
}
}
fun getInstance(project: Project): GradlePropertySnapshotService =
getExistingServiceRegistration<GradlePropertySnapshotService, Parameters>(project).service.get()
private fun Parameters.initParams(project: Project) {
// we want to record original properties (explicitly set by a user)
// before we possibly change them in configureNativeCompilerCaching.kt
val rootProject = project.rootProject
val localProperties = loadProperties(rootProject.localPropertiesFile)
for (cacheKindProperty in SUPPORTED_NATIVE_CACHE_KIND_PROPERTIES) {
rootProject.findProperty(cacheKindProperty)?.toString()?.let { value ->
gradlePropertiesCacheKindSnapshot.put(cacheKindProperty, value)
}
localProperties[cacheKindProperty]?.toString()?.let { value ->
localPropertiesCacheKindSnapshot.put(cacheKindProperty, value)
}
}
}
}
}

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

@ -1,36 +0,0 @@
/*
* 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.jetbrains.compose.internal.service.GradlePropertySnapshotService
/**
* 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? =
GradlePropertySnapshotService.getInstance(project).gradleProperties[propertyName]
override val location: String = "gradle.properties"
}
class LocalProperties(private val project: Project) : KGPPropertyProvider() {
override fun valueOrNull(propertyName: String): String? =
GradlePropertySnapshotService.getInstance(project).localProperties[propertyName]
override val location: String = "local.properties"
}
}

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

@ -9,121 +9,23 @@ import org.gradle.util.GradleVersion
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.PreviewLogger import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.PreviewLogger
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.RemoteConnection import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.RemoteConnection
import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.receiveConfigFromGradle import org.jetbrains.compose.desktop.ui.tooling.preview.rpc.receiveConfigFromGradle
import org.jetbrains.compose.experimental.internal.kotlinVersionNumbers import org.jetbrains.compose.test.utils.GradlePluginTestBase
import org.jetbrains.compose.internal.utils.Arch import org.jetbrains.compose.test.utils.TestProjects
import org.jetbrains.compose.internal.utils.OS import org.jetbrains.compose.test.utils.TestProperties
import org.jetbrains.compose.internal.utils.currentArch import org.jetbrains.compose.test.utils.checks
import org.jetbrains.compose.internal.utils.currentOS
import org.jetbrains.compose.test.utils.*
import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.Test
import java.net.ServerSocket import java.net.ServerSocket
import java.net.Socket import java.net.Socket
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.concurrent.thread import kotlin.concurrent.thread
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import java.io.File
class GradlePluginTest : GradlePluginTestBase() { class GradlePluginTest : GradlePluginTestBase() {
@Test
fun nativeCacheKind() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
val task = if (currentArch == Arch.X64) {
":subproject:linkDebugFrameworkIosX64"
} else {
":subproject:linkDebugFrameworkIosArm64"
}
// Note: we used to test with kotlin version 1.9.0 and 1.9.10 too,
// but since we now use Compose core libs (1.6.0-dev-1340 and newer) built using kotlin 1.9.21,
// the compiler crashed (older k/native doesn't support libs built using newer k/native):
// e: kotlin.NotImplementedError: Generation of stubs for class org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterPublicSymbolImpl is not supported yet
if (kotlinVersionNumbers(defaultTestEnvironment.kotlinVersion) >= KotlinVersion(1, 9, 20)) {
testWorkDir.deleteRecursively()
testWorkDir.mkdirs()
val project = TestProject(
TestProjects.nativeCacheKind,
defaultTestEnvironment.copy(useGradleConfigurationCache = false)
)
with(project) {
gradle(task, "--info").checks {
check.taskSuccessful(task)
check.logContains("-Xauto-cache-from=")
}
}
}
}
@Test
fun nativeCacheKindError() {
Assumptions.assumeTrue(currentOS == OS.MacOS)
fun withNativeCacheKindErrorProject(kotlinVersion: String, fn: TestProject.() -> Unit) {
with(testProject(
TestProjects.nativeCacheKindError,
defaultTestEnvironment.copy(kotlinVersion = kotlinVersion)
)) {
fn()
testWorkDir.deleteRecursively()
testWorkDir.mkdirs()
}
}
fun testKotlinVersion(kotlinVersion: String) {
val args = arrayOf("help")
val commonPartOfWarning = "Compose Multiplatform Gradle plugin manages this property automatically"
withNativeCacheKindErrorProject(kotlinVersion = kotlinVersion) {
gradle(*args).checks {
check.logDoesntContain("Error: 'kotlin.native.cacheKind")
check.logDoesntContain(commonPartOfWarning)
}
}
withNativeCacheKindErrorProject(kotlinVersion = kotlinVersion) {
gradleFailure(*args, "-Pkotlin.native.cacheKind=none").checks {
check.logContains("Error: 'kotlin.native.cacheKind' is explicitly set to 'none'")
check.logContains(commonPartOfWarning)
}
gradleFailure(*args, "-Pkotlin.native.cacheKind=none").checks {
check.logContains("Error: 'kotlin.native.cacheKind' is explicitly set to 'none'")
check.logContains(commonPartOfWarning)
}
}
withNativeCacheKindErrorProject(kotlinVersion = kotlinVersion) {
gradleFailure(*args, "-Pkotlin.native.cacheKind=static").checks {
check.logContains("Error: 'kotlin.native.cacheKind' is explicitly set to 'static'")
check.logContains(commonPartOfWarning)
}
}
withNativeCacheKindErrorProject(kotlinVersion = kotlinVersion) {
gradleFailure(*args, "-Pkotlin.native.cacheKind.iosX64=none").checks {
check.logContains("Error: 'kotlin.native.cacheKind.iosX64' is explicitly set to 'none'")
check.logContains(commonPartOfWarning)
}
}
withNativeCacheKindErrorProject(kotlinVersion = kotlinVersion) {
gradleFailure(*args, "-Pkotlin.native.cacheKind.iosX64=static").checks {
check.logContains("Error: 'kotlin.native.cacheKind.iosX64' is explicitly set to 'static'")
check.logContains(commonPartOfWarning)
}
}
}
testKotlinVersion("1.9.21")
}
@Test @Test
fun skikoWasm() = with( fun skikoWasm() = with(
testProject( testProject(TestProjects.skikoWasm)
TestProjects.skikoWasm,
// configuration cache is disabled as a temporary workaround for KT-58057
// todo: enable once KT-58057 is fixed
testEnvironment = defaultTestEnvironment.copy(useGradleConfigurationCache = false)
)
) { ) {
fun jsCanvasEnabled(value: Boolean) { fun jsCanvasEnabled(value: Boolean) {
modifyGradleProperties { put("org.jetbrains.compose.experimental.jscanvas.enabled", value.toString()) } modifyGradleProperties { put("org.jetbrains.compose.experimental.jscanvas.enabled", value.toString()) }

52
gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/UnsupportedCompilerPluginWarningTest.kt

@ -1,52 +0,0 @@
package org.jetbrains.compose.test.tests.integration
import org.jetbrains.compose.createWarningAboutNonCompatibleCompiler
import org.jetbrains.compose.test.utils.GradlePluginTestBase
import org.jetbrains.compose.test.utils.TestProjects
import org.jetbrains.compose.test.utils.checks
import org.junit.jupiter.api.Test
class UnsupportedCompilerPluginWarningTest : GradlePluginTestBase() {
private val androidxComposeCompilerGroupId = "androidx.compose.compiler"
private val androidxComposeCompilerPlugin = "$androidxComposeCompilerGroupId:compiler:1.4.8"
private fun testCustomCompilerUnsupportedPlatformsWarning(
platforms: String,
warningIsExpected: Boolean
) {
testProject(
TestProjects.customCompilerUnsupportedPlatformsWarning, defaultTestEnvironment.copy(
kotlinVersion = "1.8.22",
composeCompilerPlugin = "\"$androidxComposeCompilerPlugin\"",
)
).apply {
// repeat twice to check that configuration cache hit does not affect the result
repeat(2) {
gradle("-Pplatforms=$platforms").checks {
val warning = createWarningAboutNonCompatibleCompiler(androidxComposeCompilerGroupId)
if (warningIsExpected) {
check.logContainsOnce(warning)
} else {
check.logDoesntContain(warning)
}
}
}
}
}
@Test
fun testJs() {
testCustomCompilerUnsupportedPlatformsWarning("js", warningIsExpected = true)
}
@Test
fun testIos() {
testCustomCompilerUnsupportedPlatformsWarning("ios", warningIsExpected = true)
}
@Test
fun testJvm() {
testCustomCompilerUnsupportedPlatformsWarning("jvm", warningIsExpected = false)
}
}
Loading…
Cancel
Save