跳过正文

Rust 工具链、项目布局、程序的编译链接和 Cargo 配置

Rust Cargo
目录

介绍使用 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)

指定 toolchain 和 rust-toolchain.toml
#

使用 rustup default nightly 指定 缺省使用的 toolchain ,这时 cargo/rustc 等指向指定 toolchain 下的 binary。

zj@a:~/docs$ ls ~/.rustup
downloads/  settings.toml  tmp/  toolchains/  update-hashes/

zj@a:~/docs$ cat ~/.rustup/settings.toml
default_toolchain = "nightly-x86_64-apple-darwin"
profile = "default"
version = "12"

[overrides]

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*

zj@a:~/docs$ which cargo
/Users/zhangjun/.cargo/bin/cargo

zj@a:~/docs$ ls -l ~/.cargo/bin/
total 67M
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 cargo -> /Users/zhangjun/.cargo/bin/rustup*
-rwxr-xr-x 1 zhangjun 3.8M  2 26 17:37 cargo-cache*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 cargo-clippy -> /Users/zhangjun/.cargo/bin/rustup*
-rwxr-xr-x 1 zhangjun  16M  2  7 18:50 cargo-espflash*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 cargo-fmt -> /Users/zhangjun/.cargo/bin/rustup*
-rwxr-xr-x 1 zhangjun 9.2M  2  7 22:26 cargo-generate*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 cargo-miri -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 clippy-driver -> /Users/zhangjun/.cargo/bin/rustup*
-rwxr-xr-x 1 zhangjun 4.9M  2 12 13:02 convert-bdf*
-rwxr-xr-x 1 zhangjun 9.1M  2  7 18:42 espflash*
-rwxr-xr-x 1 zhangjun 8.0M  2  7 23:13 espup*
-rwxr-xr-x 1 zhangjun 1.9M  2  7 18:37 ldproxy*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rls -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rust-analyzer -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rust-gdb -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rust-gdbgui -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rust-lldb -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rustc -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rustdoc -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   33  1 19 16:31 rustfmt -> /Users/zhangjun/.cargo/bin/rustup*
lrwxr-xr-x 1 zhangjun   26  1 19 16:31 rustup -> /usr/local/bin/rustup-init*
-rwxr-xr-x 1 zhangjun  16M  2 17 15:28 sccache*

还可以通过 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;
zj@a:~/codes/rust/mydemo$ cargo --help
Rust's package manager

Usage: cargo [+toolchain] [OPTIONS] [COMMAND]
       cargo [+toolchain] [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]...

通过在项目根目录下创建 rust-toolchain.toml 文件,也可以 by 项目指定 toolchain 类型和 rust 编译器版本 ,后续执行 cargo build 时会 自动安装和使用 对应的 toolchain 和 components:

