Skip to content

Commit

Permalink
Add WASM support (#1245)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZacSweers authored Mar 26, 2024
1 parent 98410c0 commit 9220fac
Show file tree
Hide file tree
Showing 41 changed files with 288 additions and 225 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Changelog
**Unreleased**
--------------

- **New**: Add WASM targets.

0.20.0
------

Expand Down
34 changes: 27 additions & 7 deletions backstack/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (C) 2024 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask

// Copyright (C) 2022 Slack Technologies, LLC
// SPDX-License-Identifier: Apache-2.0
Expand All @@ -24,12 +25,23 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser {
testTask {
useKarma {
useChromeHeadless()
useConfigDirectory(
rootProject.projectDir
.resolve("internal-test-utils")
.resolve("karma.config.d")
.resolve("wasm")
)
}
}
}
binaries.executable()
}
// endregion

Expand Down Expand Up @@ -70,13 +82,21 @@ kotlin {
}
}
val jvmTest by getting { dependsOn(commonJvmTest) }
// We use a common folder instead of a common source set because there is no commonizer
// which exposes the browser APIs across these two targets.
jsMain { kotlin.srcDir("src/browserMain/kotlin") }
val wasmJsMain by getting { kotlin.srcDir("src/browserMain/kotlin") }
}
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>().configureEach {
// adding it here to make sure skiko is unpacked and available in web tests
// https://github.com/JetBrains/compose-multiplatform/issues/4133
compose.experimental { web.application {} }

tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions {
// Need to disable, due to 'duplicate library name' warning
// https://youtrack.jetbrains.com/issue/KT-51110
// https://youtrack.jetbrains.com/issue/KT-64115
allWarningsAsErrors = false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,28 @@ public object SaveableStateRegistryBackStackRecordLocalProvider :

@Composable
override fun provideValues(): ImmutableList<ProvidedValue<*>> {
remember {
object : RememberObserver {
override fun onForgotten() {
childRegistry.saveForContentLeavingComposition()
}

override fun onRemembered() {}

override fun onAbandoned() {}
}
}
remember { RememberObserverImpl(childRegistry) }
return list
}
}
}
}
}

// Extracted to work around a WASM bug
// https://youtrack.jetbrains.com/issue/KT-66465#focus=Comments-27-9568825.0-0
private class RememberObserverImpl(
private val childRegistry: BackStackRecordLocalSaveableStateRegistry
) : RememberObserver {
override fun onForgotten() {
childRegistry.saveForContentLeavingComposition()
}

override fun onRemembered() {}

override fun onAbandoned() {}
}

private class BackStackRecordLocalSaveableStateRegistry(
// Note: restored is snapshot-backed because consumeRestored runs in composition
// and must be rolled back if composition does not commit
Expand Down Expand Up @@ -112,7 +116,7 @@ private class BackStackRecordLocalSaveableStateRegistry(
synchronized(lock) {
val list = valueProviders.remove(key)
list?.remove(valueProvider)
if (list != null && list.isNotEmpty()) {
if (!list.isNullOrEmpty()) {
// if there are other providers for this key return list
// back to the map
valueProviders[key] = list
Expand Down
15 changes: 7 additions & 8 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinMultiplatformPluginWrapp
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
import org.jetbrains.kotlin.gradle.plugin.NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
import org.jetbrains.kotlin.gradle.targets.js.ir.DefaultIncrementalSyncTask
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import wtf.emulator.EwExtension

buildscript { dependencies { classpath(platform(libs.kotlin.plugins.bom)) } }
Expand Down Expand Up @@ -457,13 +458,11 @@ subprojects {
}
}
}
tasks.withType<KotlinNativeCompile>().configureEach {
notCompatibleWithConfigurationCache("https://youtrack.jetbrains.com/issue/KT-49933")
}
@Suppress("INVISIBLE_REFERENCE")
tasks.withType<org.jetbrains.kotlin.gradle.plugin.mpp.apple.FrameworkCopy>().configureEach {
@Suppress("INVISIBLE_MEMBER")
notCompatibleWithConfigurationCache("https://youtrack.jetbrains.com/issue/KT-49933")

// Workaround for missing task dependency in WASM
val executableCompileSyncTasks = tasks.withType(DefaultIncrementalSyncTask::class.java)
tasks.withType(KotlinJsTest::class.java).configureEach {
mustRunAfter(executableCompileSyncTasks)
}
}

Expand Down
14 changes: 8 additions & 6 deletions circuit-foundation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down Expand Up @@ -101,6 +99,10 @@ kotlin {
implementation(libs.compose.ui.testing.junit)
}
}
// We use a common folder instead of a common source set because there is no commonizer
// which exposes the browser APIs across these two targets.
jsMain { kotlin.srcDir("src/browserMain/kotlin") }
val wasmJsMain by getting { kotlin.srcDir("src/browserMain/kotlin") }
}
}

