cargo-*系ツールの紹介

Rustのビルドシステムであるcargoは、PATHに`cargo-なんとか`というバイナリが存在するとサブコマンドとして使用できます。よくあるやつですね。便利なものがcrates.io上に公開されているので、いくつかご紹介したいと思います。

# `cargo install`

その前に、cargo組み込みのコマンドである`cargo install`の説明です。Rust製バイナリの導入が簡単に行えるコマンドです。cargoパッケージ[^package]のディレクトリ内で

[^package]: 1つのパッケージには1つ以上のクレートが含まれます。あまり厳格に区別する必要はなく、パッケージのことをクレートと呼んだりします。

```bash
cargo install
```

と実行すると、そのパッケージをコンパイルし、成果物のバイナリを`~/.cargo/bin`にインストールします。`cargo uninstall バイナリ名`でアンインストールできます。

crates.io上のパッケージをコンパイル&インストールするにはパッケージ名を指定します。

```bash
cargo install ripgrep
```

Gitリポジトリから取得することも可能です。

```
cargo install --git https://github.com/BurntSushi/ripgrep.git
```

注意点として、`cargo install`コマンドでは既に同名のバイナリが存在する場合`-f`オプションを付けない限り上書きされません。

この記事では項目毎にcrates.io上のパッケージ名を括弧の中に書いているので、気になるものがあれば`cargo install パッケージ名`でインストールしてみてください。

# `cargo install-update` ([`cargo-update`](https://crates.io/crates/cargo-update))

さて、`cargo install`は便利なのですが、インストールしたものを一括でアップデートしてくれるような機能はありません。個々のパッケージが更新される度に手動で

```
cargo install -f なんとか
```

