imagej / imagej-launcher

The ImageJ native launcher
https://imagej.net/learn/launcher
BSD 2-Clause "Simplified" License
21 stars 18 forks source link

Universal (x86/arm64) launcher for Mac OS #82

Closed psobolewskiPhD closed 2 years ago

psobolewskiPhD commented 3 years ago

I've been using Fiji on an Apple Silicon M1 (arm64) MacBook Pro (Big Sur 11.4) I've started a image.sc thread regarding getting things to work smoothly native rather than in Rosetta emulation. https://forum.image.sc/t/fiji-clij-etc-native-on-apple-silicon-arm64-m1/53627/16 Everything works when architectures are matched, so everything emulated or everything native. Mixing architectures is a no go.

So for Fiji/imagej the key is native JRE. I have Azul 11, but native OpenJDK is also available via homebrew, and use of the "No JRE" Fiji. (The normal Mac OS install also works fine in Rosetta, but is markedly slower.)

The existing launcher actually works—despite being x86.

/Applications/Fiji.app/Contents/MacOS> lipo -info ImageJ-macosx       
Non-fat file: ImageJ-macosx is architecture: x86_64

It does report an architecture mismatch error and does a fallback to system Java, which results in a 2nd Fiji icon in the dock—minor inconvenience.

As noted in the thread, Apple docs show you can build for a different architecture than you run (can't test obviously) and can build universal binaries. This is also supported by cmake, which I was pleased to see is used by the imagej-launcher.

I made a fork (https://github.com/psobolewskiPhD/imagej-launcher) and tweaked CMakeLists.txt to provide the two arch for cmake (to make a universal binary). I also had some Java path issues, which may be a Big Sur thing.

The project compiled with no issue and the binary is universal: ~/dev/imagej-launcher > lipo -info ImageJ-macosx Architectures in the fat file: ImageJ-macosx are: x86_64 arm64`

I've tested this binary on my Apple Silicon M1 Mac. It works fine with the following behavior when launched from the command line to monitor errors (there's some warnings too regarding patcher/injector, but they seem of no issue):

  1. current official imagej-macosx (x86), with current official JRE (x86): Launches Fiji via Rosetta, no issues
  2. current official imagej-macosx (x86), with no JRE and native system Java (Azul 11 in my case): Launches Fiji after fallback to System Java. No issues. (~5s launch)
  3. current official imagej-macosx (x86), with native JRE inside Fiji.app (Azul 11 in my case): Wrong architecture error, system Java fallback, launches Fiji, No issues. (~5s launch)
  4. my universal imagej-macosx (x86/arm64), with current official JRE (x86): Wrong architecture error, system Java fallback, launches Fiji, No issues. (<4 s launch)
  5. my universal imagej-macosx (x86/arm64), with no JRE: System Java fallback, launches Fiji, No issues. (<4 s launch)
  6. my universal imagej-macosx (x86/arm64), with native Azul 11 JRE inside Fiji.app: Normal launch (<4 s), no issues, 1 Fiji in dock

(Also, if anyone is curious, if you put both an x86 and arm64 JRE in Fiji.app/java it's alphabetical. So adoptopen vs zulu results in wrong architecture error and system java. While adoptopen vs 1_zulu uses the included zulu.)

So from where I sit, a universal imagej-macosx for Fiji with native JRE inside is the best for Apple Silicon Macs, but all cases work fine, as long as a system Java exists. The missing case is universal launcher on x86 Mac, which I might be able to try this weekend. Here's a link to the binary if anyone wants to test it themselves, it belongs in: Fiji.app/Contents/macos https://www.dropbox.com/s/1ftxheyx85siko3/ImageJ-macosx.zip?dl=0

imagesc-bot commented 3 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/fiji-clij-etc-native-on-apple-silicon-arm64-m1/53627/16

psobolewskiPhD commented 3 years ago

4. my universal imagej-macosx (x86/arm64), with current official JRE (x86): Wrong architecture error, system Java fallback, launches Fiji, No issues. (<4 s launch)

Thinking about it more, this is a regression, since being able to run Fiji in Rosetta can certainly be desired—especially in the early going, ie. now. I’m puzzled that Get Info does not offer Open using Rosetta, as is available for other universal (x86/arm64) apps. However, the launcher can be forced into x86 mode and Fiji launches in Rosetta from the terminal: arch -x86_64 ./ImageJ-macosx Probably best that the "stable" Fiji be x86 launcher, x86 JRE, but perhaps use the universal for a No JRE version or for a arm64 JRE version.

ctrueden commented 3 years ago

@psobolewskiPhD I want to thank you for all your efforts in this direction, and apologize for my silence till now. Another user asked on in the fiji Gitter chat room about running Fiji on M1, so I linked them to this very helpful issue. :smile:

imagesc-bot commented 3 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/fiji-clij-etc-native-on-apple-silicon-arm64-m1/53627/24

evanheller commented 2 years ago

Hey Peter, thanks for your work on this, it'll only become more important as more M1 Macs are adopted in the bioimaging community.

My issue is that, while I can manage to compile the launcher into an ARM executable that runs, imagej-launcher itself seems to be incompatible with modern versions of the JDK, both the official ARM-native Oracle JDK17 (https://www.oracle.com/java/technologies/downloads/#jdk17-mac), and OpenJDK from a package manager like Homebrew.

The error is Required service is missing: net.imagej.legacy.LegacyService, preventing Fiji/ImageJ from launching. The issue seems to have come up before in discussions related to Java 9 and 10 in this repository.

We could just stick to this Azul JDK, but I'm curious to know:

psobolewskiPhD commented 2 years ago

I've only used (and still using) Azul Java 11. There is a java 8 from Azul: https://www.azul.com/downloads/?os=macos&architecture=arm-64-bit&package=jdk I've not tried it though. Never seen that message, sorry!

imagesc-bot commented 2 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/could-not-install-fiji-on-mac-os-monterey-can-any-one-help-thanks/63880/13

hinerm commented 2 years ago

@psobolewskiPhD This is fantastic.

The missing case is universal launcher on x86 Mac, which I might be able to try this weekend.

Did you get a chance to try this out? (with the "official" x86 azul java 8 JRE) If that works then I am in favor of merging this and building new bundles.

psobolewskiPhD commented 2 years ago

Sorry an x86 mac isn't conveniently available at the moment. I'll poke around maybe can figure something out...

hinerm commented 2 years ago

@psobolewskiPhD just had someone locally test the launcher on an x86 mac and it seems to work (thanks @etadobson!)

psobolewskiPhD commented 2 years ago

@psobolewskiPhD This is fantastic.

The missing case is universal launcher on x86 Mac, which I might be able to try this weekend.

Did you get a chance to try this out? (with the "official" x86 azul java 8 JRE) If that works then I am in favor of merging this and building new bundles.

The only catch is I think it won't work on M1 wanting to run x86 in Rosetta. Edit: See my comment here https://github.com/imagej/imagej-launcher/issues/82#issuecomment-858549849 If they have system Java it will fall back to that. If not, I think it will just give the no JVM found due to the arch error.

╰─ ./ImageJ-macosx                                                     (base) ─╯
Could not load Java library '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/java/macosx/adoptopenjdk-8.jdk/jre//Contents/Home/jre/lib/jli/libjli.dylib': dlopen(/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/java/macosx/adoptopenjdk-8.jdk/jre//Contents/Home/jre/lib/jli/libjli.dylib, 0x0001): tried: '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/lib/macosx/libjli.dylib' (no such file), '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/mm/macosx/libjli.dylib' (no such file), '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/java/macosx/adoptopenjdk-8.jdk/jre//Contents/Home/jre/lib/jli/libjli.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e')), '/usr/local/lib/libjli.dylib' (no such file), '/usr/lib/libjli.dylib' (no such file), '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/lib/macosx/libjli.dylib' (no such file), '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/mm/macosx/libjli.dylib' (no such file), '/Users/piotrsobolewski/Dev/Fiji MacOS Java/Fiji.app/java/macosx/adoptopenjdk-8.jdk/jre/Contents/Home/jre/lib/jli/libjli.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e')), '/usr/local/lib/libjli.dylib' (no such file), '/usr/lib/libjli.dylib' (no such file)
Warning: falling back to System JVM

Seems like the FAT binary defaults to native if possible which then clashes with the Java.

So maybe the x86 Java bundle should have x86 launcher. But the no-JRE bundle could have the FAT launcher? And a putative arm64 Java bundle could have FAT too.

hinerm commented 2 years ago

@psobolewskiPhD would you mind testing one more bundle? This should have your arm64 launcher + the arm64 zulu Java 8 JRE.

The only catch is I think it won't work on M1 wanting to run x86 in Rosetta.

I don't have a sense of how big an issue this might be - why would they want to do this? They would still have the option to just use the arm64 bundle, right? My primary concern is that users on both x86 and arm64 macs have a download that works for them.

psobolewskiPhD commented 2 years ago

Your new bundle is wierd? I don't get it. I get system Java when double click and the main menu is ImageJ (not Fiji) and About ImageJ gives just java. But I get Java 8 when using the terminal launcher and the main menu is Fiji and the About Fiji launches the fancy multi channel image. 🤷

I will download a fresh no-JRE and try transplanting the zulu +/- a fresh zulu zip.

Edit: Yeah I can't figure it out. Same behavior. Why would double click be different than ./Imagej-launcher?

Edit2: it has to be something with the Contents/Info.plist I tried:

<key>LSArchitecturePriority</key>
    <array>
        <string>arm64</string>
        <string>x86_64</string>
        <string>i386</string>
        <string>ppc64</string>
        <string>ppc</string>
    </array>

See: https://developer.apple.com/documentation/bundleresources/information_property_list/lsarchitecturepriority But the behavior hasn't changed.

I don't have a sense of how big an issue this might be - why would they want to do this? They would still have the option to just use the arm64 bundle, right? My primary concern is that users on both x86 and arm64 macs have a download that works for them.

I think it comes down to plugins. Basically, anything with a compiled dependency that hasn't setup an arm64 version will not run in an arm64 Fiji. It's all or nothing: all emulated by Rosetta2 (which btw, excludes AVX instructions) or all native arm64. I've been using all native arm64 from the start basically & chronicled some of my experiences here: https://forum.image.sc/t/fiji-clij-etc-native-on-apple-silicon-arm64-m1/ Frequently recompiling the depends is easy (see CLIJ or this launcher) but the devs of the plugin may have no idea or no interest.

imagesc-bot commented 2 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/could-not-install-fiji-on-mac-os-monterey-can-any-one-help-thanks/63880/14

imagesc-bot commented 2 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/fiji-bundles-updated-with-newest-zulu-8-jdk-fx/65809/7

imagesc-bot commented 2 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/fiji-bundles-updated-with-newest-zulu-8-jdk-fx/65809/11

imagesc-bot commented 2 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/how-to-get-fiji-to-run-natively-on-macos-m1-arm64-apple-silicon/68384/2

ctrueden commented 2 years ago

Now that we have f5942cb1484c011d016b1064b137a654c0f44a57, can this issue be closed? Of course, still need to solve #86 and cut a new release of the Launcher and update Fiji to use this new version.

psobolewskiPhD commented 2 years ago

Sure. For anyone else reading, you can now build the Universal launcher yourself, by cloning the repo and building from source (assuming you have build tools). Just cd into the root of the repo and use cmake . That should do some configuration and generate build files. Then type make and you should get a binary. You can check it using: lipo -info ImageJ-macosx Note: you do need to make sure your $JAVA_HOME env variable is set (ie. that you have a system java installed).

ctrueden commented 2 years ago

@psobolewskiPhD Thanks. You don't even need to run CMake directly; just run mvn and it should build everything (calling CMake under the hood).

mkitti commented 2 years ago

I'm starting to wonder if having a universal launcher is a good idea. Does that mean we will need to make dual-arch fat binaries for everything?

I think we should figure out how to have distinct launchers for the Intel and the Apple M processors as soon as possible.

psobolewskiPhD commented 2 years ago

What's the advantage of distinct launchers vs a universal binary? It's all determined by the Java install anyways. Edit: to make it clear: the only way to run Fiji native at the moment is by installing a native system Java and using no-JRE Fiji. These are pretty deliberate choices...

I think a plan for moving to support M1 in general is a good idea. It will make it easier to find bugs, report stuff, etc. Cross compiling in most cases isn't hard, so just getting eye balls on stuff that needs some attention seems like a win to me, but I'm obviously biased since I have an M1.

mkitti commented 2 years ago

The launcher is only the beginning. There are more native components that need M1 support. Are we going to be put into a situation where we need to make other native components universal as well? I think the answer to this is yes, although I'm not sure if it necessarily applies to everything.

If the answer is yes, then are upstream suppliers of binaries doing the same or will we need to repackage them? Looking at the homebrew discussion, it's pretty clear that they are not going to support universal binaries: https://github.com/Homebrew/brew/issues/10307

Based on the last time Apple switched processor architectures, there will come a time when we will want to drop or otherwise deprioritize Intel macOS support. Apple also dropped support for universal binaries at some point before reestablishing it for the current architecture change. How will distributing a universal binary affect our ability to split the binaries and prioritize one over the other in the future?

The question I'm asking is what kind of technical debt are we taking on by choosing to distribute a universal binary as opposed to treating aarch64-macOS as a distinct platform now rather than later?

ctrueden commented 2 years ago

Does that mean we will need to make dual-arch fat binaries for everything?

I don't think it does. We can have a fat launcher, but keep the other native libs in lib/<arch> and jars/<arch> separate. I agree @mkitti that in general we should do that. But for the launcher specifically, I don't really see the harm in having a universal one. Unless you think Apple might decide to make fat binaries stop working on future versions of macOS...?

psobolewskiPhD commented 2 years ago

I don't think they ever disabled running the old Universal binaries (PPC/x86)--there are plenty in the Fiji bundle. I think more likely Apple will stop supporting Rosetta2 at some point. Eitherway, I can see the argument for keeping the x86 bundle x86 only and making a arm64 bundle that is arm64 only, because there is no FAT JRE (that I know of), so that will always be the decider. But for the no-JRE bundle, which is currently the only arm64 option for M1, I think the FAT launcher is a real boon, because you can have multiple Java installed and just change using java_home.

mkitti commented 2 years ago

While universal mach-o binaries with PowerPC support still technically work, they became impractical at some point since new software needed to adapt for recent versions of Mac OS X which did not run on PowerPC.

Let's say a user downloads a FIJI bundle with a universal launcher. Suppose then Apple completely stops releasing new versions of macOS for Intel in three years. Perhaps a year or two after that, we find that we cannot support both recent macOS and legacy macOS with the same code as happened after the PowerPC transition. Do subsequent versions of the universal launcher contain a build with new code for the M9 processor and an a build for Intel processors using older code? At that point, how can we update the universal launcher?

The problem I am anticipating is that the use of a FIJI install will exceed Apple's transition period. At the end of that transition period, we may have an issue updating users from a universal binary to a platform specific one when the technical need arises.

Perhaps the solution is to give the universal launcher a unique name that clearly identifies what it is. Maybe one of the following.

This would be in anticipation of possibly needing a ImageJ-macosx-aarch64 in the future.

The immediate practical question for me is what do I with libblosc and other binaries that depend on it such as the HDF5 Blosc plugin I'm working on now.

evanheller commented 2 years ago

I'll weigh in as a more of a user and less a developer-- even though I know I should defer to the people doing the real work here!

mkitti commented 2 years ago

There's no "post-transition" period where you have to go back to supplying platform-specific executables.

There is a point where Apple stops supporting Intel Macs, just as they stopped supporting PowerPC Macs. After that, Apple offers no guarantee that this will continue to work. We already have enough issues with code signing as is.

If, eventually, a developer no longer feels it's necessary to support Intel, just remove it as a build target (the same way that we no longer include "power_pc" as a build target).

What makes FIJI unique is that we support the software over a very long period of time. A new install now could still be in use in five years or even ten. The problem is that we could not just stop shipping Intel support for a binary called "ImageJ-macosx". If we auto-updated that binary without Intel support, we would break FIJI for people who are still using Intel Macs.

Currently, we still have a ImageJ-macosx-tiger in the bundles. Why is that?

Basically, I'm asking the question now about the future support plans to make sure we have an easy path forward in several years.

ctrueden commented 2 years ago

What makes FIJI unique is that we support the software over a very long period of time. A new install now could still be in use in five years or even ten.

Not on macOS, unfortunately. Apple virtually guarantees that with their strategy of decisions to intentionally obsolete old hardware and software. Any scientist who buys and uses a Mac needs to be aware of this. Major example: the Java 6 version of Fiji can no longer run on macOS, because Apple Java 6 no longer works, because they don't maintain it, and it makes use of old deprecated+broken native APIs.

To clarify though, I'm not disagreeing with you @mkitti. I share your desire to make Fiji work for as long as possible on macOS. But only within the compatibility restrictions imposed by Apple. The good thing is that we also try to make old scripts and plugins continue to work in new downloads of Fiji. So even if a macOS user's Fiji stops working when they update their hardware and/or software, a project goal is for it to be straightforward for them to install a new Fiji that does work on that hardware+software, including all their old scripts, customizations, etc.

If we auto-updated that binary without Intel support, we would break FIJI for people who are still using Intel Macs.

Until now, Fiji has been a so-called portable application, meaning you can have a single Fiji.app on a thumb drive with multiple java subfolders, one for each platform you want to use it on. And then you can plug that thumb drive into whatever computer, and double-click the appropriate launcher, and Fiji will start up with the correct JDK for that platform. Same Fiji, multiple machines with different OSes/platforms.

The switch to M1/ARM has endangered this, because we cannot have Info.plist point to two different native launchers, can we? But we can point to a universal launcher, which dynamically links to the correct Java for the architecture, supporting both x86 and ARM for the foreseeable future.

If the time comes when we can no longer build a universal launcher for all architectures running macOS, then we are already sunk, because we will need to ship a different Info.plist per platform at that point, meaning we cannot have a single portable Fiji.app folder anymore. When that day comes, we can decide whether to A) discontinue support for macOS x86 or whatever old problematic architectures; or B) abandon the "portable app" design of Fiji in favor of a customized Fiji.app for each architecture of macOS. There are other reasons to embrace (B) anyway, such as less confusion on Windows and Linux (where the folder name Fiji.app makes no sense), and potentially easier distribution via platform-specific application delivery systems: Chocolatey, Homebrew, Flatpak, etc.

Currently, we still have a ImageJ-macosx-tiger in the bundles. Why is that?

Because I am afraid to remove it, in case it still benefits anyone in the wild.

psobolewskiPhD commented 2 years ago

But we can point to a universal launcher, which dynamically links to the correct Java for the architecture, supporting both x86 and ARM for the foreseeable future.

Is this implemented? Because in my tests Java version is chosen alphabetically...

ctrueden commented 2 years ago

@psobolewskiPhD It is implemented in the sense that Linux x86-64 looks in subfolders of linux-amd64, Linux x86-32 looks in subfolders of linux, Windows x86-64 looks in subfolders of win64, Windows x86-32 looks in subfolders of win32. But for macOS, it looks in subfolders of macosx without checking the architecture. Should be a simple fix to make the launcher check for arm64 and look in macos-arm64 instead, no? And likely also pretty easy to make it look there first but then fall back to macosx if nothing found, so that Rosetta2 can do its thing in that situation.

imagesc-bot commented 1 year ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/fiji-for-apple-silicon-macs/80794/2