第22章: Cargo詳細 - プロジェクト管理のマスター
学習目標
- ワークスペースの構成と管理を理解する
- Featuresを使った条件付きコンパイルを習得する
- ビルドプロファイルのカスタマイズ方法を学ぶ
- クレートの公開プロセスを理解する
- 依存関係の最適化テクニックを学ぶ
---
22.1 ワークスペース(Workspaces)
22.1.1 ワークスペースとは
ワークスペースは複数の関連するクレートを1つのプロジェクトとして管理する仕組みです。
my-workspace/
├── Cargo.toml ← ワークスペースルート
├── Cargo.lock ← 共有されるロックファイル
├── target/ ← 共有されるビルドディレクトリ
├── api/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── web/
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
└── shared/
├── Cargo.toml
└── src/
└── lib.rs
22.1.2 ワークスペースの設定
ルートCargo.toml:
# Cargo.toml (ワークスペースルート)
[workspace]
members = [
"api",
"web",
"shared",
]
# ワークスペース全体の依存関係
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
# ワークスペース全体のメタデータ
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
license = "MIT"
各メンバーのCargo.toml:
# api/Cargo.toml
[package]
name = "api"
version.workspace = true
edition.workspace = true
authors.workspace = true
[dependencies]
shared = { path = "../shared" }
serde.workspace = true
tokio.workspace = true
22.1.3 ワークスペースのメリット
┌─────────────────────────────────────────────────────────┐
│ ワークスペースの利点 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 依存関係の一元管理 │
│ - すべてのメンバーで同じバージョン │
│ - Cargo.lockが1つ │
│ │
│ 2. ビルドの効率化 │
│ - 共有のtargetディレクトリ │
│ - 重複コンパイルの排除 │
│ │
│ 3. コマンドの一括実行 │
│ - cargo test --workspace │
│ - cargo build --workspace │
│ │
└─────────────────────────────────────────────────────────┘
22.1.4 ワークスペースコマンド
# すべてのメンバーをビルド
cargo build --workspace
# 特定のメンバーのみビルド
cargo build -p api
# すべてのメンバーをテスト
cargo test --workspace
# 特定のメンバーのみテスト
cargo test -p shared
# すべてのメンバーを公開
cargo publish --workspace
---
22.2 Features(機能フラグ)
22.2.1 Featuresとは
Featuresは条件付きコンパイルを実現する仕組みです。
# Cargo.toml
[package]
name = "my-crate"
version = "0.1.0"
[features]
# デフォルトで有効なfeature
default = ["json"]
# 個別のfeature
json = ["serde_json"]
xml = ["quick-xml"]
async = ["tokio"]
full = ["json", "xml", "async"]
[dependencies]
# featureが有効なときのみ依存
serde_json = { version = "1.0", optional = true }
quick-xml = { version = "0.30", optional = true }
tokio = { version = "1", features = ["full"], optional = true }
22.2.2 Featuresの使用
コード側での制御:
// src/lib.rs
#[cfg(feature = "json")]
pub mod json {
use serde_json;
pub fn parse(input: &str) -> Result<serde_json::Value, serde_json::Error> {
serde_json::from_str(input)
}
}
#[cfg(feature = "xml")]
pub mod xml {
use quick_xml;
pub fn parse(input: &str) -> Result<String, quick_xml::Error> {
// XML パース処理
}
}
#[cfg(all(feature = "async", feature = "json"))]
pub mod async_json {
// 複数のfeatureが必要な機能
}
ビルド時の指定:
# デフォルトfeatureで実行
cargo build
# デフォルトを無効化
cargo build --no-default-features
# 特定のfeatureのみ有効
cargo build --features json
# 複数のfeatureを有効
cargo build --features "json,xml"
# すべてのfeatureを有効
cargo build --all-features
22.2.3 実践的なFeatures設計
例:データベースバックエンドの選択
[features]
default = ["sqlite"]
# データベース選択(相互排他的)
sqlite = ["rusqlite"]
postgres = ["tokio-postgres"]
mysql = ["mysql_async"]
# 追加機能
migration = ["refinery"]
connection-pool = ["deadpool"]
[dependencies]
rusqlite = { version = "0.29", optional = true }
tokio-postgres = { version = "0.7", optional = true }
mysql_async = { version = "0.31", optional = true }
refinery = { version = "0.8", optional = true }
deadpool = { version = "0.9", optional = true }
// src/lib.rs
pub trait Database {
fn connect(&mut self) -> Result<(), Error>;
fn query(&self, sql: &str) -> Result<Vec<Row>, Error>;
}
#[cfg(feature = "sqlite")]
pub mod sqlite {
use super::Database;
use rusqlite::Connection;
pub struct SqliteDatabase {
conn: Option<Connection>,
}
impl Database for SqliteDatabase {
// 実装
}
}
#[cfg(feature = "postgres")]
pub mod postgres {
// PostgreSQL実装
}
---
22.3 ビルドプロファイル
22.3.1 標準プロファイル
Cargoには4つの標準プロファイルがあります:
# dev: cargo build
[profile.dev]
opt-level = 0 # 最適化なし
debug = true # デバッグ情報あり
overflow-checks = true # オーバーフローチェック
# release: cargo build --release
[profile.release]
opt-level = 3 # 最大最適化
debug = false # デバッグ情報なし
lto = false # LTO無効
# test: cargo test
[profile.test]
opt-level = 0
debug = true
# bench: cargo bench
[profile.bench]
opt-level = 3
debug = false
22.3.2 カスタムプロファイル
# Cargo.toml
# カスタムプロファイル
[profile.production]
inherits = "release" # releaseを継承
opt-level = 3
lto = "fat" # 完全なLTO
codegen-units = 1 # 単一コード生成ユニット
panic = "abort" # パニック時に即座にアボート
strip = true # デバッグシンボル削除
[profile.fast-dev]
inherits = "dev"
opt-level = 1 # 軽い最適化
使用:
cargo build --profile production
22.3.3 最適化オプション
| オプション | 説明 | 値 |
|---|---|---|
| `opt-level` | 最適化レベル | 0, 1, 2, 3, "s", "z" |
| `lto` | Link Time Optimization | false, true, "thin", "fat" |
| `codegen-units` | 並列コンパイル単位 | 1-256 |
| `debug` | デバッグ情報 | false, true, 0, 1, 2 |
| `panic` | パニック時の動作 | "unwind", "abort" |
| `incremental` | インクリメンタルコンパイル | false, true |
| `strip` | シンボル削除 | false, true, "debuginfo", "symbols" |
サイズ最適化の例:
[profile.release]
opt-level = "z" # サイズ最適化
lto = true
codegen-units = 1
panic = "abort"
strip = true
ビルド時間最適化の例:
[profile.dev]
opt-level = 1 # 軽い最適化
incremental = true # インクリメンタルコンパイル
codegen-units = 256 # 最大並列化
---
22.4 依存関係の管理
22.4.1 依存関係の種類
[dependencies]
# 通常の依存関係
serde = "1.0"
# 特定のバージョン
regex = "=1.5.4"
# バージョン範囲
rand = ">=0.8, <0.9"
# Gitリポジトリから
my-lib = { git = "https://github.com/user/my-lib" }
# 特定のブランチ/タグ/コミット
my-lib = { git = "https://github.com/user/my-lib", branch = "develop" }
my-lib = { git = "https://github.com/user/my-lib", tag = "v1.0.0" }
my-lib = { git = "https://github.com/user/my-lib", rev = "abc123" }
# ローカルパス
shared = { path = "../shared" }
[dev-dependencies]
# テスト/ベンチマーク専用
criterion = "0.5"
proptest = "1.0"
[build-dependencies]
# ビルドスクリプト専用
cc = "1.0"
22.4.2 依存関係のバージョン指定
セマンティックバージョニング:
# キャレット要件(デフォルト)
serde = "^1.2.3" # >=1.2.3, <2.0.0
serde = "1.2.3" # 同上(^は省略可)
# チルダ要件
serde = "~1.2.3" # >=1.2.3, <1.3.0
# ワイルドカード
serde = "1.*" # >=1.0.0, <2.0.0
serde = "1.2.*" # >=1.2.0, <1.3.0
# 不等号
serde = ">=1.2.3"
serde = ">1.2, <1.5"
22.4.3 依存関係の最適化
重複排除:
# 依存関係ツリーを表示
cargo tree
# 重複を確認
cargo tree -d
# 特定のクレートの依存関係
cargo tree -p serde
Cargo.lockの管理:
# 依存関係を更新
cargo update
# 特定のクレートのみ更新
cargo update -p serde
# Cargo.lockを削除して再生成
rm Cargo.lock
cargo build
---
22.5 クレートの公開
22.5.1 公開準備
Cargo.tomlの設定:
[package]
name = "my-awesome-crate"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
description = "A short description of my crate"
documentation = "https://docs.rs/my-awesome-crate"
homepage = "https://github.com/user/my-awesome-crate"
repository = "https://github.com/user/my-awesome-crate"
license = "MIT OR Apache-2.0"
keywords = ["keyword1", "keyword2"]
categories = ["command-line-utilities"]
readme = "README.md"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
必須ファイル:
my-crate/
├── Cargo.toml
├── README.md ← 必須
├── LICENSE-MIT ← ライセンスファイル
├── LICENSE-APACHE
└── src/
└── lib.rs
22.5.2 公開プロセス
# 1. crates.ioでアカウント作成
# 2. APIトークン取得
cargo login <your-api-token>
# 3. パッケージチェック
cargo package --list
# 4. ドライラン
cargo publish --dry-run
# 5. 実際の公開
cargo publish
# 6. バージョンアップ
# Cargo.tomlでバージョンを更新
cargo publish
22.5.3 公開のベストプラクティス
セマンティックバージョニング:
MAJOR.MINOR.PATCH
0.1.0 → 0.1.1 パッチ(バグフィックス)
0.1.1 → 0.2.0 マイナー(新機能、後方互換)
0.2.0 → 1.0.0 メジャー(破壊的変更)
CHANGELOGの例:
# Changelog
## [0.2.0] - 2024-01-15
### Added
- 新機能Xを追加
### Changed
- APIのYを改善
### Fixed
- バグZを修正
## [0.1.0] - 2024-01-01
### Added
- 初回リリース
---
22.6 ビルドスクリプト
22.6.1 build.rsの基本
// build.rs
fn main() {
// 環境変数を設定
println!("cargo:rustc-env=BUILD_TIME={}", timestamp());
// リンクライブラリを指定
println!("cargo:rustc-link-lib=static=mylib");
// 再ビルド条件
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/config.toml");
}
fn timestamp() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap();
duration.as_secs().to_string()
}
使用例:
// src/main.rs
fn main() {
println!("ビルド時刻: {}", env!("BUILD_TIME"));
}
22.6.2 C/C++コードのコンパイル
[build-dependencies]
cc = "1.0"
// build.rs
fn main() {
cc::Build::new()
.file("src/wrapper.c")
.include("src")
.compile("wrapper");
}
---
22.7 Cargo設定
22.7.1 .cargo/config.toml
# .cargo/config.toml
# ビルド設定
[build]
target-dir = "target"
incremental = true
jobs = 4
# ターゲット固有の設定
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
# エイリアス
[alias]
b = "build"
r = "run"
t = "test"
c = "check"
# レジストリ設定
[registry]
default = "crates-io"
# ネットワーク設定
[net]
git-fetch-with-cli = true
22.7.2 環境変数
# ビルド時の並列数
CARGO_BUILD_JOBS=8 cargo build
# ターゲットディレクトリ
CARGO_TARGET_DIR=/tmp/cargo cargo build
# オフラインモード
CARGO_NET_OFFLINE=true cargo build
# 増分コンパイル無効化
CARGO_INCREMENTAL=0 cargo build
---
22.8 高度なテクニック
22.8.1 パッチ依存関係
# Cargo.toml
[dependencies]
my-lib = "1.0"
[patch.crates-io]
# crates.ioのクレートをローカル版に置き換え
my-lib = { path = "../my-lib-fork" }
# またはGitバージョンに置き換え
my-lib = { git = "https://github.com/user/my-lib", branch = "fix" }
22.8.2 カーゴベンダー
# 依存関係をローカルにダウンロード
cargo vendor
# .cargo/config.tomlに追加
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "vendor"
22.8.3 クロスコンパイル
# ターゲット追加
rustup target add x86_64-pc-windows-gnu
# ビルド
cargo build --target x86_64-pc-windows-gnu
# リリースビルド
cargo build --release --target x86_64-pc-windows-gnu
---
22.9 まとめ
学んだこと
- Features
- プロファイル
- クレート公開
次のステップ
次の章では、パフォーマンス最適化を学びます。
---