LoginSignup
3
1

More than 5 years have passed since last update.

proc_macro2で手続きマクロを起動したファイルのパスを取得する

Posted at

FlatBuffersの定義ファイルを指定するだけで生成されたRustコードを作ってみたので苦労した部分を共有しておきます。

FlatBuffersについてはそのうち記事を書きたいと思いますが、Protocol Buffersのようにスキーマから各言語用のコードを出力するタイプのシリアライズ形式です。

fbs/addressbook.fbs
namespace addressbook;

table Person {
  name:string;
  age:int;
}

のように定義ファイルを書いてそれをflatcというコンパイラで処理することでRustのコードを生成します。しかしそれは面倒なので以下のように書きたいと思うわけです:

src/lib.rs
use flatc_gen::flatc_gen;
flatc_gen!("../fbs/addressbook.fbs");

マクロflatc_gen!はfunction-like procedual-macroを用いて実装しました:

flatc-gen/src/lib.rs
#[proc_macro]
pub fn flatc_gen(input: TokenStream) -> TokenStream {
  let input = parse_macro_input!(input as syn::LitStr);  // 引数は文字列リテラルとしてパースします
  // 長いので略
} 

さて、手続きマクロはフル機能のRustが実行できるので、flatcのコードをダウンロードしてcmakeでコンパイルして呼び出してRustのコードを生成して、そのファイルを読み出してマクロとして展開するわけですが、問題となるのが相対パスの扱いです。
手続きマクロのコード自体が実行されるときのパスはflatc_gen!が呼び出されたファイルのディレクトリとは一般に異なります(おそらくrustcを起動したディレクトリになる)。すると上のコードの../fbs/addressbook.fbsを解決するためにはflatc_gen!が呼び出されているファイルのパスを特定する必要があります。
なお、file!()より手続きマクロの方が先に展開されるのでmacro_rules!を使って回避できません(多分)。

これが以外と大変な作業になります。この情報自体はrustcが持っているわけですが、それは現時点(2019/3)のstableではproc_macro側には提供されていません。一応proc_macro_span featureとしてproc_macro::Spanにはsource_fileというAPIがnightlyには用意されており、これを使えば行けそうですが、TokenStreamをパースするsyn crateのAPIは基本的にproc_macro2::Spanを返し、これをproc_macro::Spanに変換するAPIが無いので使えません。

そこでproc_macro2::Spanにあるsource_file関数を使おうとなるわけですが、これは普通にやるとexposeされていません。

This method is semver exempt and not exposed by default.

このメソッドを有効にするにはproc_macro2の方のunstable featureに説明があるように、rustcにフラグをつける必要があります。

RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build

これはflatc-genを使うすべてのcrateのビルド時につける必要があります。.cargo/configファイルを用意して

.cargo/config
[build]
rustflags = ["--cfg", "procmacro2_semver_exempt"]

とするのが楽でしょうが、実用するにはハードルが高すぎますね(´・ω・`)

まとめ

  • proc_macro2のunstable featureを使うとマクロを起動したファイルのパスを取得できる
  • でもAPIがまだ整備中でnightlyでもきつい
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1