Skip to content

Commit

Permalink
Supports packing macos app with a bundled ARM+x64 JRE (#1946)
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Oct 13, 2023
1 parent d84bcf7 commit 3c1ae47
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 27 deletions.
21 changes: 10 additions & 11 deletions buildSrc/src/main/kotlin/korlibs/korge/gradle/KorgeGradlePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -204,18 +204,17 @@ val Project.ext get() = extensions.getByType(ExtraPropertiesExtension::class.jav

fun Project.korge(callback: KorgeExtension.() -> Unit) = korge.apply(callback).also { it.finish() }
val Project.kotlin: KotlinMultiplatformExtension get() = this.extensions.getByType(KotlinMultiplatformExtension::class.java)
val Project.korge: KorgeExtension
get() {
val extension = project.extensions.findByName("korge") as? KorgeExtension?
return if (extension == null) {
//val newExtension = KorgeExtension(this, objectFactory = )
val newExtension = project.extensions.create("korge", KorgeExtension::class.java)
//project.extensions.add("korge", newExtension)
newExtension
} else {
extension
}
val Project.korge: KorgeExtension get() = extensionGetOrCreate("korge")

inline fun <reified T> Project.extensionGetOrCreate(name: String): T {
val extension = project.extensions.findByName(name) as? T?
return if (extension == null) {
val newExtension = project.extensions.create(name, T::class.java)
newExtension
} else {
extension
}
}

open class JsWebCopy() : Copy() {
@OutputDirectory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package korlibs.korge.gradle.targets

import korlibs.korge.gradle.*
import org.gradle.api.*

val Project.exKotlinSourceSetContainer: ExKotlinSourceSetContainer get() = extensionGetOrCreate("exKotlinSourceSetContainer")

open class ExKotlinSourceSetContainer(val project: Project) {
val kotlin = project.kotlin
val sourceSets = kotlin.sourceSets

val common by lazy { sourceSets.createPairSourceSet("common") }
val nonJs by lazy { sourceSets.createPairSourceSet("nonJs", common) }
val concurrent by lazy { sourceSets.createPairSourceSet("concurrent", common) }

// JS
val js by lazy { sourceSets.createPairSourceSet("js", common) }

// JVM
val jvm by lazy { sourceSets.createPairSourceSet("jvm", concurrent, nonJs) }

// Native
val native by lazy { sourceSets.createPairSourceSet("native", concurrent, nonJs) }
val posix by lazy { sourceSets.createPairSourceSet("posix", native) }
val darwin by lazy { sourceSets.createPairSourceSet("darwin", posix) }
val darwinMobile by lazy { sourceSets.createPairSourceSet("darwinMobile", darwin) }
val iosTvos by lazy { sourceSets.createPairSourceSet("iosTvos", darwinMobile/*, iosTvosMacos*/) }
val tvos by lazy { sourceSets.createPairSourceSet("tvos", iosTvos) }
val ios by lazy { sourceSets.createPairSourceSet("ios", iosTvos/*, iosMacos*/) }
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package korlibs.korge.gradle.targets.apple

import korlibs.korge.gradle.GameCategory
import korlibs.korge.gradle.KorgeExtension
import korlibs.korge.gradle.*

object InfoPlistBuilder {
fun GameCategory?.toUTI(): String {
Expand Down Expand Up @@ -62,6 +61,7 @@ object InfoPlistBuilder {
appendLine(" <key>CFBundleSignature</key><string>????</string>")
appendLine(" <key>LSMinimumSystemVersion</key><string>10.9.0</string>")
appendLine(" <key>NSHighResolutionCapable</key><true/>")
appendLine(" <key>LSRequiresNativeExecution</key><true/>")
}
appendLine("""</dict></plist>""")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package korlibs.korge.gradle.targets.desktop

import korlibs.korge.gradle.*
import korlibs.korge.gradle.targets.*
import korlibs.korge.gradle.targets.apple.*
import korlibs.korge.gradle.util.*
import org.gradle.api.*
import java.io.*
import java.net.*
import java.security.*

// https://stackoverflow.com/questions/13017121/unpacking-tar-gz-into-root-dir-with-gradle
// https://github.com/korlibs/universal-jre/
object DesktopJreBundler {
// https://github.com/adoptium/temurin21-binaries/releases/tag/jdk-21%2B35

data class UrlRef(val url: String, val sha256: String)

val JRE_MACOS_LAUNCHER = UrlRef(
"https://github.com/korlibs/universal-jre/releases/download/0.0.1/app",
sha256 = "4123b08e24678885781b04125675aa2f7d2af87583a753d16737ad154934bf0b"
)

val JRE_MACOS_UNIVERSAL = UrlRef(
"https://github.com/korlibs/universal-jre/releases/download/0.0.1/macos-universal-jdk-21+35-jre.tar.gz",
sha256 = "6d2d0a2e35c649fc731f5d3f38d7d7828f7fad4b9b2ea55d4d05f0fd26cf93ca"
)

val JRE_DIR = File(korgeCacheDir, "jre")
val UNIVERSAL = File(JRE_DIR, "universal")
val UNIVERSAL_JRE = File(UNIVERSAL, "jdk-21+35-jre/Contents/jre")

fun cachedFile(urlRef: UrlRef): File {
val downloadUrl = URL(urlRef.url)
val localFile = File(JRE_DIR, File(downloadUrl.file).name).ensureParents()
if (!localFile.isFile) {
println("Downloading $downloadUrl...")
val bytes = downloadUrl.readBytes()
val actualSha256 = MessageDigest.getInstance("SHA-256").digest(bytes).hex
val expectedSha256 = urlRef.sha256
if (actualSha256 != expectedSha256) {
error("URL: ${urlRef.url} expected to have $expectedSha256 but was $actualSha256")
}
localFile.writeBytes(bytes)
}
return localFile
}

fun createMacosApp(project: Project, fatJar: File) {
if (!UNIVERSAL_JRE.isDirectory) {
project.copy {
it.from(project.tarTree(cachedFile(JRE_MACOS_UNIVERSAL)))
it.into(UNIVERSAL)
}
}

val gameApp = File(project.buildDir, "platforms/jvm-macos/game.app/Contents").ensureParents()
project.copy {
it.from(UNIVERSAL_JRE)
it.into(File(gameApp, "MacOS/jre"))
}

val korge = project.korge
File(gameApp, "Resources/${korge.exeBaseName}.icns").ensureParents().writeBytes(IcnsBuilder.build(korge.getIconBytes()))
File(gameApp, "Info.plist").writeText(InfoPlistBuilder.build(korge))
val exec = File(gameApp, "MacOS/${File(korge.exeBaseName).name}").ensureParents()
exec.writeBytes(cachedFile(JRE_MACOS_LAUNCHER).readBytes())
exec.setExecutable(true)
project.copy {
it.from(fatJar)
it.into(File(gameApp, "MacOS"))
it.rename { "app.jar" }
it.filePermissions {
it.user.execute = true
it.group.execute = true
it.other.execute = true
}
}
}
}
23 changes: 21 additions & 2 deletions buildSrc/src/main/kotlin/korlibs/korge/gradle/targets/ios/Ios.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ import java.io.*
fun Project.configureNativeIos(projectType: ProjectType) {
configureNativeIosTvos(projectType, "ios")
configureNativeIosTvos(projectType, "tvos")

val exKotlinSourceSetContainer = this.project.exKotlinSourceSetContainer
this.project.kotlin.apply {
sourceSets.apply {
for (target in listOf(iosArm64(), iosX64(), iosSimulatorArm64(), tvosArm64(), tvosX64(), tvosSimulatorArm64())) {
val native = createPairSourceSet(target.name)
when {
target.isIos -> native.dependsOn(exKotlinSourceSetContainer.ios)
target.isTvos -> native.dependsOn(exKotlinSourceSetContainer.tvos)
}
}
}
}
}

val Project.xcframework by projectExtension() {
Expand Down Expand Up @@ -191,8 +204,14 @@ fun Project.configureNativeIosTvosRun(targetName: String) {
val simulatorSuffix = if (simulator) "Simulator" else "Device"
//val arch = if (simulator) "X64" else "Arm64"
//val arch2 = if (simulator) "x64" else "armv8"
val arch = if (simulator) "X64" else "Arm64"
val arch2 = if (simulator) "x86_64" else "arm64"
val arch = when {
simulator -> if (isArm) "SimulatorArm64" else "X64"
else -> "Arm64"
}
val arch2 = when {
simulator -> if (isArm) "arm64" else "x86_64"
else -> "arm64"
}
val sdkName = if (simulator) "iphonesimulator" else "iphoneos"
tasks.createThis<Exec>("${targetName}Build$simulatorSuffix$debugSuffix") {
//task.dependsOn(prepareKotlinNativeIosProject, "linkMain${debugSuffix}FrameworkIos$arch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import korlibs.korge.gradle.*
import korlibs.korge.gradle.gkotlin
import korlibs.korge.gradle.kotlin
import korlibs.korge.gradle.targets.*
import korlibs.korge.gradle.targets.desktop.*
import korlibs.korge.gradle.util.*
import korlibs.root.*
import org.gradle.api.*
Expand Down Expand Up @@ -208,6 +209,14 @@ private fun Project.addProguard() {
}
}

project.tasks.createThis<Task>("packageJvmMacosApp") {
dependsOn(packageJvmFatJar)
group = GROUP_KORGE_PACKAGE
doLast {
DesktopJreBundler.createMacosApp(project, packageJvmFatJar.outputs.files.first())
}
}

project.tasks.createThis<ProGuardTask>("packageJvmFatJarProguard") {
dependsOn(packageJvmFatJar)
group = GROUP_KORGE_PACKAGE
Expand Down
18 changes: 9 additions & 9 deletions buildSrc/src/main/kotlin/korlibs/root/RootKorlibsPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -428,20 +428,20 @@ object RootKorlibsPlugin {
}

if (supportKotlinNative) {
//val iosTvosMacos by lazy { createPairSourceSet("iosTvosMacos", darwin) }
//val iosMacos by lazy { createPairSourceSet("iosMacos", iosTvosMacos) }

//val linux by lazy { createPairSourceSet("linux", posix) }
//val macos by lazy { createPairSourceSet("macos", iosMacos) }
//val mingw by lazy { createPairSourceSet("mingw", native) }

val native by lazy { createPairSourceSet("native", concurrent) }
val posix by lazy { createPairSourceSet("posix", native) }
val darwin by lazy { createPairSourceSet("darwin", posix) }
val iosTvosMacos by lazy { createPairSourceSet("iosTvosMacos", darwin) }
val iosMacos by lazy { createPairSourceSet("iosMacos", iosTvosMacos) }

val linux by lazy { createPairSourceSet("linux", posix) }
val macos by lazy { createPairSourceSet("macos", iosMacos) }
val mingw by lazy { createPairSourceSet("mingw", native) }

val darwinMobile by lazy { createPairSourceSet("darwinMobile", darwin) }
val iosTvos by lazy { createPairSourceSet("iosTvos", darwinMobile, iosTvosMacos) }
val iosTvos by lazy { createPairSourceSet("iosTvos", darwinMobile/*, iosTvosMacos*/) }
val tvos by lazy { createPairSourceSet("tvos", iosTvos) }
val ios by lazy { createPairSourceSet("ios", iosTvos, iosMacos) }
val ios by lazy { createPairSourceSet("ios", iosTvos/*, iosMacos*/) }

for (target in mobileTargets(project)) {
val native = createPairSourceSet(target.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import com.android.build.gradle.*
import com.soywiz.kproject.internal.*
import com.soywiz.kproject.model.*
import com.soywiz.kproject.util.*
import korlibs.korge.gradle.targets.android.*
import org.gradle.api.*
import org.gradle.api.plugins.*
import org.gradle.api.tasks.testing.logging.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.*
import java.io.File
import java.io.*

@Suppress("unused")
class KProjectPlugin : Plugin<Project> {
Expand Down Expand Up @@ -140,7 +139,7 @@ class KProjectPlugin : Plugin<Project> {
val native = createPair("native").dependsOn(concurrent)
val posix = createPair("posix").dependsOn(native)
val apple = createPair("apple").dependsOn(posix)
val macos = createPair("macos").dependsOn(apple)
//val macos = createPair("macos").dependsOn(apple)
//if (hasTarget(KProjectTarget.DESKTOP)) {
// createPair("macosX64").dependsOn(macos)
// createPair("macosArm64").dependsOn(macos)
Expand Down

0 comments on commit 3c1ae47

Please sign in to comment.