Help us understand the problem. What is going on with this article?

macOS での Java のバージョン管理

ちょっと真の本題で書いたことを調べようと思って、いろいろ調べた結果の備忘録です。
でも、真の本題の方は、いまいち解決していないという。。

Java のバージョンについて

Java については、まず、言語そのものと、その動作環境である JVM があり、それらについて、公式の仕様の部分と、公式 (Oracle) やその他さまざまなところが出している実装の部分があります。

Oracle が出している Java の仕様は Java SE (Standard Edition) と呼ばれます。
アプリケーション実装に踏み込んだ仕様である Java EE (Enterprise Edition) (現在は Jakarta EE として移管) や組み込み用途での Java ME (Micro Edition) というのがあった影響で、 Java SE と名付けられていますが、完全に歴史的経緯ですね。

Oracle による Java 実装は、通称 Oracle JDK と呼ばれています。

また、準公式的な位置づけのオープンソース Java 実装として OpenJDK というのもあります。Oracle 買収前の Sun Microsystems が始めたもののようです。

そして、昨今 Java を取り巻く環境が大きく変わってきています。
他のプログラミング言語の進歩に取り残される形になった Java が変化に対応しようとする中での動きなのでしょう。

まず、短いスパンで、細かい機能修正を加えたバージョンアップが行われるようになりました。
2017-09 リリースの Java 9 以降、現在のところ、半年に1回、メジャーバージョンが更新されています。

そして Java 11 以降ですが、 Oracle のライセンス形態が変わり、公式版のいわゆる Oracle JDK 自体は利用が有償化されるようになりました。
それに変わって、今まで Oracle JDK に含まれていた機能で OpenJDK に入っていなかった部分も OpenJDK に移管され、オープンソース化されました。
OpenJDK 自体は無償で利用できますし、この流れを受けて、 OpenJDK を独自にビルドしたディストリビューションも各社・コミュニティが公開するようになりました。

つまり、今まで Java といえば、だいたい Java 6-8 あたりの Oracle JDK だけ使っていればよかったのが、各ベンダ別のさまざまなバージョンの "Java" が普及するようになっています。

References

macOS での Java のバージョン管理

本題です。主には SDKMAN! を使った方法と /usr/libexec/java_home を使った方法があります。

SDKMAN! を使った方法

SDKMAN! は、 JVM 系のバージョン管理ツールで、 Java だけではなくて、 JVM 系の言語 (Groovy, Scala など) やミドルウェアのバージョン管理までやってくれます。
もともとは GVM (Groovy enVironment Manager) だったらしい。

各バージョンの検索・インストールから、各 scope でのバージョン切り替えまでをやってくれます。詳細はここでは書きません。

内部的に JAVA_HOME を切り替えているようです。

/usr/libexec/java_home を使った方法

こちらの方法では、インストーラや Homebrew を使ってインストールした Java のバージョンの切り替えを行えます。

例えば Homebrew で以下のように AdoptOpenJDK 最新版をインストールします。
macOS Catalina では、以下のように --no-quarantine option が必要です(*)。

$ brew cask install adoptopenjdk --no-quarantine

(*) ちなみに--no-quarantine optionをつけ忘れた場合でもxattr -d com.apple.quarantine /Library/Java/JavaVirtualMachines/adoptopenjdk-13.0.1.jdkのようにすれば検疫属性を削除して起動できるようになります。

インストールした Java の一覧は、以下のコマンドで確認できます。 /Library/Java/JavaVirtualMachines/ 配下にあるものだけが表示されるようです。
ちなみに、このディレクトリはシステムベースのもので、 SDKMAN! はユーザベースでモジュールを管理することから、 SDKMAN! でインストールした Java はこのディレクトリには配置されず、したがってこのコマンドの結果には出てきません。
SDKMAN! のドキュメントにも、 /usr/libexec/java_home を使った方法との互換性はないと書かれています。

$ /usr/libexec/java_home -V

特定のバージョンの Java のパスを得たい場合は、小文字の -v オプションを利用する。

$ /usr/libexec/java_home -v 13.0

オプション無しで実行すれば、デフォルト (たぶん最新バージョン) の Java のパスが取得されます。

$ /usr/libexec/java_home

特定のバージョンに JAVA_HOME を切り替える場合は、以下のようにします。

$ export JAVA_HOME=`/usr/libexec/java_home -v 13.0`

一時的にデフォルト以外の Java バージョンを使って、実行することも可能です。

$ /usr/libexec/java_home -v 13.0 --exec java -version

お察しのとおりですが、 /usr/libexec/java_home は Java のバージョンを見るだけで、そのビルドを発行しているベンダまでは見てくれないので、複数ベンダの Java を入れた場合はうまく対処してくれないみたいです。
/Library/Java/JavaVirtualMachines/<JAVA>/Contents/Info.plist に書いてある JVMVersion のバージョンで比較をしているようですね。

ベンダも含めて管理したい場合は、 jEnv を使うのが良さそうです。

References

Homebrew で Java 1.8+ is required to install this formula みたいなエラーが出る場合

真の本題です。

SDKMAN! で入れた Java しか無い状態で、 Homebrew で bundletool をインストールしようとしたところ、以下のエラーが発生しました。

$ brew install bundletool
bundletool: Java 1.8+ is required to install this formula.
Install AdoptOpenJDK with Homebrew Cask:
  brew cask install adoptopenjdk
Error: An unsatisfied requirement failed this build.

bundletool の formula を見たところ、 depends_on :java => "1.8+" の指定があり、これを満たせていないようです。
SDKMAN! で入れた場合にも JAVA_HOME は入っているし、Homebrew のソースコード をちらっと見るには、 JAVA_HOME に適切な Java が入っていれば問題なさそうなんですが...
あまりじっくり見ていないので、何がダメなのかちょっと良くわかりませんでした。誰か分かる人教えてください><

とりあえず言われるがままに、 Homebrew で AdoptOpenJDK をインストールして、特に JAVA_HOME の切り替えなどはせずに再度 brew install bundletool を実行すると、うまくインストールできました。

$ brew cask install adoptopenjdk --no-quarantine

Homebrew でのインストール時に必要なだけで、後は SDKMAN! でインストールした Java でも動作しそうなので、すぐにアンインストールしても問題なさそうです。

$ brew cask uninstall adoptopenjdk

References

tearoom6
blog も書いてます。 https://tearoom6.hateblo.jp/
https://tearoom6.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away