Hi! Don't know if it has been already answered, I'...
# compose-desktop
f
Hi! Don't know if it has been already answered, I've searched both here and on the web without finding a clear answer. I'm building a kotlin/JVM desktop application with compose, that application will run on Windows and needs a DLL that is used from JVM through JNI. The question is: how can i bundle that DLL within the MSI package such as it will be available to the JVM the application will run into? (If I get it right the MSI, or EXE don't know exactly, will be bundled with a JVM). I've seen on the documentation that you can package resources, but I don't know if it is how I can achieve that: https://github.com/JetBrains/compose-jb/tree/master/tutorials/Native_distributions_and_local_execution#packaging-resources
m
You could follow the same strategy as, e.g., https://github.com/xerial/sqlite-jdbc does. Put the dll into a jar file and extract it at run time.
t
The packaging resources guide you linked works perfectly with loading DLLs. I am using that in my app. You don't need to extract anything this way.
m
@Thomas Good to know. Do you somehow have to set the native library path for that to work?
t
@Michael Paus Now that you mention it, yes I do have some special code for it. Here is how I did it:
Copy code
val resources = File(System.getProperty("compose.application.resources.dir"))
System.load(File(resources, "windowsNativeInterop.dll").absolutePath)
đź‘Ť 2
🙌 2
Also good to know is that on macOS, when publishing on the Apple App Store, apps are sandboxed. Native libraries can only be loaded from within the app package. Extracting from the jar and then loading it is impossible due to sandbox restrictions. So in that case, this method works great, too!
đź‘Ť 2
But for Windows apps and Mac apps outside the App Store this is not an issue.
đź‘Ť 1
f
@Thomas thank you very much, I will try with your solution and I'll let you know if it works for me too.
I can confirm that it works, both using
run
and
runDistributable
gradle tasks, initially I thought it won't work with
run
, but it does. I've just added this line inside
nativeDistributions
on build.gradle.kts file:
Copy code
appResourcesRootDir.set(project.layout.projectDirectory.dir("src/main/resources"))
Then placed the dll file inside
src/main/resources/windows
. Thanks for your help.
🎉 1
m
@Francesco Pedron Could you please check whether the dll has now been added twice to your bundle because normally everything which is located in src/main/resources is automatically added to the main jar too. Using another folder name might be better.
âž• 1
f
@Michael Paus Sure, I can check, could you please point me to where I might check this?
m
@Thomas Actually, it is very much an issue. macOS apps must be notarized unless the user bypasses Gatekeeper, and notarization will block attempts to use the unpack-from-jar-and-load trick.
My company makes a product (soon to launch) that solves all these problems, it handles packaging of Compose Desktop apps for you completely, including signing, extracting native libraries from JARs and putting them in the right place, software updates and more.
If anyone would like to test it, just get in touch. It should launch soon anyway but it's currently in private beta.
(Windows is also a pain - if your DLL is bundled in a JAR it won't get signed, and unsigned code+windows is a bad combination. It may appear to work at first, but some corporate desktops will block any unsigned code, and AV scanners will interfere with their execution unless the DLL is already very well known and has good reputation)
The product I mentioned is called Hydraulic Conveyor and it solves this by finding and extracting DLLs / native libraries from JARs, signing them (using portable code so you can e.g. sign Mac code on Windows or vice-versa), and ensuring the JVM can find them at runtime. It also deletes libraries that target the 'wrong' OS for that package, reducing disk space usage and download size.
t
@mikehearn yes, that was my point. The method I suggested does not have this issue.
m
@Thomas It does unfortunately. The issue here is not sandboxing related, it's to do with Apple's notarization requirements around the "hardened runtime".
You can hit the same issue even outside the app store.
t
No, the library is not extracted in any way, so it is not affected here. I am completely aware of the Apple requirements but that is exactly why I am using this method.
Compose Gradle plugin can be configured to put additional resource files under an installation directory.
Apple approved my app and it works, so no issues at all using this configuration.
m
Ah, I see, sorry. Your initial comment is saying to not use JAR extraction. Then later you mentioned the sandbox and said it's not an issue outside the app store, which is why I got confused. Indeed, the only way is to put the shared libraries into the app package at the top level, sandboxed or not.
t
Oh I see. I was talking about the “Adding files to packaged application” method. Now I see that “JVM resource loading” is also mentioned in the same guide, that is not what I was suggesting.
Sure, I can check, could you please point me to where I might check this?
@Francesco Pedron you can decompile the jar and you will see that the DLL is added in the jar, too, which is not what you want. You can fix this by using a different path for the resources. Instead of “src/main/resources” you can use “resources” (just like in the readme), and move your files to that directory.
f
@Michael Paus You are right mi JAR contains a "windows" folder with the Jar, will move my resources folder to the readme-suggested location. Thank also to @Thomas
m
@mikehearn 1. As far as I know the compose gradle plugin already does extract native libraries from jars and signs them. (Jpackage allone doesn’t.) 2. Will your product be able to handle sqlite-jdbc correctly? It contains a lot of different libraries for different platforms but you need only one of them per platform. Extracting them and storing them in a different location might interfere with the internal search strategy.
m
Yes, it handles that correctly. It detects OS and CPU for native libraries and removes the ones that don't match the target package. The location they're placed is the same place as other JVM libs, so it is found automatically
I haven't tried that specific library but can do so next week