介绍使用 rustup 管理 Rust 工具链,Rust 程序的目录布局,编译链接和 Cargo 配置。
rustup 和 toolchain #
rustup 用于管理 rust toolchain 和一些额外工具 (称为 component,如 rust-analyzer,通过预定义的 profile 来定义 components 集合)。
- rustup show: 显示按照和缺省的 toolchains
- rustup update: 更新所有的 toolchains
- rustup default TOOLCHAIN: 设置缺省的 toolchain
- rustup component list: 列出可用的 components
- rustup component add NAME: 添加 component
- rustup target list: 列出可用的编译器 target
- rustup target add NAME: 添加一个编译器 target
# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
brew install rustup
rustup default stable
rustup component add clippy # rust lints
rustup component add rustfmt # rust formater
rustup component add rust-src # rust 源文件
rustup component add rust-docs # 添加 rust 标准库文档
# 查看已安装的工具链
rustup toolchain list
# 安装 nightly 工具链
rustup toolchain install nightly
# 设置缺省工具链为 nightly
rustup default nightly
# cargo 也支持在命令行上指定 toolchain 版本
cargo +nightly expand
rustup 可以安装多套 toolchain,使用 rustup show
来查看:
zj@a:~/docs$ rustup show
Default host: x86_64-apple-darwin # 当前 host architecture:
rustup home: /Users/zhangjun/.rustup
installed toolchains
--------------------
nightly-2023-11-14-x86_64-apple-darwin
nightly-x86_64-apple-darwin (default)
esp
installed targets for active toolchain
--------------------------------------
riscv32imac-unknown-none-elf
riscv32imafc-unknown-none-elf
riscv32imc-unknown-none-elf
x86_64-apple-darwin
active toolchain
----------------
nightly-x86_64-apple-darwin (default)
rustc 1.78.0-nightly (fc3800f65 2024-02-26)
使用 rustup default nightly
指定 缺省使用的 toolchain ,这时 cargo/rustc 等指向该 toolchain 下的 binary。
zj@a:~/docs$ cat ~/.rustup/settings.toml
default_toolchain = "nightly-x86_64-apple-darwin"
profile = "default"
version = "12"
zj@a:~/docs$ ls ~/.rustup/toolchains/
esp/ nightly-2023-11-14-x86_64-apple-darwin/ nightly-x86_64-apple-darwin/
zj@a:~/docs$ ls -l ~/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/
total 100M
-rwxr-xr-x 1 zhangjun 30M 2 27 11:51 cargo*
-rwxr-xr-x 1 zhangjun 1.1M 2 27 11:51 cargo-clippy*
-rwxr-xr-x 1 zhangjun 1.6M 2 27 11:52 cargo-fmt*
-rwxr-xr-x 1 zhangjun 11M 2 27 11:51 clippy-driver*
-rwxr-xr-x 1 zhangjun 38M 2 27 11:51 rust-analyzer*
-rwxr-xr-x 1 zhangjun 980 2 27 11:51 rust-gdb*
-rwxr-xr-x 1 zhangjun 2.2K 2 27 11:52 rust-gdbgui*
-rwxr-xr-x 1 zhangjun 1.1K 2 27 11:51 rust-lldb*
-rwxr-xr-x 1 zhangjun 598K 2 27 11:52 rustc*
-rwxr-xr-x 1 zhangjun 12M 2 27 11:52 rustdoc*
-rwxr-xr-x 1 zhangjun 6.9M 2 27 11:52 rustfmt*
还可以通过 cargo 命令行参数或 rust-toolchain.toml
文件来 by 项目指定 toolchain 。
cargo 命令行参数:+toolchain
指定 toolchain 名称,如 cargo +nightly
:
cargo +nightly build --out-dir=out -Z unstable-options
cargo +esp fetch
: 使用 esp toolchain;
通过在项目根目录下创建 rust-toolchain.toml
文件,也可以 by 项目指定 toolchain 类型和 rust 编译器版本 ,后续执行 cargo build
时会
自动安装和使用 对应的 toolchain 和 components:https://rust-lang.github.io/rustup/overrides.html
[toolchain]
# 指定使用的 toolchain channel
channel = "1.85" # "nightly-2020-07-10"
# profile 用于定义要下载的 component 集合,默认有三种:minimal,default,complete
profile = "minimal"
# 需要额外安装的 components
components = [ "rustfmt", "clippy" ]
# 除了 host target 外需要额外安装的 target,如交叉编译的 target。
targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "x86_64-unknown-linux-gnu", "wasm32-wasip1", "x86_64-pc-windows-msvc" ]
rustup 安装的 rust toolchain 的根目录称为 sysroot
。
# 查看 rustc 的 sysroot 路径
zj@a:~/docs$ rustc --print=sysroot
/Users/zhangjun/.rustup/toolchains/nightly-x86_64-apple-darwin
zj@a:~/docs$ ls /Users/zhangjun/.rustup/toolchains/nightly-x86_64-apple-darwin
bin/ etc/ lib/ libexec/ share/
# lib 下为对应 target 的 Rust 标准库
zj@a:~/docs$ ls -l /Users/zhangjun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib
total 178M
-rw-r--r-- 1 zhangjun 4.0M 2 27 11:52 librustc-nightly_rt.asan.dylib
-rw-r--r-- 1 zhangjun 1.7M 2 27 11:52 librustc-nightly_rt.lsan.dylib
-rw-r--r-- 1 zhangjun 3.5M 2 27 11:52 librustc-nightly_rt.tsan.dylib
-rw-r--r-- 1 zhangjun 154M 2 27 11:52 librustc_driver-cf896aa21aba59ec.dylib
-rw-r--r-- 1 zhangjun 8.1M 2 27 11:51 libstd-f7827e6f67b106c3.dylib # Rust 标准库
drwxr-xr-x 23 zhangjun 736 2 27 11:52 rustlib/
# target x86_64-apple-darwin 的 Rust 标准库目录
zj@a:~/docs$ ls -l /Users/zhangjun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/
total 114M
-rw-r--r-- 1 zhangjun 406K 2 27 11:51 libaddr2line-643ab2d79f15679f.rlib
#...
-rw-r--r-- 1 zhangjun 8.1M 2 27 11:51 libstd-f7827e6f67b106c3.dylib # Rust 标准库
-rw-r--r-- 1 zhangjun 15M 2 27 11:51 libstd-f7827e6f67b106c3.rlib
-rw-r--r-- 1 zhangjun 407K 2 27 11:51 libstd_detect-92248f34803a2acc.rlib
-rw-r--r-- 1 zhangjun 5.9K 2 27 11:51 libsysroot-464a17e9cd90c9ab.rlib
-rw-r--r-- 1 zhangjun 5.8M 2 27 11:51 libtest-c1d75a053ae82a06.rlib
-rw-r--r-- 1 zhangjun 44K 2 27 11:51 libunicode_width-a4100f1e87339743.rlib
-rw-r--r-- 1 zhangjun 37K 2 27 11:51 libunwind-e6d9b66edb0ba93e.rlib
zj@a:~/docs$
对于嵌入式设备,如 esp32,官方不支持它的 target,故不提供预编译 Rust 标准库二进制,需要每次都重新编译构建 std 库;(通过项目 .cargo/config.toml 中的 [unstable] build-std = ["std", "panic_abort"]
来配置);
zj@a:~/docs$ ~/.rustup/toolchains/esp/bin/rustc --print=sysroot
/Users/zhangjun/.rustup/toolchains/esp
zj@a:~/docs$ ls /Users/zhangjun/.rustup/toolchains/esp
bin/ etc/ lib/ libexec/ share/ xtensa-esp-elf/ xtensa-esp32-elf-clang/
cargo 命令 #
cargo 全局缺省配置:~/.cargo/config.toml
,也可以 by workspace 或 package 来定义 .cargo/config.toml
文件。
Cargo Home
默认为 ~/.cargo
,或者由环境变量 CARGO_HOME
指定, 用于保存 Cargo 个人全局配置参数以及下载的 crate 包和依赖。
bin/
: cargo install 或 rustup 安装的 binary;config.toml
: Cargo 个人全局配置参数;registry/index|cache|src
:保存从 registry crates.io 下载的包;- cache:压缩的 gzip 文件,以 .crate 结尾;
- src 是 cache 中压缩包解压后的目录;
❯ ls -l ~/.cargo/
total 8.0K
drwxr-xr-x 29 alizj 928 Jul 21 16:50 bin/
-rw-r--r-- 1 alizj 245 Dec 6 2024 config.toml
-rw-r--r-- 1 alizj 300 Mar 28 2024 env
drwxr-xr-x 5 alizj 160 May 5 2024 git/
drwxr-xr-x 6 alizj 192 Mar 28 2024 registry/
常用 cargo 命令:
cargo install
: 从 crates.io 安装 binary package,默认安装到~/.cargo/bin
目录;cargo new hello_world
: 创建一个 src/main.rs 文件,build 生成一个 binary;cargo new hello_world --lib
:创建一个 src/lib.rs 文件,build 生成一个 library;cargo add tokio --features macros,rt-multi-thread
: 给项目添加指定依赖和它的 features;cargo build
:保存到 target/debug 目录下,产生 Cargo.lock 文件;cargo build --release
:打开 optimization, 保存到 target/release 目录;cargo clean && cargo build --release --quiet --timings
: –timeings 参数可以生成编译依赖的各 crate 的耗时cargo run
: 编译和运行;cargo cache
: 分析和清理全局 cache(~/.cargo);cargo update
: 更新 Cargo.lock 文件;cargo doc --open
: 查看当前项目的文档rustup doc --std
: 查看 Rust 标准库文档;
cargo +nightly build
: 指定使用的 toolchain,如 +stable, +esp, +nightly
cargo new
默认为项目创建 .git
目录,可以使用 --vcs none
关闭;
查看 cargo 调用的 rustc 参数: cargo build --verbose
查看 rustc 调用的外部命令参数:cargo build --config 'build.rustflags=["--print", "link-args"]'
使用 nightly rustc 的 -Zunpretty=expanded
参数来展开宏:https://github.com/rust-lang/rust/issues/43364
cargo +nightly build --config 'build.rustflags=["--print", "link-args", "-Z", "unpretty=expanded"]'
或则:cargo +nightly rustc -- -Zunpretty=expanded
cargo 命令向 rustc 编译器传参的方式 #
cargo rustc [options] [-- args]
: 编译当前 package(可以通过 -p 来指定 package,然后通过 –lib、–bin 等选择 target),同时传递额外的 args 给 rustc 编译器。
- 示例:
cargo rustc --lib -- -Z print-type-sizes
,cargo rustc --lib --crate-type lib,cdylib
- 通过环境变量
RUSTFLAGS
配置:
RUSTFLAGS="-C linker=./gcc-print.sh -C link-self-contained=yes" cargo build --target=aarch64-unknown-linux-musl
- 通过 –config 参数,可配置的参数为
.cargo/config.toml
文件中的内容,如build.rustflags
:
cargo build --config 'build.rustflags=["--print", "link-args"]'
rustc unstable 的选项默认需要使用 +nightly
toolchain 才能看到和使用:
# stable rustc 没有 -Z 选项
❯ rustc --help -v |grep -- -Z
$ rustc +nightly --help -v |grep -- -Z
-Z help Print unstable compiler options
通过指定 RUSTC_BOOTSTRAP=1
环境变量,可以在 stable toolchain 使用这些 unstable 特性:
❯ RUSTC_BOOTSTRAP=1 rustc --help -v |grep -- -Z
-Z help Print unstable compiler options
打印 rustc 编译耗时的 profile:
RUSTC_BOOTSTRAP=1 cargo rustc --release -- -Z self-profile
# cargo chef:
RUSTC_BOOTSTRAP=1 RUSTFLAGS='-Zself-profile' cargo chef cook --release ...
# final build:
RUSTC_BOOTSTRAP=1 RUSTFLAGS='-Zself-profile' cargo build --release ...
Package 布局 #
-
Cargo.toml 和 Cargo.lock:位于 package root 目录下。
cargo update
命令更新 Cargo.lock 文件;
-
源码:位于 src 目录;
- 缺省 lib 文件:src/lib.rs。对于目录类型的 module,则需要在目录中创建 mod.rs 文件。
- 缺省 binary 文件:src/main.rs
- 其他 binary 文件,src/bin/xxx.rs
-
Benchmarks: 位于 benches 目录;
-
示例: 位于 examples 目录;
-
集成测试:位于 tests 目录;
bin/examples/tests/benches
目录下的各文件都会被编译为 单独的可执行的二进制,但也支持目录形式的二进制,目录下必须包含 main.rs
文件以及依赖的
module 文件。
.
├── Cargo.lock
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── main.rs # binary name 为 Cargo.toml 中的 package name
│ └── bin/
│ ├── named-executable.rs # binary name 为文件名
│ ├── another-executable.rs
│ └── multi-file-executable/ # binary 也可以是目录,binary name 为目录名,目录下需要有 main.rs 文件。
│ ├── main.rs
│ └── some_module.rs # binary 依赖的 module
├── benches/
│ ├── large-input.rs
│ └── multi-file-bench/
│ ├── main.rs
│ └── bench_module.rs
├── examples/
│ ├── simple.rs
│ └── multi-file-example/
│ ├── main.rs
│ └── ex_module.rs
└── tests/
├── some-integration-tests.rs
└── multi-file-test/
├── main.rs
└── test_module.rs
cargo test #
cargo test [name]
运行单元或集成测试,它搜索两个地方:
- src/ 目录下各源码文件,包含单元测试和文档测试;
- tests/ 目录下的集成测试文件,需要 import 当前 crate 到 tests 下的文件中;
[name]
对应 tests 下的文件名或目录名,或对应 src 中 #[test]
对应的函数名关键字。
target 目录(Build Cache)和 Profile #
cargo build
将输出内容保存到 root workspace 下的 target
目录, 该目录为该 pacakge 的 Build Cache
。
也可以 by workspace 或 package 来设置 Build Cache
目录:
- 环境变量:
CARGO_TARGET_DIR
- 配置参数:
build.target-dir
- 命令行选项:
--target-dir
target 目录布局取决于是否使用了 --target
选项来构建特定平台的二进制或库。
- 如果指定了
--target
(可以有多个),则生成的目录结构:
- target/
/debug/: 如 target/thumbv7em-none-eabihf/debug/
- target/
/release/: 如 target/thumbv7em-none-eabihf/release/
- 如果没有指定
--target
,则表示构建 host arch(使用 rustup show 命令查看),构建结果保存到target/<profile>
目录下。
Cargo 内置了 4 个 <profile>
:dev(默认), test, release, bench
,根据命令行参数自动选择,也可以自定义:
-
target/debug/
:保存 dev profile 的输出;(默认,使用 –debug 选项指定)- dev 和 test profile 结果保存到 debug 目录;
-
target/release/
:保存 release profile 的输出(使用 –release 选项指定)- release 和 bench profile 结果保存到 release 目录;
-
target/foo/
:保存自定义 foo profile 的输出(使用 –profile=foo 选项指定)- 自定义 profile 保存到对应目录;
各类型 profile 对应的参数可以在 Cargo.toml 文件中进行 自定义配置 ,如指定优化级别,是否包含符号表等,具体参考后文 Profile 一节的内容。
其它在 target/
-
target/debug/:Contains the output of the package being built (the binary executables and library targets).
-
target/debug/examples/: Contains example targets.
-
target/debug/deps/: Dependencies and other artifacts.
-
target/debug/incremental/:rustc incremental output, a cache used to speed up subsequent builds.
-
target/debug/build/:Output from
build scripts(build.rs)
. -
target/doc/:Contains rustdoc documentation (
cargo doc
). -
target/package/:Contains the output of the
cargo package
andcargo publish
commands.
在 target/<profile>
目录下还存在 .d 结尾的 dep-info
文件,它们风格类似于 Makefile,包含为构建 binary/lib 需要依赖的文件:
# Example dep-info file found in target/debug/foo.d
/path/to/myproj/target/debug/foo: /path/to/myproj/src/lib.rs /path/to/myproj/src/main.rs
Cargo.toml 文件 #
Cargo by 项目的配置包括两部分:
.cargo/config.toml
: package 的 .cargo/config.toml 或全局 ~/.cargo/config.toml 文件,可以覆盖 Cargo.toml配置,如 profile;Cargo.toml
: 也称为 manifest 文件,可以引用 config.toml 中的配置,如 registry;
还可以 by 项目创建 rust-toolchain.toml
文件,用来指定该项目要安装和使用的 toolchain,要编译的 targets 列表,要下载的 components 列表。
.cargo/config.toml 文件中的配置可以使用 –config 参数重定义:cargo build --config 'build.rustflags=["--print", "link-args"]'
Cargo.toml Mainifest 文件格式 #
https://doc.rust-lang.org/cargo/reference/manifest.html
cargo-features — Unstable, nightly-only features.
[package] — Defines a package.
name — The name of the package.
version — The version of the package.
authors — The authors of the package.
edition — The Rust edition.
rust-version — The minimal supported Rust version.
description — A description of the package.
documentation — URL of the package documentation.
readme — Path to the package’s README file.
homepage — URL of the package homepage.
repository — URL of the package source repository.
license — The package license.
license-file — Path to the text of the license.
keywords — Keywords for the package.
categories — Categories of the package.
workspace — Path to the workspace for the package.
build — Path to the package build script.
links — Name of the native library the package links with.
exclude — Files to exclude when publishing.
include — Files to include when publishing.
publish — Can be used to prevent publishing the package.
metadata — Extra settings for external tools.
default-run — The default binary to run by cargo run.
autobins — Disables binary auto discovery.
autoexamples — Disables example auto discovery.
autotests — Disables test auto discovery.
autobenches — Disables bench auto discovery.
resolver — Sets the dependency resolver to use.
Target tables: (see configuration for settings)
[lib] — Library target settings. # 一个 package 只能有一个 lib,所以不是数组
[[bin]] — Binary target settings. # [[xx]] 表示是一个数组类型,可以多次重复指定
[[example]] — Example target settings.
[[test]] — Test target settings.
[[bench]] — Benchmark target settings.
Dependency tables:
[dependencies] — Package library dependencies.
[dev-dependencies] — Dependencies for examples, tests, and benchmarks.
[build-dependencies] — Dependencies for build scripts.
[target] — Platform-specific dependencies.
[badges] — Badges to display on a registry.
[features] — Conditional compilation features.
[lints] — Configure linters for this package.
[patch] — Override dependencies.
[replace] — Override dependencies (deprecated).
[profile] — Compiler settings and optimizations.
[workspace] — The workspace definition.
示例:
[package]
name = "hello_world" # the name of the package
version = "0.1.0" # the current version, obeying semver
authors = ["Alice <[email protected]>", "Bob <[email protected]>"]
edition = '2021' # 影响所有 package/test/bens/examples,默认为 2015
rust-version = "1.56" # 指定兼容的 rustc 编译器版本
description = "A short description of my package"
documentation = "https://docs.rs/bitflags"
readme = "README.md"
homepage = "https://serde.rs/"
repository = "https://github.com/rust-lang/cargo/"
license = "MIT OR Apache-2.0"
license-file = "LICENSE.txt"
keywords = ["gamedev", "graphics"
categories = ["command-line-utilities", "development-tools::cargo-plugins"]
# 所属的 workspace,适用于不在 wrokspace root 目录的情况。
workspace = "path/to/workspace/root"
# 构建脚本路径
build = "build.rs"
# 要链接到的 native lib 名称,如 git2 表示 linux 的 libgit2.a。
links = "git2"
# 发布到 crates.io 上包含和忽略的文件,如果 build script 没有输出 rerun-if-* 则也用于
# tracking 是否需要重新执行 build script;
exclude = ["/ci", "images/", ".*"]
include = ["/src", "COPYRIGHT", "/examples", "!/examples/big_example"]
# 或者 false
publish = ["some-registry-name"]
# 当 package 有多个 binary 时(如 src/bin/a.rs 和 src/bin/b.rs),指定 cargo run 默认编译运行的 binary
default-run = "a"
# package.metadata 会被 cargo 忽略,而为其他从 Cargo.toml 中读取配置信息的工具使用。
# Metadata used when generating an Android APK, for example.
[package.metadata.android]
package-name = "my-awesome-android-app"
assets = "path/to/static"
# lint 对本地 package 而非 dependencies 进行检查
[lints.rust] # rustc lint
unsafe_code = "forbid"
# 等效于 unsafe_code = { level = "forbid", priority = 0 }
[lints.clippy] # clippy lint
enum_glob_use = "deny"
[badges]
# The `maintenance` table indicates the status of the maintenance of
# the crate. This may be used by a registry, but is currently not
# used by crates.io. See https://github.com/rust-lang/crates.io/issues/2437
# and https://github.com/rust-lang/crates.io/issues/2438 for more details.
# 编译构建依赖
[dependencies]
# test、bench、example 依赖
[dev-dependencies]
# 构建脚本 build.rs 和 proc-mecro 依赖
[build-dependencies]
# target 依赖,可以使用 cfg!() 条件包含
[target.*.dependencies]
# 为编译器提供各种优化和调试的参数集合
[profile.*]
# 一个 package 只能有一个 lib,根默认为 src/lib.rs,但是可以有多个 bin/example/test/bench
# 所以 [lib] 不是列表,而 [[bin]]/[[bench]]/[[test]] 等是列表。
#
# 根 lib 配置,默认 path 是 src/lib.rs, name 默认是 package name;
# 可以通过 [lib].name 来重命名,重新指定 path。
#
# path: 未指定时, 根据 section 自动推导;
# doctest: 只对 lib 有效, 指定 cargo test 是否执行 doc example
# bench: 是否对指定的 target 搜索和执行 bench, 对于 bins/libs/bench 默认为 true
# proc-macro: 只对 lib 有效; 指定该 lib 为 proc-macro crate 类型
# crate-type: 可选值 bin/lib/rlib/dylib/cdylib/staticlib/proc-macro
# 只能对 [lib] 和 [[example]] 配置 crate-type。
# 对于 bin/test/bench,默认只能为 bin。
# required-features: 对于 lib 外的其他 target 有效, 表示只有启动对应 feature 时才build 该 target。
[lib]
name = "foo" # The name of the target. 默认是 crate name
path = "src/lib.rs" # The source file of the target.
test = true # Is tested by default.
doctest = true # Documentation examples are tested by default.
bench = true # Is benchmarked by default.
doc = true # Is documented by default.
plugin = false # Used as a compiler plugin (deprecated).
proc-macro = false # Set to `true` for a proc-macro library.
harness = true # Use libtest harness.
edition = "2015" # The edition of the target.
crate-type = ["lib"] # The crate types to generate.
required-features = [] # Features required to build this target (N/A for lib).
## 一个 package 可以有多个 bin,默认是:src/main.rs 和 src/bin/*.rs
## src/main.rs bin 的 name 和 package name 一致,可以通过 [[bin]].name 来重命名,后续可以使用 --bin name 来指定这里的 name。
## bin 可以有多个,所以 [[bin]] 是个列表
#
# binary 可以使用 crate 的 pub API,编译使用 [dependencies] 配置。
# 使用 cargo run --bin <bin-name> 来指定。
# cargo install --bin <bin-name> 安装到 ~/.cargo/bin 目录下。
[[bin]] # [[xx]] 表示是一个列表,可以重复指定多个
name = "cool-tool" # 重命名 path 对应的 bin 名称
path = "src/main.rs" # 未指定时默认为 src/main.rs
test = false
bench = false
crate-type = ["bin"] # 对于 bin,默认为 bin, 而且只能为 bin
[[bin]]
name = "frobnicator"
required-features = ["frobnicate"]
# example 列表, 各 example 可执行程序文件位于 examples 目录下, 编译后位于
# target/debug/examples 目录下, 使用 [dependencies] 和 [dev-dependenceis] 中的配置。
#
# example 是可执行的 binary(有 main() 函数),可以通过 crate-type = ["staticlib"] 将它编译为库。
#
# example 相关命令: cargo build/run/install --example <example-name>
# cargo test 默认只编译 examples 但不运行, 可以为 example 设置 test=ture 来在 test 时运行 example。
[[example]]
name = "foo"
crate-type = ["staticlib"]
# test 列表, 分为 src/ 下的单元测试和 tests 下的集成测试(只能使用 crate 公共 APIs), 为一个可执行程序。默认并行执行 tests。
# 集成测试程序使用 [dependencies] 和 [dev-dependenceis] 中的配置。
[[test]]
# bench 列表, 位于 src/ 或 benches 目录下的单独可执行程序, 使用 #[bench] 来修饰, 使用 cargo bench 来执行。
[[bench]]
[workspace]
crate type #
Rust crate 的类型如下:
- 用于 Rust crate 之间链接的 Rust 库格式类型:
lib
— Generates a library kind preferred by the compiler,currently defaults to rlib
.rlib
— A Rust static library.- 即 Rust 静态库格式,文件名为
*.rlib
,可以被其它 crate 链接; - 它打包了本 crate 以及依赖的其它 upstream crate(不含 Rust 标准库);
- 如果要链接外部语言库,则只能使用静态的外部库,否则会打印警告;
- 即 Rust 静态库格式,文件名为
dylib
— A Rust dynamic library.- 即 Rust 动态库格式,可以被其它 crate 链接;
- 用于将 Rust crate 及其依赖(含 Rust 标准库)打包为系统库格式,供其它 C/C++ 程序链接:
staticlib
— A native static library. 即系统静态库格式,文件名为 *.a- staticlib 不可以被其它 Rust crate 链接
- 它的主要功能时将被 Rust crate 及其依赖(还包含 Rust 标准库)打包到生成的
*.a
静态库中,从而可以被其它语言如 C/C++ 的程序进行链接。 - 该 *.a 本身可能有动态库依赖,在链接该 *.a 时也需要链接这些动态库,可以使用
--print=native-static-libs
打印。
cdylib
— A native dynamic library. 即系统动态库格式;- 和 staticlib 类似,也时将 Rust crate 及其依赖导包到生成的 *.so 动态库中,从而可以被其它语言如 C/C++ 的程序进行链接。
- ELF 可执行程序类型,以及 proc-macro 类型
- bin — A runnable executable program.
- proc-macro — Generates a format suitable for a procedural macro library that may be loaded by the compiler.
lib、rlib、dylib 是 Rust 编译器识别和使用的 Rust 自己的静态库和动态库格式,Rust crate 只有生成这三种库格式才能被其它 Rust crate 链接。
- lib 是默认的 crate type, 实际是 rlib 的别名;
- rlib 中包含 crate 及其依赖的 upstream crate 的对象文件(不包含 Rust 标准库,rlib 称为 Rust 静态库);
staticlib 和 cdylib 是将 Rust 代码打包到外部库中,然后被其它语言的程序链接(不能被其它 Rust crate 链接)。
- 它会将该 Rust 及依赖的其它 crate 和 Rust 标准库都打包到生成的 staticlib 或 cdynlib 中,从而可以被 C/C++ 程序链接。
- 由于 rustc 默认会对导出符号名称进行 mangle,所以为了让 C/C 程序能正确链接符号,在 Rust 代码中需要对导出的符号使用
#[no_mangle]
在编译 crate 时,可以通过 rustc 的 --crate-type
参数来指定生成的 库文件类型。
$ cargo rustc --lib -- -Z print-type-sizes
$ cargo rustc --lib --crate-type lib,cdylib
对于 Cargo 项目,cargo 在调用 rustc 时默认设置的 crate type 如下:
- src/lib.rs 默认为 lib crate type,它是 rlib 的别名,即 Rust 静态库格式。
- 还可以配置 Cargo.toml 的
[lib].crate-type 参数
,来指定其它 crate type,如bin, lib, rlib, dylib, cdylib, staticlib, and proc-macro
- 还可以配置 Cargo.toml 的
- src/main.rs、src/bin/*.rs、以及 tests、benches、examples 默认为 bin crate type。
- 这些文件的默认类型为 bin,不可以修改。
对于 src/lib.rs 和 examples,它们的 crate type 可以通过 Cargo.toml 的 [lib].crate-type
参数来配置,如 bin, lib, rlib, dylib, cdylib, staticlib, and proc-macro
还可以使用宏来指定 crate type #![crate_type = "bin"]
。
参考:
- https://doc.rust-lang.org/rustc/command-line-arguments.html?highlight=bundle#--crate-type-a-list-of-types-of-crates-for-the-compiler-to-emit
- https://doc.rust-lang.org/reference/linkage.html
- https://doc.rust-lang.org/cargo/reference/cargo-targets.html
Rust crate 编译结果分析 #
一个测试 crate:
// src/lib.rs
// 需要是 pub,这样才会在生成的 rlib 中包含
pub fn hello() {
println!("hello!");
}
cargo build 测试,默认生成 rlib 库:
- rlib 库实际是 ar 格式,即 object 文件的集合;
- nm 可以打印 rlib 中的符号,需要进行 demangle 后才能看到真实的符号名称;
- rlib 打包了 crate 及其依赖的 crate 的所有对象文件;
rustc 编译 crate 的过程:
- 将 foo.rs 编译为 foo.rcgu.o 对象文件;
- 将 crate 的多个 *.rs 文件的 *.rcgu.o 文件打包到一个 rlib 文件中。
- RCGU:Rust Code Gen Unit
- 将 binary crate 和依赖的多个 crate 的 rlib 文件链接到一起,形成 binary;
rlib 生成的顺序: #
❯ cargo build # 或则,cargo rustc --lib
# --timeings 参数可以生成编译依赖的各 crate 的耗时
> cargo clean && cargo build --release --quiet --timings
❯ find target/ -type f -name '*.rlib'
target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib
target/debug/libmy_demo.rlib
❯ file target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib
target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib: current ar archive
❯ ar t target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib
__.SYMDEF
lib.rmeta
my_demo-2c918bd8bd6b1c77.6fg39t3e0vwd88d9zbr7ioa1j.1y07x8w.rcgu.o
my_demo-2c918bd8bd6b1c77.aq8mw1jgqf27i1b4p5k0bzldz.1y07x8w.rcgu.o
❯ nm target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib
lib.rmeta:
target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib:lib.rmeta: no symbols
my_demo-2c918bd8bd6b1c77.6fg39t3e0vwd88d9zbr7ioa1j.1y07x8w.rcgu.o:
U __ZN3std2io5stdio6_print17h068a7c8fe03701f4E
U __ZN4core3fmt2rt38_$LT$impl$u20$core..fmt..Arguments$GT$9new_const17ha3888ccef9b469abE
0000000000000000 T __ZN7my_demo5hello17h4b85738f5c82ab0fE
0000000000000034 s l_anon.b27ae52ee22afa02c25801ca93529f61.0
0000000000000040 s l_anon.b27ae52ee22afa02c25801ca93529f61.1
0000000000000000 t ltmp0
0000000000000034 s ltmp1
0000000000000040 s ltmp2
00000000000000ca s ltmp3
0000000000000298 s ltmp4
00000000000002b8 s ltmp5
my_demo-2c918bd8bd6b1c77.aq8mw1jgqf27i1b4p5k0bzldz.1y07x8w.rcgu.o:
0000000000000000 T __ZN4core3fmt2rt38_$LT$impl$u20$core..fmt..Arguments$GT$9new_const17ha3888ccef9b469abE
0000000000000048 s l_anon.d56747e2a7f22d5a93d4373863465371.0
0000000000000000 t ltmp0
0000000000000048 s ltmp1
0000000000000635 s ltmp2
0000000000001020 s ltmp3
0000000000001040 s ltmp4
# 使用 rustfilt 对 rust 符号进行 demangle
❯ nm target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib|rustfilt
target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib:lib.rmeta: no symbols
lib.rmeta:
my_demo-2c918bd8bd6b1c77.6fg39t3e0vwd88d9zbr7ioa1j.1y07x8w.rcgu.o:
U _std::io::stdio::_print
U _core::fmt::rt::<impl core::fmt::Arguments>::new_const
0000000000000000 T _my_demo::hello
0000000000000034 s l_anon.b27ae52ee22afa02c25801ca93529f61.0
0000000000000040 s l_anon.b27ae52ee22afa02c25801ca93529f61.1
0000000000000000 t ltmp0
0000000000000034 s ltmp1
0000000000000040 s ltmp2
00000000000000ca s ltmp3
0000000000000298 s ltmp4
00000000000002b8 s ltmp5
my_demo-2c918bd8bd6b1c77.aq8mw1jgqf27i1b4p5k0bzldz.1y07x8w.rcgu.o:
0000000000000000 T _core::fmt::rt::<impl core::fmt::Arguments>::new_const
0000000000000048 s l_anon.d56747e2a7f22d5a93d4373863465371.0
0000000000000000 t ltmp0
0000000000000048 s ltmp1
0000000000000635 s ltmp2
0000000000001020 s ltmp3
0000000000001040 s ltmp4
# 也使用 nm --demangle 来对 lib 中的 rust 符号进行 demangle
❯ nm --demangle target/debug/deps/libmy_demo-2c918bd8bd6b1c77.rlib
编译生成 dylib 类型:
- dylib 是 Rust 自身的标准库格式,实际是 ELF 文件类型;
- 可以使用 objdump 打印其中的符号表,并进行 demangle
❯ cargo rustc --lib -- --crate-type dylib
❯ ls target/debug/
build/ deps/ examples/ incremental/ libmy_demo.d libmy_demo.rlib
❯ ls -l target/debug/deps/
total 1.5M
-rwxr-xr-x 1 alizj 1.5M Jul 4 11:14 libmy_demo-1752cfb8404360eb.dylib*
-rw-r--r-- 1 alizj 12K Jul 4 11:14 libmy_demo-1752cfb8404360eb.rlib
-rw-r--r-- 1 alizj 2.5K Jul 4 11:14 libmy_demo-1752cfb8404360eb.rmeta
-rw-r--r-- 2 alizj 2.7K Jul 4 11:14 my_demo-1752cfb8404360eb.6fg39t3e0vwd88d9zbr7ioa1j.07hj7xi.rcgu.o
-rw-r--r-- 2 alizj 5.9K Jul 4 11:14 my_demo-1752cfb8404360eb.aq8mw1jgqf27i1b4p5k0bzldz.07hj7xi.rcgu.o
-rw-r--r-- 1 alizj 388 Jul 4 11:14 my_demo-1752cfb8404360eb.d
# 生成了动态库 dylib 库文件
❯ file target/debug/deps/libmy_demo-1752cfb8404360eb.dylib
target/debug/deps/libmy_demo-1752cfb8404360eb.dylib: Mach-O 64-bit dynamically linked shared library arm64
❯ objdump --syms target/debug/deps/libmy_demo-1752cfb8404360eb.dylib |rustfilt |wc -l
11309
❯ objdump --syms target/debug/deps/libmy_demo-1752cfb8404360eb.dylib |rustfilt |head
target/debug/deps/libmy_demo-1752cfb8404360eb.dylib: file format mach-o arm64
SYMBOL TABLE:
0000000000000958 l F __TEXT,__text _<alloc::collections::btree::map::Iter<K,V> as core::iter::traits::iterator::Iterator>::next
0000000000000a94 l F __TEXT,__text _<T as core::any::Any>::type_id
0000000000000ac4 l F __TEXT,__text _<T as core::any::Any>::type_id
0000000000000af4 l F __TEXT,__text _<T as core::any::Any>::type_id
0000000000000b24 l F __TEXT,__text _<bool as core::fmt::Debug>::fmt
0000000000000b34 l F __TEXT,__text _<&T as core::fmt::Debug>::fmt
编译生成 staticlib 类型:
- staticlib 是系统静态库(*.a) 类型;
- 它打包了 crate 及依赖的 crate 的所有符号(包含 Rust 标准库),可以被其它 C/C++ 程序链接;
- 由于 rustc 默认会对导出符号名称进行 mangle,所以为了让 C/C 程序能正确链接符号,在 Rust 代码中需要对导出的符号使用
#[no_mangle]
❯ cargo rustc --lib -- --crate-type staticlib
❯ ls target/debug/
build/ deps/ examples/ incremental/ libmy_demo.d libmy_demo.rlib
❯ ls -l target/debug/deps/
total 16M
-rw-r--r-- 1 alizj 16M Jul 4 11:17 libmy_demo-6c6f35ce0e39f05f.a
-rw-r--r-- 1 alizj 12K Jul 4 11:17 libmy_demo-6c6f35ce0e39f05f.rlib
-rw-r--r-- 1 alizj 2.5K Jul 4 11:17 libmy_demo-6c6f35ce0e39f05f.rmeta
-rw-r--r-- 2 alizj 2.7K Jul 4 11:17 my_demo-6c6f35ce0e39f05f.6fg39t3e0vwd88d9zbr7ioa1j.14m2vgu.rcgu.o
-rw-r--r-- 2 alizj 5.9K Jul 4 11:17 my_demo-6c6f35ce0e39f05f.aq8mw1jgqf27i1b4p5k0bzldz.14m2vgu.rcgu.o
-rw-r--r-- 1 alizj 384 Jul 4 11:17 my_demo-6c6f35ce0e39f05f.d
❯ ar t target/debug/deps/libmy_demo-6c6f35ce0e39f05f.a |head
__.SYMDEF
my_demo-6c6f35ce0e39f05f.6fg39t3e0vwd88d9zbr7ioa1j.14m2vgu.rcgu.o
my_demo-6c6f35ce0e39f05f.aq8mw1jgqf27i1b4p5k0bzldz.14m2vgu.rcgu.o
my_demo-6c6f35ce0e39f05f.am4vmyp6gsgp9crfpou4ngk1b.14m2vgu.rcgu.o
std-af0f282b96954ac9.std.72c7846bd0c5b2df-cgu.0.rcgu.o
panic_unwind-eafbb5ea5df11687.panic_unwind.29942598dde052fe-cgu.0.rcgu.o
object-091f97e9f7b1e9a0.object.63ce0cc8faeaeb20-cgu.0.rcgu.o
memchr-89dd1b3eaceaf16a.memchr.9fc0d4dd789ae0d1-cgu.0.rcgu.o
addr2line-9be47fa9e342462b.addr2line.b61a8a1856850aac-cgu.0.rcgu.o
gimli-3ecc0aa72e38a2f0.gimli.9346c6a51b682f64-cgu.0.rcgu.o
❯ ar t target/debug/deps/libmy_demo-6c6f35ce0e39f05f.a |wc -l
380
❯ nm --demangle target/debug/deps/libmy_demo-6c6f35ce0e39f05f.a |head -20
my_demo-6c6f35ce0e39f05f.6fg39t3e0vwd88d9zbr7ioa1j.14m2vgu.rcgu.o:
U std::io::stdio::_print::h068a7c8fe03701f4
U core::fmt::rt::_$LT$impl$u20$core..fmt..Arguments$GT$::new_const::ha3888ccef9b469ab
0000000000000000 T my_demo::hello::h4b85738f5c82ab0f
0000000000000034 s l_anon.b27ae52ee22afa02c25801ca93529f61.0
0000000000000040 s l_anon.b27ae52ee22afa02c25801ca93529f61.1
0000000000000000 t ltmp0
0000000000000034 s ltmp1
0000000000000040 s ltmp2
00000000000000ca s ltmp3
0000000000000298 s ltmp4
00000000000002b8 s ltmp5
my_demo-6c6f35ce0e39f05f.aq8mw1jgqf27i1b4p5k0bzldz.14m2vgu.rcgu.o:
0000000000000000 T core::fmt::rt::_$LT$impl$u20$core..fmt..Arguments$GT$::new_const::ha3888ccef9b469ab
0000000000000048 s l_anon.d56747e2a7f22d5a93d4373863465371.0
0000000000000000 t ltmp0
0000000000000048 s ltmp1
0000000000000635 s ltmp2
参考:
- https://fasterthanli.me/articles/why-is-my-rust-build-so-slow
- https://sharnoff.io/blog/why-rust-compiler-slow
编译依赖 [dependencies] #
依赖来源可以是 crates.io/git/本地 path
,对于 git/本地 path 来源,version 是可选的,但如果指定,则必须要匹配。
指定版本:
[dependencies]
time = "0.1.12" # 表示等效版本: >=0.1.12, <0.2.0,后续 cargo update 时会自动更新升级版本
log = "^1.2.3" # 表示严格使用 "1.2.3" 版本
# 支持的版本
1.2.3 := >=1.2.3, <2.0.0
1.2 := >=1.2.0, <2.0.0
1 := >=1.0.0, <2.0.0
0.2.3 := >=0.2.3, <0.3.0
0.2 := >=0.2.0, <0.3.0
0.0.3 := >=0.0.3, <0.0.4
0.0 := >=0.0.0, <0.1.0
0 := >=0.0.0, <1.0.0
~1.2.3 := >=1.2.3, <1.3.0
~1.2 := >=1.2.0, <1.3.0
~1 := >=1.0.0, <2.0.0
* := >=0.0.0
1.* := >=1.0.0, <2.0.0
1.2.* := >=1.2.0, <1.3.0
>= 1.2.0
> 1
< 2
= 1.2.3
>= 1.2, < 1.5
指定 git 来源:
# 自定义 registry
# Specifying dependencies from other registries, 指定的 registry 必须在 .cargo/config.tmol 中配置
some-crate = { version = "1.0", registry = "my-registry" }
# 使用最新提交
regex = { git = "https://github.com/rust-lang/regex.git" }
# 指定 branch/tag/rev
regex = { git = "https://github.com/rust-lang/regex.git", branch = "next" }
# rev 格式
rev = "refs/pull/493/head"
rev = "4c59b707"
指定本地目录来源:
# 指定 path
# 在 hello_world pacakge 目录下创建 hello_utils package
$ cargo new hello_utils
# hello_world/Cargo.toml
[dependencies]
hello_utils = { path = "hello_utils" }
hello_utils = { path = "hello_utils", version = "0.1.0" }
平台相关依赖:
[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"
[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"
[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"
[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"
[target.'cfg(target_arch = "x86")'.dependencies]
native-i686 = { path = "native/i686" }
[target.'cfg(target_arch = "x86_64")'.dependencies]
native-x86_64 = { path = "native/x86_64" }
不支持使用可选 feature 来指定依赖,如 [target.'cfg(feature="fancy-feature")'.dependencies]
,但可以使用 [features]
机制。
测试依赖 [dev-dependencies] #
与编译依赖 [dependencies]
格式类似,但是不是 cargo build
时使用,而是在编译 tests/examples/benchs
时使用。
- dev-dependencies 不会被传递到依赖它的 package;
[dev-dependencies]
tempdir = "0.3"
[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"
build 依赖 [build-dependencies] #
与编译依赖 [dependencies]
格式类似,但在 构建脚本(build.rs) 或 proc macro
及其依赖中使用。
编译 build script (buidl.rs)
时,不使用 dependencies/dev-dependenices 依赖,而只使用 build-dependencies。
这是因为 build script 和 package src 是分开编译的:先编译 build script 然后再编译 package。
# build script/proc macro 及其依赖
[build-dependencies]
cc = "1.0.3"
[target.'cfg(unix)'.build-dependencies]
cc = "1.0.3"
开启 package 的 features #
[dependencies]
# 启用 default-features 和 features 列表中的 feature
serde = { version = "1.0.118", features = ["derive"] }
[dependencies.awesome]
version = "1.3.5"
# 不启用 default feature,启用指定的 feature 列表
default-features = false
features = ["secure-password", "civet"]
# 等效于
[dependencies]
awesome = { version = "1.3.5", default-features = false,features = ["secure-password", "civet"]}
重命名 dependences package #
例如同一个 package 的不同版本、不同位置;
[package]
name = "mypackage"
version = "0.0.1"
[dependencies]
foo = "0.1"
# package 指定实际的 package 名称
bar = { git = "https://github.com/example/project.git", package = "foo" }
baz = { version = "0.1", registry = "custom", package = "foo" }
# 后续代码使用
use foo; // crates.io
use bar; // git repository
use baz; // registry `custom`
[dependencies]
# 可选依赖,重命名为 bar
bar = { version = "0.1", package = 'foo', optional = true }
[features]
# 开启该可选依赖,并开启 log-debug 特性。
log-debug = ['bar/log-debug']
继承 workspace 的 dependences #
在 workspace 的 member pacakge 的 Cargo.toml 中只能使用 workspace/features/optional 三个配置 key;
- features 列表是在 workspace 基础上的 增量 features ;
# [PROJECT_DIR]/Cargo.toml
[workspace]
members = ["bar"]
# workspace 级别的全局依赖
[workspace.dependencies]
cc = "1.0.73"
rand = "0.8.5"
regex = { version = "1.6.0", default-features = false, features = ["std"] }
# worksapce member packge
# [PROJECT_DIR]/bar/Cargo.toml
[package]
name = "bar"
version = "0.2.0"
[dependencies]
# workspace=true 表示继承 workspace 的依赖配置,这时只能指定 features/optional 两个配置参数。
regex = { workspace = true, features = ["unicode"] } # 在 workspace features 的基础上增加 unicode feature
[build-dependencies]
cc.workspace = true
[dev-dependencies]
rand.workspace = true
patch dependencies #
replace 语法已经被抛弃,建议统一使用 patch。
[package]
name = "my-library"
version = "0.1.0"
[dependencies]
uuid = "1.0"
# patch 对 dependencies 中同名的 package 参数进行修改
# crates-io:包的索引仓库
[patch.crates-io]
# 使用本地的 uuid,未指定 version 时使用前面指定的 1.0 版本;
uuid = { path = "../path/to/uuid" }
# 或者,使用 git 的 uuid
uuid = { git = 'https://github.com/uuid-rs/uuid.git' }
后续使用 my-library pacakge 的其他代码也需要指定同样的 patch,如:
[package]
name = "my-binary"
version = "0.1.0"
[dependencies]
my-library = { git = 'https://example.com/git/my-library' }
uuid = "1.0"
[patch.crates-io]
uuid = { git = 'https://github.com/uuid-rs/uuid.git' }
除了 cartes-io 的 package 外,也可以 patch 自定义 registry 的 package:
[patch."https://github.com/your/repository"]
my-library = { path = "../my-library/path" }
多版本 patch:
[patch.crates-io]
serde = { git = 'https://github.com/serde-rs/serde.git' }
serde2 = { git = 'https://github.com/example/serde.git', package = 'serde', branch = 'v2' }
其它例子:
[dependencies.baz]
git = 'https://github.com/example/baz.git'
[patch.'https://github.com/example/baz']
baz = { git = 'https://github.com/example/patched-baz.git', branch = 'my-branch' }
config.toml paths 重载 #
如果不想通过 Cargo.toml 的 [patch] section
来重载依赖,可以在项目的 .cargo/config.toml
中通过 paths
来指定要重载的 package:
paths = ["/path/to/uuid"] # 数组需要指向包含 Cargo.toml 文件的目录
features #
feature 提供了一种条件编译和可选依赖的机制。
在 Cargo.toml 的 [features]
中定义一系列 feature, 然后在命令行 cargo build --features "fa fb"
来进行启用它们。
- cargo 的 –features 底层是传给 rustc 的 –cfg 选项来实现的, 如 –cfg ‘verbose’ 或 –cfg ‘feature=“serde” feature=“xxx”’;
也可以在 [dependencies]
中为依赖的 package 启用 feature 列表。
Cargo.toml 中定义 features 列表:
[features]
# 定义一个 webp feature,它不依赖(启用)其它 feature
webp = []
代码中可以通过 cfg!()
宏来判断该 feature 是否被启用, 实现条件编辑:
#[cfg(feature = "webp")]
pub mod webp;
features 选择:
- 定义 features 列表(feature 之间可以相互依赖)或者要启用的可选 dependency package;
- feature 名不能和 dependencies 列表中的 package 名重复。
- 这是由于如果 dep 中的 package 为 optional 时, cargo 会自动创建一个同名的 feature;
- 默认所有 feature 都是 未启用 , 可以 使用 default 来指定默认启用的 features。
default feature:在 build 时, 不管是否指定 --features
, 默认都会启用 default feature ,通过 cargo --no-default-features
来关闭。对于 dependence 中的 package, 使用 default-features = false
来关闭它的缺省 feature。
[features]
# 缺省启用的 features, 除非显式通过 --no-default-features 或 default-features=false 来关闭。
default = ["ico", "webp"]
# feature 值数组元素为:
# 1. 其他 feature;
# 2. 启用可选依赖:dep:optional_package;
# 3. 启用依赖 package 的 feature:package/feature
bmp = []
png = []
ico = ["bmp", "png"] # 启用 ico feature 时, 自动启用 bmp 和 png feature
webp = []
可选依赖: Cargo 默认为可选依赖隐式创建一个同名的 feature(值为 dep:pkg
), 后续启用该 feature 时才会引入对应可选依赖:
[dependencies]
gif = { version = "0.11.1", optional = true } # 可选依赖
[features]
# cargo 默认为可选依赖创建一个同名的 feature, 如下面的 gif,它的值为 'dep:gif',其中 dep: 表示可选依赖。
#gif = ["dep:gif"]
# mygif 依赖上面隐式创建的 gif feature
mygif = ["gif"]
如果定义一个 feature 来依赖可选依赖(格式:dep:pkg
),则默认不会为可选依赖创建同名 feautre:
[dependencies]
ravif = { version = "0.6.3", optional = true }
rgb = { version = "0.8.25", optional = true }
[features]
# 启用 avif feature 时也启用这两个可选依赖,不会创建同名的 ravif/rgb feature,
avif = ["dep:ravif", "dep:rgb"]
启用依赖 package 的 features:
[dependencies]
# 启用该 package 的 derive feature 和 default features
serde = { version = "1.0.118", features = ["derive"] }
# 关闭该 package 的 default feature, 只启用 zlib feature
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
可以在 [features]
中启用依赖包的特定 features, 语法: package-name/feature-name
,表示启用 package-name 的 feature-name;
[dependencies]
# 没有启用该 package 的任何 feature
jpeg-decoder = { version = "0.1.20", default-features = false }
[features]
# 启用 parallel feature 时启用 jpeg-decoder(不需要为可选包)的 rayon feature
parallel = ["jpeg-decoder/rayon"]
如果 package 是 optional 的则会启用该 package。如果不想启用它, 则需要使用 package-name?/feature-name
语法,这样只有其它启用这个可选依赖时,才会启用这个 feature。
[dependencies]
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }
[features]
# 如果其他 feature 启用了 rgb 依赖, 则本 feature 也启用 rgb 依赖并开启它的 serde feature
serde = ["dep:serde", "rgb?/serde"]
cargo 的 feature 命令行参数:
--features FEATURES
: Enablesthe listed features
. Multiple features may be separated with commas or spaces. If using spaces, be sure to use quotes around all the features if running Cargo from a shell (such as--features "foo bar"
). If building multiple packages in a workspace, thepackage-name/feature-name
syntax can be used to specify features for specific workspace members.- 底层传给 rustc 的 –cfg 选项, 如
--cfg 'verbose' or --cfg 'feature="serde" feature="xx"'
;
- 底层传给 rustc 的 –cfg 选项, 如
--all-features
: Activates all features of all packages selected on the command-line.--no-default-features
: Does not activate the default feature of the selected packages.
缺省情况下, cargo build/test/bench/docs 和 clippy 等只会编译 default features (除非传递 –no-default-features)。
profiles #
Profile 提供了一种修改编译器参数的方式,如优化级别、调试符号表等。
Cargo 提供了4 种内置的 Profiles:dev, release, test, and bench
,也可以自定义 profile:
Cargo 只参考 root workspace 的 Cargo.toml 中 profile 设置, 而忽略 member package 的 Cargo.toml 中的 profile 设置。
另外 packge 的 .cargo/config.toml 中 profile 配置会覆盖 Cargo.toml 中的配置。
profile 参数解释:https://doc.rust-lang.org/cargo/reference/profiles.html
缺省 profiles:
-
dev:适用于 cargo build 或 cargo install –debug
[profile.dev] opt-level = 0 # 不优化,这时 cfg(debug_assertions) 为 true debug = true # 带 DWARF 调试符号表 split-debuginfo = '...' # Platform-specific. strip = "none" # 不删除符号表 debug-assertions = true # debug 模式 overflow-checks = true # 检查溢出 lto = false panic = 'unwind' # unwind incremental = true # 增量构建 codegen-units = 256 rpath = false
-
release: 适用于加了 –release 选项的命令, 也是 cargo install 的缺省参数:
[profile.release] opt-level = 3 # 高级优化 debug = false # 不含 DWARF 调试符号表 split-debuginfo = '...' # Platform-specific. strip = "none" # 不删除符号表 debug-assertions = false # 不是 debug 模式 overflow-checks = false lto = false panic = 'unwind' incremental = false codegen-units = 16 rpath = false
-
test: 用于 cargo test 命令, 复用 dev profile
-
bench: 用于 cargo bench 命令, 复用 release profile
cargo 默认不优化 build script/proc macro 及它们的依赖,即编译 [build-dependencies]
中的 package 时默认不优化。
自定义 profile:
[profile.release-lto]
inherits = "release"
lto = true
后续使用 –profile 来指定要使用的 profile, 输出保存到 target/release-lto 目录
cargo build --profile release-lto
Profile 选择:默认根据指定的命令,自动选择的 profile:
- cargo run, cargo build, cargo check, cargo rustc:
dev
profile - cargo test:
test
profile - cargo bench:
bench
profile - cargo install:
release
profile
可以使用 --profile=NAME
来选择指定的 profile,--release
等效于 --profile=release
。
选中的 profile 适用于所有 cargo target: lib/bin/examples/tests/benchs
也可以按照 profile 为各 package 重载参数,优先级:从高到低
[profile.dev.package.name] — A named package.
[profile.dev.package."*"] — For any non-workspace member.
[profile.dev.build-override] — Only for build scripts, proc macros, and their dependencies.
[profile.dev] — Settings in Cargo.toml.
Default values built-in to Cargo.
# 为 foo package 指定使用 -Copt-level=3 参数
# 也可指定 package 版本:[profile.dev.package."foo:2.1.0"]
[profile.dev.package.foo]
opt-level = 3
# 为依赖设置缺省值,* 表示所有依赖 packages(不含当前 workspace member package)
[profile.dev.package."*"]
opt-level = 2
# 为 build scripts 和 proc-macros 及其依赖设置参数
[profile.dev.build-override]
opt-level = 0
codegen-units = 256
debug = false # when possible
[profile.release.build-override]
opt-level = 0
codegen-units = 256
workspace #
workspace 是 package 集合(称为 workspace member),用于对它们进行统一管理。
workspace 级别的 Cargo.toml 文件如下配置参数:
[workspace] — Defines a workspace.
resolver — Sets the dependency resolver to use.
members — Packages to include in the workspace.
exclude — Packages to exclude from the workspace.
default-members — Packages to operate on when a specific package wasn’t selected.
package — Keys for inheriting in packages. package 的 author、name 等信息
dependencies — Keys for inheriting in package dependencies. 依赖
lints — Keys for inheriting in package lints.
metadata — Extra settings for external tools.
[patch] — Override dependencies.
[replace] — Override dependencies (deprecated).
[profile] — Compiler settings and optimizations.
cargo build 使用 workspace 级别 Cargo.toml 文件中的 [patch]/[replace]/[profile.*]
, 而忽略 member package 中对应配置(编译器会警告)。
Root Package
: Cargo.toml 中包含 [workspace]
和一个 [package]
定义的 package:
注意:不是 workspace.package,而是和 workspace 同级别的 package。
workspace.package 用于定义可以被 member package 继承使用的信息。
[workspace]
[package]
name = "hello_world"
version = "0.1.0"
authors = ["Alice <[email protected]>", "Bob <[email protected]>"]
Virtual workspace
: Cargo.toml 只包含 [workspace]
但不含同级别的 [package]
,必须指定 resolver = "2";
- 一般通过
package.edition
来自动推导, 但是 virtual workspace 由于没有 package 定义, 所以需要明确指定。
# [PROJECT_DIR]/Cargo.toml
[workspace]
members = ["hello_world"]
exclude = ["crates/foo", "path/to/other"]
resolver = "2" # 必须指定
# [PROJECT_DIR]/hello_world/Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
authors = ["Alice <[email protected]>", "Bob <[email protected]>"]
各 cargo 支持 --package
参数来选择 workspace 的某个 member package, 或 --workspace
选择所有 mermber package。
如 cargo check --workspace
对所有 member package 进行检查。
如果没有指定这两个参数(–package 或 –workspace), cargo 当前目录来判断:
- 如果是 package 目录, 则使用对应的 package;
- 如果是 virtual workspace 或 workspace root 目录, 则选择 所有 package ;
指定 default-members
后,在 virtal workspace 根目录编译时只选择指定的 packages 而非默认的所有 packages:
[workspace]
members = ["path/to/member1", "path/to/member2", "path/to/member3/*"]
default-members = ["path/to/member2", "path/to/member3/foo"]
resolver = "2";
各 member package 共享 workspace root 的 Cargo.lock 和构建缓存 target/ 目录:
- 共享 Cargo.lock: 确保所有 member package 使用相同的依赖版本。
- 共享 target/ 共享缓存:加快构建速度;
各 member package 可以有自己的 Cargo.toml 和 .cargo/config.toml 文件:
- Cargo.toml:定义自己的 dependencies 以及继承自 workspace 的信息,可以继承的信息包括:package/dependencies/lints
- .cargo/config.toml: package 自己的 cargo 配置;
cargo 会检查 member package 及其所有父目录的 .cargo/config.toml 文件,然后按照优先级将相关内容进行 merge。
- 所以,worksapce 级别也可以定义 .cargo/config.toml 文件,作为所有 member package 的缺省值。
# [PROJECT_DIR]/Cargo.toml
[workspace]
members = ["bar", "crates/*"] # 支持 glob 匹配
[workspace.package] # 可以被 member package 继承的 package 信息
version = "1.2.3"
authors = ["Nice Folks"]
description = "A short description of my package"
documentation = "https://example.com/bar"
# 统一定义的依赖列表(member package 不一定使用),member package 的 dependencies/build-dependencies/dev-dependencies 均可使用
[workspace.dependencies]
cc = "1.0.73"
rand = "0.8.5"
# 不能声明为 optional
regex = { version = "1.6.0", default-features = false, features = ["std"] }
# 统一定义的 lints 规则
[workspace.lints.rust]
unsafe_code = "forbid"
# 统一定义的 metadata
[workspace.metadata.webcontents]
root = "path/to/webproject"
tool = ["npm", "run", "build"]
# [PROJECT_DIR]/bar/Cargo.toml
[package]
name = "bar"
# 继承 workspace.package 配置信息
version.workspace = true
authors.workspace = true
description.workspace = true
documentation.workspace = true
[dependencies]
# 继承 workspace.package 的包配置(如版本、features 等),并额外增加的 features
regex = { workspace = true, features = ["unicode"] }
[build-dependencies]
# cc 使用 workspace.dependencies 中定义的 cc package 信息
cc.workspace = true
[dev-dependencies]
rand.workspace = true
# 继承自 workspace 的 lints
[lints]
workspace = true
.cargo/config.toml 文件 #
https://doc.rust-lang.org/cargo/reference/config.html
Cargo 的配置包括 4 部分:
- Cargo.toml: 也称 manifest 文件,可以引用 config.toml 中的配置,如 registry;
- config.toml: by 项目 package 的 .cargo/config.toml 或用户缺省的 ~/.cargo/config.toml 文件。
- 可以覆盖 Cargo.toml 中的部分配置,如 profile。
- CARGO_XX 环境变量:如配置
target.x86_64-unknown-linux-gnu.runner
对应CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER
; - 命令行参数
--config
:如cargo --config net.git-fetch-with-cli=true fetch
,可以多次指定--config
,参数会被 merge。
优先级:命令行参数 –config 》环境变量 》配置文件
各种配置中 binary 或 path 的路径:
- 环境变量、命令行参数中:相对于当前工作目录;
- 配置文件中的 binary:如果没有路径分隔符,在 PATH 搜索。
- 配置文件中的目录:如果不是绝对路径,则相对于 .cargo/ 所在的目录。
[target.x86_64-unknown-linux-gnu]
runner = "foo" # 使用 PATH 搜索 foo
[source.vendored-sources]
# Directory is relative to the parent where `.cargo/config.toml` is located.
# For example, `/my/project/.cargo/config.toml` would result in `/my/project/vendor`.
directory = "vendor"
命令行示例:
# Most shells will require escaping.
cargo --config http.proxy="http://example.com" …
# Spaces may be used.
cargo --config "net.git-fetch-with-cli = true" …
# TOML array example. Single quotes make it easier to read and write.
cargo --config 'build.rustdocflags = ["--html-in-header", "header.html"]' …
# Example of a complex TOML key.
cargo --config "target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'.runner = 'my-runner'" …
# Example of overriding a profile setting.
cargo --config profile.dev.package.image.opt-level=3 …
cargo 会在多级目录中查找 .cargo/config.toml 文件:如果当前工作目录是 /projects/foo/bar/baz/
,则 cargo 读取 config.toml 的顺序:
- 所以,可以在 worksapce 级别的 .cargo/config.toml 中定义缺省配置,被各 member package 所继承。
/projects/foo/bar/baz/.cargo/config.toml
/projects/foo/bar/.cargo/config.toml
/projects/foo/.cargo/config.toml
/projects/.cargo/config.toml
/.cargo/config.toml
$CARGO_HOME/config.toml # which defaults to:
Windows: %USERPROFILE%\.cargo\config.toml
Unix: $HOME/.cargo/config.toml
对于同一个 key,如果在多个 config.toml 中有配置, 则会 merge 到一起 。下级目录的配置覆盖前面的配置。
如果当前工作目录是 workspace root,则 cargo 不会读取 member package 下的 .cargo/config.toml 文件。
.cargo/config.toml
配置文件举例:
- build.target: 默认为
host arch
,指定要编译生成的 target triple 列表,编译结果保存到 target// 目录下; - build.rustflags:传递给 rustc 的编译器参数;
- 例如配置:
build.rustflags = ["-C", "link-arg=-fuse-ld=mold"]
表示调用rustc -C link-arg=-fuse-ld=mold
- 可以设置的 rustflags 列表 Codegen Options:https://doc.rust-lang.org/rustc/codegen-options/index.html
- 例如配置:
- env: 传递给 build script、rustc、cargo run、cargo build 等 cargo 启动的进程的额外环境变量;
- target.
.runner:如果指定,在运行 cargo run, cargo test 和 cargo bench 时将生成的 binary 作为 runner 的参数。
对于传递给 rustc 的编译器参数 rustflags
,优先级如下(使用第一个):
CARGO_ENCODED_RUSTFLAGS
environment variable.RUSTFLAGS
environment variable.- All matching
target.<triple>.rustflags
andtarget.<cfg>.rustflags
config entries joined together. build.rustflags
config value.
关于 [build]
和 [target.<target>]
的配置语义,参考:20250529-gcc-clang-rustc-compiling-toolchain.md
# https://doc.rust-lang.org/cargo/reference/config.html
# path dependency overrides,重载 Cargo.toml 中的 dependencies
paths = ["/path/to/override"]
[alias] # cargo command aliases
b = "build" # cargo b 等效为 cargo build
c = "check"
t = "test"
r = "run"
rr = "run --release"
recursive_example = "rr --example recursions" # alias 支持递归定义
space_example = ["run", "--release", "--", "\"command list\""]
[build]
jobs = 1 # number of parallel jobs, defaults to # of CPUs
rustc = "rustc" # the rust compiler tool
rustc-wrapper = "…" # run this wrapper instead of `rustc`
rustc-workspace-wrapper = "…" # run this wrapper instead of `rustc` for workspace members
rustdoc = "rustdoc" # the doc generator tool
# build for the target triple (ignored by `cargo install`)
target = ["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"]
target-dir = "target" # path of where to place all generated artifacts
rustflags = ["…", "…"] # custom flags to pass to all compiler invocations
rustdocflags = ["…", "…"] # custom flags to pass to rustdoc
incremental = true # whether or not to enable incremental compilation
dep-info-basedir = "…" # path for the base directory for targets in depfiles
[target.<triple>] # 特定 target triple 的参数
linker = "…" # linker to use
runner = "…" # wrapper to run executables
rustflags = ["…", "…"] # custom flags for `rustc`
[target.<cfg>] # 匹配 cfg!() 宏的 target 的参数,如 cfg(all(target_arch = "arm", target_os = "none"))
runner = "…" # wrapper to run executables
rustflags = ["…", "…"] # custom flags for `rustc`
[target.<triple>.<links>] # `links` build script override
rustc-link-lib = ["foo"]
rustc-link-search = ["/path/to/foo"]
rustc-flags = ["-L", "/some/path"]
rustc-cfg = ['key="value"']
rustc-env = {key = "value"}
rustc-cdylib-link-arg = ["…"]
metadata_key1 = "value"
metadata_key2 = "value"
[doc]
browser = "chromium" # browser to use with `cargo doc --open`, overrides the `BROWSER` environment variable
[env] # 传给 cargo 命令启动的进程的额外环境变量
# Set ENV_VAR_NAME=value for any process run by Cargo
ENV_VAR_NAME = "value"
# Set even if already present in environment
ENV_VAR_NAME_2 = { value = "value", force = true }
# Value is relative to .cargo directory containing `config.toml`, make absolute
ENV_VAR_NAME_3 = { value = "relative/path", relative = true }
# when to display a notification about a future incompat report
[future-incompat-report]
frequency = 'always'
[cargo-new] # cargo new 参数
vcs = "none" # VCS to use ('git', 'hg', 'pijul', 'fossil', 'none')
[http]
debug = false # HTTP debugging
proxy = "host:port" # HTTP proxy in libcurl format
ssl-version = "tlsv1.3" # TLS version to use
ssl-version.max = "tlsv1.3" # maximum TLS version
ssl-version.min = "tlsv1.1" # minimum TLS version
timeout = 30 # timeout for each HTTP request, in seconds
low-speed-limit = 10 # network timeout threshold (bytes/sec)
cainfo = "cert.pem" # path to Certificate Authority (CA) bundle
check-revoke = true # check for SSL certificate revocation
multiplexing = true # HTTP/2 multiplexing
user-agent = "…" # the user-agent header
[install] # cargo install 参数,默认为 ~/.cargo
root = "/some/path" # `cargo install` destination directory
[net]
retry = 3 # network retries
git-fetch-with-cli = true # use the `git` executable for git operations
offline = true # do not access the network
[net.ssh]
known-hosts = ["..."] # known SSH host keys
[patch.<registry>]
# Same keys as for [patch] in Cargo.toml
[profile.<name>] # Modify profile settings via config.
inherits = "dev" # Inherits settings from [profile.dev].
opt-level = 0 # Optimization level.
debug = true # Include debug info.
split-debuginfo = '...' # Debug info splitting behavior.
strip = "none" # Removes symbols or debuginfo.
debug-assertions = true # Enables debug assertions.
overflow-checks = true # Enables runtime integer overflow checks.
lto = false # Sets link-time optimization.
panic = 'unwind' # The panic strategy.
incremental = true # Incremental compilation.
codegen-units = 16 # Number of code generation units.
rpath = false # Sets the rpath linking option.
[profile.<name>.build-override] # Overrides build-script settings.
# Same keys for a normal profile.
[profile.<name>.package.<name>] # Override profile for a package.
# Same keys for a normal profile (minus `panic`, `lto`, and `rpath`).
[registries.<name>] # registries other than crates.io
index = "…" # URL of the registry index
token = "…" # authentication token for the registry
[registry]
default = "…" # name of the default registry
token = "…" # authentication token for crates.io
[source.<name>] # source definition and replacement
replace-with = "…" # replace this source with the given named source
directory = "…" # path to a directory source
registry = "…" # URL to a registry source
local-registry = "…" # path to a local registry source
git = "…" # URL of a git repository source
branch = "…" # branch name for the git repository
tag = "…" # tag name for the git repository
rev = "…" # revision for the git repository
[term]
quiet = false # whether cargo output is quiet
verbose = false # whether cargo provides verbose output
color = 'auto' # whether cargo colorizes output
hyperlinks = true # whether cargo inserts links into output
progress.when = 'auto' # whether cargo shows progress bar
progress.width = 80 # width of progress bar