Rustのprostクレートのメモ。
|
|
prostは、Rustでprotobufを扱うクレート(ライブラリーおよびビルドツール)。
protoファイルからRustのソースコードを生成することが出来る。
コンパイル時(および実行時)に使うprostと、ビルド時(protoファイルからRustのソースを生成する)に使うprost-buildがあり、両方を使用する。
ビルド時には、裏でprotoc(protobufのコンパイラー)が実行される。
なので、開発環境にprotocをインストールしておく必要がある。
本来なら、protoc単体でprotoファイルから各種言語のソースコードを生成できるのだが、バージョン28.2時点ではRustには正式対応していないようだ。
(試しに実行してみたが、よく分からないエラーが出たので断念したorz)
なので、prost-buildを使うのが、今のところ一番確実。
protocは以下の手順でインストールする。
prost-build実行時にprotocが見つからないとエラーになる。
エラーメッセージには「パスを通すか環境変数PROTOCに実行ファイルの場所を記述する」と出ているのだが、自分が試した範囲では環境変数PROTOCは認識してくれなかったので、パスを通しておく方が確実だと思う。
prostおよびprost-buildの例は、Snazzyを見るのが分かりやすい。
まず、Cargo.tomlにprostとprost-buildを追加する。
〜 [dependencies] prost = "0.13" [build-dependencies] prost-build = "0.13"
次に、protoファイルをプロジェクト内に配置する。
Snazzyでは「プロジェクト/src」直下にitems.protoを置いているが、自分としてはsrcの下はrsファイルだけにしたいので、「プロジェクト/proto」というディレクトリーに置くことにした。
syntax = "proto3"; package snazzy.items; // A snazzy new shirt! message Shirt { enum Size { SMALL = 0; MEDIUM = 1; LARGE = 2; } string color = 1; Size size = 2; }
プロジェクト内にprotoファイルを配置したら、build.rsにそのprotoファイルの場所を記述する。
(build.rsは、ビルド時に実行されるRustソースコード)
extern crate prost_build; fn main() { prost_build::compile_protos( &["proto/items.proto"], &["proto/"] ).unwrap(); }
compile_protos()の第1引数にprotoファイルを指定する。
配列なので、複数ファイルを指定可能。protoファイルが複数ある場合は、全て指定する。
指定するパスは、プロジェクトディレクトリーからの相対パス。
ディレクトリーを指定することは出来ない。(サブディレクトリーを探索してくれると便利だと思うんだけど)
なお、protoファイルの中で他のprotoファイルをimportしている場合は、importされるprotoファイルは(compile_protos()で指定しなくても )自動的に読み込まれるようだ。
第2引数は、protoファイルを置いてあるディレクトリー。
(なんでファイルの他にディレクトリーを指定しないといけないのかは謎…)
そして、ビルドを実行する。
$ cargo build
問題が無ければ、build.rsで指定したprotoファイルが読まれ、Rustのソースコードが生成される。
生成されるファイルは、「target/debug/build/プロジェクト名-よく分からない十六進数/out/snazzy.items.rs」。
この十六進数はハッシュ値か何かだと思うが、ビルドする度に数値が異なるディレクトリーが増えてくる。どれが最新かはよく分からない^^;
生成されるファイル名は、protoファイルの中に記述されていたpackageのようだ。
最後に、生成されたrsファイルをモジュールに加える。
(モジュールとして追加しないと、生成された構造体や列挙型を使うことが出来ない)
pub mod snazzy { pub mod items { include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs")); } }
snazzy.items.rsの中にはShirt構造体が定義されているが、「mod snazzy { mod items { }}
」で囲むと、「snazzy::items::Shirt
」として使用できるようになる。
生成されたrsファイル内の構造体の中にエラーがあると、「::prost::Message
」の箇所がエラーになる。
error[E0412]: cannot find type `Size2` in module `super`
--> C:\\example\ffi\ffi-example-rust\target\debug\build\ffi-example-rust-3f216621b1ac0cb6\out/snazzy.items.rs:3:28
|
3 | #[derive(Clone, PartialEq, ::prost::Message)]
| ^^^^^^^^^^^^^^^^ not found in `super`
このエラーは、以下のような状態で発生した。
syntax = "proto3"; package snazzy; enum Size2 { SMALL = 0; MEDIUM = 1; LARGE = 2; }
syntax = "proto3"; package snazzy.items; import "items2.proto"; ←追加 // A snazzy new shirt! message Shirt { enum Size { SMALL = 0; MEDIUM = 1; LARGE = 2; } string color = 1; Size size = 2; Size2 size2 = 4; ←追加 }
pub mod snazzy { pub mod items { include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs")); } // items2が入っていない }
つまり、protoファイルには問題は無いし、生成されたrsファイルにも問題は無いのだが、
items2.protoから生成されたsnazzy.rsがモジュールに含まれていないので、ビルド対象になっていない。
そのため、それに依存しているsnazzy.items.rsでエラーが発生したというわけ。
この場合、items2.protoから生成されたrsファイルをモジュールに含めてビルドし直せば解決する。
pub mod snazzy { include!(concat!(env!("OUT_DIR"), "/snazzy.rs")); pub mod items { include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs")); } }
しかし原因が分かってみればエラーになって当然の内容だったけど、
エラー発生個所が「::prost::Message
」だし、エラーメッセージが「not found in
`super`
」で、意味不明だ…orz
まぁその前に「cannot find type `Size2` in module `super`
」というメッセージも出ているので、Size2が見つからない→Size2はitems2.protoで定義している、というところから推測できるかもしれないけど…。
また、エラーメッセージの「super」の意味は、items.protoのパッケージが「snazzy.items」でitems2.protoのパッケージが「snazzy」だから、パッケージ (Rust的にはモジュール)上の親(super)ということだと思う…。