Is Swing the only remaining way to build truly cro...
# announcements
s
Is Swing the only remaining way to build truly cross-platform Desktop UIs (without building OS-specific distributables)?
r
Java FX
s
JavaFX is now separate from the JRE so you need to create platform-specific bundles
a
Arguably swing doesn't offer this either for any group of target end users outside of the limited niches of certain enterprise deployments and software developers that have a reason to have a desktop jre installed. For everyone else you end up bundling to avoid sending them off to get OS specific dependencies.
b
Isn't electron truly cross-platform?
c
cross platform without building OS specific binaries? Not sure what you're asking. Seems like just a web app, no?
c
Electron has a different binary per platform.
It's impossible to be truly cross-platform without different binaries though. The solution that was found is that the user installs a ‘VM' (OS-specific, like the JRE) and then your app can be a single binary for all platforms. But that requires the user to download a JRE (or Python, or...), and the current ‘fashion' is to bundle it in your app so the user doesn't have to care (because there still isn't a proper way to handle dependencies on desktop)
s
In case you don't find a way around OS-specific distributables. This is how a project of mine uses jpackage to create os-specific distributables:
Copy code
tasks {
    register("buildInstaller") {
        dependsOn("build")

        doLast {
            if (JavaVersion.current() < JavaVersion.VERSION_16) {
                throw GradleException("Require JDK 16+ to run 'jpackage' (currently ${JavaVersion.current()})")
            }
            println("""
                these packaging tools have to be installed because they are required by jpackage:
                    - on Red Hat Linux: the rpm-build package
                    - on Ubuntu Linux: the fakeroot package
                    - on macOS: Xcode command line tools
                    - on Windows: WiX 3.0 or later
            """.trimIndent())
            val projectVersion = project.version.toString()
            JPackage.buildInstaller(
                name = "VoidChess",
                description = "a chess program",
                appVersion = projectVersion,
                inputDir = "build/libs",
                destinationDir = "build/installer",
                mainJar = "voidchess-$projectVersion-all.jar",
                addModules = listOf("java.desktop"),
                winIcoIconPath = "about/shortcut-icon2.ico",
                winShortcut = true,
                winMenu = true,
                linuxPngIconPath = "about/shortcut-icon2.png",
                linuxShortcut = true,
                linuxMenuGroup = "Games",
                macIcnsIconPath = "about/shortcut-icon2.icns"
            )
        }
    }
}
(notice the
addModules
entry to create bundle a minimal jre with your app. An entry point to googling more about this is looking for
jlink
).
JPackage.buildInstaller
is defined in the buildSrc: https://github.com/simon-void/voidchess/blob/master/buildSrc/src/main/kotlin/JPackage.kt Notice that this doesn't support cross-platform, so you need e.g. a windows-client to build a windows-version, but you're CI-setup (e.g. GitHub Actions) should already support selecting the OS of your build-machine.
s
I want to distribute one jar (this is a JVM project) and be able to run it under any JRE.
s
that's the default 😆 just have a gradle file like this
Copy code
plugins {
  kotlin("jvm") version "1.5.21"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_8

repositories {
  mavenCentral()
}
and do a
./gradlew clean build
and check the build/libs folder.
c
@Stephan Schroeder that JAR will not be executable. You should add the
application
plugin, but even then it generates a ZIP, not a uber jar.
shadowJar
can generate a uber jar that contains all dependencies, and can be ran with
java -jar <…>.jar
s
@CLOVIS you're right, i forgot about the dependencies. Ok, so in order to have access to
shadowJar
you'll have to add this to this plugin section
Copy code
id("com.github.johnrengelman.shadow") version "7.0.0"
here's the link to that plugin's page: https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow