JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.24k stars 1.11k forks source link

Internal compiler error when attempting to live edit multiplatform Compose code #4974

Closed natanfudge closed 1 week ago

natanfudge commented 1 week ago

Describe the bug In a Compose MP project, while running on an Android device, live editing Android source set code is possible. However, if you edit in a multiplatform (common) source set, an internal compiler error occurs.

Affected platforms Android Versions

To Reproduce Steps to reproduce the behavior:

  1. Open any Compose multiplatform app that supports Android

  2. Make absolutely sure to have this in your android {} block in the app module's build.gradle (The example multiplatform Compose app doesn't do this):

    buildFeatures {
        compose = true
    }
  3. Launch Android app in an integrated emulator , use Android Studio to get the below error. The IntelliJ IDEA Android plugin simply says "Test sources not supported: filexyz.kt"

  4. Edit some Compose code in the commonMain source set

  5. Observe "Out Of Date" error: image Here it is as text:

    Internal Error in App.kt. Internal Error During Code Gen. java.lang.ClassCastException: class org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments cannot be cast to class org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments (org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments and org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments are in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @787c3cc) at com.android.tools.idea.run.deployment.liveedit.CompileScopeImpl.backendCodeGen(CompileScope.kt:198) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compileKtFile$1$generationState$1.invoke(LiveEditCompiler.kt:165) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compileKtFile$1$generationState$1.invoke(LiveEditCompiler.kt:164) at com.android.tools.idea.run.deployment.liveedit.PerformanceTracker.record(PerformanceTracker.kt:28) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compileKtFile$1.invokeSuspend(LiveEditCompiler.kt:164) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compileKtFile$1.invoke(LiveEditCompiler.kt) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compileKtFile$1.invoke(LiveEditCompiler.kt) at com.android.tools.idea.run.deployment.liveedit.CompileScopeKt$runWithCompileLock$1.invokeSuspend(CompileScope.kt:248) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(Builders.kt:38) at com.android.tools.idea.run.deployment.liveedit.CompileScopeKt.runWithCompileLock(CompileScope.kt:246) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler.compileKtFile(LiveEditCompiler.kt:146) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler.access$compileKtFile(LiveEditCompiler.kt:40) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compile$compileCmd$1.invoke(LiveEditCompiler.kt:91) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compile$compileCmd$1.invoke(LiveEditCompiler.kt:79) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compile$1.invokeSuspend(LiveEditCompiler.kt:133) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compile$1.invoke(LiveEditCompiler.kt) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler$compile$1.invoke(LiveEditCompiler.kt) at com.intellij.openapi.application.impl.RwLockHolder.readAction(RwLockHolder.kt:271) at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:845) at com.android.tools.idea.run.deployment.liveedit.LiveEditCompiler.compile(LiveEditCompiler.kt:126) at com.android.tools.idea.run.deployment.liveedit.LiveEditProjectMonitor.processChanges(LiveEditProjectMonitor.java:558) at com.android.tools.idea.run.deployment.liveedit.LiveEditProjectMonitor.doOnManualEditTrigger(LiveEditProjectMonitor.java:488) at com.android.tools.idea.run.deployment.liveedit.LiveEditProjectMonitor$4.invoke(LiveEditProjectMonitor.java:539) at com.android.tools.idea.run.deployment.liveedit.LiveEditProjectMonitor$4.invoke(LiveEditProjectMonitor.java:488) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:840)

Expected behavior Edited code is live edited into the Android device

Current workaround - recommended for any and all users of Jetbrains Compose, even if they don't even target Android:

  1. Add the following to the android { block in composeApp/build.gradle.kts:
    android {
    // ...
    buildFeatures {
        compose = true
    }
    }
  2. Start writing in the Android source set only. In an existing app, cut & paste all your common code into androidMain.
  3. Test your app on an Android device (using Android Studio is preferred because it has the more up-to-date version of Live Edit that works much better) to take advantage of the Live Edit functionality, allowing instant GUI updates on code save, improving iteration speed significantly, especially for small changes.
  4. Be careful not to use any JVM-only APIs (java.* and most Kotlin reflection)
  5. When you are ready to publish your application, cut & paste all of your androidMain code into common, and resolve any platform specific errors (usually <1% of the work).
  6. Publish in all platforms
  7. For the next versions of your app, go back to step 2. A script could be used to speed up the cutting & pasting.
MatkovIvan commented 1 week ago

Supporting live edit for Multiplatform is tracked here - https://github.com/JetBrains/compose-multiplatform/issues/2063, closing this as duplicate

For issues with Android Studio tooling, please use appropriate Google issue tracker.

natanfudge commented 1 week ago

@MatkovIvan can we get an official comment on #2036?