xcodeshjava-homesdkman

How to make /usr/libexec/java_home find JDK installed using SDKMAN on MacOS?


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?


Solution

  • The /usr/libexec/java_home seems 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_home into returning SDKMAN's current JDK.

    There are 2 issues that we need to work around:

    On MacOS, manually installed JDKs are installed in /Library/Java/JavaVirtualMachines and look like this (non-exhaustive):

    jdk-root-folder/
      Contents/
        Info.plist
        Home/
          <actual JDK files here>
    

    But SDKMAN only installs the actual JDK files. The solution is to fake everything SDKMAN does not install:

    Now, stuff relying on /usr/libexec/java_home to find a JDK should work, including XCode Script Build Phase using /bin/sh.