そんなに需要がないだろうなあ、と思いつつも、ちょっと面白かったので記事にしました。
Rust で書いたソースコードを Google製ビルド・ツール Bazel を使ってビルドします。
Rust の場合、通常であれば迷わず Cargo でビルドしたいところです。
しかし、多言語を扱う monorepo スタイルの開発であったり、C/C++ソースも一緒にコンパイルしたい場合、複数言語のビルドを一貫して行える Bazel という選択肢がでてきます。
(tensorflow/rust を使うときもですかね)
参考: 大規模ビルドシステムにおけるRustの扱いに関する議論
(ここでも議論されています)
Bazel では、ビルドするためのルールを BUILD というファイルに記述します。
様々な言語向けのルールが既にたくさん用意されているので、BUILD にダウンロード設定を記述して使うことができます。
ありがたいことに Rust もありました。(なければ自作すれば良いのですが、、、)
これを使って、 Bazel で Rust をビルドしてみます。
ソースコードの完全版はGithubに公開しておりますので、合わせてご覧くださいませ。
https://github.com/x1-/rust_bazel_sandbox
Bazel のインストール(for Mac)
まずは Bazel をインストールします。
Macであれば brew で簡単に入るのですが、 Bazel はバージョン・センシティブなため brew install bazel は あまりオススメしません。
オススメしない。けれど簡単
$ brew install bazel
※ OracleJDK >= 8 のインストールが必要です。
オススメ
1. xcodebuildのライセンスに同意していない場合は同意する
$ sudo xcodebuild -license accept
2. インストール・スクリプトをダウンロードする
GitHubのリリースページからインストールしたいバージョンのインストール・スクリプトをダウンロードします。
$ curl -O https://github.com/bazelbuild/bazel/releases/download/0.13.0/bazel-0.13.0-without-jdk-installer-darwin-x86_64.sh
※ OracleJDK >= 8 が既にインストールされている場合、without-jdk で大丈夫です。
3. インストール・スクリプトを実行する
$ chmod +x bazel-0.13.0-installer-darwin-x86_64.sh $ ./bazel-0.13.0-installer-darwin-x86_64.sh --user
–user フラグを付けると $HOME/bin に bazel がインストールされます。
※ これはお好みで…
4. 実行パスに bazel を追加する
bash, zsh
$ export PATH="$PATH:$HOME/bin"
fish
$ set PATH $PATH $HOME/bin
これは各shellの設定ファイルに(~/.bashrc とか ~/.config/fish/config.fish とか)追記しておきます。
以上でbazelのインストールは完了です。
詳しくは 公式のインストール・ページ を見てください。
プロジェクトの作成
Bazelでのビルドを試すために、新しく Rust プロジェクを作ります。
rust_bazel_sandbox という名前にしました。
$ cargo init rust_bazel_sandbox $ cd rust_bazel_sandbox $ tree . . ├── Cargo.toml └── src └── main.rs
Bazelでビルドする場合、通常と少しディレクトリ構成を変える必要があります。
下記のように変更します。
$ tree . . ├── cargo │ └── Cargo.toml └── src └── main.rs
WORKSPACEの作成
Bazel でビルドする場合、プロジェクトのルートに WORKSPACE というファイルを作成します。(ビルド・ルートと言ったほうがいいかも)
$ tree . . ├── WORKSPACE # new! ├── cargo │ └── Cargo.toml └── src └── main.rs
この WORKSPACE ファイルに Rust のビルドルールを記述します。
Rust 用ビルドルールの追加
Rust 用ビルドルール は bazelbuild/rules_rust に公開されています。
bazel の git_repository ルールと load ルールを使ってルールを定義します。
git_repository( name = "io_bazel_rules_rust", commit = "b07eca4e2903c81e136b7ec13cef57b403044180", remote = "https://github.com/bazelbuild/rules_rust.git", ) load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repositories") rust_repositories()
commit には好きな時点のコミット・ハッシュを指定します。
私は現時点(2018-05-23)での最新コミット・ハッシュを指定しました。
これで Rust をビルドするための rust_binary ルールなどが使えるようになります。
アプリケーション・ビルドルールの記述
プロジェクトのルートに BUILD という名前のファイルを作成します。
$ tree . . ├── BUILD # new! ├── WORKSPACE ├── cargo │ └── Cargo.toml └── src └── main.rs
この BUILD ファイルに main.rs のビルドを記載します。
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_binary") rust_binary( name = "rust_bazel_sandbox", srcs = ["src/main.rs"], )
ここまでできたら一旦ビルドしてみましょう。
ビルド
$ bazel build //:rust_bazel_sandbox --verbose_failures .......... INFO: Analysed target //:rust_bazel_sandbox (7 packages loaded). INFO: Found 1 target... Target //:rust_bazel_sandbox up-to-date: bazel-bin/rust_bazel_sandbox INFO: Elapsed time: 35.190s, Critical Path: 0.48s INFO: Build completed successfully, 4 total actions
かんたんですね。
// がルートを表します。
Bazel のルートは WORKSPACE ファイルが置いてある場所になります。
WORKSPACE が置いてあるからWORKSPACEのルート、わかりやすいですね。
:rust_bazel_sandbox は BUILD に定義した name です。
よって上記のコマンドは、 ルート直下にある rust_bazel_sandbox という名前のビルドルールをビルドしたことになります。
サブディレクトリがある場合は、
//subdir:rust_bazel_sandbox
のようになります。
依存の管理
普通にアプリケーションを開発していれば、ライブラリに依存させたいことが多々あります。
Cargo であれば Cargo.toml に依存を書くところですが、 Bazel では BUILD ファイルに依存を書きます。
しかし、Cargoが解決してくれていた、依存するライブラリが依存するライブラリの・・・といったネスト依存を、手動で全て記述するのは不可能に近いです。
そこで cargo-raze という非常に便利な BUILD ファイル生成ツールを使います。
これを使うと、 Cargo.toml に記載された依存に従って、Bazel 用 BUILD ファイルを生成することができます。
使い方は README に記載のとおりですが、 cargo raze のように cargo コマンドとして使います。
これを導入していきます。
cargo-raze のインストール
$ cargo install cargo-vendor $ cargo install cargo-raze
これで cargo raze コマンドが使えるようになります。
Cargo.toml の編集
まずは Cargo.toml に依存定義を追加します。
[package] name = "rust_bazel_sandbox" version = "0.1.0" authors = ["x1-"] [[bin]] name = "main" path = "src/main.rs" [dependencies] base64 = "*"
base64 への依存を追加しました。
さらに、 [raze] ディレクティブを追加します。
[raze] workspace_path = "//cargo" target = "x86_64-apple-darwin" genmode = "Remote"
carg-raze には ローカルにソースコードを保持する vendor モードと、ビルド時にリモート・リポジトリからソースをフェッチする Remote モードがあるのですが、私は Remote の方を使いました。
せっかくなので ソースコード(src/main.rs)の方でも base64 を使うように変更しておきます。
extern crate base64; fn main() { println!("Hello, world!"); println!("{:?}", base64::encode("Hello, world!".as_bytes())); }
cargo-raze の実行
次に、 cargo/ ディレクトリ(Cargo.tomlを置いたディレクトリ)で cargo raze コマンドを実行します。
$ cd ./cargo $ cargo raze
すると、cargo, cargo/remote 配下に依存ライブラリ用の BUILD ファイルが生成されます。
$ cd ../ $ tree . . ├── BUILD ├── WORKSPACE ├── bazel-bin -> .../execroot/__main__/bazel-out/darwin-fastbuild/bin ├── bazel-genfiles -> .../execroot/__main__/bazel-out/darwin-fastbuild/genfiles ├── bazel-out -> .../execroot/__main__/bazel-out ├── bazel-rust_test -> .../execroot/__main__ ├── bazel-testlogs -> .../execroot/__main__/bazel-out/darwin-fastbuild/testlogs ├── cargo │ ├── BUILD # new │ ├── Cargo.lock │ ├── Cargo.toml │ ├── crates.bzl # new │ └── remote # new │ ├── BUILD │ ├── base64-0.9.1.BUILD │ ├── byteorder-1.2.3.BUILD │ └── safemem-0.2.0.BUILD └── src └── main.rs
WORKSPACE ファイルの編集
Remoteモードの場合は WORKSPACE ファイルに下記を追加します。
load("//cargo:crates.bzl", "raze_fetch_remote_crates") raze_fetch_remote_crates()
BUILD ファイルの編集
これで完了なわけではありません。
BUILD ファイルの方にも依存を定義する必要があります。
cargo/remote/base64-0.9.1 へは、 //cargo:base64 というエイリアスが作成されるので(Cargo.tomlに依存を記述するとエイリアスが生成されます)、 これを deps に追加します。
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_binary") rust_binary( name = "rust_bazel_sandbox", srcs = ["src/main.rs"], deps = [ "//cargo:base64", ], )
これで依存関係も含んだビルドを行うことができるようになりました。
$ bazel build //:rust_bazel_sandbox INFO: Analysed target //:rust_bazel_sandbox (8 packages loaded). INFO: Found 1 target... Target //:rust_bazel_sandbox up-to-date: bazel-bin/rust_bazel_sandbox INFO: Elapsed time: 4.200s, Critical Path: 2.07s INFO: Build completed successfully, 5 total actions
bazel-bin/ に実行ファイルが生成されるので実行してみます。
$ ./bazel-bin/rust_bazel_sandbox Hello, world! "SGVsbG8sIHdvcmxkIQ=="
普通に実行できました!
さて、ここまでは問題ないのですが、 hyper-rustls を依存に追加したところ、超絶エラーとなってしまいました(T T)
後編では、これをどうやって解決したかを解説したいと思います。