Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

GraalVMのSubstrateVM (1): ネイティブイメージ関連のコードを読む準備

そういえば、Graal JITコンパイラのコードはいろいろ読んでみたことはあるけれど、ネイティブイメージのSubstrateVMの方はまったく読んでいないなあ、と思いました。

ネイティブイメージを生成してみる、的なことを、1年ほど前に試しただけでした。

www.sakatakoichi.com

というわけで、読めるかもわかりませんが、準備していきます。

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で書かれており、ここから、ライブラリ生成時のメインクラスがわかりました。

やはり、ネイティブイメージ自体を生成している処理を見てみたいですね。また読み進めます。