Cargo 高级特性 #
概述 #
除了基本的项目管理和依赖管理功能外,Cargo 还提供了许多高级特性,用于定制构建流程、条件编译和跨平台开发。本节将深入介绍这些高级功能。
构建脚本 #
什么是构建脚本? #
构建脚本(build script)是在编译项目之前运行的 Rust 程序,用于:
- 生成代码
- 编译本地库
- 设置环境变量
- 检测系统配置
创建构建脚本 #
在项目根目录创建 build.rs 文件:
rust
// build.rs
fn main() {
// 构建脚本代码
println!("cargo:rerun-if-changed=build.rs");
// 设置环境变量
println!("cargo:rustc-env=MY_VAR=value");
// 添加编译器标志
println!("cargo:rustc-link-lib=static=mylib");
}
构建脚本输出 #
构建脚本通过 println! 输出指令:
rust
fn main() {
// 重新运行条件
println!("cargo:rerun-if-changed=src/template.txt");
println!("cargo:rerun-if-changed=build.rs");
// 设置环境变量
println!("cargo:rustc-env=VERSION=1.0.0");
println!("cargo:rustc-env=BUILD_TIME={}", chrono::Local::now());
// 添加库搜索路径
println!("cargo:rustc-link-search=native=/path/to/lib");
// 链接库
println!("cargo:rustc-link-lib=static=mylib");
println!("cargo:rustc-link-lib=dylib=mylib");
// 编译器标志
println!("cargo:rustc-flags=-L /path/to/lib");
// 警告
println!("cargo:warning=This is a warning");
}
构建脚本依赖 #
toml
# Cargo.toml
[build-dependencies]
cc = "1.0"
bindgen = "0.69"
rust
// build.rs
use cc::Build;
fn main() {
// 编译 C 代码
Build::new()
.file("src/native.c")
.compile("native");
println!("cargo:rerun-if-changed=src/native.c");
}
代码生成 #
rust
// build.rs
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("generated.rs");
let mut f = File::create(&dest_path).unwrap();
f.write_all(b"
pub fn generated_function() -> &'static str {
\"Generated code\"
}
").unwrap();
println!("cargo:rerun-if-changed=build.rs");
}
rust
// src/main.rs
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
fn main() {
println!("{}", generated_function());
}
构建脚本环境变量 #
| 变量 | 描述 |
|---|---|
OUT_DIR |
输出目录 |
TARGET |
目标三元组 |
HOST |
主机三元组 |
NUM_JOBS |
并行任务数 |
OPT_LEVEL |
优化级别 |
DEBUG |
是否调试构建 |
PROFILE |
构建配置名 |
CARGO_MANIFEST_DIR |
项目根目录 |
CARGO_PKG_VERSION |
包版本 |
CARGO_PKG_NAME |
包名 |
特性标志 #
定义特性 #
toml
[features]
default = ["std"]
std = []
alloc = []
serde = ["dep:serde"]
json = ["serde", "dep:serde_json"]
full = ["std", "serde", "json"]
使用特性 #
rust
// 条件编译
#[cfg(feature = "std")]
pub fn std_function() {
// 需要 std 特性
}
#[cfg(feature = "serde")]
pub fn with_serde() {
// 需要 serde 特性
}
#[cfg(all(feature = "std", feature = "serde"))]
pub fn std_with_serde() {
// 同时需要两个特性
}
#[cfg(not(feature = "std"))]
pub fn no_std_function() {
// 不需要 std 特性
}
可选依赖 #
toml
[dependencies]
serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
tokio = { version = "1.0", optional = true }
[features]
default = []
serde-support = ["dep:serde"]
json = ["serde-support", "dep:serde_json"]
async = ["dep:tokio"]
依赖特性 #
toml
[features]
# 启用依赖的特性
full-tokio = ["tokio/full"]
derive = ["serde/derive"]
特性组合 #
toml
[features]
default = ["std"]
std = []
async = ["tokio/rt", "tokio/net"]
json = ["serde", "serde_json"]
full = ["std", "async", "json"]
条件编译 #
rust
// 基本条件
#[cfg(feature = "feature1")]
fn function1() {}
// 组合条件
#[cfg(all(feature = "feature1", feature = "feature2"))]
fn function_both() {}
#[cfg(any(feature = "feature1", feature = "feature2"))]
fn function_either() {}
#[cfg(not(feature = "feature1"))]
fn function_not() {}
// 平台条件
#[cfg(target_os = "linux")]
fn linux_only() {}
#[cfg(target_os = "windows")]
fn windows_only() {}
#[cfg(target_os = "macos")]
fn macos_only() {}
// 架构条件
#[cfg(target_arch = "x86_64")]
fn x86_64_only() {}
#[cfg(target_arch = "aarch64")]
fn aarch64_only() {}
// 组合平台条件
#[cfg(all(unix, not(target_os = "macos")))]
fn unix_not_macos() {}
cfg_attr #
rust
// 条件属性
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct MyStruct {
pub field: i32,
}
#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(target_os = "windows", path = "windows.rs")]
mod platform;
条件编译 #
目标条件 #
rust
// 操作系统
#[cfg(target_os = "linux")]
#[cfg(target_os = "windows")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "android")]
#[cfg(target_os = "ios")]
// 架构
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
#[cfg(target_arch = "arm")]
#[cfg(target_arch = "aarch64")]
// 指针宽度
#[cfg(target_pointer_width = "32")]
#[cfg(target_pointer_width = "64")]
// 字节序
#[cfg(target_endian = "little")]
#[cfg(target_endian = "big")]
// 家族
#[cfg(unix)]
#[cfg(windows)]
#[cfg(target_family = "unix")]
#[cfg(target_family = "windows")]
特性条件 #
rust
#[cfg(feature = "my_feature")]
#[cfg(not(feature = "my_feature"))]
#[cfg(all(feature = "a", feature = "b"))]
#[cfg(any(feature = "a", feature = "b"))]
调试条件 #
rust
#[cfg(debug_assertions)]
fn debug_only() {}
#[cfg(not(debug_assertions))]
fn release_only() {}
自定义条件 #
toml
# .cargo/config.toml
[build]
rustflags = ["--cfg", "my_custom_condition"]
rust
#[cfg(my_custom_condition)]
fn custom() {}
跨平台编译 #
添加目标 #
bash
# 添加编译目标
rustup target add x86_64-pc-windows-gnu
rustup target add aarch64-unknown-linux-gnu
rustup target add wasm32-unknown-unknown
编译命令 #
bash
# 编译到特定目标
cargo build --target x86_64-pc-windows-gnu
cargo build --target aarch64-unknown-linux-gnu
cargo build --target wasm32-unknown-unknown
平台特定依赖 #
toml
[target.'cfg(windows)'.dependencies]
winapi = "0.3"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.x86_64-pc-windows-gnu.dependencies]
winapi = "0.3"
[target.aarch64-unknown-linux-gnu.dependencies]
openssl = "0.10"
平台特定代码 #
rust
#[cfg(target_os = "linux")]
mod platform {
pub fn init() {
// Linux 初始化
}
}
#[cfg(target_os = "windows")]
mod platform {
pub fn init() {
// Windows 初始化
}
}
#[cfg(target_os = "macos")]
mod platform {
pub fn init() {
// macOS 初始化
}
}
pub use platform::init;
自定义构建配置 #
配置文件 #
toml
# .cargo/config.toml
[build]
jobs = 4
target-dir = "target"
rustflags = ["-C", "target-cpu=native"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[env]
RUST_LOG = "debug"
MY_VAR = "value"
[alias]
b = "build"
c = "check"
r = "run"
t = "test"
br = "build --release"
环境变量 #
bash
# 编译器标志
RUSTFLAGS="-C target-cpu=native" cargo build
# 目标目录
CARGO_TARGET_DIR=/tmp/target cargo build
# 离线模式
CARGO_NET_OFFLINE=true cargo build
# 增量编译
CARGO_INCREMENTAL=1 cargo build
链接配置 #
链接本地库 #
rust
// build.rs
fn main() {
// 添加搜索路径
println!("cargo:rustc-link-search=native=/usr/local/lib");
// 链接静态库
println!("cargo:rustc-link-lib=static=mylib");
// 链接动态库
println!("cargo:rustc-link-lib=dylib=mylib");
// 链接框架(macOS)
println!("cargo:rustc-link-lib=framework=CoreFoundation");
}
链接系统库 #
rust
// build.rs
fn main() {
// pkg-config
let library = pkg_config::find_library("openssl").unwrap();
for path in library.link_paths {
println!("cargo:rustc-link-search=native={}", path.display());
}
for lib in library.libs {
println!("cargo:rustc-link-lib={}", lib);
}
}
编译优化 #
Profile 配置 #
toml
[profile.dev]
opt-level = 0
debug = true
split-debuginfo = "packed"
debug-assertions = true
overflow-checks = true
lto = false
panic = "unwind"
incremental = true
codegen-units = 256
[profile.release]
opt-level = 3
debug = false
split-debuginfo = "..."
debug-assertions = false
overflow-checks = false
lto = false
panic = "unwind"
incremental = false
codegen-units = 16
strip = false
[profile.release-with-debug]
inherits = "release"
debug = true
[profile.release-lto]
inherits = "release"
lto = true
codegen-units = 1
LTO 配置 #
toml
[profile.release]
lto = false # 禁用
lto = true # 完全 LTO
lto = "thin" # Thin LTO(更快)
lto = "fat" # 完全 LTO
优化大小 #
toml
[profile.release]
opt-level = "s" # 优化大小
opt-level = "z" # 更小的大小
strip = true # 移除符号
lto = true # 链接时优化
codegen-units = 1 # 单个代码生成单元
常见用例 #
生成版本信息 #
rust
// build.rs
fn main() {
let version = env::var("CARGO_PKG_VERSION").unwrap();
let git_hash = get_git_hash();
println!("cargo:rustc-env=VERSION={}", version);
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
println!("cargo:rerun-if-changed=build.rs");
}
fn get_git_hash() -> String {
use std::process::Command;
let output = Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
.unwrap();
String::from_utf8_lossy(&output.stdout).trim().to_string()
}
rust
// src/main.rs
fn main() {
println!("Version: {}", env!("VERSION"));
println!("Git Hash: {}", env!("GIT_HASH"));
}
编译 C 代码 #
rust
// build.rs
use cc::Build;
fn main() {
Build::new()
.file("src/native.c")
.include("src/include")
.flag("-O2")
.compile("native");
println!("cargo:rerun-if-changed=src/native.c");
println!("cargo:rerun-if-changed=src/include/native.h");
}
生成绑定 #
rust
// build.rs
use bindgen::Builder;
fn main() {
let bindings = Builder::default()
.header("src/wrapper.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
println!("cargo:rerun-if-changed=src/wrapper.h");
}
下一步 #
掌握高级特性后,继续学习 最佳实践 了解 Cargo 使用的最佳实践!
最后更新:2026-03-28