[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/

# Host 对应的 lib,下面含有 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 对应的库路径
zj@a:~/docs$ ls -l /Users/zhangjun/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/
total 900K
-rw-r--r-- 1 zhangjun  347  2 27 11:52 components
drwxr-xr-x 9 zhangjun  288  2 27 11:52 etc/
-rw-r--r-- 1 zhangjun 1.5K  2 27 11:51 manifest-cargo-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun  148  2 27 11:51 manifest-clippy-preview-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun  147  2 27 11:51 manifest-rust-analyzer-preview-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun   24  2 27 11:51 manifest-rust-docs-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun 106K  2 27 11:51 manifest-rust-src
-rw-r--r-- 1 zhangjun  354  2 27 11:51 manifest-rust-std-riscv32imac-unknown-none-elf
-rw-r--r-- 1 zhangjun  358  2 27 11:51 manifest-rust-std-riscv32imafc-unknown-none-elf
-rw-r--r-- 1 zhangjun  350  2 27 11:51 manifest-rust-std-riscv32imc-unknown-none-elf
-rw-r--r-- 1 zhangjun 2.4K  2 27 11:51 manifest-rust-std-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun 1.1K  2 27 11:52 manifest-rustc-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun  142  2 27 11:52 manifest-rustfmt-preview-x86_64-apple-darwin
-rw-r--r-- 1 zhangjun 738K  2 27 11:52 multirust-channel-manifest.toml
-rw-r--r-- 1 zhangjun  755  2 27 11:52 multirust-config.toml
drwxr-xr-x 3 zhangjun   96  2 27 11:51 riscv32imac-unknown-none-elf/
drwxr-xr-x 3 zhangjun   96  2 27 11:51 riscv32imafc-unknown-none-elf/
drwxr-xr-x 3 zhangjun   96  2 27 11:51 riscv32imc-unknown-none-elf/
-rw-r--r-- 1 zhangjun    1  2 27 11:52 rust-installer-version
drwxr-xr-x 3 zhangjun   96  2 16 22:35 src/
drwxr-xr-x 4 zhangjun  128  2 27 11:52 x86_64-apple-darwin/

# 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  31K  2 27 11:51 libadler-012b85af78d2e1e4.rlib
-rw-r--r-- 1 zhangjun 5.5M  2 27 11:51 liballoc-27a4130e9caf3dbd.rlib
-rw-r--r-- 1 zhangjun 9.4K  2 27 11:51 libcfg_if-1c50acaefe5e8363.rlib
-rw-r--r-- 1 zhangjun 2.4M  2 27 11:51 libcompiler_builtins-3d585ed5e5cc3d6b.rlib
-rw-r--r-- 1 zhangjun  45M  2 27 11:51 libcore-ef02792cbce15279.rlib
-rw-r--r-- 1 zhangjun 761K  2 27 11:51 libgetopts-13a3162b3fbe1bc7.rlib
-rw-r--r-- 1 zhangjun 5.2M  2 27 11:51 libgimli-07e60025fa97b8c1.rlib
-rw-r--r-- 1 zhangjun 1.5M  2 27 11:51 libhashbrown-bbf0bc722950189b.rlib
-rw-r--r-- 1 zhangjun 1.8M  2 27 11:51 liblibc-534c4169514c4211.rlib
-rw-r--r-- 1 zhangjun 942K  2 27 11:51 libmemchr-38249612a8c7bcf3.rlib
-rw-r--r-- 1 zhangjun 276K  2 27 11:51 libminiz_oxide-186a5aaa2a4560b8.rlib
-rw-r--r-- 1 zhangjun 7.7M  2 27 11:51 libobject-a926fc7b8b5c7158.rlib
-rw-r--r-- 1 zhangjun  12K  2 27 11:51 libpanic_abort-121a5a42c0c500b7.rlib
-rw-r--r-- 1 zhangjun  34K  2 27 11:51 libpanic_unwind-b56fd508764bd539.rlib
-rw-r--r-- 1 zhangjun 4.5M  2 27 11:51 libproc_macro-ce17747687ef7ea0.rlib
-rw-r--r-- 1 zhangjun 263K  2 27 11:51 libprofiler_builtins-1776d848f132c95d.rlib
-rw-r--r-- 1 zhangjun 4.0M  2 27 11:51 librustc-nightly_rt.asan.dylib
-rw-r--r-- 1 zhangjun 1.7M  2 27 11:51 librustc-nightly_rt.lsan.dylib
-rw-r--r-- 1 zhangjun 3.5M  2 27 11:51 librustc-nightly_rt.tsan.dylib
-rw-r--r-- 1 zhangjun 604K  2 27 11:51 librustc_demangle-cad1e1d85b3aa75e.rlib
-rw-r--r-- 1 zhangjun 6.0K  2 27 11:51 librustc_std_workspace_alloc-dfedd200d2724108.rlib
-rw-r--r-- 1 zhangjun 8.5K  2 27 11:51 librustc_std_workspace_core-ee727fcfee1ad974.rlib
-rw-r--r-- 1 zhangjun  11K  2 27 11:51 librustc_std_workspace_std-2a492e681a99d4e2.rlib
-rw-r--r-- 1 zhangjun 8.1M  2 27 11:51 libstd-f7827e6f67b106c3.dylib  # Rust 标准库 binary
-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/

target triple
#

https://doc.rust-lang.org/cargo/appendix/glossary.html#target

Target Triple — A triple is a specific format for specifying a target architecture.

Triples may be referred to as a target triple which is the architecture for the artifact produced, and the host triple which is the architecture that the compiler is running on.

The target triple can be specified with the --target command-line option or the build.target config option. The general format of the triple is <arch><sub>-<vendor>-<sys>-<abi> where:

第一部分:

  • arch: The base CPU architecture, for example x86_64, i686, arm, thumb, mips, etc.
  • sub: The CPU sub-architecture, for example arm has v7, v7s, v5te, etc.

第二部分:

  • vendor: The vendor, for example unknown, apple, pc, nvidia, etc. CPU 制造商不明确的为 unknown

第三部分:其中 abi 可省略,如 darwin;

  • sys: The system name, for example linux, windows, darwin, etc.

    • none is typically used for bare-metal without an OS.
  • abi: The ABI, for example gnu, android, eabi, etc.

    • gnu 表示使用 glibc 库
    • elf 一般用于 bare-metal 的嵌入式 bin 固件程序

Some parameters may be omitted. Run rustc --print=target-list for a list of supported targets.

Tier1 with Host Tools

  • aarch64-unknown-linux-gnu ARM64 Linux (kernel 4.1, glibc 2.17+) 1
  • i686-pc-windows-gnu 32-bit MinGW (Windows 7+) 2 3
  • i686-pc-windows-msvc 32-bit MSVC (Windows 7+) 2 3
  • i686-unknown-linux-gnu 32-bit Linux (kernel 3.2+, glibc 2.17+) 3
  • x86_64-apple-darwin 64-bit macOS (10.12+, Sierra+)
  • x86_64-pc-windows-gnu 64-bit MinGW (Windows 7+) 2
  • x86_64-pc-windows-msvc 64-bit MSVC (Windows 7+) 2
  • x86_64-unknown-linux-gnu 64-bit Linux (kernel 3.2+, glibc 2.17+)

Rust 程序的编译、链接以及交叉编译
#

rustc 使用 LLVM 编译器实现,支持交叉编译(但需要安装对应 target 和交叉编译工具链)。

  • 即使纯 Rust 程序(不含 C/C++ 源码,也不需要链接 C/C++ 库),如果要生成非 host arch 的二进制,也需要交叉编译。

cargo build 默认编译生成 host arch 二进制, 也可以通过 --target.cargo/config.toml 的 [build].target 指定其它架构(可以指定多个,编译时生成多个 target 的二进制,放到 target// 目录下):

使用 rustup show 命令查看当前 host arch

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)

查看 rustc 编译器支持的 target 列表(Rust 官方为这些 target 提供了对应的 Rust 标准库):

zj@a:~/docs$ rustc --print=target-list
aarch64-apple-darwin
aarch64-apple-ios
aarch64-apple-ios-macabi
aarch64-apple-ios-sim
aarch64-apple-tvos
aarch64-apple-tvos-sim
aarch64-apple-watchos
//...
x86_64-unknown-redox
x86_64-unknown-uefi
x86_64-uwp-windows-gnu
x86_64-uwp-windows-msvc
x86_64-win7-windows-msvc
x86_64-wrs-vxworks
x86_64h-apple-darwin

rust toolchian 默认只安装了 host arch 对应的 target,其它 target 需要单独安装,安装 target 实际是安装对应的 Rust 标准库:

  • -linux-gnu 结尾的 target:动态链接 glibc
  • -linux-musl 结尾的 target:动态链接 musl
rustup target add x86_64-unknown-linux-gnu

# 安装的对应架构标准库
❯ ls ~/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-6f060101dda10b7a.*
/Users/alizj/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-6f060101dda10b7a.rlib
/Users/alizj/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-6f060101dda10b7a.so*

# 打印已安装的 target
root@ubuntu:~# rustup target list |grep installed
aarch64-unknown-linux-gnu (installed)
aarch64-unknown-linux-musl (installed)
x86_64-unknown-linux-gnu (installed)
x86_64-unknown-linux-musl (installed)

除了 Rust 标准库,还需要安装 target 对应的 编译工具链,如 gcc/llvm,后续编译时使用它提供的链接器:

  • -linux-gnu 结尾的 target:需要安装链接 glibc 的工具链;
  • -linux-musl 结尾的 target:需要安装链接 musl 的工具链;
brew tap messense/macos-cross-toolchains
brew install x86_64-unknown-linux-musl
brew install aarch64-unknown-linux-musl

cargo build 时,使用命令行参数或 .cargo/config.toml 文件来配置 编译工具链提供的链接器及其参数

  • --target 指定目标架构,当 host arch 与指定的 target 不一致时即为交叉编译。
cargo build --target=x86_64-unknown-linux-gnu

示例:MacOS aarch64 交叉编译 amd64 和 aarch64 linux 程序
#

  1. 安装 gcc 交叉编译工具链,后续交叉编译时需要使用其中的 linker:
brew tap messense/macos-cross-toolchains
brew install x86_64-unknown-linux-musl
brew install aarch64-unknown-linux-musl
  1. 安装 target 对应的 Rust 标准库
# 默认已安装
rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-unknown-linux-gnu

# 静态链接,可以使用 musl 目标
rustup target add x86_64-unknown-linux-musl
rustup target add aarch64-unknown-linux-musl

# 打印支持和已安装的 target
root@ubuntu:~# rustup target list |grep installed
aarch64-unknown-linux-gnu (installed)
aarch64-unknown-linux-musl (installed)
x86_64-unknown-linux-gnu (installed)
x86_64-unknown-linux-musl (installed)

# taget 安装到了 ~/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/ 目录下,主要安装的内容是 rust std 库。
root@ubuntu:~# ls ~/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/
  1. 配置项目 .cargo/config.toml 使用安装的交叉编译工具链中的链接器名称和参数:

# 缺省构建的 target 类型(一般是 host arch)和编译器参数
[build]
  # 构建目标架构,默认为当前操作系统的架构如 x86_64-unknown-linux-gnu,可以配置为 x86_64-unknown-linux-musl 实现静态链接。
  # 也可以通过 cargo build --target 或 `CARGO_BUILD_TARGE` 环境变量指定。
  target = "x86_64-unknown-linux-gnu"  # 字符串或列表,为列表时表示构建多个架构的二进制

  # 缺省使用的链接器
  linker = "aarch64-linux-musl-gcc"

  # 传递给 rustc 的参数可以是字符串或字符串数组,如:"-L native=/path/to/lib" 指定动态库查找路径。
  # 也可以通过 cargo build --config "build.rustflags='xxx'" 或环境变量
  # `CARGO_BUILD_RUSTFLAGS` `CARGO_ENCODED_RUSTFLAGS`、`RUSTFLAGS` 指定。
  rustflags = ["…", "…"]

# 按照 target 类型配置链接器和编译器参数
[target.aarch64-unknown-linux-musl]   # aarch64-unknown-linux-musl 是已安装的 rust 工具链
  linker = "aarch64-linux-musl-gcc"   # aarch64-linux-musl-gcc 是已安装的 gcc 工具链
  rustflags = ["…", "…"]

[target.x86_64-unknown-linux-musl]
  linker = "x86_64-linux-musl-gcc"
  rustflags = ["…", "…"]

示例:

# https://github.com/zed-industries/zed/blob/main/.cargo/config.toml

[build]
# v0 mangling scheme provides more detailed backtraces around closures
rustflags = ["-C", "symbol-mangling-version=v0", "--cfg", "tokio_unstable"]

[alias]
xtask = "run --package xtask --"

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]

