There's some important context here: Understanding Oracle's Java on Mac
The standard JDK utilities (java, javac, etc) are actually installed at /Library/Java/JavaVirtualMachines/(JDK_VERSION)/Contents/Home/bin
.
/usr/bin
is in the path, and in that directory you'll find all the usual JDK utilities. When you type java
(or any other command), those are the ones it finds, but they are actually symlinks to executables with the same names in /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands
. Those executables are just wrappers that interrogate your JAVA_HOME environment variable (which should point to the actual install location) and invoke the real binaries found there. I'm unclear on why anyone thought these two layers of abstraction were necessary, but it is what it is.
At some point after the release of Java 9, symlinks and wrappers for jshell
were added to macOS, but it appears that no such thing was done for jlink
.
To keep things working consistently going forward, I'd recommend writing an equivalent wrapper, putting it in the right location, and added a symlink to it in /usr/bin
.
Unfortunately, since the wrapper scripts are under /System
, you cannot create or modify anything, even as root, until you disable System Integrity Protection. This takes a few minutes and involves a few reboots, but it's easy to do:
- Reboot your machine. While it reboots, hold down ⌘R. This will cause the machine to start up in Recovery Mode. You can release the keys when you see the progress bar.
- Once in Recovery Mode, choose Terminal from the Utilities menu.
- At the prompt, type
csr disable
. You'll be prompted to reboot to cause the changes to take place. Do that as well.
Once you're back in regular mode, open Terminal and do the following:
% sudo vi /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jlink
Password:
Enter your password at the prompt.
In vi
, switch to Insert mode by pressing I, then enter the following text:
#!/bin/bash
$JAVA_HOME/bin/jlink $@
Then exit Insert mode by pressing esc, and save and exit by typing :wq.
Issue the following commands to make the script executable, create the symlink, make the symlink executable, and check your work:
% sudo chmod +x /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jlink
% sudo ln -s /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jlink /usr/bin/jlink
% sudo chmod +x /usr/bin/jlink
% ls -la $(which jlink)
lrwxr-xr-x 1 root wheel 75B Jun 19 10:33 /usr/bin/jlink@ -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jlink
% ls -la $(which java)
lrwxr-xr-x 1 root wheel 74B Sep 25 2017 /usr/bin/java@ -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
% jlink --version
10.0.1
%
Now it's time to re-enable System Integrity Protection. Reboot back into Recovery Mode (instructions in step 1).
- Once in Recovery Mode, choose Terminal from the Utilities menu.
- At the prompt, type
csr enable
. You'll be prompted to reboot once more.
If you'd rather not muck with disabling/re-enabling SIP, you can just create the script at /usr/bin/jlink
.
Hopefully some future version of macOS will include these by default.
/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/bin/jlink
, thanks! I will accept this answer when the timer allows me to. However,which java
indicates a totally different directory as I updated my main post to show. I don't think that is a custom change, I believe that is the default. – Protochordate