と打たなければなりません。cargoはパッケージマネージャではなくビルドシステムなので、あくまで`make install`的なコマンドということのようです（更新機能を追加する[issue](https://github.com/rust-lang/cargo/issues/2082)は立てられていますが）。

しかしパッケージの更新を自分で調べてアップデートするのは不便なので、自動化するツールが存在します。[`cargo-update`](https://github.com/nabijaczleweli/cargo-update)です。パッケージ名と異なり、`cargo-install-update`というバイナリがインストールされます。

使い方は簡単です。

```bash
cargo install-update -a
```

これだけでcrates.ioからインストールしたパッケージが更新されます。gitリポジトリからインストールしたものの自動更新は今の所できないようです。

# `cargo outdated` ([`cargo-outdated`](https://crates.io/crates/cargo-outdated))

Cargoのバージョン指定はSemVerなのですが、デフォルト（比較演算子を省略した場合）がnpmでいう`^`になっています。

```toml:Cargo.toml
[dependencies]
clap = "1.0.0" # >=1.0.0 <2.0.0
toml = "0.1.0" # >=0.1.0 <0.2.0
```

`cargo update`を行うと、この制約に合致する範囲でcrates.io上の最新バージョンが選択されます。しかし、crates.io上にこの範囲を越える新しいバージョンが公開されていたとしてもcargoは教えてくれません。なので気が付いたら古いバージョンのライブラリを使い続けていたなんてことがあります。

そこで役立つのが[`cargo-outdated`](https://github.com/kbknapp/cargo-outdated)です。`cargo update`を実行すると、現在のプロジェクトが依存しているパッケージの新しいバージョンが公開されている場合に教えてくれます。

```
> cat Cargo.toml
[package]
name = "hello"
version = "0.1.0"

[dependencies]
clap = "1.0.0"
toml = "0.1.0"


> cargo update
    Updating registry `https://github.com/rust-lang/crates.io-index`


> cargo outdated
Checking for SemVer compatible updates...Done
Checking for the latest updates...Done
The following dependencies have newer versions available:

    Name             Project Ver  SemVer Compat  Latest Ver
    clap                1.5.5          --          2.19.3
    clap->ansi_term     0.7.5          --          0.9.0
    clap->bitflags      0.3.3          --          0.7.0
    clap->strsim        0.4.1          --          0.6.0
    clap->vec_map       0.4.0          --          0.6.0
    toml                0.1.30         --          0.2.1
```

`Project Ver`が現在使用されているバージョンで、`SemVer Compat`は`cargo update`を実行することで更新できるバージョン、`Latest Ver`はCargo.tomlを書き換えないと更新されないバージョンを示しています。

# `cargo tree` ([`cargo-tree`](https://crates.io/crates/cargo-tree))

Rust界隈では小さなクレートをたくさん作る傾向があるようです。なので、ちょっとしたプロジェクトのつもりでも間接的に多数のクレートに依存していたりします。この依存クレートをツリー形式で表示してくれるのが`cargo-tree`です。

[racer](https://github.com/phildawes/racer)パッケージで`cargo tree`を実行してみた結果を示します。

```:racerのレポジトリ内
> cargo tree
racer v2.0.3
├── clap v2.19.1
│   ├── ansi_term v0.9.0
│   ├── bitflags v0.7.0
│   ├── libc v0.2.18
│   ├── strsim v0.5.2
│   ├── term_size v0.2.1
│   │   └── libc v0.2.18 (*)
│   ├── unicode-segmentation v0.1.3
│   ├── unicode-width v0.1.3
│   └── vec_map v0.6.0
├── env_logger v0.3.5
│   ├── log v0.3.6
│   └── regex v0.1.80
│       ├── aho-corasick v0.5.3
│       │   └── memchr v0.1.11
│       │       └── libc v0.2.18 (*)
│       ├── memchr v0.1.11 (*)
│       ├── regex-syntax v0.3.9
│       ├── thread_local v0.2.7
│       │   └── thread-id v2.0.0
│       │       ├── kernel32-sys v0.2.2
│       │       │   └── winapi v0.2.8
│       │       └── libc v0.2.18 (*)
│       └── utf8-ranges v0.1.3
├── log v0.3.6 (*)
├── syntex_errors v0.52.0
│   ├── libc v0.2.18 (*)
│   ├── log v0.3.6 (*)
│   ├── rustc-serialize v0.3.22
│   ├── syntex_pos v0.52.0
│   │   └── rustc-serialize v0.3.22 (*)
│   ├── term v0.4.4
│   │   ├── kernel32-sys v0.2.2 (*)
│   │   └── winapi v0.2.8 (*)
│   └── unicode-xid v0.0.3
├── syntex_syntax v0.52.0
│   ├── bitflags v0.7.0 (*)
│   ├── libc v0.2.18 (*)
│   ├── log v0.3.6 (*)
│   ├── rustc-serialize v0.3.22 (*)
│   ├── syntex_errors v0.52.0 (*)
│   ├── syntex_pos v0.52.0 (*)
│   ├── term v0.4.4 (*)
│   └── unicode-xid v0.0.3 (*)
├── toml v0.2.1
│   └── rustc-serialize v0.3.22 (*)
└── typed-arena v1.2.0
```

各クレートは最大一回だけ依存ツリーが展開され、二回目の出現からは`(*)`という印が付き省略されます。

# `cargo graph` ([`cargo-graph`](https://crates.io/crates/clippy))

`cargo-tree`はターミナル上にツリーを描画するものでしたが、こちらは依存クレートのグラフを描画するコマンドです。`cargo graph`を実行するとdot言語でグラフが出力されるので、煮るなり焼くなりできます。

また例はracerです。

```:racerのレポジトリ内
cargo graph | dot -Gsplines=ortho -Earrowhead=open -Earrowsize=0.5 -Tpng -Ograph.png
```

![graph.png](https://qiita-image-store.s3.amazonaws.com/0/61872/4e62f46d-1f3e-b9bd-0402-f7285b0534ff.png)

# `cargo modules` ([`cargo-modules`](https://crates.io/crates/cargo-modules))

コードのモジュール構造をツリー形式で表示するものです。各モジュールの可視性(`pub`か否か)や条件コンパイル属性を表示してくれます。

ripgrepの`rg`バイナリを構成するモジュールを見てみましょう。

```:ripgrepのレポジトリ内
> cargo modules -b rg

rg : crate
 ├── app : private
 ├── args : private
 ├── atty : private
 ├── pathutil : private
 ├── printer : private
 │   └── tests : private @ #[cfg(test)]
 ├── search_buffer : private
 │   └── tests : private @ #[cfg(test)]
 ├── search_stream : private
 │   └── tests : private @ #[cfg(test)]
 ├── unescape : private
 │   └── tests : private @ #[cfg(test)]
 └── worker : private
```

# `cargo clippy` ([`clippy`](https://crates.io/crates/clippy))

lintツールです。必須です。各lintの説明が並んでいるhttps://github.com/Manishearth/rust-clippy/wiki をざっと眺めるだけでもかなり役に立ちます。

Nightlyコンパイラが必要なので、clippyの為にローカルではNightlyで開発・CIでstableで動くかチェックみたいな感じにしている人も多そうですが、そろそろ標準添付されそうな雰囲気ですね。

以前はちょっと試してみるだけでもCargo.tomlを弄ったり`#![plugin]`属性を入れたりと面倒な作業が必要だったような気がしますが、最近では一度実行するだけならパッケージのディレクトリ内で

```
cargo clippy
```

と打つだけになったようです。注意点として、clippyをコンパイルするのに使ったのと同じコンパイラでないと動作しません。`rustup update`でNightlyコンパイラを更新したら`cargo install -f clippy`としてclippyを再コンパイルする必要があります。

参考記事: [rust-clippyの使い方](http://qiita.com/ubnt_intrepid/items/193de47ed9faf656b596)

# `cargo benchcmp` ([`cargo-benchcmp`](https://crates.io/crates/cargo-benchcmp))

Rustには言語組み込みのベンチマーク機能があります（unstable）。`cargo-benchcmp`はそのベンチマークを簡単に比較できるツールです。

`benches/moji.rs`というベンチマークコードがあるとします。次のようにして、コード変更前と変更後でベンチマーク結果を書き出します。

```
> cargo bench --bench moji > old
> ...
> cargo bench --bench moji > new
```

これらをbenchcmpコマンドに引数として渡すことで比較が行われます。実行時間の変化がパーセンテージで表示されます。もし新しい方のベンチマークで性能が低下した場合、赤くハイライトされます（この例では13%の性能低下が起きているので、ターミナル上の文字は赤くなっていました）。

```
> cargo benchcmp old new
 name              old ns/iter  new ns/iter  diff ns/iter  diff % 
 bench_moji_count  38           43                      5  13.16%
```

# `cargo expand` ([`cargo-expand`](https://crates.io/crates/cargo-expand))

Rustは[マクロ](https://doc.rust-lang.org/book/macros.html)や[トレイトの`derive`](https://doc.rust-lang.org/book/traits.html#deriving)といったコード生成機構を備えています。マクロ呼び出しや`#[derive]`属性はコンパイル時に実際のソースコードへ展開されるのですが、生成されたコードに問題がある場合、コンパイラのエラーメッセージからは問題の箇所がよく分からない場合があります。

`cargo expand`はコンパイラによって展開された後のコードを表示してくれるツールです。実際のコードを確認することで生成されたコードの問題点が見つけやすくなります。

`cargo expand`の出力例を示します。マクロやderiveだけでなく、stdのインポートなどの定型コードも追加されていることが分かりますね。

元コード:

```rust
#[derive(Debug, Clone)]
struct Foo<T>(T);

fn bar() {
    println!("{}", 3.14);
}
```

`cargo expand`の出力:

```rust
#![feature(prelude_import)]
#![no_std]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std as std;
struct Foo<T>(T);
#[automatically_derived]
#[allow(unused_qualifications)]
impl<T: ::std::fmt::Debug> ::std::fmt::Debug for Foo<T> {
    fn fmt(&self, __arg_0: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Foo(ref __self_0_0) => {
                let mut builder = __arg_0.debug_tuple("Foo");
                let _ = builder.field(&&(*__self_0_0));
                builder.finish()
            }
        }
    }
}
#[automatically_derived]
#[allow(unused_qualifications)]
impl<T: ::std::clone::Clone> ::std::clone::Clone for Foo<T> {
    #[inline]
    fn clone(&self) -> Foo<T> {
        match *self {
            Foo(ref __self_0_0) => Foo(::std::clone::Clone::clone(&(*__self_0_0))),
        }
    }
}

fn bar() {

    ::io::_print(::std::fmt::Arguments::new_v1(
        {
            static __STATIC_FMTSTR: &'static [&'static str] = &["", "\n"];
            __STATIC_FMTSTR
        },
        &match (&3.14,) {
            (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Display::fmt)],
        },
    ));
}
```

# おわりに

他にもまだこういった便利なツールが存在します。[ここ](https://github.com/kud1ing/awesome-rust#build-system)などを参考に探してみて下さい。