🦉

Cargo のワークスペースの構造がやっと理解できた

2022/04/01に公開

はじめに

これは本家のドキュメントで躓いた理解力の乏しい人向けの記事です。

本家では add で adder と add-one を作る例になっていたため私はまず名前で混乱しました。その上、常識的に考えて add が人に説明するときのワークスペース名なわけがないだろうと勝手な先入観を持ってしまいました。そこでドキュメントには書いてないけどあらかじめワークスペースとなるその親ディレクトリを cargo new で用意しとかないといけないのかな? と、ディレクトリ構造を深くしてしまったり、add 自体を cargo new で作ったりと無駄な試行錯誤を重ねてしまいました。

わかったことを一言で言えば、

  • 共通の何もない親に子のディレクトリ名を列挙した Cargo.toml を置く

たったそれだけのことでした。

なんなら本家のドキュメントその一行でよかったぐらいです。

確認用スクリプト

少し長いかもしれないけどこの内容と実行結果を見るだけで確実に理解できるでしょう。

#!/bin/sh
rm -fr /tmp/my_workspace1
mkdir -p /tmp/my_workspace1
cd /tmp/my_workspace1

cargo new --bin my_project1
cargo new --bin my_project2

(cd my_project1 && cargo build)
(cd my_project2 && cargo build)

exa -T -F -I "deps|incremental|debug|*.d|*.TAG"

(cd my_project1 && cargo clean)
(cd my_project2 && cargo clean)

cat <<'EOF' > Cargo.toml
[workspace]
members = [
  "my_project1",
  "my_project2",
]
EOF

cargo build

exa -T -F -I "deps|incremental|debug|*.d|*.TAG"

実行結果

./
├── my_project1/
│  ├── Cargo.lock
│  ├── Cargo.toml
│  ├── src/
│  │  └── main.rs
│  └── target/
└── my_project2/
   ├── Cargo.lock
   ├── Cargo.toml
   ├── src/
   │  └── main.rs
   └── target/
./
├── Cargo.lock
├── Cargo.toml
├── my_project1/
│  ├── Cargo.lock
│  ├── Cargo.toml
│  └── src/
│     └── main.rs
├── my_project2/
│  ├── Cargo.lock
│  ├── Cargo.toml
│  └── src/
│     └── main.rs
└── target/

一つ目は単に my_project1 と my_project2 が別々にあるだけです。二つ目は親に Cargo.toml と target ディレクトリがあるのがわかります。

まとめ・わかったこと

  • cargo のサブコマンドとして workspace があるわけではない
    • ググると cargo workspace という表記があったりするがなんも関係ない
  • 共通のワークスペースディレクトリ(親)を cargo new で作るべからず
  • 親は空でよい
    • cargo new で作った子のディレクトリがあるだけという意味
  • 子のディレクトリ名を列挙した Cargo.toml を新規で親に置く
    • 親の Cargo.toml は子を作ったあとで置く
    • 先に作ってしまうと cargo new --bin my_project1 をするとき、親の Cargo.toml を読み込んで、あとから作成する予定の my_project2 がないと騷ぎ立てて面倒なことになる
  • 親で cargo build すると子がビルドされる
  • ビルド結果は親の target/ に生成される
    • いろいろ集約されることで依存関係がいい感じになったりビルド時間が短縮できる
  • 親から cargo test すると子のテストを順に実行してくれる
  • とはいえ親から cargo run としても子を順番に実行したりはしない
    • 何を実行したらいいのかわからないと言われる
  • 親から cargo run -p my_project1 とすると実行するものを指定できる
    • my_project1 のなかで cargo run するのと同じなので別に親から実行しなくてもよい

参照

https://doc.rust-jp.rs/book-ja/ch14-03-cargo-workspaces.html

Discussion