遊びでKotlin Compiler Pluginを作ってみました。色々と試行錯誤して分かったことをメモしておきます。
Compiler Pluginはbyte codeを編集することができます。ただし、Undocumentedなので自力で作るのはだいぶ大変です。
すでにあるものとして、All-open、android-extension などがあります。
細かい説明は Kotlin Conf 2018 のセッションを見ると良いと思います。
今回はこれらを見ながら自分で簡単なものを作ってみたので、どのように作るかをまとめておきます。
今回の例では、各メソッドの一番最初に println
で実行するメソッド名を表示するようにしてみます。
サンプル
細かいコードは省略してるので、サンプルのほうも合わせて参照してください。
注意
繰り返しになりますが、 Undocumentedなので注意が必要です。この記事は試行錯誤して自分なり分かったことを書いていますので、その点注意してください。間違いなどあれば指定してもらえると嬉しいです。
Compiler Pluginの実装
まず CommandLineProcessor
と ComponentRegistrar
が必要になります。それに加えて実際の拡張処理を必要に応じて追加します。
CommandLineProcessor
CommandLineProcessor
はCompiler Pluginの起点になるのと、オプションに関しての処理することになります。
ComponentRegistrar
次に ComponentRegistrar
は実際の処理を行う拡張処理を登録します。この登録されたものが実行されて実際の処理を行うことになります。
ComponentRegistrar
で使ってる、 ClassBuilderInterceptorExtension
は後ほど説明します。
拡張処理
拡張処理はいくつかベースとなる処理があります。
今回はクラス作成の処理を拡張する ClassBuilderInterceptorExtension
を使います。
他には、All-openでも使われてる、 アクセス修飾子に対して処理を行う DeclarationAttributeAltererExtension
というのもあります。
このあたりうまく説明できない+完全に理解してるわけではないので、雰囲気で見てもらえると。。。
ClassBuilderInterceptorExtension
はFactoryが必要なので、それを実装します。
次に実際にメソッドの処理を書き換えてみます。Compiler PluginはByte Codeを直接書く必要があります。
META-INF/services登録
CommandLineProcessor
と ComponentRegistrar
については
META-INF/services に登録します。
このあたりはサンプルのコードを見てもらえると。
Compiler Pluginを動かす
作ったCompilerをビルドしてjarファイルを作っておきます。
サンプルとして次のようなコードで試してみたいと思います。
Compiler Pluginなしで実行
最初は比較としてCompiler Pluginなしで実行すると次のような出力になります。
Hello World
test
コード上に書いた println
のみが実行されてるのがわかります。
Compiler Pluignを使って実行
次にCompiler Pluginを使ってみます。色々やり方があるのですが、GradleのCompiler Optionを指定する方法でやってみます。
これで実行するとCompiler Pluginが出力を追加するようになります。
[Run `main` function]
Hello World
[Run `test` function]
test
(本当は kotlinc
で試したかったんですけど、どうにもうまく行かなくてGradleを使いました)
Gradle Pluginを作る
Compiler Optionに指定するは面倒なので、Gradle Pluginを使って、簡単に使えるようにします。
Gradle Pluginの細かいところは省略しますが、基本は通常のGradle Pluginを作るのと同じです。
Compiler Pluginを使うためには、 KotlinGradleSubplugin
というのを実装する必要があります。以下のような感じになります。
こちらも META-INF/services に登録が必要です。
あとは、これを作ったプラグインを使うようにするだけです。
Gradle Pluginがビルド時にCompiler PluginをDLしてきてて適用するような感じになります。
注意としては、Compiler PluginはGradleが解決できるRepositoryに配置する必要があります。サンプルではローカルのリポジトリを追加してあります。