そういえば、Graal JITコンパイラのコードはいろいろ読んでみたことはあるけれど、ネイティブイメージのSubstrateVMの方はまったく読んでいないなあ、と思いました。
ネイティブイメージを生成してみる、的なことを、1年ほど前に試しただけでした。
というわけで、読めるかもわかりませんが、準備していきます。
GraalVMのコードは、GitHub https://github.com/oracle/graal にありますので、まだの方はcloneしてください。
MX
GraalVMは、ビルドにMXというツールを使っています。MavenやGradleではありません。単純に読むだけなら、MXは不要ですが、あると便利です。あとで使います。
MXをセットアップしましょう。
$ git clone https://github.com/graalvm/mx.git $ cd mx $ export PATH=`pwd`:$PATH $ mx -v Welcome to Mx version 5.195.1
GraalVMのプロジェクト構造
GraalVMのソースコードは、機能ごとに分かれています。
$ ls -1 3rd_party_licenses.txt CONTRIBUTING.md LICENSE README.md ci.hocon ci_includes common.hocon compiler docs examples regex sdk substratevm sulong tools truffle vm
たとえば、compilerはGraal JITコンパイラ、sulongはLLVM関連です。substratevmが、今回の対象となる、ネイティブイメージ生成、実行用VMの機能です。substratevmディレクトリを見てみます。
$ ls -1 substratevm/src com.oracle.graal.pointsto com.oracle.objectfile com.oracle.svm.core com.oracle.svm.core.genscavenge com.oracle.svm.core.graal com.oracle.svm.core.jdk8 com.oracle.svm.core.jdk9 com.oracle.svm.core.posix com.oracle.svm.core.windows com.oracle.svm.driver com.oracle.svm.graal com.oracle.svm.hosted com.oracle.svm.jline com.oracle.svm.jni com.oracle.svm.junit com.oracle.svm.libffi com.oracle.svm.native com.oracle.svm.native.jni com.oracle.svm.native.libchelper com.oracle.svm.native.strictmath com.oracle.svm.polyglot com.oracle.svm.reflect com.oracle.svm.test com.oracle.svm.thirdparty com.oracle.svm.truffle com.oracle.svm.truffle.nfi com.oracle.svm.tutorial native-image-maven-plugin native-image-maven-plugin-test org.graalvm.polyglot.nativeapi org.graalvm.polyglot.nativeapi.native
srcディレクトリの中に、多数のモジュールが入っています。このモジュール1つ1つに、またsrcディレクトリがあります。
$ ls -1 substratevm/src/com.oracle.graal.pointsto com.oracle.graal.pointsto.iml src
このような構造なので、単純にIDEでsubstratevmディレクトリを開くと、コンパイルエラーの表示が出てきます。
大丈夫です。MXで、IDEの設定ファイルを出力させることができます。IntelliJ IDEA用の設定ファイルを出力します。
mx intellijinit Using SDK definitions from /Users/koichi.sakata/Library/Preferences/IntelliJIdea2018.2/options/jdk.table.xml created /Users/iamjvm/code/graal/substratevm/.idea/.name created /Users/iamjvm/code/graal/substratevm/src/com.oracle.graal.pointsto/com.oracle.graal.pointsto.iml ...
先述のlsで、com.oracle.graal.pointsto.iml
というIntelliJ IDEAの設定ファイルがあったのも、mx intellijinit
で出力したためです。
なお、Eclipseはmx eclipseinit
で出力できるようです。IDEへ対応させる方法は、こちらのドキュメントにあります。
graal/IDEs.md at master · oracle/graal · GitHub
MXをセットアップしておくと便利、というのは、このためでした。このコマンドは、SubstrateVMだけでなく、Graal JITコンパイラなど、すべての機能のソースコードに対して使えます。
MXのビルドスクリプト
MXのビルドスクリプトは、なんとPythonで書きます。mx.xxxディレクトリに、スクリプトがあります。
$ ls -1 eclipse-settings mx.substratevm.iml mx_substratevm.py mx_substratevm.pyc mx_substratevm_benchmark.py mx_substratevm_benchmark.pyc rebuild-images.sh suite.py suite.pyc tools-chromeinspector.properties tools-junit.properties tools-native-image.properties tools-profiler.properties tools-regex.properties tools-truffle.properties
ネイティブイメージライブラリのビルドを見ていきます。このビルドは、アプリケーションのネイティブイメージを生成するものではないことに、注意してください。ライブラリ自体をビルドする処理です。mx_substratevm.py
を見ます。
@mx.command(suite.name, 'native-image') def native_image_on_jvm(args, **kwargs): ... run_java([ '-Dorg.graalvm.version=' + svm_version, '-Dnative-image.root=' + suite_native_image_root(), '-cp', os.pathsep.join(driver_cp), mx.dependency('substratevm:SVM_DRIVER').mainClass] + save_args, **kwargs)
run_javaの詳細は省略しましすが、Javaのmainメソッドを実行しています。実行するクラスは、substratevm:SVM_DRIVER
の値です。suite.py
に定義されています。実質これはJSONです。
... "SVM_DRIVER": { "subDir": "src", "description" : "SubstrateVM native-image building tool", "mainClass": "com.oracle.svm.driver.NativeImage", "dependencies": [ "com.oracle.svm.driver", ], "distDependencies": [ "LIBRARY_SUPPORT", ], }, ...
com.oracle.svm.driver.NativeImage
がメインクラスとわかりました。
public static void main(String[] args) { try { build(new DefaultBuildConfiguration(args)); } catch (NativeImageError e) { boolean verbose = Boolean.valueOf(System.getenv("VERBOSE_GRAALVM_LAUNCHERS")); NativeImage.show(System.err::println, "Error: " + e.getMessage()); Throwable cause = e.getCause(); while (cause != null) { NativeImage.show(System.err::println, "Caused by: " + cause); cause = cause.getCause(); } if (verbose) { e.printStackTrace(); } System.exit(1); } }
mainメソッドがあります。MXからこのメインメソッドが呼び出され、ビルドしてライブラリが生成されます。mxbuildディレクトリに出力されます。
まとめ
今回は、GraalVMのソースを読むために、主にMXについてまとめました。MXを使えば、GraalVMのソースをIDEで読みやすくなります。MXのビルドスクリプトはPythonで書かれており、ここから、ライブラリ生成時のメインクラスがわかりました。
やはり、ネイティブイメージ自体を生成している処理を見てみたいですね。また読み進めます。