DatL4g / KCEF

Kotlin implementation of jcefmaven with more modern setup and depending on JetBrains/jcef
https://datl4g.github.io/KCEF/
Apache License 2.0
28 stars 5 forks source link

packageDmg and install run init KCEF error #2

Closed LiuPack closed 5 months ago

LiuPack commented 5 months ago
InstallationDirectory
at dev.datlag.kcef.KCEFException$installationDirectory.<clinit>(KCEFException.kt)at dev.datlag.kcef.KCEFBuilder.install$kcef(KCEFBuilder.kt:192)at dev.datlag.kcef.KCEF$init$installResult$1.invokeSuspend(KCEF.kt:105)at dev.datlag.kcef.KCEF$init$installResult$1.invoke(KCEF.kt)at dev.datlag.kcef.KCEF$init$installResult$1.invoke(KCEF.kt)at dev.datlag.kcef.common.ExtendCoroutineKt$suspendCatching$2.invokeSuspend(ExtendCoroutine.kt:19)at dev.datlag.kcef.common.ExtendCoroutinekt$suspendCatching$2.invoke(ExtendCoroutine.kt)at dev.datlag.kcef.common.ExtendCoroutineKt$suspendCatching$2.invokelExtendCoroutine.ktat kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)at dev.datlag.kcef.common.ExtendCoroutinekt.suspendCatching(ExtendCoroutine.kt:16)at dev.datlag.kcef.KCEF.init(KCEF.kt:104)
at dev.datlag.kcef.KCEF.init(KCEF.kt:45)at ComposableSingletons$MainKt$lambda-1$1$1$1$1.invokeSuspend(main.kt:43)at kotlin.coroutines.ivm.internal.BaseContinuationlmpl.resumeWith(Continuationlmpl.kt:33)at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)at kotlinx.coroutines.scheduling.Tasklmpl.run(Tasks.kt:103)at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793at kotlinx.coroutines,scheduling,CoroutineScheduler$Worker,runWorkerlCoroutineScheduler.kt:697)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

build release dmg ,install and run KCEF.init error

this is my KCEF Init

Window(
        title = "玩 Android",
        icon = painterResource("android_studio.png"),
        state = rememberWindowState(width = 350.dp, height = 700.dp),
        onCloseRequest = ::exitApplication,
    ) {
        window.minimumSize = Dimension(350, 700)
        var restartRequired by remember { mutableStateOf(false) }
        var downloading by remember { mutableStateOf(0f) }
        var initialized by remember { mutableStateOf(false) }
        var failedMsg by remember { mutableStateOf("") }
        LaunchedEffect(Unit) {
            withContext(Dispatchers.IO) {
                KCEF.init(builder = {
                    progress {
                        onDownloading {
                            downloading = max(it, 0f)
                        }
                        onInitialized {
                            initialized = true
                        }
                    }
                }, onError = {
                    failedMsg = it?.stackTraceToString().orEmpty()
                    it?.printStackTrace()
                }, onRestartRequired = {
                    restartRequired = true
                })
            }
        }
        if (restartRequired) {
            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, content = {
                Text(text = failedMsg)
            })
        } else {
            if (initialized) {
                App()
            } else {
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center,
                    content = {
                        Text(text = "Downloading $downloading%")
                    })
            }
        }
        DisposableEffect(Unit) {
            onDispose {
                KCEF.disposeBlocking()
            }
        }
    }

this is my build.gradle

compose.desktop {
    application {
        buildTypes.release.proguard {
            configurationFiles.from("compose-desktop.pro")
        }
        mainClass = "MainKt"
        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "玩 Android"
            packageVersion = "1.0.0"
            macOS {
                dockName = "玩 Android"
                setDockNameSameAsPackageName = true
                bundleID = "org.liupack.wanandroid"
                iconFile.set(project.file("launcher/icon.icns"))
            }
            linux {
                iconFile.set(project.file("launcher/icon.png"))
            }
            windows {
                iconFile.set(project.file("launcher/icon.ico"))
            }
        }
    }
}

afterEvaluate {
    tasks.withType<JavaExec> {
        jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
        jvmArgs(
            "--add-opens", "java.desktop/java.awt.peer=ALL-UNNAMED"
        )

        if (System.getProperty("os.name").contains("Mac")) {
            jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/sun.lwawt=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
        }
    }
}
DatL4g commented 5 months ago

Installation Directory

You should provide a proper installation directory. This can depend on the platform the application is running on.

I built a tooling library that can help you. The most basic (core) module is enough, so all you need is

dependencies {
  implementation("dev.datlag.tooling:tooling:1.1.0") // can be used in commonMain
}

Then you can initialize KCEF like this:


