I am making iOS apps in Kotlin, which relies on having a Script Build Phase calling Gradle from XCode. With a JDK installed using SDKMAN, it does not work and produces this error:
The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.
The JDK installed with SDKMAN is working correctly on my system, SDKMAN sets JAVA_HOME to /Users/{user}/.sdkman/candidates/java/current by sourcing its sdkman-init.sh script from .bash_profile or .zshrc. But XCode Script Phase uses /bin/sh, and it does not seem to read these kinds of files.
I tried exporting JAVA_HOME in files like ~/.profile, /etc/profile, etc. with no result.
After some research, I found that this output is from a call to /usr/libexec/java_home producing no result. I also found that manually calling /usr/libexec/java_home inside a Terminal where JAVA_HOME is set correctly does not pick it up, so toying with JAVA_HOME is useless in this case: it just doesn't read it.
So, how do you make the /usr/libexec/java_home command find JDK installed using SDKMAN on MacOS?
The
/usr/libexec/java_homeseems to be mostly undocumented, it's very hard to find information about how it works. Combining the little information about it found online and some trial and error, I managed to trick/usr/libexec/java_homeinto returning SDKMAN's current JDK.There are 2 issues that we need to work around:
/usr/libexec/java_homeknowsOn MacOS, manually installed JDKs are installed in
/Library/Java/JavaVirtualMachinesand look like this (non-exhaustive):But SDKMAN only installs the actual JDK files. The solution is to fake everything SDKMAN does not install:
/Library/Java/JavaVirtualMachinesfor your JDK, I'll call minesdkman-currentbut the name does not matter.Contentsfolder insidesdkman-currentHomeinside theContentsfolder, linking to your actual JDK:Info.plistfile in theContentsfolder with the following content: You can mostly use whatever<string>values you want in there, but note that removing any of the entries will prevent/usr/libexec/java_homefrom finding your JDK. I'm setting its version to 9999 so that its always the one returned by/usr/libexec/java_home.Now, stuff relying on
/usr/libexec/java_hometo find a JDK should work, including XCode Script Build Phase using/bin/sh.