第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
- 条件付きコンパイル - オプショナル依存関係

  • プロファイル
- 最適化設定 - カスタムプロファイル

  • クレート公開
- crates.ioへの公開 - セマンティックバージョニング

次のステップ

次の章では、パフォーマンス最適化を学びます。

---

参考資料