plugins { kotlin("multiplatform") } fun pkgConfig(vararg flags: String): List { val result = providers.exec { commandLine(listOf("pkg-config") + flags + listOf("gtk+-3.0", "webkit2gtk-4.1")) isIgnoreExitValue = true } if (result.result.get().exitValue != 0) throw GradleException( "Linux desktop dependencies are missing. On Ubuntu/Debian run: " + "sudo apt install build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev\n" + result.standardError.asText.get() ) return result.standardOutput.asText.get().trim().split(Regex("\\s+")).filter { it.isNotEmpty() } } val nativeDir = layout.projectDirectory.dir("src/native") val objectFile = layout.buildDirectory.file("native/keluna.o") val compileHost by tasks.registering(Exec::class) { inputs.dir(nativeDir) outputs.file(objectFile) doFirst { objectFile.get().asFile.parentFile.mkdirs() commandLine(listOf("cc", "-Wall", "-Wextra", "-Werror", "-c", "-fPIC", nativeDir.file("keluna.c").asFile.absolutePath, "-o", objectFile.get().asFile.absolutePath) + pkgConfig("--cflags")) } } val generated = layout.buildDirectory.dir("generated/keluna") val web = rootProject.layout.projectDirectory.dir("keluna-web-runtime") val shared = rootProject.layout.projectDirectory.dir("examples/shared") val snakeCss = rootProject.layout.projectDirectory.file("examples/snake/src/commonMain/kotlin/dev/keluna/snake/Snake.css") val workshopCss = rootProject.layout.projectDirectory.file("examples/workshop/src/commonMain/kotlin/dev/keluna/workshop/WorkshopForm.css") val embedWebResources by tasks.registering { inputs.dir(web); inputs.dir(shared); inputs.file(snakeCss); inputs.file(workshopCss) outputs.dir(generated) doLast { // Encode UTF-8 bytes as integer literals, avoiding Kotlin and HTML interpolation. val css = listOf(web.file("defaults.css"), shared.file("theme.css"), shared.file("Counter.css")) .joinToString("\n") { it.asFile.readText() } val values = mapOf( "browserRuntime" to web.file("runtime.js").asFile.readText(), "styles" to css, "snakeStyles" to (web.file("defaults.css").asFile.readText() + "\n" + snakeCss.asFile.readText()), "workshopStyles" to (web.file("defaults.css").asFile.readText() + "\n" + workshopCss.asFile.readText()) ) val source = "package dev.keluna.example\n" + values.entries.joinToString("\n") { (name, value) -> "val $name: String = byteArrayOf(${value.toByteArray(Charsets.UTF_8).joinToString(",")}).decodeToString()" } generated.get().file("WebResources.kt").asFile.apply { parentFile.mkdirs(); writeText(source) } } } kotlin { linuxX64 { compilations.getByName("main") { cinterops.create("keluna") { definitionFile.set(nativeDir.file("keluna.def")) includeDirs(nativeDir) } compileTaskProvider.configure { dependsOn(embedWebResources) } } binaries { executable { entryPoint = "dev.keluna.example.main" } executable("snake") { entryPoint = "dev.keluna.snake.main" baseName = "snake" } executable("workshop") { entryPoint = "dev.keluna.workshop.main" baseName = "workshop" } withType { linkTaskProvider.configure { dependsOn(compileHost) } linkerOpts(objectFile.get().asFile.absolutePath) // Host WebKitGTK/GTK can require newer glibc symbols than the // Kotlin/Native sysroot provides. Resolve shared-library imports // against the host runtime; executable/object symbols remain checked. linkerOpts("--allow-shlib-undefined") // Kotlin/Native invokes ld directly, rather than the compiler driver. // Retain host library paths that pkg-config normally omits. linkerOpts(pkgConfig("--keep-system-libs", "--libs").flatMap { flag -> when { flag == "-pthread" -> listOf("-lpthread") flag.startsWith("-Wl,") -> flag.removePrefix("-Wl,").split(',') else -> listOf(flag) } }) } } } sourceSets.commonMain { kotlin.srcDir("../examples/shared") kotlin.srcDir(generated) dependencies { implementation(project(":keluna-ui")); implementation(project(":keluna-runtime")); implementation(project(":examples:snake")); implementation(project(":examples:workshop")) } } }