[target.aarch64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]

[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-Objc -all_load"]

[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-Objc -all_load"]

[target.'cfg(target_os = "windows")']
rustflags = [
    "--cfg",
    "windows_slim_errors",        # This cfg will reduce the size of `windows::core::Error` from 16 bytes to 4 bytes
    "-C",
    "target-feature=+crt-static", # This fixes the linking issue when compiling livekit on Windows
]

更多信息,参考后文的 .cargo/config.toml 配置 一节。

或者在命令行上为 cargo build 指定构建参数:

  • --target x86_64-unknown-linux-musl : 对应 [build].target
  • --config "target.x86_64-unknown-linux-musl.linker='x86_64-linux-musl-gcc'" : 对应 [target.aarch64-unknown-linux-musl].linker
cargo build --target x86_64-unknown-linux-musl --config "target.x86_64-unknown-linux-musl.linker='x86_64-linux-musl-gcc'"
  1. 交叉编译:

使用 --target 指定目标架构或者使用 .cargo/config.toml[build].target 来指定 :

cargo build --target aarch64-unknown-linux-gnu --release
cargo build --target x86_64-unknown-linux-gnu --release

# 或者静态链接
cargo build --target x86_64-unknown-linux-musl --release
cargo build --target aarch64-unknown-linux-musl --release

交叉编译参考:

  1. https://www.rectcircle.cn/posts/linux-dylib-detail-6-lang-rust/
  2. https://rust-lang.github.io/rustup/cross-compilation.html

使用 build script 和 cc crate 编译和链接 C/C++ 源码和三方库
#

rustc 程序可以调用其它链接器(如系统的 gcc 或 llvm 链接器)来链接 C/C++ 库,使用其中定义的变量、类型和函数等内容。

有三种实现方式:

  1. 源文件集成:将 C/C++ 源码集成到 Rust 程序中,需要在编译 Rust 代码前,
  2. 库文件集成:又包含两种类型,都需要在编译 Rust 程序时与之链接;
  3. 库文件位于 Rust 程序目录中,即 Rust 源码自包含;
  4. 库文件位于系统库文件目录中;
  5. 混合方式:1 和 2 混合的方式。

一般使用专门的 crate 来支持 Rust 程序调用 C/C++ 库,该 crate 的命名惯例是 xx-sys , 如 libduckdb-sys,该 crate 一般包含 2 部分内容:

  1. C 库(动态或静态) 文件,或者包含对应 C/C++ 源代码文件;
  2. Rust 程序的 extern "ABI" {} 中声明的 C/C++ 库中变量或函数的 Rust 封装;

Rust 程序只需要 import 和 use 该 xx-sys 即可编译和链接 C/C++ 源文件或库文件中的代码。

对于第 1 部分内容,如果是 C/C++ 源文件,则需要在编译 Rust 程序前,先使用 build script(build.rs) 机制来编译这部分源码,然后在编译 Rust 程序时将生成的库与之链接。

  • cargo build 会自动先编译执行 build.rs, 然后编译 Rust 程序。
// https://doc.rust-lang.org/cargo/reference/build-script-examples.html

// build.rs

use std::process::Command;
use std::env;
use std::path::Path;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    // Note that there are a number of downsides to this approach, the comments
    // below detail how to improve the portability of these commands.
    Command::new("gcc").args(&["src/hello.c", "-c", "-fPIC", "-o"])
                       .arg(&format!("{}/hello.o", out_dir))
                       .status().unwrap();
    Command::new("ar").args(&["crus", "libhello.a", "hello.o"])
                      .current_dir(&Path::new(&out_dir))
                      .status().unwrap();

    println!("cargo::rustc-link-search=native={}", out_dir);
    println!("cargo::rustc-link-lib=static=hello");
    println!("cargo::rerun-if-changed=src/hello.c");
}

上面 build script 问题:

  1. The gcc command itself is not portable across platforms. For example it’s unlikely that Windows platforms have gcc, and not even all Unix platforms may have gcc. The ar command is also in a similar situation.
  2. These commands do not take cross-compilation into account. If we’re cross compiling for a platform such as Android it’s unlikely that gcc will produce an ARM executable.

使用 cc crate 进行重写,可以更好的解决上面的问题:

// Cargo.toml
[build-dependencies]
cc = "1.0"

// build.rs
fn main() {
    cc::Build::new()
        .file("src/hello.c")
        .compile("hello");
    println!("cargo::rerun-if-changed=src/hello.c");
}

The cc crate abstracts a range of build script requirements for C code:

  1. It invokes the appropriate compiler (MSVC for windows, gcc for MinGW, cc for Unix platforms, etc.).
  2. It takes the TARGET variable into account by passing appropriate flags to the compiler being used.
  3. Other environment variables, such as OPT_LEVEL, DEBUG, etc., are all handled automatically. The stdout output and OUT_DIR locations are also handled by the cc library.

cc crate 使用 系统的编译工具链 来编译 C/C++ 代码并生成与 Rust 程序进行 静态链接 的 cargo 指令,最终生成一个静态链接的 Rust 可执行程序。

cc crate 需要在 build script(build.rs) 中使用,它编译源文件,生成 链接到 Rust 程序的静态库文件

// https://docs.rs/cc/latest/cc/

// build.rs
cc::Build::new()
    .file("foo.c")
    .file("bar.c")
    .compile("foo"); // 编译生成 libfoo.a 静态库

然后通过 FFI 机制来使用生成的 libfoo.a 库中的 C 函数:

  • 这时不需要指定 #[link(name = "foo")], 因为 buidl.rs 中的 cc 代码会自动生成静态链接的 cargo指令。
// 不需要指定 #[link(name = "foo")]
extern "C" {
    fn foo_function();
    fn bar_function(x: i32) -> i32;
}

pub fn call() {
    unsafe {
        foo_function();
        bar_function(42);
    }
}

fn main() {
    call();
}

对于第 2 部分内容,即上面 C/C++ 接口的 Rust 封装(FFI),除了手动开发外,也可使用 bindgen crate 来自动生成,它提供了从 C 库 header 文件自动生成 Rust extern block 封装表示的功能。

如果链接的是系统的库文件,则第 1 部分内容是可选的,在第 2 部分的 extern block 中使用 #[link(name="mylib] 来指定链接到系统库,还可以在项目的 .cargo/config.toml 或 build.rs 或命令行中指定 Rust 程序要链接的三方库名称。

#[link(name = "mylib")]
extern "C" {
    fn foo_function();
    fn bar_function(x: i32) -> i32;
}

pub fn call() {
    unsafe {
        foo_function();
        bar_function(42);
    }
}

fn main() {
    call();
}

如果库文件不在系统标准搜索路径中,可以使用 build script 来指定搜索路径:

fn main() {
    println!(r"cargo:rustc-link-search=native=/home/alizj/libgit2-0.25.1/build");
}

通过使用 pkg-config crate 和 system-deps crate,可以更灵活的为 Rust 指定要链接的系统库:

使用 cc/bindgen/pkg-config 的综合示例:https://doc.rust-lang.org/cargo/reference/build-script-examples.html

build script(build.rs)
#

Some example use cases of build scripts are:

  1. Building a bundled C library.
  2. Finding a C library on the host system.
  3. Generating a Rust module from a specification.
  4. Performing any platform-specific configuration needed for the crate.

cargo build 在编译 Rust 源程序前自动执行 build script build.rs 使用 [build-dependencies] 中声明的依赖),用于生成 cargo 指令:

  1. 生成链接器参数:cargo::rustc-link-arg, 与 rustc 的 -C link-arg=FLAG option 等效。
  2. 添加 lib 搜索路径和库文件: cargo::rustc-link-lib=LIBcargo::rustc-link-search=[KIND=]PATH
  3. 生成编译器参数:cargo::rustc-flags=FLAGS
  • 与项目 .cargo/config.toml 文件中的 [build][target.*][rustflags] 功能类似。

