JDK 9 + Mac OS + jlink?
Asked Answered
P

3

9

I installed the release version of JDK 9 on Mac OS.

jshell works great, Jigsaw module support works, but there is no jlink:

➜  java --version
java 9
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

This comes up empty:

find /System/Library/Frameworks/JavaVM.framework/Versions/Current/ -iname jlink\*

FYI:

➜  ls -l $(which java)
lrwxr-xr-x  1 root  wheel  74 Nov  7  2016 /usr/bin/java -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java
Protochordate answered 25/9, 2017 at 18:46 Comment(0)
S
5

You can verify your JAVA_HOME using which java and make sure it points to the default installation path which ideally should be

/Library/Java/JavaVirtualMachines...

[for e.g. I use it as export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/] and further you can find the jlink in the bin folder of Contents

find /Library/Java/JavaVirtualMachines/jdk-9.jdk -iname jlink\* 

which should return

/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/bin

Attaching a screenshot for reference of the location its installed:-

enter image description here

Note: Though in the screenshot, the command doesn't run successfully but its recognized.

Stairway answered 25/9, 2017 at 18:48 Comment(2)
It is installed at /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
@Protochordate ya probably try updating the bash_profile to export updated JAVA_HOME and that might fix which java as well.Stairway
H
3

To add the JDK 9 tools to your path, add the following to the file .bashrc of your home directory:

export JAVA_HOME=$(/usr/libexec/java_home -v 9)
export PATH="$JAVA_HOME/bin:$PATH"

Did you notice the -v 9? you can change that to 1.8 if you ever want to switch back to JDK 1.8. For any newbie who can’t locate .bashrc in the Finder: press ⌘⇧. (command shift dot) to reveal hidden files.

Handwoven answered 26/9, 2017 at 12:31 Comment(0)
S
3

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:

  1. 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.
  2. Once in Recovery Mode, choose Terminal from the Utilities menu.
  3. At the prompt, type csr disable. You'll be prompted to reboot to cause the changes to take place. Do that as well.
  4. 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:
    
  5. Enter your password at the prompt.

  6. In vi, switch to Insert mode by pressing I, then enter the following text:

    #!/bin/bash
    $JAVA_HOME/bin/jlink $@
    
  7. Then exit Insert mode by pressing esc, and save and exit by typing :wq.

  8. 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
    % 
    
  9. Now it's time to re-enable System Integrity Protection. Reboot back into Recovery Mode (instructions in step 1).

  10. Once in Recovery Mode, choose Terminal from the Utilities menu.
  11. 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.

Stream answered 19/6, 2018 at 13:58 Comment(6)
Just a followup note: I thought I'd be able to optimize these steps so that you only go into recovery mode once, and just create the requisite file while you're there. Unfortunately, that disk won't be mounted. If you're comfortable with such things, I imagine there's a way to mount the disk from recovery mode and then make the change.Stream
Probably the wrapper should look more like this: "$(/usr/libexec/java_home)/bin/jlink" "${@}" as this works with currently activated JVM.Pointillism
It’s been a while, and I’ll have to re-verify, but IIRC, I wrote the script to mimic the behavior of the other Java binary wrapper scripts in /usr/bin.Stream
Using ${JAVA_HOME} has its advantages, as it allows you to change JDK on the fly by exporting new value of this variable. It is also a disadvantage, as you have actually to export it. (JAVA_HOME is not set by default).Pointillism
It’s definitely a tradeoff. Seems to me that since I’m merely filling in a gap in another solution, aligning my decision with theirs is more prudent than making my decision independently. If they don’t align, then /usr/bin/jlink might point to a different JDK than, say, /usr/bin/javac. That’d be bad!Stream
If the original wrappers where shell scripts, we could align properly, 'cos we would know, how they did it (and you're right—it's not very prudent to have inconsistent environment), but those wrappers are binary files, so who can tell?...Pointillism

© 2022 - 2024 — McMap. All rights reserved.