Skip to content

Commit

Permalink
Allow to use Compose on multiple Kotlin versions (#2366)
Browse files Browse the repository at this point in the history
JS target supports a lower version (1.7.10), because we have a bug in Koltin 1.7.20

Compose 1.2.0 will support:

1.7.20 and 1.7.10 for Android and Desktop
1.7.10 for JS
We will release the new patchset (1.2.1) with 1.7.2X support for JS later
  • Loading branch information
igordmn committed Oct 8, 2022
1 parent 87df95c commit 3996233
Show file tree
Hide file tree
Showing 18 changed files with 251 additions and 89 deletions.
12 changes: 7 additions & 5 deletions README.md
Expand Up @@ -4,10 +4,9 @@

# Compose Multiplatform, by JetBrains
![](artwork/readme/apps.png)
Compose Kotlin UI framework port for desktop platforms (macOS, Linux, Windows) and Web, components outside of the core Compose repository
at https://android.googlesource.com/platform/frameworks/support.
Compose Kotlin UI framework port for desktop platforms (macOS, Linux, Windows) and Web, components outside of the core [Compose repository](https://android.googlesource.com/platform/frameworks/support).

Preview functionality (check your application UI without building/running it) for desktop platforms is available via IDEA plugin (https://plugins.jetbrains.com/plugin/16541-compose-multiplatform-ide-support).
Preview functionality (check your application UI without building/running it) for desktop platforms is available via [IDEA plugin](https://plugins.jetbrains.com/plugin/16541-compose-multiplatform-ide-support).

## Tutorials
### Compose for Desktop
Expand Down Expand Up @@ -70,6 +69,9 @@ Note that when you use Compose Multiplatform, you setup your project differently
* [LWJGL integration](experimental/lwjgl-integration) - An example showing how to integrate Compose with [LWJGL](https://www.lwjgl.org)
* [CLI example](experimental/build_from_cli) - An example showing how to build Compose without Gradle

## Getting latest version of Compose Multiplatform ##
## Versions ##

See https://github.com/JetBrains/compose-jb/releases/latest for the latest stable release or https://github.com/JetBrains/compose-jb/releases for all stable and dev releases.
* [The latest stable release](https://github.com/JetBrains/compose-jb/releases/latest)
* [The latest dev release](https://github.com/JetBrains/compose-jb/releases)
* [Compatability and versioning overview](VERSIONING.md)
* [Changelog](CHANGELOG.md)
19 changes: 10 additions & 9 deletions FEATURES.md → VERSIONING.md
Expand Up @@ -21,12 +21,13 @@ Knowing issues on older versions:

### Kotlin compatibility

Compose version | Kotlin version
--- | ---
1.0.0+ | 1.5.31
1.0.1-rc2+ | 1.6.10


### Gradle plugin compatibility

* 1.0.0 works with Gradle 6.7 or later (7.2 is the latest tested version).
A new version of Kotlin may be not supported immediately after its release. But after some time we will release a version of Compose Multiplatform
that supports it.
Starting from 1.2.0, Compose Multiplatform supports multiple versions of Kotlin.

Kotlin version | Minimal Compose version | Notes
--- | --- | ---
1.5.31 | 1.0.0
1.6.20 | 1.1.1
1.7.10 | 1.2.0
1.7.20 | 1.2.0 | JS is not supported (will be fixed in the next versions)
3 changes: 0 additions & 3 deletions examples/validateExamples.sh
Expand Up @@ -25,7 +25,4 @@ runGradle issues package
runGradle notepad package
runGradle todoapp-lite package
runGradle visual-effects package
runGradle web-compose-bird build
runGradle web-landing build
runGradle web-with-react build
runGradle widgets-gallery package
24 changes: 24 additions & 0 deletions examples/validateExamplesWithJs.sh
@@ -0,0 +1,24 @@
#!/bin/bash

# Script to build most of the examples, to verify if they can compile.
# Don't add examples, which don't depend on maven.pkg.jetbrains.space, because they won't be able to compile.

set -euo pipefail

if [ "$#" -ne 2 ]; then
echo "Specify Compose and Kotlin version. For example: ./validateExamplesWithJs.sh 1.1.1 1.6.10"
exit 1
fi
COMPOSE_VERSION=$1
KOTLIN_VERSION=$2


runGradle() {
pushd $1
./gradlew $2 -Pcompose.version=$COMPOSE_VERSION -Pkotlin.version=$KOTLIN_VERSION
popd
}

runGradle web-compose-bird build
runGradle web-landing build
runGradle web-with-react build
4 changes: 0 additions & 4 deletions gradle-plugins/buildSrc/src/main/kotlin/BuildProperties.kt
Expand Up @@ -15,10 +15,6 @@ object BuildProperties {
fun composeVersion(project: Project): String =
System.getenv("COMPOSE_GRADLE_PLUGIN_COMPOSE_VERSION")
?: project.findProperty("compose.version") as String
fun composeCompilerVersion(project: Project): String =
project.findProperty("compose.compiler.version") as String
fun composeCompilerCompatibleKotlinVersion(project: Project): String =
project.findProperty("compose.compiler.compatible.kotlin.version") as String
fun testsAndroidxCompilerVersion(project: Project): String =
project.findProperty("compose.tests.androidx.compiler.version") as String
fun testsAndroidxCompilerCompatibleVersion(project: Project): String =
Expand Down
1 change: 0 additions & 1 deletion gradle-plugins/compose/build.gradle.kts
Expand Up @@ -29,7 +29,6 @@ val buildConfig = tasks.register("buildConfig", GenerateBuildConfig::class.java)
classFqName.set("org.jetbrains.compose.ComposeBuildConfig")
generatedOutputDir.set(buildConfigDir)
fieldsToGenerate.put("composeVersion", BuildProperties.composeVersion(project))
fieldsToGenerate.put("composeCompilerVersion", BuildProperties.composeCompilerVersion(project))
fieldsToGenerate.put("composeGradlePluginVersion", BuildProperties.deployVersion(project))
}
tasks.named("compileKotlin") {
Expand Down
@@ -0,0 +1,19 @@
package org.jetbrains.compose

import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType

internal object ComposeCompilerCompatability {
fun compilerVersionFor(kotlinVersion: String): ComposeCompilerVersion? = when (kotlinVersion) {
"1.7.10" -> ComposeCompilerVersion("1.3.0-alpha01")
"1.7.20" -> ComposeCompilerVersion(
"1.3.2-alpha01",
unsupportedPlatforms = setOf(KotlinPlatformType.js)
)
else -> null
}
}

internal data class ComposeCompilerVersion(
val version: String,
val unsupportedPlatforms: Set<KotlinPlatformType> = emptySet()
)
Expand Up @@ -13,13 +13,18 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget

class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {
private var composeCompilerArtifactProvider = ComposeCompilerArtifactProvider { null }
private lateinit var composeCompilerArtifactProvider: ComposeCompilerArtifactProvider

override fun apply(target: Project) {
super.apply(target)
target.plugins.withType(ComposePlugin::class.java) {
val composeExt = target.extensions.getByType(ComposeExtension::class.java)
composeCompilerArtifactProvider = ComposeCompilerArtifactProvider { composeExt.kotlinCompilerPlugin.orNull }

composeCompilerArtifactProvider = ComposeCompilerArtifactProvider(
kotlinVersion = target.getKotlinPluginVersion()
) {
composeExt.kotlinCompilerPlugin.orNull
}
}
}

Expand Down Expand Up @@ -52,6 +57,7 @@ class ComposeCompilerKotlinSupportPlugin : KotlinCompilerPluginSupportPlugin {

override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
val target = kotlinCompilation.target
composeCompilerArtifactProvider.checkTargetSupported(target)
return target.project.provider {
platformPluginOptions[target.platformType] ?: emptyList()
}
Expand Down
Expand Up @@ -5,33 +5,67 @@

package org.jetbrains.compose.internal

import org.jetbrains.compose.ComposeBuildConfig
import org.jetbrains.compose.ComposeCompilerCompatability
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact

internal class ComposeCompilerArtifactProvider(private val customPluginString: () -> String?) {
private const val KOTLIN_COMPATABILITY_LINK =
"https://github.com/JetBrains/compose-jb/blob/master/VERSIONING.md#kotlin-compatibility"

internal class ComposeCompilerArtifactProvider(
private val kotlinVersion: String,
customPluginString: () -> String?
) {
fun checkTargetSupported(target: KotlinTarget) {
require(!unsupportedPlatforms.contains(target.platformType)) {
"This version of Compose Multiplatform doesn't support Kotlin " +
"$kotlinVersion for ${target.platformType} target. " +
"Please see $KOTLIN_COMPATABILITY_LINK " +
"to know the latest supported version of Kotlin."
}
}

private var unsupportedPlatforms: Set<KotlinPlatformType> = emptySet()

val compilerArtifact: SubpluginArtifact
get() {
val customPlugin = customPluginString()
val customCoordinates = customPlugin?.split(":")
return when (customCoordinates?.size) {
null -> DefaultCompiler.pluginArtifact
1 -> {
val customVersion = customCoordinates[0]
check(customVersion.isNotBlank()) { "'compose.kotlinCompilerPlugin' cannot be blank!" }
DefaultCompiler.pluginArtifact.copy(version = customVersion)

init {
val customPlugin = customPluginString()
val customCoordinates = customPlugin?.split(":")
when (customCoordinates?.size) {
null -> {
val version = requireNotNull(
ComposeCompilerCompatability.compilerVersionFor(kotlinVersion)
) {
"This version of Compose Multiplatform doesn't support Kotlin " +
"$kotlinVersion. " +
"Please see $KOTLIN_COMPATABILITY_LINK " +
"to know the latest supported version of Kotlin."
}
3 -> DefaultCompiler.pluginArtifact.copy(
groupId = customCoordinates[0],
artifactId = customCoordinates[1],
version = customCoordinates[2]

compilerArtifact = DefaultCompiler.pluginArtifact(
version = version.version
)
else -> error("""
unsupportedPlatforms = version.unsupportedPlatforms
}
1 -> {
val customVersion = customCoordinates[0]
check(customVersion.isNotBlank()) { "'compose.kotlinCompilerPlugin' cannot be blank!" }
compilerArtifact = DefaultCompiler.pluginArtifact(version = customVersion)
}
3 -> compilerArtifact = DefaultCompiler.pluginArtifact(
version = customCoordinates[2],
groupId = customCoordinates[0],
artifactId = customCoordinates[1],
)
else -> error("""
Illegal format of 'compose.kotlinCompilerPlugin' property.
Expected format: either '<VERSION>' or '<GROUP_ID>:<ARTIFACT_ID>:<VERSION>'
Actual value: '$customPlugin'
""".trimIndent())
}
}
}

val compilerHostedArtifact: SubpluginArtifact
get() = compilerArtifact.run {
Expand All @@ -47,10 +81,13 @@ internal class ComposeCompilerArtifactProvider(private val customPluginString: (
const val GROUP_ID = "org.jetbrains.compose.compiler"
const val ARTIFACT_ID = "compiler"
const val HOSTED_ARTIFACT_ID = "compiler-hosted"
const val VERSION = ComposeBuildConfig.composeCompilerVersion

val pluginArtifact: SubpluginArtifact
get() = SubpluginArtifact(groupId = GROUP_ID, artifactId = ARTIFACT_ID, version = VERSION)
fun pluginArtifact(
version: String,
groupId: String = GROUP_ID,
artifactId: String = ARTIFACT_ID,
): SubpluginArtifact =
SubpluginArtifact(groupId = groupId, artifactId = artifactId, version = version)
}
}

Expand Down
Expand Up @@ -22,7 +22,14 @@ import org.junit.jupiter.api.Test
class GradlePluginTest : GradlePluginTestBase() {
@Test
fun jsMppIsNotBroken() =
with(testProject(TestProjects.jsMpp)) {
with(
testProject(
TestProjects.jsMpp,
testEnvironment = defaultTestEnvironment.copy(
kotlinVersion = TestProperties.composeJsCompilerCompatibleKotlinVersion
)
)
) {
gradle(":compileKotlinJs").build().checks { check ->
check.taskOutcome(":compileKotlinJs", TaskOutcome.SUCCESS)
}
Expand Down
@@ -0,0 +1,56 @@
/*
* Copyright 2020-2022 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.test.tests.integration

import org.gradle.testkit.runner.TaskOutcome
import org.gradle.testkit.runner.UnexpectedBuildFailure
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
import org.junit.jupiter.api.assertThrows

class KotlinCompatabilityTest : GradlePluginTestBase() {
@Test
fun testKotlinMpp_1_7_10() = testMpp("1.7.10")

@Test
fun testKotlinJsMpp_1_7_10() = testJsMpp("1.7.10")

@Test
fun testKotlinMpp_1_7_20() = testMpp("1.7.20")

@Test
fun testKotlinJsMpp_1_7_20() {
assertThrows<UnexpectedBuildFailure> {
testJsMpp("1.7.20")
}
}

private fun testMpp(kotlinVersion: String) = with(
testProject(
TestProjects.mpp,
testEnvironment = defaultTestEnvironment.copy(kotlinVersion = kotlinVersion)
)
) {
val logLine = "Kotlin MPP app is running!"
gradle("run").build().checks { check ->
check.taskOutcome(":run", TaskOutcome.SUCCESS)
check.logContains(logLine)
}
}

private fun testJsMpp(kotlinVersion: String) = with(
testProject(
TestProjects.jsMpp,
testEnvironment = defaultTestEnvironment.copy(kotlinVersion = kotlinVersion)
)
) {
gradle(":compileKotlinJs").build().checks { check ->
check.taskOutcome(":compileKotlinJs", TaskOutcome.SUCCESS)
}
}
}
Expand Up @@ -18,31 +18,34 @@ internal class ComposeCompilerArtifactProviderTest {
fun defaultCompilerArtifact() {
assertArtifactEquals(
Expected.jbCompiler,
Actual.compiler(null)
Actual.compiler(null, TestProperties.composeCompilerCompatibleKotlinVersion)
)
}

@Test
fun defaultCompilerHostedArtifact() {
assertArtifactEquals(
Expected.jbCompilerHosted,
Actual.compilerHosted(null)
Actual.compilerHosted(null, TestProperties.composeCompilerCompatibleKotlinVersion)
)
}

@Test
fun customVersion() {
assertArtifactEquals(
Expected.jbCompiler.copy(version = "10.20.30"),
Actual.compiler("10.20.30")
Actual.compiler("10.20.30", TestProperties.composeCompilerCompatibleKotlinVersion)
)
}

@Test
fun customCompiler() {
assertArtifactEquals(
Expected.googleCompiler.copy(version = "1.3.1"),
Actual.compiler("androidx.compose.compiler:compiler:1.3.1")
Actual.compiler(
"androidx.compose.compiler:compiler:1.3.1",
TestProperties.androidxCompilerCompatibleKotlinVersion
)
)
}

Expand All @@ -51,7 +54,10 @@ internal class ComposeCompilerArtifactProviderTest {
// check that we don't replace artifactId for non-jb compiler
assertArtifactEquals(
Expected.googleCompiler.copy(version = "1.3.1"),
Actual.compilerHosted("androidx.compose.compiler:compiler:1.3.1")
Actual.compilerHosted(
"androidx.compose.compiler:compiler:1.3.1",
TestProperties.composeCompilerCompatibleKotlinVersion
)
)
}

Expand All @@ -64,7 +70,7 @@ internal class ComposeCompilerArtifactProviderTest {

private fun testIllegalCompiler(pluginString: String?) {
try {
Actual.compiler(pluginString)
Actual.compiler(pluginString, "")
} catch (e: Exception) {
return
}
Expand All @@ -73,11 +79,11 @@ internal class ComposeCompilerArtifactProviderTest {
}

object Actual {
fun compiler(pluginString: String?) =
ComposeCompilerArtifactProvider { pluginString }.compilerArtifact
fun compiler(pluginString: String?, kotlinVersion: String) =
ComposeCompilerArtifactProvider(kotlinVersion) { pluginString }.compilerArtifact

fun compilerHosted(pluginString: String?) =
ComposeCompilerArtifactProvider { pluginString }.compilerHostedArtifact
fun compilerHosted(pluginString: String?, kotlinVersion: String) =
ComposeCompilerArtifactProvider(kotlinVersion) { pluginString }.compilerHostedArtifact
}

object Expected {
Expand Down

0 comments on commit 3996233

Please sign in to comment.