build.rs 在 FFI 中得到广泛应用:它可以用来编译 C/C++ 源码生成静态库,然后生成 Cargo 指令来将 Rust 程序与之链接。

build script 可以输出 Cargo 识别的指令:

  1. cargo::rerun-if-changed=PATH — Tells Cargo when to re-run the script.
  2. cargo::rerun-if-env-changed=VAR — Tells Cargo when to re-run the script.

链接器参数: 3. cargo::rustc-link-arg=FLAG — Passes custom flags to a linker for benchmarks, binaries, cdylib crates, examples, and tests. 4. cargo::rustc-link-arg-bin=BIN=FLAG — Passes custom flags to a linker for the binary BIN. 5. cargo::rustc-link-arg-bins=FLAG — Passes custom flags to a linker for binaries. 6. cargo::rustc-link-arg-tests=FLAG — Passes custom flags to a linker for tests. 7. cargo::rustc-link-arg-examples=FLAG — Passes custom flags to a linker for examples. 8. cargo::rustc-link-arg-benches=FLAG — Passes custom flags to a linker for benchmarks. 9. cargo::rustc-link-lib=LIB — Adds a library to link. # 为 Rust 程序指定要链接的库文件 10. cargo::rustc-link-search=[KIND=]PATH — Adds to the library search path. # 库文件搜索路径

