Skip to content

Commit

Permalink
Support auto-installation of box64 when required + fix libX11 loading…
Browse files Browse the repository at this point in the history
… on Linux (#1459)

* Fixes libX11 loading on Linux K/N

* Support auto-installation of box64 when required

* Some fixes allowing to run linuxX64 in arm box64

* Include Arm64 target
  • Loading branch information
soywiz authored Mar 24, 2023
1 parent ee376cc commit 8bfef88
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,39 +1,92 @@
package com.soywiz.korge.gradle.targets

import com.soywiz.korge.gradle.util.*
import org.gradle.configurationcache.extensions.*

object WineHQ {
val EXEC = "wine64"
}

enum class CrossExecType(val cname: String, val interp: String) {
object Box64 {
val VERSION = "v0.2.2"

fun exec(vararg params: String): SystemExecResult {
return executeSystemCommand(ArrayList<String>().apply {
when {
isWindows -> add("wsl")
isMacos -> add("lima")
}
addAll(params)
}.toTypedArray())
}

fun whichBox64(): String? = exec("which", "box64").let { it.takeIf { it.success }?.stdout }

fun ensureBox64(): String {
val box64 = whichBox64()
if (box64 == null) {
exec("mkdir", "-p", "/tmp/box64").stdout.trim()
val box64Path = exec("realpath", "/tmp/box64").stdout.trim()

exec("sudo", "apt", "update")
exec("sudo", "apt", "-y", "install", "git", "build-essential", "cmake")
exec("git", "clone", "-b", VERSION, "https://github.com/ptitSeb/box64.git", box64Path)
exec("cmake", "-S", box64Path, "-B", box64Path)
exec("make", "-C", box64Path)
exec("sudo", "make", "-C", box64Path, "install")
}
return box64 ?: whichBox64() ?: error("Couldn't install box64")
}
}

class CommandLineCrossResult(val hasBox64: Boolean) {
fun ensure() {
if (hasBox64) {
Box64.ensureBox64()
}
}
}

enum class CrossExecType(val cname: String, val interp: String, val arch: String = "X64") {
WINDOWS("mingw", "wine"),
LINUX("linux", "lima");
LINUX("linux", "lima"),
LINUX_ARM("linux", "lima", arch = "Arm64"),
;

val valid: Boolean get() = when (this) {
WINDOWS -> !isWindows
LINUX -> !com.soywiz.korge.gradle.targets.isLinux
LINUX -> !isLinux
LINUX_ARM -> !isLinux && isArm
}

val interpCapital = interp.capitalized()
val nameWithArch = "${cname}X64"
val archNoX64: String = if (arch == "X64") "" else arch
val interpCapital = interp.capitalized() + archNoX64
val nameWithArch = "${cname}$arch"
val nameWithArchCapital = nameWithArch.capitalized()

fun commands(vararg args: String): Array<String> {
return ArrayList<String>().apply {
fun commands(vararg args: String): Pair<CommandLineCrossResult, Array<String>> {
var hasBox64 = false
val array = ArrayList<String>().apply {
when (this@CrossExecType) {
WINDOWS -> {
if (isArm && !isMacos) add("box64") // wine on macos can run x64 apps via rosetta, but linux needs box64 emulator
if (isArm && !isMacos) {
add("box64")
hasBox64 = true
} // wine on macOS can run x64 apps via rosetta, but linux needs box64 emulator
add(WineHQ.EXEC)
}
LINUX -> {
LINUX, LINUX_ARM -> {
// @TODO: WSL
if (isWindows) add("wsl") else add("lima")
if (isArm) add("box64")
if (isArm && this@CrossExecType != LINUX_ARM) {
add("box64")
hasBox64 = true
}
}
}
addAll(args)
}.toTypedArray()
return CommandLineCrossResult(hasBox64) to array
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,15 @@ fun Project.configureNativeDesktopCross() {
val linkTask = project.tasks.findByName(linkTaskName) as? KotlinNativeLink?
//println("!!!!!!!!!linkTaskName=$linkTaskName :: $linkTask")
linkTask ?: continue
project.tasks.createThis<Exec>("runNative${deb}${type.interpCapital}") {
val runNativeCrossName = "runNative${deb}${type.interpCapital}"
//println("Creating $runNativeCrossName")
project.tasks.createThis<Exec>(runNativeCrossName) {
group = "run"
dependsOn(linkTask)
commandLineCross(linkTask.binary.outputFile.absolutePath, type = type)
val result = commandLineCross(linkTask.binary.outputFile.absolutePath, type = type)
doFirst {
result.ensure()
}
this.environment("WINEDEBUG", "-all")
workingDir = linkTask.binary.outputDirectory
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.soywiz.korge.gradle.targets.native

import com.soywiz.korge.gradle.targets.CrossExecType
import com.soywiz.korge.gradle.targets.*
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.options.Option

fun Exec.commandLineCross(vararg args: String, type: CrossExecType) {
commandLine(*type.commands(*args))
fun Exec.commandLineCross(vararg args: String, type: CrossExecType): CommandLineCrossResult {
val (result, array) = type.commands(*args)
commandLine(*array)
return result
}

abstract class KotlinNativeCrossTest : org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest() {
Expand All @@ -20,7 +22,7 @@ abstract class KotlinNativeCrossTest : org.jetbrains.kotlin.gradle.targets.nativ

@get:Internal
override val testCommand: TestCommand = object : TestCommand() {
val commands get() = type.commands()
val commands get() = type.commands().second

override val executable: String
get() = commands.first()
Expand All @@ -31,11 +33,13 @@ abstract class KotlinNativeCrossTest : org.jetbrains.kotlin.gradle.targets.nativ
testGradleFilter: Set<String>,
testNegativeGradleFilter: Set<String>,
userArgs: List<String>
): List<String> =
listOfNotNull(
): List<String> {
type.commands().first.ensure()
return listOfNotNull(
*commands.drop(1).toTypedArray(),
this@KotlinNativeCrossTest.executable.absolutePath,
) +
testArgs(testLogger, checkExitCode, testGradleFilter, testNegativeGradleFilter, userArgs)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.soywiz.korge.gradle.util

import java.io.*

fun executeSystemCommand(command: Array<String>, cwd: File? = null, envs: Map<String, String>? = null): SystemExecResult {
val output = StringBuilder()
var exitCode = -1
val processBuilder = ProcessBuilder(*command)
cwd?.let { processBuilder.directory(it) }
envs?.let { processBuilder.environment().putAll(it) }
processBuilder.redirectErrorStream(true)
val process = processBuilder.start()
BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
output.append(line).append("\n")
println(line)
}
exitCode = process.waitFor()
}
return SystemExecResult(exitCode, output.toString())
}
class SystemExecResult(var exitCode: Int, var stdout: String) {
val success get() = exitCode == 0
val failed get() = !success
}
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,10 @@ object RootKorlibsPlugin {
tasks.createThis<Exec>("runNative${deb}${type.interpCapital}") {
group = "run"
dependsOn(linkTask)
commandLineCross(linkTask.binary.outputFile.absolutePath, type = type)
val result = commandLineCross(linkTask.binary.outputFile.absolutePath, type = type)
doFirst {
result.ensure()
}
this.environment("WINEDEBUG", "-all")
workingDir = linkTask.binary.outputDirectory
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ internal val DEBUG_DYNAMIC_LIB = platform.posix.getenv("DEBUG_DYNAMIC_LIB")?.toK

public actual open class DynamicLibraryBase actual constructor(val names: List<String>) : DynamicSymbolResolver {
var name: String = names.firstOrNull() ?: "unknown"
val handle = names
val explored = names
.flatMap {
when {
it.endsWith(".dylib", ignoreCase = true) -> listOf(it)
it.endsWith(".dll", ignoreCase = true) -> listOf(it)
else -> listOf(it, "$it.so", "$it.so.1", "$it.so.2", "$it.so.3", "$it.so.4", "$it.so.5", "$it.so.6", "$it.so.7", "$it.so.8", "$it.so.9")
else -> {
val itnoso = it.replace(Regex("\\.so(\\.\\d+)?$"), "")
listOf(it, "$itnoso.so", "$itnoso.so.1", "$itnoso.so.2", "$itnoso.so.3", "$itnoso.so.4", "$itnoso.so.5", "$itnoso.so.6", "$itnoso.so.7", "$itnoso.so.8", "$itnoso.so.9")
}
}
}
val handle = explored
.firstNotNullOfOrNull { name ->
this@DynamicLibraryBase.name = name
val handle = dlopen(name, RTLD_LAZY)
Expand All @@ -27,7 +31,7 @@ public actual open class DynamicLibraryBase actual constructor(val names: List<S
//?: error("Can't load $names")
init {
if (DEBUG_DYNAMIC_LIB) println("Loaded '$names'...$handle")
if (handle == null) println("Couldn't load '$names' library")
if (handle == null) println("Couldn't load '$names' library : explored=$explored")
}
public actual val isAvailable get() = handle != null
override fun getSymbol(name: String): KPointer? {
Expand Down
7 changes: 5 additions & 2 deletions korgw/src/jvmMain/kotlin/com/soywiz/korgw/x11/X11Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,11 @@ internal interface X11Impl : X11 {
//int width, int height, int border_width,
//int border, int background);
fun XDefaultGC(display: X11.Display?, scn: Int): X11.GC?
fun XBlackPixel(display: X11.Display?, scn: Int): Int
fun XWhitePixel(display: X11.Display?, scn: Int): Int
//fun XBlackPixel(display: X11.Display?, scn: Int): Int
//fun XWhitePixel(display: X11.Display?, scn: Int): Int
fun XBlackPixel(display: X11.Display?, scn: Int): Int = 0
fun XWhitePixel(display: X11.Display?, scn: Int): Int = -1

fun XStoreName(display: X11.Display?, w: X11.Window?, window_name: String)
fun XSetIconName(display: X11.Display?, w: X11.Window?, window_name: String)
fun XLookupKeysym(e: X11.XEvent?, i: Int): Int
Expand Down
8 changes: 5 additions & 3 deletions korgw/src/linuxMain/kotlin/com/soywiz/korgw/X11GameWindow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import com.soywiz.korio.lang.*
import kotlinx.cinterop.*
import platform.posix.*

internal object X11 : DynamicLibrary("libX11.so") {
internal object X11 : DynamicLibrary("libX11") {
val XDefaultScreen by func<(d: CDisplayPointer) -> Int>()
val XRootWindow by func<(d: CDisplayPointer, scr: Int) -> Window>()
val XBlackPixel by func<(d: CDisplayPointer, scr: Int) -> Int>()
val XWhitePixel by func<(d: CDisplayPointer, scr: Int) -> Int>()
fun XBlackPixel(d: CDisplayPointer, scr: Int): Int = 0
fun XWhitePixel(d: CDisplayPointer, scr: Int): Int = -1
//val XBlackPixel by func<(d: CDisplayPointer, scr: Int) -> Int>()
//val XWhitePixel by func<(d: CDisplayPointer, scr: Int) -> Int>()
val XStoreName by func<(d: CDisplayPointer, w: Window, title: CString) -> Unit>()
val XSetIconName by func<(d: CDisplayPointer, w: Window, title: CString) -> Unit>()
val XDestroyWindow by func<(d: CDisplayPointer, w: Window) -> Unit>()
Expand Down
7 changes: 6 additions & 1 deletion korgw/src/nativeMain/kotlin/com/soywiz/kgl/KmlGlNativeExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,12 @@ open class GLFuncBase<T : Function<*>>(val name: String? = null) {
protected fun _getValue(property: KProperty<*>): CPointer<CFunction<T>>? {
if (!_set.value) {
_set.value = true
_value.value = glGetProcAddressT(getFuncName(property))
_value.value = try {
glGetProcAddressT(getFuncName(property))
} catch (e: Throwable) {
e.printStackTrace()
null
}
}
return _value.value
}
Expand Down

0 comments on commit 8bfef88

Please sign in to comment.