Hey everyone! I've been working on Nucleus, a Grad...
# compose-desktop
u
Hey everyone! I've been working on Nucleus, a Gradle plugin that brings modern packaging to Compose Desktop (and any JVM app). It uses electron-builder under the hood to produce native installers for all platforms — 16 formats in total (DMG, PKG, NSIS, MSI, AppX, DEB, RPM, AppImage, Snap, Flatpak…). A few highlights: - Auto-update — generates electron-builder compatible update metadata + a Kotlin runtime library to check, download, and install updates - Deep links & file associations — cross-platform protocol handlers (myapp://...) with single-instance forwarding - Project Leyden / AOT cache — JDK 25+ AOT cache support to dramatically speed up startup time, with a dedicated runtime API to handle the training phase - CI/CD ready — composite GitHub Actions for 6-runner multi-platform builds, universal macOS binaries, and MSIX bundles - One DSL — everything configured from a single nucleus.application { } block I'm looking for someone with an Apple Developer ID certificate who could help me test macOS code signing and notarization. I don't have one myself, so I haven't been able to validate that part end-to-end. Repo: https://github.com/kdroidFilter/Nucleus
👏 6
🔥 7
👏🏻 1
👍 3
🚀 13
👏🏾 1
👍🏾 1
kodee happy 15
s
CC @fourlastor since you've had some experience in the area with your own packager 🙂
u
I've worked extensively on sandboxing, in particular, to generate a pkg ou of the box that can be distributed on the Mac App Store.
👍 1
It's not finished yet; it's not available on the Gradle portal.
f
Sweet! This looks like to be working on a different level to what construo does, basically creating a whole app package instead of just a runnable folder I haven't looked at the whole source code, but one piece of advice I can give is: if you're packaging for different OSes, be aware of the unix permissions when packaging from windows (ie package FOR linux FROM windows), as they are lost due to the windows file system not being aware of them - what I did was to set the permissions by hand when creating the zip file (and creating the zip file with apache something instead of the gradle api) I think other than that it looks like it's using graal (or something of that sort) to create the executables?
u
Thanks a lot, but I don’t plan on handling cross-compilation at all. I think that nowadays, we all deploy using CI, and we all know how to manage build matrices. On the contrary, I’m providing ready-to-use actions to build the app automatically. This way, I avoid all those issues entirely, and honestly, I think using Leyden with cross-compilation would be impossible anyway.
f
ah fair enough yep! another thing I did bash my head against is that if you're minimizing the jar JRE, you might want to include at least some default modules - I have
<http://jdk.crypto.ec|jdk.crypto.ec>
included by default because this isn't going to be directly referenced by the application, and is basically necessary to do any https request (I'm not familiar with Leyden so I don't know if this applies)
u
Actually, my module uses JPackage to create the release; I based it on the official module code and I package it with Electron Builder.
And I added lots of features like AOT cache, macOS 26 icons, sandboxing, universal binaries.
f
yep, but JPackage will use JLink (which minimizes the JRE), and JLink uses JDeps. JDeps analyzes the jar to check which modules are needeed - crypto modules are loaded by reflection so it will miss them, at least in my experience
the easiest way to verify if that's a problem is to try to do a simple application that does an https request, and see if it crashes with class not found
m
This looks very exciting. How does this Auto-update feature work from a users point of view? Is it fully automatic or does it interact with the user? Can I force a user to update?
u
There is one, you can try it, download the previous release and try the auto-update 🙂
@Michael Paus It really depends on how he installed it. If he installed it as a DMG on a Mac, then you can force it. And if you declared in NSIS that the app is user-only, then you can too; otherwise, it will require an elevation. On Linux, it absolutely requires the user's permission.
🙏 1
f
for the mac app signing, something I got a PR for was that if you have dots on your folder names, it will break app signing (I have no idea why, not having a mac to test it myself)
s
Sounds quite interesting. I do have the apple certificates and already published some compose apps to the mac appstore, so I could help test it. I'm currently also fighting with compose's packaging tasks, the way how resources are bundled
u
@S. Ah great! Could you try cloning the repo locally and notarizing/signing the example app as both a pkg and a dmg? The pkg should be automatically sandboxed. You can also try including some native libraries in it.
👍🏻 1
Use jdk 25 (best with jbr) if you enable aot cache
a
Hmm, I ran into this issue when I tried to make AppCDS work: JBR-9098 AppCDS writes local paths into .jsa archive Does the AOT thing in JDK 25 not suffer from it?
u
No — JDK 25’s AOT Cache does not suffer from JBR-9098. That issue is specific to legacy AppCDS (.jsa) embedding absolute classpath paths, while the JDK 25 AOT cache (.aot via
-XX:AOTCacheOutput
) does not bake local filesystem paths into the archive.
a
Awesome, I’ll have to try it.
🔥 1
u
You can download the sample release on my github repo, they use leyden
m
great work. i have one question what difference your nucleus and jetbrain desktop native Distributions.
u
m
@אליהו הדס great thanks for sharing
🙌 1
j
@אליהו הדס awesome stuff! Definitely keeping an eye on this. Small nitpick on that comparison: splash is very much possible with
compose.desktop
, should be listed as "manual"
u
@Jacob Ras Yes, thank you, I've corrected it.
nice spin 1
j
One thing I ran into with
compose.desktop
is that the install dir is not configurable (separately from the name), which could lead to conflicts. I see a lot of settings for Windows, but is there any way to tweak the install dir name? (Context: https://kotlinlang.slack.com/archives/C01D6HTPATV/p1746528337324759?thread_ts=1745932745.300959&amp;cid=C01D6HTPATV)
u
@Jacob Ras Not yet, I recommend opening an issue on GitHub 🙂
m
@אליהו הדס is that any issue download library
u
@Meet I haven’t made the announcement that I published it yet 😅, but there was an error in the documentation, a dash instead of a dot.
m
ohh i not read it ok i waiting publish
u
It’s published, but I haven’t checked myself yet that it actually works 😅
m
ok
u
m
i have one question if i only use runtime auto-update libary and no other nucleus plugin use it and jdk 21 there is any issue faced it? i know just published today.
u
@Meet There shouldn't be a problem, but I'd be happy if you tested it yourself.
m
ok great
u
@Jacob Ras That’s why I based my work on Electron Builder, because given Electron’s popularity, I figured that the vast majority of problems have probably already been solved. The repository has more than 3,500 commits and over 7,000 closed issues.
m
great job. if any issue face i will let know
🙌 1
a
my god, i spent much of January working on packaging my app for most of these formats, i would be so happy to delete that work and use this instead
l
I’ve been using many of your open-source libraries in my CMP project, like ComposeNativeTray. Thank you so much for all your hard work—*you’re an absolute legend!*
kodee loving 1
s
regarding the AOT cache for a compose app, is it theoretically better to only focus on loading code responsible for the UI during the training run, or should I also include initialisation parts, like setting up an sqlite db? nucleus docs suggest to skip heavier things. afaik the training run should closely match a production run, so currently I'm not skipping anything and startup is quite a bit faster, though I guess the bigger difference will be on windows which I haven't tested yet. it also gained almost 100mb in package size, went from 216mb to 307mb
u
I corrected the documentation; on the contrary, if these resource-intensive tasks are executed at startup, it must be done.
However, even if it increases by 100 MB, it compresses very well; your final bundle size will only increase by about 20 MB.
I also recommend enabling native library cleanup; you can free up 20-30MB.
s
alright, I see. the numbers I posted are from the appstore's build metadata, so that should be the final installation size. download size is about half.
👍 1
u
I just released a major update, version 1.3.2. I'll post new post soon.
🚀 1
This notably provides experimental support for native compilation via graalvm