编译器参数: 11. cargo::rustc-flags=FLAGS — Passes certain flags to the compiler. 12. cargo::rustc-cfg=KEY[=“VALUE”] — Enables compile-time cfg settings. 13. cargo::rustc-check-cfg=CHECK_CFG – Register custom cfgs as expected for compile-time checking of configs.

环境变量参数: 14. cargo::rustc-env=VAR=VALUE — Sets an environment variable. 15. cargo::rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates.

  1. cargo::error=MESSAGE — Displays an error on the terminal.
  2. cargo::warning=MESSAGE — Displays a warning on the terminal.
  3. cargo::metadata=KEY=VALUE — Metadata, used by links scripts.

#[cfg()]/#[cfg_attr()]/cfg!() 条件编译
#

https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options

使用 #[cfg()]/#[cfg_attr()]/cfg!() 来对源码进行条件编译。

编译器根据 configuration predicate 是否为 true/false 来决定是否编译,包含如下类型:

  1. A configuration option. It is true if the option is set and false if it is unset.

  2. all() with a comma separated list of configuration predicates. It is false if at least one predicate is false. If there are no predicates, it is true.

  3. any() with a comma separated list of configuration predicates. It is true if at least one predicate is true. If there are no predicates, it is false.

  4. not() with a configuration predicate. It is true if its predicate is false and false if its predicate is true.

