mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
Merge pull request #2961 from nrobi144/fix/desktop-proguard-jackson-crash
fix(desktop): fix release build crashes (ProGuard, VLC, jlink modules)
This commit is contained in:
@@ -91,6 +91,9 @@ compose.desktop {
|
||||
|
||||
jvmArgs += "-Xmx2g"
|
||||
|
||||
// VLC plugin path fallback — used if JNA setenv and bundled discovery both fail
|
||||
jvmArgs += "-Dvlc.plugin.path=\$APPDIR/resources/vlc/plugins"
|
||||
|
||||
// Forward platform-preview overrides from the gradle invocation to the
|
||||
// launched app's JVM so `./gradlew :desktopApp:run -Damethyst.platform=GNOME`
|
||||
// works in addition to the env-var form (`AMETHYST_PLATFORM=GNOME`).
|
||||
@@ -101,7 +104,15 @@ compose.desktop {
|
||||
nativeDistributions {
|
||||
appResourcesRootDir.set(project.layout.projectDirectory.dir("src/jvmMain/appResources"))
|
||||
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm)
|
||||
modules("java.management") // Required by kmp-tor TorRuntime
|
||||
// Output of ./gradlew suggestRuntimeModules (+ java.management already present)
|
||||
modules(
|
||||
"java.instrument", // Runtime instrumentation (agent/profiler hooks)
|
||||
"java.management", // Required by kmp-tor TorRuntime
|
||||
"java.prefs", // java.util.prefs (desktop persistence)
|
||||
"java.sql", // JDBC metadata (Jackson, SQLite driver)
|
||||
"jdk.security.auth", // JAAS authentication callbacks
|
||||
"jdk.unsupported", // sun.misc.Unsafe (VLCJ ByteBufferFactory)
|
||||
)
|
||||
|
||||
packageName = "Amethyst"
|
||||
packageVersion = appVersion
|
||||
@@ -143,6 +154,7 @@ compose.desktop {
|
||||
// whose declared return type the JVM verifier rejects (R8 doesn't hit
|
||||
// this — it generates bridges differently from ProGuard).
|
||||
buildTypes.release.proguard {
|
||||
version.set("7.9.1") // Kotlin 2.3 metadata support
|
||||
configurationFiles.from(project.file("compose-rules.pro"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,16 @@
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# kmp-tor — loads native Tor daemon via JNI reflection
|
||||
-keep class io.matthewnelson.** { *; }
|
||||
|
||||
# Coil image loader — uses ServiceLoader for decoder/fetcher registration
|
||||
-keep class coil3.** { *; }
|
||||
|
||||
# OkHttp/Okio — platform detection and I/O via reflection
|
||||
-keep class okhttp3.** { *; }
|
||||
-keep class okio.** { *; }
|
||||
|
||||
# ============================================================================
|
||||
# Optimize sub-pass — disable the one that produces invalid okio bytecode
|
||||
# ============================================================================
|
||||
@@ -185,3 +195,9 @@
|
||||
# to detect logging. We ship slf4j-nop; keep it intact so detection succeeds.
|
||||
-keep class org.slf4j.** { *; }
|
||||
-dontwarn org.slf4j.**
|
||||
|
||||
# ============================================================================
|
||||
# Kotlin 2.3 stdlib stubs — compile-time classes with no JVM runtime class
|
||||
# ============================================================================
|
||||
-dontwarn kotlin.concurrent.atomics.**
|
||||
-dontwarn kotlin.jvm.internal.EnhancedNullability
|
||||
|
||||
+27
-2
@@ -20,8 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.desktop.service.media
|
||||
|
||||
import com.sun.jna.Function
|
||||
import com.sun.jna.NativeLibrary
|
||||
import uk.co.caprica.vlcj.binding.lib.LibC
|
||||
import uk.co.caprica.vlcj.binding.support.runtime.RuntimeUtil
|
||||
import uk.co.caprica.vlcj.factory.discovery.strategy.BaseNativeDiscoveryStrategy
|
||||
|
||||
@@ -36,6 +36,14 @@ class MacOsVlcDiscoverer :
|
||||
arrayOf("libvlc\\.dylib", "libvlccore\\.dylib"),
|
||||
arrayOf("%s/plugins"),
|
||||
) {
|
||||
/** Plugin path discovered during [setPluginPath], available after discovery. */
|
||||
var discoveredPluginPath: String? = null
|
||||
private set
|
||||
|
||||
/** Whether [setPluginPath] successfully set the process env var. */
|
||||
var envVarSet: Boolean = false
|
||||
private set
|
||||
|
||||
override fun supported(): Boolean {
|
||||
val os = System.getProperty("os.name").lowercase()
|
||||
return "mac" in os
|
||||
@@ -52,5 +60,22 @@ class MacOsVlcDiscoverer :
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setPluginPath(pluginPath: String?): Boolean = LibC.INSTANCE.setenv(PLUGIN_ENV_NAME, pluginPath, 1) == 0
|
||||
override fun setPluginPath(pluginPath: String?): Boolean {
|
||||
if (pluginPath == null) return false
|
||||
discoveredPluginPath = pluginPath
|
||||
return try {
|
||||
// Call setenv directly via JNA Function API. This bypasses vlcj's
|
||||
// LibC interface binding which fails on macOS 13+ because dlsym
|
||||
// can't resolve the versioned symbol `setenv$3b99ba0d`.
|
||||
val setenv = Function.getFunction("c", "setenv")
|
||||
val result = setenv.invokeInt(arrayOf<Any>(PLUGIN_ENV_NAME, pluginPath, 1)) == 0
|
||||
envVarSet = result
|
||||
result
|
||||
} catch (_: Throwable) {
|
||||
// JNA Function call also failed — VlcjPlayerPool will use
|
||||
// --plugin-path factory arg as fallback.
|
||||
envVarSet = false
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-3
@@ -55,6 +55,9 @@ object VlcjPlayerPool {
|
||||
private val idleThumbPlayers = ConcurrentLinkedQueue<EmbeddedMediaPlayer>()
|
||||
private const val MAX_THUMB_POOL_SIZE = 2
|
||||
|
||||
// Cached plugin path for audio factory creation (set during init)
|
||||
private var cachedPluginPath: String? = null
|
||||
|
||||
// Audio player pool (shared factory with --no-video)
|
||||
private var audioFactory: MediaPlayerFactory? = null
|
||||
private val allAudioPlayers = mutableListOf<MediaPlayer>()
|
||||
@@ -76,12 +79,13 @@ object VlcjPlayerPool {
|
||||
|
||||
return try {
|
||||
// Try bundled VLC first, then fall through to system VLC
|
||||
val macOsDiscoverer = MacOsVlcDiscoverer()
|
||||
val discovery =
|
||||
try {
|
||||
val nd =
|
||||
NativeDiscovery(
|
||||
BundledVlcDiscoverer(),
|
||||
MacOsVlcDiscoverer(),
|
||||
macOsDiscoverer,
|
||||
)
|
||||
val found = nd.discover()
|
||||
if (found) {
|
||||
@@ -99,7 +103,34 @@ object VlcjPlayerPool {
|
||||
val systemDiscovery = NativeDiscovery().discover()
|
||||
println("VLC: system discovery ${if (systemDiscovery) "succeeded" else "failed"}")
|
||||
}
|
||||
val f = MediaPlayerFactory("--no-xlib")
|
||||
|
||||
// Delete stale VLC plugin cache on macOS to avoid spam warnings
|
||||
if ("mac" in System.getProperty("os.name").lowercase()) {
|
||||
try {
|
||||
val cacheDir = java.io.File(System.getProperty("user.home"), "Library/Caches/org.videolan.vlc")
|
||||
cacheDir.listFiles()?.filter { it.name.startsWith("plugins") }?.forEach { it.delete() }
|
||||
} catch (_: Throwable) {
|
||||
// Best-effort cache cleanup
|
||||
}
|
||||
}
|
||||
|
||||
// Build factory args — add --plugin-path fallback if env var wasn't set
|
||||
val factoryArgs = mutableListOf("--no-xlib")
|
||||
if (!macOsDiscoverer.envVarSet) {
|
||||
val pluginPath =
|
||||
macOsDiscoverer.discoveredPluginPath
|
||||
?: System.getProperty("vlc.plugin.path")
|
||||
?: VlcResourceResolver.findVlcDir()?.let { "${it.absolutePath}/plugins" }
|
||||
if (pluginPath != null) {
|
||||
factoryArgs += "--plugin-path=$pluginPath"
|
||||
println("VLC: using --plugin-path fallback: $pluginPath")
|
||||
}
|
||||
}
|
||||
|
||||
cachedPluginPath = macOsDiscoverer.discoveredPluginPath
|
||||
?: System.getProperty("vlc.plugin.path")
|
||||
|
||||
val f = MediaPlayerFactory(*factoryArgs.toTypedArray())
|
||||
factory = f
|
||||
available.set(true)
|
||||
println("VLC: MediaPlayerFactory created successfully")
|
||||
@@ -184,7 +215,9 @@ object VlcjPlayerPool {
|
||||
|
||||
val af =
|
||||
audioFactory ?: try {
|
||||
MediaPlayerFactory("--no-video", "--no-xlib").also { audioFactory = it }
|
||||
val audioArgs = mutableListOf("--no-video", "--no-xlib")
|
||||
cachedPluginPath?.let { audioArgs += "--plugin-path=$it" }
|
||||
MediaPlayerFactory(*audioArgs.toTypedArray()).also { audioFactory = it }
|
||||
} catch (_: Throwable) {
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user