Mac上でGraalVMでネイティブイメージを作ってHello Worldしようとしたが挫折した話

この記事は Javaのカレンダー 2日目の記事になります。

https://qiita.com/advent-calendar/2023/java


# はじめに

最近コードを触ることから離れていたのですが、最近少し時間ができたので、前から気になっていたGraalVMを触ってみることにしました。

本番環境で動かしている会社もあるんだろうか？

ということで、やってみた系の記事になります。

# GraalVMとは

Javaコードからネイティブバイナリを作る事ができるツールです。
ネイティブアプリを作れるということは、JVMを利用する場合の良くない特徴の、JVM特有の起動の遅さやJVMのランタイムを環境に入れなければ動作できないことを回避することができます。

つまり、アプリ高速に起動できたり、アプリ単体で起動できるということです！
もしかしたらアプリのパフォーマンスも高いのでは？

しかし、デメリットもあります。ネイティブアプリになるため、ビルドした環境と同じ環境でしか動かなかったり、JVMがOSなどの環境の違いを埋めていた部分の影響を受けることになります。


ただ、最近はdockerが使われることも多くなってきており、JVMがなくても同じ環境を作り出せるため、ネイティブアプリのデメリットを最小にしながら、起動スピードやパフォーマンスなどのメリットを最大化出来る可能性があります。


ということで、実験として触ってみたいと思って、個人PCで動作をさせてみたいと思って、今回の環境を作ってみようと思いました。

# 今回利用した環境

個人で普段使っているPCでMacBook Pro M1を使います。
メモリ 8 GB
OSのバージョンはVentura (13.6.1)

スペックは普通かな。
開発するならメモリはもっと欲しいけど、最近はブラウザとslackとzoomとdiscordしか使わないので、お手頃なPCにしてました。


# GraalVMセットアップ

早速環境セットアップします！

### ダウンロード

こちらのページからダウンロードできそうです。
https://www.graalvm.org/downloads/#

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/3c5f9db6-bac6-3e1a-5cf1-2bc1e072798d.png)

VMからネイティブは色々とハマりそうな気がするので、枯れた古いバージョンのJava17をダウンロードしました。

ちゃんと動かせたらバージョンアップしてみたいですね。


gzファイルがダウンロードできたので、解凍して適当なフォルダに展開しておきます。
> graalvm-jdk-17_macos-x64_bin.tar.gz


Javaフォルダに移動しておきます。
```
sudo mv graalvm-jdk-17.0.9+11.1 /Library/Java/JavaVirtualMachines
```

### パスの設定

実行パスを設定します。
```
 export PATH=/Library/Java/JavaVirtualMachines/graalvm-jdk-17.0.9+11.1/Contents/Home/bin:$PATH
```

JAVA_HOMEも設定しておきます。
```
 export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-jdk-17.0.9+11.1/Contents/Home

```

exportだとウィンドウを閉じると設定が消えてしまうので、zshrcに書いておいてもいいかも。
まあ、IDEを使うなら、その中でプロジェクト設定できるはず！
ここではこのまま行きます。

動作確認！
``` 
java -version 
```

> java version "17.0.9" 2023-10-17 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.9+11.1 (build 17.0.9+11-LTS-jvmci-23.0-b21)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.9+11.1 (build 17.0.9+11-LTS-jvmci-23.0-b21, mixed mode, sharing)

うん！動いた！


# 実装コード

では、早速コードを書いて動かしてみたいと思います！

### Javaコード書いてやってみる

```java
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
}
```

Java21ではないので、まだ `public static String[] argsが` 必要です。

--- 
ビルドします！
```
javac HelloWorld.java
```

成功すると何も表示されません。失敗したときだけエラーがでます。

--- 
まずは普通にvmで動かしてみます

```
java HelloWorld
```
> Hello, World!

動きましたー

---


では、ネイティブイメージを作ります！

```
native-image HelloWorld
```

結構時間掛かる。
数行のHello Worldなのに数分は掛かります。。


> Produced artifacts:
 /hello_world/helloworld (executable)
================================================================================
Finished generating 'helloworld' in 1m 26s.

他のことをしながら待っていると
helloworldのネイティブイメージができたっぽいです！

--- 

動かしてみます！


```
./helloworld
```

> zsh: segmentation fault  ./helloworld


動かない、、、、、。