Expand Down
10 changes: 4 additions & 6 deletions circuit-overlay/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down
14 changes: 8 additions & 6 deletions circuit-retained/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down Expand Up @@ -80,6 +78,10 @@ kotlin {
implementation(projects.circuitRetained)
}
}
// We use a common folder instead of a common source set because there is no commonizer
// which exposes the browser APIs across these two targets.
jsMain { kotlin.srcDir("src/browserMain/kotlin") }
val wasmJsMain by getting { kotlin.srcDir("src/browserMain/kotlin") }
}

targets.configureEach {
Expand Down
10 changes: 4 additions & 6 deletions circuit-runtime-presenter/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down
18 changes: 11 additions & 7 deletions circuit-runtime-screen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,22 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

applyDefaultHierarchyTemplate()

sourceSets { commonMain { dependencies { api(libs.compose.runtime) } } }
sourceSets {
commonMain { dependencies { api(libs.compose.runtime) } }
// We use a common folder instead of a common source set because there is no commonizer
// which exposes the browser APIs across these two targets.
jsMain { kotlin.srcDir("src/browserMain/kotlin") }
val wasmJsMain by getting { kotlin.srcDir("src/browserMain/kotlin") }
}

targets.configureEach {
compilations.configureEach {
Expand Down
10 changes: 4 additions & 6 deletions circuit-runtime-ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down
10 changes: 4 additions & 6 deletions circuit-runtime/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down
14 changes: 8 additions & 6 deletions circuit-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
// endregion

Expand Down Expand Up @@ -63,6 +61,10 @@ kotlin {
}
val jvmTest by getting { dependsOn(commonJvmTest) }
val androidUnitTest by getting { dependsOn(commonJvmTest) }
// We use a common folder instead of a common source set because there is no commonizer
// which exposes the browser APIs across these two targets.
jsMain { kotlin.srcDir("src/browserMain/kotlin") }
val wasmJsMain by getting { kotlin.srcDir("src/browserMain/kotlin") }
}
}

Expand Down
31 changes: 25 additions & 6 deletions circuitx/effects/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,23 @@ kotlin {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
}
if (hasProperty("enableWasm")) {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = property("POM_ARTIFACT_ID").toString()
browser {
testTask {
useKarma {
useChromeHeadless()
useConfigDirectory(
rootProject.projectDir
.resolve("internal-test-utils")
.resolve("karma.config.d")
.resolve("wasm")
)
}
}
}
binaries.executable()
}
// endregion

Expand All @@ -54,7 +65,7 @@ kotlin {
}
}
val iosTest by getting { dependencies { dependsOn(commonTest) } }
val jsTest by getting { dependencies { dependsOn(commonTest) } }
val browserTest by creating { dependencies { dependsOn(commonTest) } }
val jvmTest by getting { dependencies { dependsOn(commonTest) } }
val androidUnitTest by getting {
dependsOn(commonTest)
Expand All @@ -65,6 +76,10 @@ kotlin {
implementation(libs.androidx.compose.ui.testing.manifest)
}
}
// We use a common folder instead of a common source set because there is no commonizer
// which exposes the browser APIs across these two targets.
jsTest { kotlin.srcDir("src/browserTest/kotlin") }
val wasmJsTest by getting { kotlin.srcDir("src/browserTest/kotlin") }
}
targets.configureEach {
compilations.configureEach {
Expand All @@ -73,6 +88,10 @@ kotlin {
}
}

// adding it here to make sure skiko is unpacked and available in web tests
// https://github.com/JetBrains/compose-multiplatform/issues/4133
compose.experimental { web.application {} }

android {
namespace = "com.slack.circuitx.sideeffects"

Expand Down
Loading

0 comments on commit 9220fac

Please sign in to comment.