其中 Configuration options 可以是 name 或 key="value" 格式,编译器 根据 name 是否设置,或 key 是否值为 value ,进行条件编译:

  • name 是单标识符,如 unix 或 windows
  • key=value,key 是标识符,value 是字符串。
  • key 可以重复,如 feature="xx", feature="yy" 来表示根据是否启用 feature xx 和 yy 来进行条件编译.
  • key 和 value 前后可以有空格,如 foo="bar" 等效于 foo = "bar"

部分 name 或 key 是编译器自动设置的,另外一部分是在调用编译器时传入,如通过 rustc --cfg 设置 :

  • rustc --cfg "unix" program.rs
  • rustc --cfg 'verbose' --cfg 'feature="serde"', 分别对应 #[cfg(verbose)] 和 #[cfg(feature="serde")]
#[cfg(target_os = "macos")]
fn macos_only() {
  // ...
}

// This function is only included when either foo or bar is defined
#[cfg(any(foo, bar))]
fn needs_foo_or_bar() {
  // ...
}


#[cfg(all(unix, target_pointer_width = "32"))]
fn on_32bit_unix() {
  // ...
}

// This function is only included when foo is not defined
#[cfg(not(foo))]
fn needs_not_foo() {
  // ...
}

// This function is only included when the panic strategy is set to unwind
#[cfg(panic = "unwind")]
fn when_unwinding() {
  // ...
}

# cfg_attr 是在条件有效时自动设置 attr
#[cfg_attr(feature = "magic", sparkles, crackles)]
fn bewitched() {}
// When the `magic` feature flag is enabled, the above will expand to:
#[sparkles]
#[crackles]
fn bewitched() {}


let machine_kind = if cfg!(unix) {
  "unix"
} else if cfg!(windows) {
  "windows"
} else {
  "unknown"
};

println!("I'm running on a {} machine!", machine_kind);

key 可以是任意的,但是 rustc/cargo 定义了一些特定含义的 key:

  1. Cargo.toml 中定义的 features 列表(默认都不开启):
  • default feature:表示未通过 rustc --cfg 'feature="xx"'cargo build --features "xx yy" 启用 feature 时,默认启用的 feature 列表;
  • feature:也被用于开启 option 的 dependencies;
  1. 各种预定义的 target_* key,如 target_arch/target_os/target_family 等;

使用 rustc --print cfg 查看条件编译宏 #[cfg] 可以使用的条件,如果要查看非 host target 的 cfg,可以指定 –target 参数。

zj@a:~/docs$ rustup show |grep host
Default host: x86_64-apple-darwin

# 查看指定 target 的参数:--target=x86_64-win7-windows-msvc
zj@a:~/docs$ rustc --print cfg
debug_assertions
overflow_checks
panic="unwind"
relocation_model="pic"
target_abi=""
target_arch="x86_64"
target_endian="little"
target_env=""
target_family="unix"
target_feature="cmpxchg16b"
target_feature="fxsr"
target_feature="lahfsahf"
target_has_atomic="128"
target_has_atomic_equal_alignment="128"
target_os="macos"
target_pointer_width="64"
target_thread_local
target_vendor="apple"
unix
zj@a:~/docs$

cargo
#

cargo 全局缺省配置:~/.cargo/config.toml,也可以 by workspace 或 package 来定义 .cargo/config.toml 文件。

Cargo Home 默认为 ~/.cargo,或者由环境变量 CARGO_HOME 指定, 用于保存 Cargo 个人全局配置参数以及下载的 crate 包和依赖。

  • config.toml: Cargo 个人全局配置参数;
  • bin/: cargo install 或 rustup 安装的 binary;
  • registry/index|cache|src:保存从 registry crates.io 下载的包;
    • cache:压缩的 gzip 文件,以 .crate 结尾;
    • src 是 cache 中压缩包解压后的目录;

常用 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 build :保存到 target/debug 目录下,产生 Cargo.lock 文件;
  • cargo build --release:打开 optimization, 保存到 target/release 目录;
  • cargo run: 编译和运行

cargo new 默认为项目创建 .git 目录,可以使用 --vcs none 关闭;

Package 布局
#

  • Cargo.toml 和 Cargo.lock 位于 package root 目录下。cargo udpate 会更新 Cargo.lock 文件;
  • 源码位于 src 目录;
    • 缺省 lib 文件:src/lib.rs。如果目录模式的 module,则需要在目录中创建 mod.rs 文件。
    • 缺省 binary 文件:src/main.rs
    • 其他 binary 文件,src/bin/xxx.rs
  • Benchmarks 位于 benches 目录;
  • Examples 位于 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] 运行单元或集成测试,它搜索两个地方:

  1. src/ 目录下各源码文件,包含单元测试和文档测试;
  2. 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 architecture(使用 rustup show 查看),构建结果保存到 target/ 目录下。

Cargo 内置了 4 个 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/ 目录下的目录含义(以 debug 为例):

  • 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 and cargo 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 项目的配置包括两部分:

  1. .cargo/config.toml: package 的 .cargo/config.toml 或全局 ~/.cargo/config.toml 文件,可以覆盖 Cargo.toml配置,如 profile;
  2. Cargo.toml: 也称为 manifest 文件,可以引用 config.toml 中的配置,如 registry;