/**
* Your provided app name should not contain whitespaces, as some systems do not support it
* So instead of "My application", do something like "My-application"
*
* Your provided app name should not be localized, so it should not change if the user switches his language
* The app name has to be a constant value
*/
val appWriteableRootFolder = Tooling.getApplicationWriteableRootFolder("your-app-name") ?: File("./")
val kcefInstallDir = File(appWriteableRootFolder, "kcef-bundle")

KCEF.init(
    builder = {
        installDir(kcefInstallDir)
        // all other configuration
    },
    // all other configuration
)

Build Script

Looking at your build script indicates that the browser won't work on Mac if you package it.

The afterEvaluate method is only working in development environments. You can completly remove it and configure it like this:

compose {
    desktop {
        application {
            jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/sun.java2d=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/java.awt.peer=ALL-UNNAMED")

            if (System.getProperty("os.name").contains("Mac")) {
                jvmArgs("--add-opens", "java.desktop/sun.lwawt=ALL-UNNAMED")
                jvmArgs("--add-opens", "java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
            }

            // all other configuration
        }
    }
}

Here is a full working example of the build script: https://github.com/DatL4g/Burning-Series/blob/compose/app/desktop/build.gradle.kts

LiuPack commented 5 months ago

安装目录

您应该提供正确的安装目录。这可能取决于运行应用程序的平台。

我构建了一个可以帮助您的工具库。最基本的(核心)模块就足够了,所以你所需要的只是

dependencies {
  implementation("dev.datlag.tooling:tooling:1.1.0") // can be used in commonMain
}

然后你可以像这样初始化 KCEF:

/**
* Your provided app name should not contain whitespaces, as some systems do not support it
* So instead of "My application", do something like "My-application"
*
* Your provided app name should not be localized, so it should not change if the user switches his language
* The app name has to be a constant value
*/
val appWriteableRootFolder = Tooling.getApplicationWriteableRootFolder("your-app-name") ?: File("./")
val kcefInstallDir = File(appWriteableRootFolder, "kcef-bundle")

KCEF.init(
  builder = {
      installDir(kcefInstallDir)
      // all other configuration
  },
  // all other configuration
)

构建脚本

查看您的构建脚本表明,如果您打包浏览器,它将无法在 Mac 上运行。

该方法仅适用于开发环境。您可以完全删除它并按如下方式配置它:afterEvaluate

compose {
  desktop {
      application {
          jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/sun.awt=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/sun.java2d=ALL-UNNAMED")
            jvmArgs("--add-opens", "java.desktop/java.awt.peer=ALL-UNNAMED")

          if (System.getProperty("os.name").contains("Mac")) {
              jvmArgs("--add-opens", "java.desktop/sun.lwawt=ALL-UNNAMED")
                jvmArgs("--add-opens", "java.desktop/sun.lwawt.macosx=ALL-UNNAMED")
          }

          // all other configuration
      }
  }
}

以下是构建脚本的完整工作示例: https://github.com/DatL4g/Burning-Series/blob/compose/app/desktop/build.gradle.kts

nice! it's working !! thanks!!!

tomastiminskas commented 5 months ago

Hi @LiuPack I'm facing a similar issue. I could implement KCEF on my compose desktop app and make it to work without issue swhen running from IntelliJ Idea. But when creating the MSI file and installing the app on Windows I'm always getting into onRestartRequired callback on the initialization.

I tried using Tooling.getApplicationWriteableRootFolder but having the same issue. Any ideas on how to solve this?

DatL4g commented 5 months ago

@tomastiminskas It's required to clean the project before packaging ./gradlew clean or at least delete the kcef-bundle directory. When this is included in the package it does not have all required permissions to read/write and execute.

Otherwise just try to debug where the package will be saved when you use the Tooling method. I had no issues while testing it in this full working application: https://github.com/DatL4g/Burning-Series/blob/compose/app/desktop/src/jvmMain/kotlin/dev/datlag/burningseries/InitCEF.kt

tomastiminskas commented 5 months ago

@DatL4g I copied the implementation of the example project and even when running from IntelliJ Idea I'm getting on the onError callback with the following message:

"The provided archive contains a bad (malicious) file".

Thanks in advance for you help

LiuPack commented 5 months ago

@DatL4g I copied the implementation of the example project and even when running from IntelliJ Idea I'm getting on the onError callback with the following message:

"The provided archive contains a bad (malicious) file".

Thanks in advance for you help

Hi @tomastiminskas This is how I currently handle it. You can control the different installation paths of run debug and packageMsi through a variable. The default File("jcef-bundle") is used under run debug, and Tooling.getApplicationWriteableRootFolder is used under packageMsi. code in https://github.com/LiuPack/wanandroid/blob/master/composeApp/src/jvmMain/kotlin/main.kt

tomastiminskas commented 5 months ago

Thanks @LiuPack it worked.