Rust项目复杂了之后,单个crate放不下所有代码。Cargo workspace可以把多个crate放在一个仓库里统一管理,共享依赖版本,一起编译测试。
workspace基本配置
在项目根目录创建Cargo.toml,声明workspace成员:
# 根 Cargo.toml
[workspace]
members = [
"core", # 核心库
"api", # HTTP API
"cli", # 命令行工具
"worker", # 后台任务
"shared", # 共享类型和工具
]
resolver = "2"
每个成员是一个独立的crate,有自己的Cargo.toml和src/目录:
my-project/
+-- Cargo.toml # workspace根配置
+-- Cargo.lock # 统一的lock文件
+-- core/
| +-- Cargo.toml
| +-- src/lib.rs
+-- api/
| +-- Cargo.toml
| +-- src/main.rs
+-- cli/
| +-- Cargo.toml
| +-- src/main.rs
+-- worker/
| +-- Cargo.toml
| +-- src/main.rs
+-- shared/
+-- Cargo.toml
+-- src/lib.rs
成员间依赖
成员crate之间通过path依赖引用:
# api/Cargo.toml
[package]
name = "api"
version = "0.1.0"
edition = "2021"
[dependencies]
core = { path = "../core" }
shared = { path = "../shared" }
axum = "0.6"
tokio = { version = "1", features = ["full"] }
统一依赖版本
Cargo 1.64+ 支持workspace级别的依赖声明,避免各crate版本不一致:
# 根 Cargo.toml
[workspace]
members = ["core", "api", "cli", "worker", "shared"]
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
anyhow = "1.0"
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
成员crate引用时用workspace = true:
# core/Cargo.toml
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
sqlx = { workspace = true }
anyhow = { workspace = true }
这样所有crate用的serde版本一定相同,升级时只改根Cargo.toml一个地方。
构建和测试
# 构建所有成员
cargo build
# 构建指定成员
cargo build -p api
# 测试所有成员
cargo test
# 测试指定成员
cargo test -p core
# 运行指定binary
cargo run -p api
cargo run -p cli -- --help
# 检查所有成员
cargo clippy --workspace
Cargo.lock在workspace根目录统一管理,保证所有成员用相同版本的依赖。
小技巧
- 把共享的类型定义放在
sharedcrate里,避免循环依赖 [workspace.metadata]可以存自定义信息,配合cargo-make等工具使用- CI里用
cargo test --workspace一次跑完所有测试 - 如果某个crate不想发布到crates.io,加
publish = false
workspace是Rust项目超过一定规模后的标配,用好了代码组织清晰很多。