本页目录

Workspace 与项目管理

当项目包含多个 interdependent crate(如核心库+API server+CLI)时,workspace 让它们共享同一个 Cargo.lock 和 target 目录——编译缓存复用、依赖版本统一。patch 允许在开发时临时替换某个依赖的源码,build.rs 在编译前执行代码生成。

Workspace: 多 crate 同一仓库

当项目包含多个 interdependent crate(如 core lib + API server + CLI),把它们放在一个 workspace 中有三方面的好处:

[workspace]
members = ["crates/core", "crates/api", "crates/cli"]
resolver = "2"
  1. 共享编译产物⁠: 所有成员共用一个 target/ 目录——依赖只需要编译一次,磁盘和编译时间大幅降低
  2. 一致版本⁠: Cargo.lock 在 workspace 层面共享——所有成员看到同一版本的依赖
  3. 内部互相引用⁠: api 可以用 path = "../../crates/core" 直接引用 core(不需发布到 crates.io)

[workspace.dependencies]: 共享版本声明

1.64+ 支持在根 Cargo.toml 中声明版本,成员用 key 引用:

[workspace.dependencies]
serde = "1"
tokio = { version = "1", features = ["full"] }

# 各成员: [dependencies] → serde = { workspace = true }

这解决了多 crate 项目中"依赖版本散落各处、升级时忘改某个"的经典问题。改一处版本,全部成员更新。

[patch]: 临时替换依赖

当上游 crate 有 bug、你需要用 fork 直到 upstream 修复时:

[patch.crates-io]
serde = { git = "https://github.com/myfork/serde", branch = "fix" }

注意:[patch] 中的版本必须在 workspace 全局兼容——如果两个成员一个依赖 serde 1.0 另一个依赖 serde 1.3,fork 必须同时满足两者的 API。这就是为什么 fork 最好只改内部逻辑,不改公开 API。

build.rs 进阶

// build.rs — 编译主代码之前执行
fn main() {
    // Rerun 条件: 仅在这些条件变化时重跑
    println!("cargo:rerun-if-changed=src/c/wrapper.h");
    println!("cargo:rerun-if-env-changed=MY_LIB_PATH");

    // 链接系统库
    println!("cargo:rustc-link-search=native={}", my_lib_dir);
    println!("cargo:rustc-link-lib=static=my_lib");

    // 向主代码传递 cfg — 使 #[cfg(has_avx2)] 可用
    println!("cargo:rustc-cfg=has_avx2");
}

build.rs 常用于:编译 C/C++ 依赖(通过 cc crate)、检测系统库是否存在、生成代码(protobuf, OpenAPI)、设置 feature flags。

参考

  • Cargo Book: workspaces, build scripts
  • cc crate: docs.rs/cc

Keywords: workspace, members, patch, build.rs, shared dependencies, multi-crate, Cargo.lock