还可以 by 项目创建 rust-toolchain.toml 文件,用来指定该项目要安装和使用的 toolchain,要编译的 targets 列表,要下载的 components 列表。

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.
  [[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]

# 构建脚本和 proc-mecro 依赖
[build-dependencies]

# target 依赖,可以使用 cfg!() 条件包含
[target.*.dependencies]

# 为编译器提供各种优化和调试的参数集合
[profile.*]

# 一个 crate 只能有一个根 lib(可以有多个 module),但是可以有多个 bin/example/test/bench
#
# 根 lib 配置,默认 path 是 src/lib.rs, name 默认是 crate name;
# path: 未指定时, 根据 section 自动推导;
# doctest: 只对 lib 有效, 指定 cargo test 是否执行 doc example
# bench: 是否对指定的 target 搜索和执行 bench, 对于 bins/libs/bench 默认为 true
# proc-macro: 只对 lib 有效;
# crate-type: 可选值 bin/lib/rlib/dylib/cdylib/staticlib/proc-macro,对于 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).

# binary 列表,默认是 src/main.rs, name 默认是 crate name
#
# 额外 binary 位于 src/bin 目录下,可以通过 [[bin]] 来配置。

# binary 可以使用 crate 的 pub API,编译使用 [dependencies] 配置。
# 使用 cargo run --bin <bin-name> 来指定。
# cargo install --bin <bin-name> 安装到 ~/.cargo/bin 目录下。
[[bin]] # [[xx]] 表示是一个列表,可以重复指定多个
    name = "cool-tool"
    test = false
    bench = false

[[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]

编译依赖 [dependencies]
#

依赖来源可以是 crates.io/git/本地 path,对于 git/本地 path 来源,version 是可选的,但如果指定,则必须要匹配。

指定版本:

[dependencies]
time = "0.1.12" # 表示等效版本: >=0.1.12, <0.2.0
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 = falsefeatures = ["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 选择:

  1. 定义 features 列表(feature 之间可以相互依赖)或者要启用的可选 dependency package;
  2. feature 名不能和 dependencies 列表中的 package 名重复。
  • 这是由于如果 dep 中的 package 为 optional 时, cargo 会自动创建一个同名的 feature;
  1. 默认所有 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 : Enables the 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, the package-name/feature-name syntax can be used to specify features for specific workspace members.
    • 底层传给 rustc 的 –cfg 选项, 如 --cfg 'verbose' or --cfg 'feature="serde" feature="xx"';
  • --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  # 不优化
    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 当前目录来判断:

  1. 如果是 package 目录, 则使用对应的 package;
  2. 如果是 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 部分:

  1. Cargo.toml: 也称 manifest 文件,可以引用 config.toml 中的配置,如 registry;
  2. config.toml: by 项目 package 的 .cargo/config.toml 或用户缺省的 ~/.cargo/config.toml 文件。
  • 可以覆盖 Cargo.toml 中的部分配置,如 profile。
  1. CARGO_XX 环境变量:如配置 target.x86_64-unknown-linux-gnu.runner 对应 CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER
  2. 命令行参数 --config:如 cargo --config net.git-fetch-with-cli=true fetch,可以多次指定 --config,参数会被 merge。

优先级:命令行参数 –config 》环境变量 》配置文件

各种配置中 binary 或 path 的路径:

  1. 环境变量、命令行参数中:相对于当前工作目录;
  2. 配置文件中的 binary:如果没有路径分隔符,在 PATH 搜索。
  3. 配置文件中的目录:如果不是绝对路径,则相对于 .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 的编译器参数;
  • env: 传递给 build script、rustc、cargo run、cargo build 等 cargo 启动的进程的额外环境变量;
  • target..runner:如果指定,在运行 cargo run, cargo test 和 cargo bench 时将生成的 binary 作为 runner 的参数。

对于传递给 rustc 的编译器参数,优先级如下(使用第一个):

  1. CARGO_ENCODED_RUSTFLAGS environment variable.
  2. RUSTFLAGS environment variable.
  3. All matching target..rustflags and target..rustflags config entries joined together.
  4. build.rustflags config value.
# 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

cargo-cache
#

可用于清理 ~/.cargo 下的 cache:

cargo install cargo-cache

zj@a:~/docs$ cargo cache

Cargo cache '/Users/zhangjun/.cargo':

Total:                              10.34 GB
  22 installed binaries:           174.00 MB
  Registry:                         10.13 GB
    Registry index:                 34.67 MB
    1303 crate archives:           216.96 MB
    1303 crate source checkouts:     9.88 GB
  Git db:                           29.68 MB
    4 bare git repos:                3.24 MB
    4 git repo checkouts:           26.44 MB

cargo-sweep
#

清理项目 target/ 中未使用的 crate:

cargo install cargo-sweep
cargo sweep --time 30
cargo sweep --installed

cargo-udeps
#

提供 cargo +nightly udeps 命令,打印当前项目 Cargo.toml 中声明但未使用的依赖 crate

cargo install cargo-udeps --locked

sccache
#

cargo build 是 by workspace 来保存 build cache。使用 sccache,可以实现跨 workspace 的共享 build cache。

  • 设置 build.rustc-wrapper 为 sccache 路径;(如 ~/.cargo/config.toml 参数)。
[build]
rustc-wrapper = "/path/to/sccache"

或者使用环境变量:export RUSTC_WRAPPER=/path/to/sccache

安装和使用:

#安装
cargo install sccache

zj@a:~/docs$ sccache --show-stats
Compile requests                    300
Compile requests executed           272
Cache hits                            1
Cache hits (Rust)                     1
Cache misses                        271
Cache misses (C/C++)                191
Cache misses (Rust)                  80
Cache timeouts                        0
Cache read errors                     0
Forced recaches                       0
Cache write errors                    0
Compilation failures                  0
Cache errors                          0
Non-cacheable compilations            0
Non-cacheable calls                  28
Non-compilation calls                 0
Unsupported compiler calls            0
Average cache write               0.001 s
Average compiler                  1.946 s
Average cache read hit            0.005 s
Failed distributed compilations       0

Non-cacheable reasons:
crate-type                           22
-                                     6

Cache location                  Local disk: "/Users/zhangjun/Library/Caches/Mozilla.sccache"
Use direct/preprocessor mode?   yes
Version (client)                0.7.7
Cache size                          186 MiB
Max cache size                       10 GiB
zj@a:~/docs$

cargo tree 和 cargo metadata
#

cargo tre 显示项目的依赖树:

rust-analyzer on  master via 🦀 v1.84.1
❯ cargo tree
base-db v0.0.0 (/Users/alizj/go/src/github.com/rust-lang/rust-analyzer/crates/base-db)
├── cfg v0.0.0 (/Users/alizj/go/src/github.com/rust-lang/rust-analyzer/crates/cfg)
│   ├── intern v0.0.0 (/Users/alizj/go/src/github.com/rust-lang/rust-analyzer/crates/intern)
│   │   ├── dashmap v5.5.3
│   │   │   ├── cfg-if v1.0.0
│   │   │   ├── hashbrown v0.14.5
│   │   │   ├── lock_api v0.4.12
│   │   │   │   └── scopeguard v1.2.0
│   │   │   │   [build-dependencies]
│   │   │   │   └── autocfg v1.3.0
│   │   │   ├── once_cell v1.19.0
│   │   │   └── parking_lot_core v0.9.10
│   │   │       ├── cfg-if v1.0.0
│   │   │       ├── libc v0.2.169
│   │   │       └── smallvec v1.13.2

反过来,看某个 package/crate 为何会被依赖:

❯ cargo tree --invert --package [email protected]
clap v4.5.20
└── apsarabot v0.0.1 (/Users/alizj/go/src/gitlab.alibaba-inc.com/aone-ci-component/apsarabot)

cargo metadata:用 JSON 显示项目以来信息:

 cargo metadata | jq | head -30
warning: please specify `--format-version` flag explicitly to avoid compatibility problems
{
  "packages": [
    {
      "name": "addr2line",
      "version": "0.24.2",
      "id": "registry+https://github.com/rust-lang/crates.io-index#[email protected]",
      "license": "Apache-2.0 OR MIT",
      "license_file": null,
      "description": "A cross-platform symbolication library written in Rust, using `gimli`",
      "source": "registry+https://github.com/rust-lang/crates.io-index",
      "dependencies": [
        {
          "name": "rustc-std-workspace-alloc",
          "source": "registry+https://github.com/rust-lang/crates.io-index",
          "req": "^1.0.0",
          "kind": null,
          "rename": "alloc",
          "optional": true,
          "uses_default_features": true,
          "features": [],
          "target": null,
          "registry": null
        },
        {
          "name": "clap",
          "source": "registry+https://github.com/rust-lang/crates.io-index",
          "req": "^4.3.21",
          "kind": null,
          "rename": null,
          "optional": true,

cargo info 显示 package 信息,如 features 列表
#

❯ cargo info clap
clap #argument #cli #arg #parser #parse
A simple to use, efficient, and full-featured Command Line Argument Parser
version: 4.5.20 (latest 4.5.31)
license: MIT OR Apache-2.0
rust-version: 1.74
documentation: https://docs.rs/clap/4.5.20
repository: https://github.com/clap-rs/clap
crates.io: https://crates.io/crates/clap/4.5.20
features:
 +default                  = [std, color, help, usage, error-context, suggestions]
  color                    = [clap_builder/color]
  error-context            = [clap_builder/error-context]
  help                     = [clap_builder/help]
  std                      = [clap_builder/std]
  suggestions              = [clap_builder/suggestions]
  usage                    = [clap_builder/usage]
  cargo                    = [clap_builder/cargo]
  debug                    = [clap_builder/debug, clap_derive?/debug]
  deprecated               = [clap_builder/deprecated, clap_derive?/deprecated]
  derive                   = [dep:clap_derive]
  env                      = [clap_builder/env]
  string                   = [clap_builder/string]
  unicode                  = [clap_builder/unicode]
  unstable-derive-ui-tests = []
  unstable-doc             = [clap_builder/unstable-doc, derive]
  unstable-ext             = [clap_builder/unstable-ext]
  unstable-styles          = [clap_builder/unstable-styles]
  unstable-v5              = [clap_builder/unstable-v5, clap_derive?/unstable-v5, deprecated]
  wrap_help                = [clap_builder/wrap_help]
note: to see how you depend on clap, run `cargo tree --invert --package [email protected]`

相关文章

18. 测试:testing
·
Rust
Rust 测试
sqlx
·
Rust Rust-Crate
sqlx 是异步的 SQL mapper。
1. 标识符和注释:identify/comment
·
Rust
Rust 标识符介绍
10. 泛型和特性:generic/trait
·
Rust
Rust 泛型和特性