Skip to content

Commit

Permalink
Adding a gradle task to build Sparkle Motion into a Mac app.
Browse files Browse the repository at this point in the history
Ideally it would come up as a normal-looking Mac application, but lwjgl and Swing aren't compatible because they both want full control of the main thread's event loop. So unfortunately it starts the app with a taskbar icon but no menu bar or windows.

* Bumped shadow jar plugin to 8.3.0.
* Removed -Djava.awt.headless=true java arg and JOGL deps, not needed.

See also:
* What does the -XstartOnFirstThread VM Argument do / mean?
* jpackage-gradle-plugin for the official java packaging wrapper
  • Loading branch information
xian committed Sep 5, 2024
1 parent 4dd92e5 commit 724c9e7
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 13 deletions.
116 changes: 103 additions & 13 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.dsl.JsSourceMapEmbedMode
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
Expand Down Expand Up @@ -34,7 +35,7 @@ plugins {
kotlin("plugin.serialization") version Versions.kotlin
id("org.jetbrains.dokka") version Versions.dokka
id("com.google.devtools.ksp") version Versions.ksp
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.gradleup.shadow") version "8.3.0"
id("com.github.ben-manes.versions") version "0.39.0"
id("maven-publish")
id("name.remal.check-dependency-updates") version "1.0.211"
Expand All @@ -52,12 +53,14 @@ repositories {
group = "org.baaahs"
version = "0.0.1"

val JVM_VERSION = 19

fun kotlinw(target: String): String =
"org.jetbrains.kotlin-wrappers:kotlin-$target"

kotlin {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(19)
languageVersion = JavaLanguageVersion.of(JVM_VERSION)
vendor = JvmVendorSpec.ADOPTIUM
}

Expand Down Expand Up @@ -124,11 +127,6 @@ kotlin {
}
implementation("com.danielgergely.kgl:kgl-lwjgl:${Versions.kgl}")

// GLSL support via JOGL:
implementation("org.jogamp.gluegen:gluegen-rt-main:${Versions.jogl}")
implementation("org.jogamp.jogl:jogl-all-main:${Versions.jogl}")
// implementation("com.danielgergely.kgl:kgl-jogl:${Versions.kgl}")

// MDNS support:
implementation("org.jmdns:jmdns:3.5.7")

Expand Down Expand Up @@ -236,22 +234,29 @@ application {
if (isMac()) {
applicationDefaultJvmArgs += listOf(
"-XstartOnFirstThread", // required for OpenGL: https://github.com/LWJGL/lwjgl3/issues/311
"-Djava.awt.headless=true" // required for Beat Link; otherwise we get this: https://jogamp.org/bugzilla/show_bug.cgi?id=485
)
}
}

// include JS artifacts in any JAR we generate
tasks.named<Jar>("jvmJar").configure {
fun findWebpackTask(): TaskProvider<KotlinWebpack> {
val taskName = if (isProductionBuild || project.gradle.startParameter.taskNames.contains("installDist")) {
"jsBrowserProductionWebpack"
} else {
"jsBrowserDevelopmentWebpack"
}
return tasks.named<KotlinWebpack>(taskName)
}

tasks.named<Jar>("jvmJar").configure {
// include JS artifacts in jar
from(findWebpackTask().map { it.outputDirectory.file(it.mainOutputFileName) }) {
into("htdocs")
}
}

// bring output file along into the JAR
val webpackTask = tasks.named<KotlinWebpack>(taskName)
from(webpackTask.map { it.outputDirectory.file(it.mainOutputFileName) }) {
tasks.named<ShadowJar>("shadowJar") {
// include JS artifacts in shadow jar
from(findWebpackTask().map { it.outputDirectory.file(it.mainOutputFileName) }) {
into("htdocs")
}
}
Expand Down Expand Up @@ -351,6 +356,91 @@ tasks.withType<DependencyUpdatesTask> {
}
}

tasks.register("macApp") {
group = "application"
description = "Creates a macOS .app bundle for Sparkle Motion."

val shadowJarTask = tasks.named("shadowJar")
dependsOn(shadowJarTask)

val appName = "Sparkle Motion"
val appDir = buildDir("$appName.app")
val contentsDir = "$appDir/Contents"
val macosDir = "$contentsDir/MacOS"
val resourcesDir = "$contentsDir/Resources"
val javaDir = "$contentsDir/Java"

doLast {
copy {
from(shadowJarTask) {
include("*.jar")
rename { "sparklemotion.jar" }
}
into(javaDir)
}

copy {
from("src/macApp/resources/SparkleMotion.icns")
into(resourcesDir)
}

// Create MacOS directory and add executable script
mkdir(macosDir)
val javaArgs = listOf(
"-Xms512m", "-Xmx2048m",
"-Dapple.laf.useScreenMenuBar=true",
"-Xdock:name='Sparkle Motion'",
"-Xdock:icon=\"\$DIR/Contents/Resources/SparkleMotion.icns\"",
"-XstartOnFirstThread",
"-jar \"\$DIR/Contents/Java/sparklemotion.jar\""
)
file("$macosDir/SparkleMotion").writeText("""
#!/bin/bash
DIR="$(cd "$(dirname "$0")" && pwd)/../.."
java ${javaArgs.joinToString(" ")}
""".trimIndent())
file("$macosDir/SparkleMotion").setExecutable(true)

// Create Info.plist
file("$contentsDir/Info.plist").writeText("""
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>$appName</string>
<key>CFBundleDisplayName</key>
<string>$appName</string>
<key>CFBundleIdentifier</key>
<string>com.example.sparklemotion</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleExecutable</key>
<string>SparkleMotion</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleIconFile</key>
<string>SparkleMotion.icns</string>
<key>LSMinimumSystemVersion</key>
<string>10.9.0</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
</dict>
</plist>
""".trimIndent())
}
}

tasks.build {
dependsOn(tasks.named("macApp"))
}

// Janky. See https://github.com/google/ksp/issues/963#issuecomment-1780330007.
gradle.projectsEvaluated {
tasks {
Expand Down
Binary file added src/macApp/resources/SparkleMotion.icns
Binary file not shown.

0 comments on commit 724c9e7

Please sign in to comment.