このあと色々頑張ったのだけど動かなかった・・・。

ということで、本当は挫折の記事ではなくで、動かしてみてパフォーマンス検証とかしたかったのだけど、数時間Macのアップデートなどに格闘した結果、この記事のタイトルが変わり、挫折した話となりました。


# ハマったところ

``` java -version ``` のコマンドを実行したときに以下の警告が出てきました。

XXX は壊れているため開けません。 mac特有の問題？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/03485c74-fb23-a4a9-ced5-8c515a4c5ba0.png)

このコマンドで解決
```
sudo xattr -r -d com.apple.quarantine /Library/Java/JavaVirtualMachines/graalvm-jdk-17.0.9+11.1/
```

ダウンロードしたばかりのファイルはクリーンな属性を付与してあげる必要があるみたいです。


---

`native-image` で完成したネイティブイメージを実行しようとしたら動かない！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/4b162adc-6e03-7c29-7ae5-a75b49966ac2.png)


このIssueはCloseされているんだけど、これっぽい？
> https://github.com/oracle/graal/issues/5778

ここでコメントされている対処を進めてみます。

こちらのサイトでXCodeをダウンロードするっぽいですが、
``` https://developer.apple.com/download/all/?q=Xcode ``` 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/7d282e79-d175-f127-85df-5b22859b3416.png)

IDEのXCodeは必要ないので、Command Line Tools をいれることにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/8f95033c-2616-5e26-fdd2-c110e3b8dc00.png)

MacのバージョンとあっているCommand Line Toolsを選ばないと古くても、新しくてもインストールできませんでした。。
Venturaだったので、Command Line Tools のバージョンは14をやってみるとインストールができました。 (OSのバージョンとXCodeのバージョンを揃えて欲しい。。。)

(VenturaはOSバージョン13なのですが、XCodeバージョンの13は入れることができず、正しいバージョンは14でした。。)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/c923a817-96dc-43a1-28cb-782eca9dc7bc.png)


インストールできたので進めようとすると、また違うエラー！
```
xcode-select --install
xcode-select: error: command line tools are already installed, use "Software Update" in System Settings to install updates
```

普通にXCodeを入れた方が早そうなので、XCodeを入れることにしました。
でも、バージョンは14をダウンロードします。 (最新のXCodeバージョンは15でした)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/4ce7c276-e03b-1b77-b756-c6e4aa210922.png)

ダウンロードと解凍に結構時間がかかります。


インストールが済んだら進めていきます。
```
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
```

成功すると特に何も起きません。


---

```
sudo xcodebuild -license
```

> By typing 'agree' you are agreeing to the terms of the software license agreements. Type 'print' to print them or anything else to cancel, [agree, print, cancel] 

`agree` とタイプして、ライセンス承認します。

--- 

動作確認します
```
xcodebuild -version
```

> Xcode 14.3.1
Build version 14E300c

ちゃんと動いた！


この環境で、ネイティブイメージを作り直し、実行し直しても同じエラーで動かず😢
---------------


ちゃんとアップデートも実行してみました
```
softwareupdate --all --install --force
```

> Software Update Tool
Finding available software

アップデートを待ちます。


どんなに待ってもアップデートが完了せず。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/93627908-1e46-4459-7dc6-880085d72919.png)

`softwareupdate` のコマンドでOSのアップデートも走るみたいでしたが、コンソール上からはアップデートの実行が完了せず、システム設定からアップデートを実行しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17213/930f6c77-a617-11be-2c6c-66eac1bdd856.png)


OS再インストール後にも、ネイティブイメージを作り直し、実行し直しても同じエラーで動かず。。

---

あとは、MacOSが今はVenturaなのですが、最新のSonomaにアップデートするかどうか迷ったのですが、ちょっと今回は諦めました。

# まとめ

ネイティブイメージ化するだけかなって思っていたのですが、見事にネイティブイメージのデメリットを踏んで、環境周りのハマりどころにハマってしまいました。

Macの環境依存は他のOSよりも独特のところも多く、ハマりどころも多いのかもしれません。
mac環境のビルドはなかなか厳しいです。次はDockerを使ってビルドし手もいいかもですね。


ここまでだと普通のJVMの偉大さを感じるだけですが、また機会があればチャレンジしたいと思います。

この問題の解決方法を知っている方が居たらコメント頂けると嬉しいです！