## 何も指定せずにNative Imageをビルドしても動くのなんでだろう?
この前、Micornautのアプリをnative-imageでビルドして遊んで
その後に、Micronautとは関係なく、Nettyをnative-imageでビルドして遊んで
ふと「あれ?MicronautもNetty使ってるはずなんだけど、何も指定せずにNative Imageをビルドしても動くのなんでだろう?」と思って色々見て回ったメモ。
## build-native-image.sh
がなくなってる
きしださんのブログや
kencharosさんのQiita記事を見てると
MicronautがNative Imageビルド用の build-native-image.sh
というファイルがあったみたい。なんだけど、僕の生成したプロジェクトの中には見当たらない。
探してみたら、1.1.0.M2までは提供していたんだけど、その次のリリースで削除されたみたいね。
build-native-image.sh
の中身はこうなってる
./gradlew assemble java -cp build/libs/@app.name@-*.jar io.micronaut.graal.reflect.GraalClassLoadingAnalyzer native-image --no-server \ --class-path build/libs/@app.name@-*.jar \ -H:ReflectionConfigurationFiles=build/reflect.json \ -H:EnableURLProtocols=http \ -H:IncludeResources="logback.xml|application.yml" \ -H:Name=@app.name@ \ -H:Class=@defaultPackage@.Application \ -H:+ReportUnsupportedElementsAtRuntime \ -H:+AllowVMInspection \ --allow-incomplete-classpath \ --rerun-class-initialization-at-runtime='sun.security.jca.JCAUtil$CachedSecureRandomHolder,javax.net.ssl.SSLContext' \ --delay-class-initialization-to-runtime=io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.ssl.util.ThreadLocalInsecureRandom,com.sun.jndi.dns.DnsClient
それが、現在の最新版の1.1.0ではこんな感じでビルドできる
./gradlew assemble native-image --no-server -cp build/libs/hello-graal-*.jar
シンプルになったね。だから build-native-image.sh
は削除したってことみたい。いいね。でも、じゃあ GraalClassLoadingAnalyzer
とか、native-imageのオプションはどこにいったんだろう?
## native-image.properties
mn create-app
をするときに --features graal-native-image
オプションをつけると、こういうファイルが生成される。(アプリ名は hello-graal
にしてる)
❯ cat src/main/resources/META-INF/native-image/hello.graal/hello-graal-application/native-image.properties Args = -H:IncludeResources=logback.xml|application.yml \ -H:Name=hello-graal \ -H:Class=hello.graal.Application
ふむ。この native-image.properties
について調べると、ロジコさんが翻訳してくれてるこの記事の後半に書いてあった。
GraalVMの最近のリリース以降、ネイティブイメージのビルドについて別の重要な改善をしています。JARファイル中のMETA-INF/native-imageにnative-image.propertiesファイルを埋め込むことができるようになりました。native-imageツールは、このリソースの場所にあるすべてのファイルを自動的に処理し、それらを使用してnative-imageのコマンドライン引数を作成します。
なるほど。
GraalVMのドキュメントを探してみたけど native-image.properties
についての記述は見つからなかったので、ソースを見てみた。たぶんここかなぁ。
META-INF/native-image
ディレクトリーの中で native-image.properties
って名前で終わってるファイルをチェックしてるみたいね。
## ということは、Nettyは?
と思ってJARファイルの中を見てみたら、あった。そういうことかー。
## で、その元になってるファイルはどこにあるん?
と思って、JARを見たら micronaut-http-netty-1.1.0.jar
の中に入ってた。
native-image.properties
で「自分と同じディレクトリーにある reflection-config.json
を読み込んでくれー」って言ってる。
## なんとなく、ソースも見たい
と思って探してみた。これだな。
@TypeHint(value = { NioServerSocketChannel.class, NioSocketChannel.class }, typeNames = {"sun.security.ssl.SSLContextImpl$TLSContext", "sun.nio.ch.SelectorImpl"}, accessType = {TypeHint.AccessType.ALL_DECLARED_CONSTRUCTORS, TypeHint.AccessType.ALL_DECLARED_FIELDS} ) public class NettyThreadFactory {
@TypeHint
アノテーションや @Introspected
アノテーションがついてると、Native Image用のプロパティファイルを生成するみたい。
このアノテーションの処理はコンパイルタイムに GraalTypeElementVisitor
がやってるっぽいね。
この仕組みができたから GraalClassLoadingAnalyzer
の役目は終了したっぽい。 1.1.0
では削除されてる。
## Substitutionsに関して
は、ここだな。この前Netty試したときに書いたやつがそのまま書いてある。
## ライブラリー側でnative-image対応が可能になる
ロジコさんの記事に戻って見てみると、こう書いてある。
正しく適用すれば、Javaライブラリの作者は上述のメカニズムを使ってライブラリにnative-image互換性を持たせることができます。
なるほどなぁ。
## 気になるのは
こんな風に native-image.properties
に書いてある場合
Args = -H:+ReportUnsupportedElementsAtRuntime \ --allow-incomplete-classpath
知らないうちにオプションが適用されてしまってるんだろうなぁというところかな。
あと、Nettyのログは java.util.logging
に限定されることになるんかな?(よく分かってない
## まとめ
Micronaut 1.1.0 からは、Native Imageのビルドが簡単になってたよ。面白かった。