The Cargo Book

Cargo Logo

Cargo 是Rust包经理。Cargo 会下载您 Rust 的包依赖项,编译您的包,生成可分发的包,并将它们上传到crates.io - Rust 社区的包注册表。你可以为这本书做出贡献在GitHub.

章节

入门

要开始使用 Cargo,请安装 Cargo(和 Rust)并设置您的第一个箱子.

Cargo 指南

该指南将为您提供,有关如何使用 Cargo 开发 Rust 包的所有信息.

Cargo 参考

该参考文献涵盖了 Cargo 各个领域的细节.

常见问题

Getting Started

要开始使用 Cargo,请安装 Cargo(和 Rust),并设置您的第一个箱子.

安装

安装 Rust 和 Cargo

获得 Cargo 的最简单方法是使用rustup脚本,获取当前稳定版本的 Rust:

在 Linux 和 macOS 系统上,这可以通过以下方式完成:

$ curl -sSf https://static.rust-lang.org/rustup.sh | sh

它将下载一个脚本,然后开始安装。如果一切顺利,您会看到:

Rust is installed now. Great!

在 Windows 上,下载并运行rustup-init.exe。它将在控制台中启动安装,并在成功时显示上述消息.

在此之后,你可以使用rustup命令,安装beta或者nightly版本的 Rust 和 Cargo。

有关其他安装选项和信息,请访问 Rust 网站的安装页面.

从源头构建 Cargo

或者,你可以从源头构建 Cargo.

Cargo 的第一步

要使用 Cargo 启动新项目,请使用cargo new:

$ cargo new hello_world --bin

我们传递--bin,是因为我们正在制作一个二进制程序(默认): 如果我们正在创建一个库(lib),我们就会把传递--lib.

让我们来看看 Cargo 为我们带来了什么:

$ cd hello_world
$ tree .
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

这就是我们开始所需要的一切。首先,让我们看看Cargo.toml:

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"

[dependencies]

这被称为一个manifest元清单,它包含了 Cargo 编译项目所需的所有元数据.

src/main.rs有啥:

fn main() {
    println!("Hello, world!");
}

Cargo 为我们创造了一个”hello_world”.我们来编译它:

$ cargo build
   Compiling hello_world v0.1.0 (file:///path/to/project/hello_world)

然后运行它:

$ ./target/debug/hello_world
Hello, world!

我们也可以直接使用cargo run,它会自行编译,然后运行它, 一步到位:

$ cargo run
     Fresh hello_world v0.1.0 (file:///path/to/project/hello_world)
   Running `target/hello_world`
Hello, world!

走得更远

有关使用 Cargo 的更多详细信息,请查看Cargo 指南

Cargo Guide

本指南将为您提供有关如何使用 Cargo 开发 Rust 包的所有信息.

为什么 Cargo 存在

Cargo 是一个工具,允许 Rust 项目声明其各种依赖项,并确保您始终获得可重复的构建。

为了实现这一目标,Cargo 做了四件事:

  • 引入两个,包含各种项目信息的元数据文件。
  • 获取,并构建项目的依赖项.
  • 正确使用参数,以调用rustc或其他构建工具,构建你的项目。
  • 介绍,更容易使用 Rust 项目的约定(规范/风格)。

创建一个新项目

要使用 Cargo 启动新项目,请使用cargo new:

$ cargo new hello_world --bin

我们传递--bin,是因为我们正在制作一个二进制程序(默认): 如果我们正在创建一个库(lib),我们就会把传递--lib。默认情况下,这个目录会初始化为一个新的git存储库,如果您不希望它这样做,请传递--vcs none

让我们来看看 Cargo 为我们带来了什么:

$ cd hello_world
$ tree .
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

这就是我们开始所需要的一切首。先让我们看看Cargo.toml:

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"

[dependencies]

这被称为一个manifest元清单,它包含了 Cargo 编译项目所需的所有元数据.

src/main.rs有啥:

fn main() {
    println!("Hello, world!");
}

Cargo 为我们创造了一个”hello_world”.我们来编译它:

$ cargo build
   Compiling hello_world v0.1.0 (file:///path/to/project/hello_world)

然后运行它:

$ ./target/debug/hello_world
Hello, world!

我们也可以直接使用cargo run,它会自行编译,然后运行它, 一步到位:

$ cargo run
     Fresh hello_world v0.1.0 (file:///path/to/project/hello_world)
   Running `target/hello_world`
Hello, world!

您会注意到已创建了几个新文件和目录:

$ tree .
.
|-- Cargo.lock
|-- Cargo.toml
|-- src
|   `-- main.rs
`-- target
    `-- debug
        |-- build
        |-- deps
        |   |-- hello_world-6ad0b2df81336e7f
        |   |-- hello_world-6ad0b2df81336e7f.d
        |   `-- hello_world-6ad0b2df81336e7f.dSYM
        |       `-- Contents
        |           |-- Info.plist
        |           `-- Resources
        |               `-- DWARF
        |                   `-- hello_world-6ad0b2df81336e7f
        |-- examples
        |-- hello_world
        |-- hello_world.d
        |-- hello_world.dSYM -> deps/hello_world-6ad0b2df81336e7f.dSYM
        |-- incremental
        |   // ...
        `-- native

15 directories, 19 files

这个Cargo.lock文件啊,是包含我们的依赖项的有关信息(即便还没有依赖),其内容看起来可不是很有趣啊。再有就是target目录包含所有构建产品(二进制文件..),并且,可以看出,Cargo 默认生成调试(debug)版本。您可以使用cargo build --release,这会在开启优化的情况下,编译文件:

$ cargo build --release
   Compiling hello_world v0.1.0 (file:///path/to/project/hello_world)

cargo build --release将结果二进制文件放入target/release,而不再是target/debug目录.

调试模式的编译是开发的默认设置 - 编译时间较短,因为编译器不进行优化,但代码运行速度较慢。发布(release)模式编译需要更长时间,但代码运行速度更快.

在现有的 Carog 项目上工作

如果您下载使用 Cargo 的现有项目,那么它很容易上手.

首先,从某个地方获取项目.在这个例子中,我们将使用rand项目,其从 GitHub 上的存储库克隆而来:

$ git clone https://github.com/rust-lang-nursery/rand.git
$ cd rand

要建立,使用cargo build:

$ cargo build
   Compiling rand v0.1.0 (file:///path/to/project/rand)

这将获取所有依赖项,然后与项目一起构建它们.

从 crates.io 添加依赖项

crates.io是 Rust 社区的中央存储库,用作发现和下载包的位置。cargo默认配置为,使用它来查找请求的包.

获取托管在crates.io的依赖’库’,将它添加到您的Cargo.toml.

添加依赖项

如果你的Cargo.toml,还没有[dependencies]部分,添加它,然后列出您要使用的包名称和版本。这个例子增加了一个time箱(crate)依赖:

[dependencies]
time = "0.1.12"

版本字符串是semver版本要求。该指定依赖项文档 提供了有关此处选项的更多信息.

如果我们还想添加一个regex箱子依赖,我们不需要为每个箱子都添加[dependencies]。下面就是你的Cargo.toml文件整体,看起来像依赖于timeregex箱:

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]

[dependencies]
time = "0.1.12"
regex = "0.1.41"

重新运行cargo build,Cargo 将获取新的依赖项及其所有依赖项,将它们全部编译,然后更新Cargo.lock:

$ cargo build
      Updating registry `https://github.com/rust-lang/crates.io-index`
   Downloading memchr v0.1.5
   Downloading libc v0.1.10
   Downloading regex-syntax v0.2.1
   Downloading memchr v0.1.5
   Downloading aho-corasick v0.3.0
   Downloading regex v0.1.41
     Compiling memchr v0.1.5
     Compiling libc v0.1.10
     Compiling regex-syntax v0.2.1
     Compiling memchr v0.1.5
     Compiling aho-corasick v0.3.0
     Compiling regex v0.1.41
     Compiling hello_world v0.1.0 (file:///path/to/project/hello_world)

我们的Cargo.lock包含有关,我们使用的所有这些依赖项的哪个版本的确实信息.

现在,如果regexcrates.io上更新了,在我们选择cargo update之前,我们仍会使用相同的版本进行构建.

你现在可以使用regex箱了,通过在main.rs使用extern crate

extern crate regex;

use regex::Regex;

fn main() {
    let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
    println!("Did our date match? {}", re.is_match("2014-01-01"));
}

运行它将显示:

$ cargo run
   Running `target/hello_world`
Did our date match? true

项目布局

Cargo 使用文件放置惯例,以便轻松进入新的 Cargo 项目:

.
├── Cargo.lock
├── Cargo.toml
├── benches
│   └── large-input.rs
├── examples
│   └── simple.rs
├── src
│   ├── bin
│   │   └── another_executable.rs
│   ├── lib.rs
│   └── main.rs
└── tests
    └── some-integration-tests.rs
  • Cargo.tomlCargo.lock存储在项目的根目录中.
  • 源代码进入src目录.
  • 默认库文件是src/lib.rs.
  • 默认的可执行文件是src/main.rs.
  • 其他可执行文件,可以放入src/bin/*.rs.
  • 集成测试进入tests目录(单元测试进到,正在测试的每个文件中).
  • 示例进入examples目录.
  • 基准进入benches目录.

这些将在更详细的清单描述说明中解释.

Cargo.toml 与 Cargo.lock

Cargo.tomlCargo.lock各有其目的。在我们谈论它们之前,这是一个总结:

  • Cargo.toml是从广义上描述你的依赖,并由你编写.
  • Cargo.lock包含有关您的依赖项的确切信息。它由 Cargo 维护,不应手动编辑.

如果您正在构建,其他项目要依赖的库,请将Cargo.lock放置在你的.gitignore。如果您正在构建可执行文件,如命令行工具或应用程序,请检查Cargo.lock位于git管理下。如果你对这是为什么感到好奇,请参阅“为什么二进制文件在版本控制系统中有Cargo.lock,而库没有?” - FAQ .

让我们再挖掘一下.

Cargo.toml是一个manifest(清单),我们可以在其中指定一系列关于我们项目的不同元数据的文件。例如,我们可以说我们依赖于另一个项目:

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand.git" }

这个项目有一个依赖关系rand箱。在这种情况下,我们已经说过,我们依赖于 GitHub 上的特定 Git 存储库。由于我们尚未指定任何其他信息,因此 Cargo 假定我们打算使用最新提交的master分支构建我们的项目。

听起来不错? 嗯,但有一个问题: 如果你今天构建这个项目,然后你发送一份副本给我,我明天构建这个项目,可能会发生一些不好的事情。因在此期间,可能会有更多的rand提交,我的构建将包括新的提交,而你的不会。因此,我们会得到不同的构建。这很糟糕,因为我们需要可重复的构建.

我们可以通过放置一个rev来解决这个问题,写入我们Cargo.toml:

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand.git", rev = "9f35b8e" }

现在我们的构建将是相同的。但是有一个很大的缺点:现在我们每次想要更新库时,都必须手动考虑 SHA-1。这既乏味又容易出错.

那现在Cargo.lock登场了。由于它的存在,我们不需要手动跟踪确切的修订版本: Cargo 将为我们做。当我们有这样的清单时:

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand.git" }

Cargo 将采取最新的提交,并在我们第一次构建时,将这些信息写入我们的Cargo.lock。该文件将如下所示:

[root]
name = "hello_world"
version = "0.1.0"
dependencies = [
 "rand 0.1.0 (git+https://github.com/rust-lang-nursery/rand.git#9f35b8e439eeedd60b9414c58f389bdc6a3284f9)",
]

[[package]]
name = "rand"
version = "0.1.0"
source = "git+https://github.com/rust-lang-nursery/rand.git#9f35b8e439eeedd60b9414c58f389bdc6a3284f9"

你可以看到这里有更多的信息,包括我们用来构建的确切修订版本。现在,当您将项目交给其他人时,他们将使用完全相同的 SHA,即使我们没有在我们的项目Cargo.toml中指定它.

当我们准备选择,更新库的版本时,Cargo 会自动重新计算依赖关系,并为我们更新内容:

$ cargo update           # updates all dependencies
$ cargo update -p rand   # updates just “rand”

这将写出一个新的Cargo.lock与新版本信息。请注意cargo update参数,实际上会是是一个包 ID 规范,和rand只是一个简短的规范.

测试

Cargo 可以使用cargo test命令运行您的测试。Cargo 寻找在两个地方运行的测试 :在你src中的每个文件,和tests/中的任何测试。测试你的src文件应该是单元测试,并在tests/中的应该是整合式测试。因此,您需要将包装箱导入到tests的文件中.

这是在我们的项目中,运行cargo test的一个例子,目前没有测试:

$ cargo test
   Compiling rand v0.1.0 (https://github.com/rust-lang-nursery/rand.git#9f35b8e)
   Compiling hello_world v0.1.0 (file:///path/to/project/hello_world)
     Running target/test/hello_world-9c2b65bbb79eabce

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

如果我们的项目有测试,我们会看到更多的输出与正确的测试数量.

您还可以通过传递过滤器,来运行特定测试:

$ cargo test foo

这将运行任何匹配的foo测试.

cargo test还运行其他检查。例如,它将编译您包含的任何示例(examples),并且还将测试文档中的示例。请看在 Rust 文档中的测试指南,了解更多详细信息.

持续集成

Travis CI

要在 Travis CI 上测试您的项目,这里有一个.travis.yml文件示例:

language: rust
rust:
  - stable
  - beta
  - nightly
matrix:
  allow_failures:
    - rust: nightly

这将在所有三个 rust 版本下,进行测试,但 nightly 的任何破坏,都不会使整体构建失败。请看看Travis CI Rust 文档了解更多信息.

GitLab CI

要在 GitLab CI 上测试您的包,这里有一个.gitlab-ci.yml文件示例:

stages:
  - build

rust-latest:
  stage: build
  image: rust:latest
  script:
    - cargo build --verbose
    - cargo test --verbose

rust-nightly:
  stage: build
  image: rustlang/rust:nightly
  script:
    - cargo build --verbose
    - cargo test --verbose
  allow_failure: true

这将测试 stable 版本和 nightly 版本,但 nightly 的任何破损,都不会使整体构建失败。欲获得更多信息,请看GitLab CI.

构建 缓存

Cargo 在单个工作区,共享其中所有包的构建工件。今天,Cargo 不会在不同的工作区共享构建结果,但使用第三方工具可以实现类似的结果,sccache

装置sccache,用cargo install sccache安装它,并在调用 Cargo 之前,设置RUSTC_WRAPPER环境变量成sccache。如果你使用 bash,更好是田间export RUSTC_WRAPPER=sccache.bashrc文件。有关更多详细信息,请参阅 sccache 文档.

Cargo 参考

该参考文献涵盖了 Cargo 各个领域的细节.

依赖指定

您的箱子,可以依赖多个来源的库,如crates.iogit的存储库或本地文件系统上的子目录。您还可以临时覆盖依赖项的位置 - 例如, 便于能够测试您在本地工作的依赖项中的错误修复。您可以为不同的平台,和或仅在开发期间使用不同的依赖项。我们来看看如何做到这些.

指定依赖,来自 crates.io

默认情况下,Cargo 是准备好,在crates.io上查找依赖项。在这种情况下,只需要名称和版本字符串。在Cargo 指南,我们选择了一个依赖项-time箱:

[dependencies]
time = "0.1.12"

字符串"0.1.12"是一个semver版本格式字符串。由于此字符串中没有任何运算符,因此它的解释方式与我们指定的"^0.1.12"方式相同,而^被称为跳脱条件.

Caret requirements(跳脱条件)

跳脱条件: 允许 SemVer 兼容更新指定版本。新的版本允许更新的条件是,不修改最左边的非零数字(无论major,minor,patch)。在这种情况下,如果我们执行了cargo update -p time,Cargo 应该更新我们的0.1.13版本(如果是最新的0.1.z发布),但不会更新为0.2.0。相反,我们若将版本字符串指定为^1.0,Cargo 应更新至1.1,如果是最新的1.y发布,但不是2.0版本。0.0.x并不与任何其他版本兼容.

以下是一些跳脱条件的例子以及它们允许的版本:

^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

此兼容性约定与 SemVer ,在处理 1.0.0 之前的版本方面有所不同。虽然 SemVer 说在 1.0.0 之前没有兼容性,但 Cargo 认为0.x.y是兼容0.x.z,这里y ≥ zx > 0.

Tilde 条件

Tilde 条件指定具有更新最小版本的一定能力。如果指定 major 版本,minor 版本和 patch 程序版本,或仅指定 major 版本和 minor 版本,则仅允许 patch 程序级别更改。如果仅指定 major 版本,则允许进行 minor 和 patch 级别更改.

~1.2.3是 Tilde 条件的一个例子.

~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0

通配符要求

通配符条件允许任何通配符所在的版本.

*1.*1.2.*是通配符条件的示例.

* := >=0.0.0
1.* := >=1.0.0 <2.0.0
1.2.* := >=1.2.0 <1.3.0

Inequality requirements(范围条件)

范围条件允许手动指定要依赖的版本范围或确切版本.

以下是范围条件的一些示例:

>= 1.2.0
> 1
< 2
= 1.2.3

多版本条件

多个版本,要求用逗号分隔,例如>= 1.2, < 1.5.

依赖指定,来自 git 存储库

依赖于位于git存储库的库,您需要指定的最小信息,为一个git字段,其是存储库的github位置:

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand" }

Cargo 将取得git,然后在这个位置找到一个存储库的请求箱子的Cargo.toml。方式是对git存储库里面的任何地方(不一定在根目录) - 例如,指定工作区中的成员包名称,和设置git到包含工作区的存储库).

由于我们尚未指定任何其他信息,因此 Cargo 假定我们打算使用最新的提交master分支,来构建我们的包。你可以将git字段和revtag, 还有branch,这些用于指定其他内容的字段组合起来。这是一个指定您希望在名为next分支上,使用最新提交的示例:

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand", branch = "next" }

路径,依赖指定

随着时间的推移,我们来自指南hello_world示例已大幅增长! 它已经到了我们可能想分出一个单独的箱子供其他人使用的地步。为此,Cargo 支持路径依赖通常是位于一个存储库中的子箱。让我们开始在hello_world包的内部制作一个新的箱子:

# inside of hello_world/
$ cargo new hello_utils

这将创建一个新文件夹hello_utils,里面有一个Cargo.tomlsrc文件夹已准备好进行配置。为了告诉 Cargo,请打开hello_world/Cargo.toml,并添加你的hello_utils依赖:

[dependencies]
hello_utils = { path = "hello_utils" }

这告诉 Cargo 我们依赖于一个叫做hello_utils的箱子,这能在hello_utils文件夹找到(相对于,写在Cargo.toml路径).

就是这样! 下一步cargo build将自动构建hello_utils,以及它自己的所有依赖项,其他人也可以开始使用它。但是,crates.io不允许仅使用 路径指定依赖项 的包。如果我们想发布我们的hello_world箱子,我们需要发布一个版本hello_utilscrates.io,并在依赖项行中指定其版本:

[dependencies]
hello_utils = { path = "hello_utils", version = "0.1.0" }

依赖覆盖

Cargo 中有许多方法支持,覆盖依赖关系以及控制依赖关系图。但是,这些选项通常仅在工作区级别可用,并且不通过依赖项传播。换句话说,”应用程序”具有覆盖依赖关系的能力,但”库”却没有。

许多场景,会产生想,覆盖依赖性或以其他方式改变某些依赖关系的愿望。然而,他们中的大多数都可以归结为,将箱子发布到 crates.io 之前使用箱子(覆盖依赖)的能力。例如:

  • 您编写的 crate ,也用于您编写的更大应用程序中,并且您希望测试在更大应用程序内,crate的错误修复情况。
  • 不是你编写的上游包,现在其 git 存储库的主分支上,有一个新功能或错误修复,您要测试它。
  • 您即将发布新版本的 major 版本,但您希望在整个软件包中进行集成测试,以确保新的主要版本能够正常运行.
  • 您已经为上游的软件包提交了一个针对您找到的错误的修复程序,但是您希望立即让您的应用程序依赖,此程序包的固定修复版本,以避免错误修复程序被拒绝合并.

这些场景目前都是通过[patch] 清单部分 解决的,从历史上看,其中一些方案是[replace]部分解决的,但我们在这里会记录[patch]解决的部分。

测试一个错误修复

假设你正在使用uuid crate,但是当你正在研究它时,你会发现一个错误.但是,你很有进取心,所以你决定尝试修复这个 bug! 最初你的清单看起来像:

[package]
name = "my-library"
version = "0.1.0"
authors = ["..."]

[dependencies]
uuid = "1.0"

我们要做的第一件事是克隆uuid存储库,到本地:

$ git clone https://github.com/rust-lang-nursery/uuid

接下来我们将编辑my-library-Cargo.toml,为:

[patch.crates-io]
uuid = { path = "../path/to/uuid" }

在这里,我们宣布我们是*修补(patch)*来源crates-io,其有一个新的依赖,这将有效地添加本地(签出 checkout)版本uuid到 crates.io 注册表,指向本地包。

接下来我们需要确保我们的锁(lock)文件已更新为,使用此新版本uuid,所以我们的包使用本地签出的副本,而不是 crates.io 中的副本。[patch]工作方式是它将从../path/to/uuid加载依赖,然后每当 crates.io 查询uuid的版本时,它会返回本地版本.

这意味着本地签出的版本号很重要,会影响是否使用该补丁。我们的清单宣布uuid = "1.0",这意味着我们只会解析>= 1.0.0, < 2.0.0,和 Cargo 的贪婪解析算法,也意味着我们将解析到该范围内的最大版本。通常情况下这并不重要,因为 git 存储库的版本已经更大,或与 crates.io 上发布的最大版本相匹配,但重要的是要记住这一点!

无论如何,通常您现在需要做的就是:

$ cargo build
   Compiling uuid v1.0.0 (.../uuid)
   Compiling my-library v0.1.0 (.../my-library)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs

就是这样! 您现在正在使用本地版本uuid构建(注意构建输出中括号中的路径)。如果您没有看到构建本地路径版本,那么您可能需要运行cargo update -p uuid --precise $version,这里$version是本地签出版本的uuid副本。

一旦你修复了你最初发现的错误,你要做的下一件事就是将其作为拉取请求提交给uuid箱子本身。一旦你完成了这个,你也可以更新下[patch]部分。[patch]里面的内容列表就像是[dependencies]部分,所以一旦你的拉动请求合并,你就可以改变你的path依赖:

[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

Working with an unpublished minor version

与 一个未发布的次要版本,一起工作

现在让我们稍微改变一下,从错误修复,变成要添加功能。在努力my-library的同时,你发现需要uuid箱的一个全新的功能。而您已实现uuid此功能,并在[patch]上面进行本地测试,并提交了拉取请求。让我们来看看在实际发布之前,你如何继续使用和测试它。

我们也说当前版本的uuid,在 crates.io 上是1.0.0版本,但从提交那时起,git 存储库的主分支已更新为1.0.1。此分支包含您之前提交的新功能。要使用此存储库,我们将编辑我们的Cargo.toml,看起来像

[package]
name = "my-library"
version = "0.1.0"
authors = ["..."]

[dependencies]
uuid = "1.0.1"

[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

注意我们对本地uuid的依赖已更新为1.0.1,因为这是我们在箱子发布后实际需要的东西。但是,这个版本在 crates.io 上不存在,所以我们提供给它清单的[patch]部分.

现在,当我们的库被构建时,它将uuid从 git 存储库取出,并解析到存储库中的 1.0.1 ,而不是尝试从 crates.io 下载版本。一旦 1.0.1 发布在 crates.io 上,那[patch]部分就可以删除了。

值得注意的是,[patch]连带关系。假设您在更大的包中使用my-library,例如:

[package]
name = "my-binary"
version = "0.1.0"
authors = ["..."]

[dependencies]
my-library = { git = 'https://example.com/git/my-library' }
uuid = "1.0"

[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

记住这[patch]连带关系,但只能在顶层,所以我们的my-library消费者不得不重写[patch]部分(如有必要的话)。不过,在这里,新的uuid箱子会适用对uuid的依赖和my-library -> uuid的依赖,两个依赖指定了。该uuid箱 将被解析为整个 crate 关系图 的 1.0.1 版本,并且它是将从 git 存储库中提取。

Overriding repository URL

覆盖 注册表 URL

如果要覆盖的依赖项不是加载自crates.io,你将不得不改变一下你的[patch]使用方式:

[patch."https://github.com/your/repository"]
my-library = { path = "../my-library/path" }

就是这样!

Prepublishing a breaking change

预发布一个重要变化

让我们来看看最后一个场景。若要使用一个新的主要版本的箱子,其通常伴随着重大变化。而要坚持使用我们以前的箱,这意味着我们将创建 2.0.0 版本uuid箱。在我们提交了所有上游更改后,我们可以更新我们的my-library清单,看起来像:

[dependencies]
uuid = "2.0"

[patch.crates-io]
uuid = { git = "https://github.com/rust-lang-nursery/uuid", branch = "2.0.0" }

就是这样!与前面的示例一样,2.0.0 版本实际上,并不存在于 crates.io 上,但我们仍然可以通过[patch]部分使用。作为一个思考练习,让我们再看看my-binary(被使用)的再次表现:

[package]
name = "my-binary"
version = "0.1.0"
authors = ["..."]

[dependencies]
my-library = { git = 'https://example.com/git/my-library' }
uuid = "1.0"

[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid', branch = '2.0.0' }

请注意,这实际上将解析为两个版本的uuid箱。该my-binary箱子将继续使用 1.x.y 系列的uuid箱子,但是my-library箱 会使用 2.0.0 版本uuid。这将允许您通过依赖关系图逐步推出对包的更改,而无需一次性更新所有内容。

Overriding with local dependencies

覆盖 本地依赖项

有时你只是暂时在一个箱子上工作,而你不想修改Cargo.toml中像上诉的[patch]部分。对于这个用例,Cargo 提供了更为有限的覆盖版本路径覆盖.

路径覆盖是通过.cargo/config指定,而不是Cargo.toml,你可以寻找有关此配置的更多文档。在.cargo/config内,你要指定的是一个名为paths字段:

paths = ["/path/to/uuid"]

该数组应填充包含Cargo.toml的目录。在这种情况下,我们只是添加uuid,所以它将是唯一一个被覆盖的。此路径可以是包含该路径的绝对路径或相对.cargo文件夹的路径.

路径覆盖,比[patch]部分的限制更严格,但是,路径覆盖不能改变依赖图的结构。而当使用路径替换时,前一组依赖项必须完全匹配新的Cargo.toml规格。如此,就意味着路径覆盖不能用于向箱添加依赖项的测试,而换成[patch]在该种情况下使用。因此,路径覆盖的使用,通常会与快速错误修复分隔开来,而不是大更新分开。

注意:使用本地配置覆盖路径,仅适用于已发布到crates.io的包。您无法使用此功能告诉 Cargo 如何查找本地未发布的箱。

Platform specific dependencies

平台决定依赖

特定于平台的依赖项采用相同的格式,但在target下列出。像正常 Rust 一样的#[cfg]语法,将用于定义这些部分:

[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"

[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"

[target.'cfg(target_arch = "x86")'.dependencies]
native = { path = "native/i686" }

[target.'cfg(target_arch = "x86_64")'.dependencies]
native = { path = "native/x86_64" }

与 Rust 一样,这里的语法支持notany,和all运算符组合各种 cfg 名称/值对。请注意cfg语法仅在 Cargo 0.9.0(Rust 1.8.0)之后可用.

除了#[cfg]语法,Cargo 还支持列出依赖关系适用的完整目标:

[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"

[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"

如果您使用的是自定义目标规范,请引用完整路径和文件名:

[target."x86_64/windows.json".dependencies]
winhttp = "0.4.0"

[target."i686/linux.json".dependencies]
openssl = "1.0.1"
native = { path = "native/i686" }

[target."x86_64/linux.json".dependencies]
openssl = "1.0.1"
native = { path = "native/x86_64" }

Development dependencies

开发(Dev)依赖项

你可以添加一个[dev-dependencies]表格到Cargo.toml,其格式相当于[dependencies]:

[dev-dependencies]
tempdir = "0.3"

编译用于构建的包时,不会使用 Dev 依赖,但用于编译测试,示例和基准。

这些依赖关系是不会传播到依赖于此包的其他包.

您还可以让dev-dependencies具有特定目标的开发依赖项,而不是dependencies标题。例如:

[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"

Build dependencies

构建 依赖项

您可以在构建脚本中使用,依赖其他基于 Cargo 的箱。依赖关系是由清单的build-dependencies部分定义:

[build-dependencies]
cc = "1.0.3"

构建脚本并不是有权访问中 dependencies要么dev-dependencies部分列出的依赖项。除非也在dependencies部分下面列出,否则构建依赖项同样不可用于包本身。包本身及其构建脚本是分开构建的,因此它们的依赖关系不重合。通过将独立依赖用于独立目的,使 Cargo 更简单,更清洁。

Choosing features

选择 特性

如果您依赖的包提供条件特性,您可以指定使用哪个:

[dependencies.awesome]
version = "1.3.5"
default-features = false # 不会包括默认特性, 和 任君选
                         # 单特性
features = ["secure-password", "civet"]

有关 features 的更多信息,请参阅清单文档.

Renaming dependencies in Cargo.toml

Cargo.toml中的重命名依赖项

Cargo.toml[dependencies]部分的时候,您为依赖项编写的字段通常与您在代码中导入的包的名称相匹配。但是,对于某些项目,您可能希望在代码中引用具有不同名称的包,而不管它是如何在 crates.io 上发布的。例如,您可能希望:

  • 避免在 Rust 代码常用use foo as bar.
  • 依赖箱子的多个版本.
  • 依赖来自不同注册表管理机构的同名箱.

为了支持这个 ,Cargo 在[dependencies]部分使用 一个package字段,决定应该依赖哪个包:

[package]
name = "mypackage"
version = "0.0.1"

[dependencies]
foo = "0.1"
bar = { git = "https://github.com/example/project", package = "foo" }
baz = { version = "0.1", registry = "custom", package = "foo" }

在此示例中,Rust 代码中现在提供了三个包:


# #![allow(unused_variables)]
#fn main() {
extern crate foo; // crates.io
extern crate bar; // git repository
extern crate baz; // registry `custom`
#}

所有这三个箱的包名称在他们自己Cargo.toml,都是foo,所以我们明确地告知 Cargo ,使用的是我们想要的package字段(如 package = “foo”包名,即我们在本地调用其他东西)。如果没有指定package,则默认为所请求的依赖项的名称。

请注意,如果您有一个可选的(optional)依赖项,例如:

[dependencies]
foo = { version = "0.1", package = 'bar', optional = true }

你依赖于一个bar箱子,其来自 crates.io,但你箱子有一个foo特性,取代了一个bar特性。也就是说,在重命名时,特性的名称拿掉了依赖项的名称,而不是包名称。

启用传递依赖项的工作方式类似,例如我们可以将以下内容,添加到上面的清单中:

[features]
log-debug = ['foo/log-debug'] # 使用 'bar/log-debug' 就会出现一个错误!

The Manifest Format

清单格式

每个包的这个Cargo.toml文件称为清单. 每个清单文件由一个或多个部分(表格)组成.

The [package] section

[package]部分

Cargo.toml的第一部分是[package].

[package]
name = "hello_world" # the name of the package
version = "0.1.0"    # the current version, obeying semver
authors = ["Alice <a@example.com>", "Bob <b@example.com>"]

所有这三个字段都是必要性的.

The version field

version 字段

Cargo 烘烤的概念是语义版本控制,所以确保你遵循一些基本规则:

  • 在您达到 1.0.0 之前,任何事情都会发生,但是如果您进行了重大变化的更新,则增加次要(minor)版本。在 Rust 语言中,重大变化包括,向结构添加字段,或增加变量到枚举。
  • 在 1.0.0 之后,只在增加主要(major)版本时进行重大变化。不要破坏建筑.
  • 在 1.0.0 之后,不要在补丁级别(patch)的版本添加任何新的公共 API(没有任何新的pub)。如果添加pub结构、特性、字段、类型、函数、方法或其他任何东东,则总是增加次要版本。
  • 使用具有三个数字部分的版本号,如 1.0.0,而不是 1.0。

The edition field (optional)

edition 字段 (可选)

您可以在Cargo.toml中的edition字段,选择一个特定的 Rust 版本,用于您的包。 如果没有指定版本,它将默认为 2015。

[package]
# ...
edition = '2018'

这个edition字段会影响到您的包编译的版本。若是通过cargo new得来的项目,Cargo 将始终让edition字段设置为最新版本。设置[package]下的edition字段将影响包中的所有目标/箱,包括测试套件、基准、二进制文件、示例等。

The build field (optional)

build 字段 (可选)

此字段指定包根目录中的文件,该文件是构建脚本,用于生成本机代码。可以在构建脚本指导中找到更多信息..

[package]
# ...
build = "build.rs"

The links field (optional)

links 字段 (可选)

此字段指定,要链接到的本机库名,更多信息可以在构建脚本指南的links部分.

[package]
# ...
links = "foo"
build = "build.rs"

The documentation field (optional)

documentation 字段 (可选)

此字段指定托管箱(crate)文档的网站的 URL。如果清单文件中没有指定 URL,crates.io自动将你的箱子连接到相应的箱子的docs.rs页.

来自特定主机的文档链接被列入黑名单。如果已知主机不承载文档,并且可能具有恶意意图,例如广告跟踪网络,则主机被添加到黑名单中。下列主机的 URL 就被列入黑名单:

  • rust-ci.org

来自黑名单主机的文档 URL 将不会出现在 crates.io 上,并且可能被 docs.rs 链接替换。

The exclude and include fields (optional)

excludeinclude 字段 (可选)

出于打包和重建包的目的,您可以显式地指定一组globs模式,匹配项应被忽略或包含。如exclude字段标识了在发布包时,不包括的一组文件,以及检测何时重建包时,应该忽略的文件,而include就是显式指定一定包含的文件。

如果一个 VCS 被用于一个包,则exclude字段将被植入 VCS 的忽略设置(例如 Git 的.gitignore)。

[package]
# ...
exclude = ["build/**/*.o", "doc/**/*.md"]
[package]
# ...
include = ["src/**/*", "Cargo.toml"]

选项是相互排斥的: include设置覆盖exclude。 注意include必须是文件的详尽列表,否则可能不包括必要的源文件。

Migrating to gitignore-like pattern matching

转移成 类gitignore 模式匹配

这些配置的当前解释实现都基于 UNIX Globs,如glob。 若是我们想要 Cargo 的includeexclude尽可能配置为类似于gitignore。可看看这个gitignore规范,其也是基于 Globs 的,但是还有许多其他的特性,这些特性使模式编写更容易,控制也更多。因此,我们正在迁移这些配置规则的解释实现,以使用ignore,并认真对待gitignore文件的每一条行规则。见跟踪问题有关迁移的更多细节。

The publish field (optional)

publish 字段 (可选)

这个publish字段通过错误,防止将包(crate),发布到包注册中心(如crates.io)。

[package]
# ...
publish = false

The workspace field (optional)

workspace 字段 (可选)

这个workspace字段可用于配置此包将属于的工作区。如果没有指定,这将被推断为文件系统中第一个 Cargo.toml 的[workspace]

[package]
# ...
workspace = "path/to/workspace/root"

有关更多信息,请参见下面的工作区(workspace)表格的文档.

Package metadata

包 元信息

[package]部分会接受许多可选的元数据字段:

[package]
# ...

# 关于包的简短介绍. 这不会以任何格式呈现
# 到 crates.io (又名 这不是markdown).
description = "..."

# 这些URL指向有关包的更多信息 这些是
# 旨在成为相关数据的网页入口, 不一定兼容
# VCS工具(类似的)等.
documentation = "..."
homepage = "..."
repository = "..."

# 这指向包根目录下的文件 (与 `Cargo.toml` 相对的).
# 该文件的内容会存储,并在注册表中编入索引。
# crates.io 将渲染此文件,并将结果放在包的页面上.
readme = "..."

# 这是一个,最多五个描述此箱的关键字的列表. 关键词
# 可以在 crates.io 上搜索, 和你可以选择任何单词
# 帮助别人找到这个箱子。
keywords = ["...", "..."]

# 这是此箱子最适合的(最多五个)类别的列表.
# 类别是 crates.io/category_slugs 上可用的固定列表, 和
# 他们必须完全匹配.
categories = ["...", "..."]

# 这是此包的SPDX 2.1许可证表达式.  目前
# crates.io将根据白名单的已知许可证和SPDX许可证列表2.4中的异常标识符,
# 验证提供的许可证。目前不支持括号。
#
# 使用AND和OR的许可证表达式
# 运算符以获得更明确的语义。
license = "..."

# 如果程序包使用非标准许可证, 则可以指定此 key
# 代替上述 key 和 必须指向相对于此清单的文件
# (类似于 readme key).
license-file = "..."

# 要在crates.io上显示的徽章规范,的可选项。
#
#  - 与当前可用的构建状态有关的徽章是
#   Appveyor, CircleCI, GitLab, 和 TravisCI.
# - 与代码测试覆盖有关的可用徽章是 Codecov 和
#   Coveralls.
# - 还有基于 isitmaintained.com的维护相关徽章
#   其中说明了问题解决时间,未决问题的百分比和未来
#   维护意图。
#
# 若要求一个`repository` key, 就表示一个`user/repo` 格式的存储库
[badges]

# Appveyor: `repository` 是必须的. `branch` 是可选的; 默认为 `master`
# `service` 是可选的; 有效值是 `github` (默认), `bitbucket`, 和
# `gitlab`; `id` 是可选的; 如果你想改用,可以指定appveyor 项目ID.
# `project_name` 是可选的; 使用在 repository
# 名称 与 appveyor 项目名称 不同的情况.
appveyor = { repository = "...", branch = "master", service = "github" }

# Circle CI: `repository` 是必须的. `branch` 是可选的; 默认为 `master`
circle-ci = { repository = "...", branch = "master" }

# GitLab: `repository` 是必须的. `branch` 是可选的; 默认为 `master`
gitlab = { repository = "...", branch = "master" }

# Travis CI: `repository`为 "<user>/<project>"格式 是必须的.
# `branch` 是可选的; 默认为 `master`
travis-ci = { repository = "...", branch = "master" }

# Codecov: `repository` 是必须的. `branch` 是可选的; 默认为 `master`
# `service` 是可选的; 有效值是 `github` (默认), `bitbucket`, 和
# `gitlab`.
codecov = { repository = "...", branch = "master", service = "github" }

# Coveralls: `repository` 是必须的. `branch` 是可选的; 默认为 `master`
# `service` 是可选的; 有效值是 `github` (默认) 和 `bitbucket`.
coveralls = { repository = "...", branch = "master", service = "github" }

# 是否保持解决时间: `repository` 是必须的.
is-it-maintained-issue-resolution = { repository = "..." }

# 它是否保持未解决问题的百分比: `repository` 是必须的.
is-it-maintained-open-issues = { repository = "..." }

# Maintenance: `status` 是必须的. 可用的选项是 `actively-developed`,
# `passively-maintained`, `as-is`, `experimental`, `looking-for-maintainer`,
# `deprecated`, 和 默认为 `none`, 不会在 crates.io 显示徽章.
maintenance = { status = "..." }

这个crates.io注册中心将呈现描述、显示许可证、链接到三个 URL 并根据关键字进行分类。这些字段为注册表的用户提供有用的信息,并且还影响箱子的搜索排名。在发布箱的’展示栏’,省略任何东西都是非常令人沮丧的。

SPDX 2.1 许可证表达式被记录在案在这里。 许可证列表的当前版本可用的,在这里,版本 2.4 是可用的,在这里.

The metadata table (optional)

metadata 表格 (可选)

默认情况下,Cargo 将对Cargo.toml不使用的字段发出警告,协助检测错别字等。就像这个package.metadata表格,但是,完全不写了的话, Cargo 将不会被警告。这个表格可在Cargo.toml,用于将包配置存储好。 例如:

[package]
name = "..."
# ...

# 当要生成一个 Android APK,这个元信息会被使用, 例如.
[package.metadata.android]
package-name = "my-awesome-android-app"
assets = "path/to/static"

Dependency sections

依赖 部分

指定依赖-那页有关[dependencies][dev-dependencies][build-dependencies]和特定目标的[target.*.dependencies]部分的信息。

The [profile.*] sections

[profile.*] 部分

Cargo 支持了,可通过顶层 配置文件(profile) 调用 rustc 的自定义配置。任何清单都可以声明一个配置文件,但是实际上只读取顶级包的配置文件。所有依赖项的配置文件都将被重写,这样做是为了让顶级包能够控制,其依赖项如何编译的。

目前有四个受支持的配置文件名称,它们都具有相同的配置。下面列出了可用的配置,以及每个配置文件的默认设置.

# 此为 开发配置文件, 给 `cargo build` 所使用.
[profile.dev]
opt-level = 0      # 控制编译器构建的`--opt-level`。
                   # 0-1适合调试。 2是良好优化的。最大为 3。
                   # 's' 企图优化大小, 'z' 则 进一步优化大小.
debug = true       # (u32 or bool) 包括调试信息(调试符号).
                   # 相当于 `-C debuginfo=2` 编译器 标志.
rpath = false      # 控制 编译器 是否应该设置加载器路径.
                   # 若为 true, 传递 `-C rpath` 标志 给 编译器.
lto = false        # 链接时间优化通常会减少二进制文件和静态库的大小
                   # 但会增加编译时间.
                   # 若是 true, 传递 `-C lto` 标志 给 编译器, 和 若是一个
                   # 字符串值 像 'thin' ,那会传递 `-C lto=thin`
                   # 给 编译器
debug-assertions = true # 控制是否启用调试断言
                   # (e.g. debug_assert!() 和 算术溢出检查)
codegen-units = 16 # if > 1 并行代码生成,以改善
                   # 编译时间, 但阻止了些优化.
                   # 传递 `-C codegen-units`.
panic = 'unwind'   # 恐慌策略 (`-C panic=...`), 也可以是 'abort'
incremental = true # 是否启用增量编译
overflow-checks = true # 使用溢出检查进行整数运算。
                   # 传递 `-C overflow-checks=...`标志 给 compiler.

# 发布(release)的配置文件, 用于 `cargo build --release` (和 依赖项的
# `cargo test --release`,  包括本地 library 或 binary).
[profile.release]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 16
panic = 'unwind'
incremental = false
overflow-checks = false

# 测试的配置文件, 用于 `cargo test` (对于 `cargo test --release`,可看
# `release` 和 `bench` 配置文件).
[profile.test]
opt-level = 0
debug = 2
rpath = false
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

# 基准的配置文件, 用于`cargo bench` (和 要测试的目标 和
# 单元测试的 `cargo test --release`).
[profile.bench]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 16
panic = 'unwind'
incremental = false
overflow-checks = false

The [features] section

[features] 部分

Cargo 支持特性,允许表达:

  • 条件编译选项(通过cfg属性);
  • 可选的依赖项,增强了包,但不是必需的;还有
  • 可选依赖项的簇,如postgres,其中就包括postgrespostgres-macros包,以及可能的其他包(如开发时的模拟库、调试工具等)。

包的特性也可以是可选的依赖项,也可以是一组其他特性。指定特性的格式是:

[package]
name = "awesome"

[features]
# 默认的可选包集。大多数人都想使用这些
# 包, 但它们是严格可选的。请注意,`session`不是包
# 而是此清单中列出的另一个功能。
default = ["jquery", "uglifier", "session"]

# 没有依赖关系的特性,主要用于条件编译,
# 像 `#[cfg(feature = "go-faster")]`.
go-faster = []

# `secure-password` 特性 需要 bcrypt 包. 这种别名
将允许人们以更高级别的方式讨论该 特性 和 允许
# 此软件包将在未来为该特性添加更多要求.
secure-password = ["bcrypt"]

# 特性可用于重新导出其他包的特性. `awesome`包的 `session`
# 特性将确保 cookie/session 也是可用的
session = ["cookie/session"]

[dependencies]
# 这些包是强制性的,是该软件包发行版的核心。
cookie = "1.2.0"
oauth = "1.1.0"
route-recognizer = "=2.1.0"

# 所以可选依赖项的列表, 其中一些是上面的
# `features`. 它们可以通过应用程序选择加入。
jquery = { version = "1.0.2", optional = true }
uglifier = { version = "1.5.3", optional = true }
bcrypt = { version = "*", optional = true }
civet = { version = "*", optional = true }

使用awesome包:

[dependencies.awesome]
version = "1.3.5"
default-features = false # 不包括默认功能,和可选,
                         # 任君选 个性化特性
features = ["secure-password", "civet"]

Rules

规则

特性的使用遵循一些规则:

  • 特性名称不能与清单中的其他包名称冲突。这是因为他们被选择加入features = [...],而它只有一个命名空间。
  • 除此default特性之外,所有的特性都是可选的。若要退出默认功能,请使用default-features = false,任君选择个人特性.
  • 特性群组不允许周期性地相互依赖.
  • 开发 依赖项不能是可选的.
  • 特性群组只能引用可选的依赖项.
  • 当选择一个特性时,Cargo 将调用具有--cfg feature="${feature_name}"rustc。如果包含一个特性群组,那么它将包括所有单独的特性。这可以通过#[cfg(feature = "foo")]在代码中进行测试..

主要注意的是,显露的特性,实际上不激活任何可选的依赖项。这就允许包在不需要新的依赖项的情况下,于内部启用/禁用特性。

Usage in end products

生产终点的用法

该特性的一个主要用例是在最终产品中,指定可选特性。例如,Servo 包可能希望包含可选特性,人们可以在构建时,启用或禁用它。

在这种情况下,Servo 将在Cargo.toml描述特性,且用命令行标志来启用这些特性:

$ cargo build --release --features "shumway pdf"

可以使用--no-default-features,排除默认特性。

Usage in packages

包(库)的用法

在大多数情况下,在库中可选依赖的概念,最好将其表示为顶级应用程序所依赖的单独包。

然而,像 Iron 或 Piston 这样的高级软件包会需要排布多个软件包以便于安装。当前的 Cargo 系统允许它们将一些强制依赖项,整合到一个包中,以便于安装。

在某些情况下,包可能希望为可选依赖项,提供额外的管理:

  • 将多个低层可选依赖项,组合到一个单独的高级特性中;
  • 由包用户指定推荐(或建议)要包括的包;
  • 包括特性(类似secure-password在激励示例中),这只在可选的依赖项可用时才能工作,并且很难实现为单独的包(例如,设计一个与 OpenSSL 完全解耦的 IO 包可能过于困难,那这时,就可通过包含单独的包来选择相关特性)。

在几乎所有情况下,在设计牢固的高级包之外,使用这些特性都是反模式的。如果某个特性是可选的,那么它几乎可以肯定地表示为单独的包。

The [workspace] section

[workspace] 部分

包可以定义一个工作区,它是一组箱,所有箱将共享相同Cargo.lock和输出目录。这个[workspace]表格可以定义为:

[workspace]

# 可选字段,从路径依赖推断(如果不存在)。
# 此处必须给出,包含的其他非路径依赖。
# 特别是, 对于 一个虚拟清单,所有成员都要列出来。
members = ["path/to/member1", "path/to/member2", "path/to/member3/*"]

# 可选字段, 如果不存在则为空
exclude = ["path1", "path/to/dir2"]

工作区作为 Cargo 的RFC 1525一部分被添加到 Cargo 中,并具有许多属性:

  • 工作区可以包含多个箱,其中一个是根箱.
  • 这个根箱Cargo.toml包含[workspace]表格,但不要求必有其他配置.
  • 每当编译工作区中的任何箱时,输出被放置在工作区根。 即紧挨着根箱Cargo.toml.
  • 工作区中所有箱的那个锁定文件驻留在工作区根.
  • Cargo.toml[patch][replace][profile.*]部分,只认根箱的清单,而忽略成员箱的。

这个工作区的根箱,由其清单中存在的[workspace]指定,并负责定义整个工作区。所有驻留在工作区目录中的path依赖项都变成成员。您可以通过members字段将附加包添加到工作区中。请注意,显式列出的工作区成员,也在工作区中包含了它们的路径依赖项。有时候,一个包可能有很多工作区成员,并且都保持最新会很麻烦。

路径依赖也可以使用globs匹配多个路径。 最后,exclude字段 可以用于将工作路径中的路径列入黑名单。如果根本不希望某些路径依赖项存在于工作区中,那么这非常有用.

这个package.workspace清单字段(如上所述)用于成员箱中,以指向工作区的根箱。如果省略此字段,则推断它是文件系统(向上的父目录)中,清单包含[workspace]的第一个箱。

箱可以指定package.workspace或指定[workspace]。 也就是说,箱不能同时作为工作区中的根箱(包含[workspace]),和另一个工作区的成员箱(包含package.workspace)

大多数时间工作区都不需要处理。因cargo newcargo init将自动处理工作区配置。

Virtual Manifest

虚拟清单

在工作区清单中,如果package表格存在,则工作区根箱将被视为普通包和工作区。如果package表格不存在工作区清单中,那它被称为虚拟清单

Package selection

Package 部分

在工作区中,与包相关的 Cargo 命令,如cargo build,会应用-p / --package--all命令行参数选定的包。当未指定时,可选default-members配置被使用:

[workspace]
members = ["path/to/member1", "path/to/member2", "path/to/member3/*"]
default-members = ["path/to/member2", "path/to/member3/foo"]

default-members指定时,必会扩展到子集的members中.

若是default-members未指定,如果它是包,则默认为根清单,或者若是虚拟工作区,就为每个成员的清单(如同--all在命令行上).

The project layout

项目布局

如果包是可执行文件,则将主源文件命名为src/main.rs。 如果它是一个库,请命名主源文件src/lib.rs

Cargo 也将处理位于src/bin/*.rs任何文件作为可执行文件。如果可执行文件包含不止一个源文件,则可以使用src/bin目录下,又一个包含main.rs文件的目录,而该目录将被视为具有父目录名称的可执行文件。但是,一旦添加了[[bin]]部分见下文,Cargo 将不再自动建立src/bin/*.rs文件。 相反,你必须创建一个[[bin]]部分,给出你想要生成的每个文件。

您的包可以(可选地)包含命名为examplestestsbenches文件夹,Cargo 将分别将其视为包含示例、集成测试和基准。类似于bin目标,它们可以由单个文件或拥有main.rs文件的目录组成。

▾ src/           # 包含源文件的目录
  lib.rs         # 库和包的主要入口点
  main.rs        # 包生成可执行文件的主要入口点
  ▾ bin/         # (可选)包含其他可执行文件的目录
    *.rs
  ▾ */           # (可选)包含多文件可执行文件的目录
    main.rs
▾ examples/      # (可选)示例
  *.rs
  ▾ */           # (可选)包含多文件示例的目录
    main.rs
▾ tests/         # (可选)集成测试
  *.rs
  ▾ */           # (可选)包含多文件测试的目录
    main.rs
▾ benches/       # (可选)基准
  *.rs
  ▾ */           # (可选)包含多文件基准的目录
    main.rs

为了在创建文件和文件夹之后,为包构造代码,应该记住使用 Rust 的模块系统,您可以在这本找到。

(译):中文

Examples

示例

位于examples下方的文件,是库提供的功能示例用法。编译时,它们被放置在target/examples目录。

它们可以编译为可执行文件(用main()函数)或,库。和可通过使用extern crate <library-name>导入库。 当您运行测试以保护它们免遭篡改时,它们会被编译。

可以使用命令cargo run --example <example-name>运行单个可执行示例.

指定crate-type将示例编译为库(有关箱类型的附加信息可在Rust 参考找到):

[[example]]
name = "foo"
crate-type = ["staticlib"]

可以使用命令cargo build --example <example-name>构建单个库实例.

Tests

测试

当你运行cargo test,Cargo 会:

  • 编译并运行库的单元测试,这些测试位于lib.rs(当然,任何标记为#[cfg(test)]部分将考虑为同个阶段);
  • 编译并运行嵌入到文档区块内部的库的文档测试;
  • 编译并运行您库的集成测试
  • 编译你库的例子.

Integration tests

集成测试

tests/*.rs的每个文件是一个集成测试。当你运行cargo test,Cargo 将编译每个文件作为一个单独的箱子。箱可以通过使用extern crate <library-name>链接(导入)您的库,就像其他导入项一样。

Cargo 不会自动编译tests子目录内的文件,但是,集成测试可以像往常一样从这些目录导入模块。例如,如果希望多个集成测试共享一些代码,可以将共享代码放入tests/common/mod.rs,然后为每个测试文件添加mod common;

Configuring a target

配置为一个目标

所有的[[bin]][lib][[bench]][[test]][[example]]部分都支持类似的配置,用于指定应该如何构建目标。双括号[[bin]]部分,是TOML格式的数组。这意味着你可以在您的箱中写多个[[bin]],这样就会生成几个可执行文件。

下面的例子使用[lib],但它也适用于所有其他部分。除非另有说明,下面所有列出的值都是对应选项的默认值

[package]
# ...

[lib]
# 生成目标与库的名称. 本该默认是
# 包名, 替换所有破折号
# 为 下划线. (Rust `extern crate` 声明会参考该名;
# 因此,该值必须是可用的有效Rust标识符.)
name = "foo"

# 该字段,指向 crate 的入口(位置), 路径相对于 `Cargo.toml`.
path = "src/lib.rs"

# 一个给目标启用单元测试 的 标志. 会被 `cargo test`使用.
test = true

# 一个给目标启用文档测试 的 标志. 只与库相关
# , 不会影响其他部分。会被
# `cargo test`使用.
doctest = true

# 一个给目标启用基准 的 标志. 会被 `cargo bench`使用.
bench = true

# 一个给目标启用文档 的 标志. 会被 `cargo doc`使用.
doc = true

# 若该目标为 编译器扩展, 那要把该字段设为 true
# ,以让 Cargo 正确编译和,可用于所有依赖项.
plugin = false

# 若该目标为 "macros 1.1" 程序宏, 那要把该字段设为 true
proc-macro = false

# 若设为 false, `cargo test` 会为 rustc 省略 `--test` 标志, 这
# 阻止它生成测试工具 这在二进制存在,
# 构建管理测试运行器本身的情况下,有用.
harness = true

# 若设置了,那 目标会使用一个与`[package]`配置不同的版本
# , 也许是,编译一个库
2018年版本或,编译单元测试的2015年版本. 默认情况下
# 所有目标都使用`[package]`中指定的版本进行编译。
edition = '2015'

这个[package]还包括可选的autobins,autoexamples,autotestsautobenches,来明确 进入/退出 自动发现特定的目标种类。

The required-features field (optional)

required-features 字段 (可选)

这个required-features字段指定目标需要构建的特性。如果未选择任何所需的特性,则将跳过目标。这只与[[bin]][[bench]][[test]][[example]]部分有影响,它没有影响[lib]

[features]
# ...
postgres = []
sqlite = []
tools = []

[[bin]]
# ...
required-features = ["postgres", "tools"]

Building dynamic or static libraries

构建 动态 或 静态 库

如果您的包生成一个库,则可以通过在Cargo.toml显式地指明构建的库类型:

# ...

[lib]
name = "..."
crate-type = ["dylib"] # 也能是 `staticlib`

可用的选项是dylibrlibstaticlibcdylibproc-macro。 您应该只在包中使用一次此选项。Cargo 总是根据(包括的)包的要求来编译包(依赖项)。

您可以阅读Rust 参考手册中更多关于不同的箱类型

The [patch] Section

[patch] 部分

这部分可以用来重写其他副本的依赖项。语法类似于[dependencies]部分:

[patch.crates-io]
foo = { git = 'https://github.com/example/foo' }
bar = { path = 'my/local/bar' }

[dependencies.baz]
git = 'https://github.com/example/baz'

[patch.'https://github.com/example/baz']
baz = { git = 'https://github.com/example/patched-baz', branch = 'my-branch' }

这个[patch]表格由,类似依赖表格的子表组成。[patch]后的每个字段是正在修补的源 URL,或者crates-io(如果你正在修改HTTPS://CRATESIO注册表)。在上面的例子中,crates-io可以用 Git URL 替换,例如https://github.com/rust-lang-nursery/log;第二个示例中的[patch]部分使用此来指定一个名为baz的源。

这些表格中的每个项都是一个正常的依赖关系规范,与[dependencies]清单的部分一样。[patch]部分中列出的依赖项,被解析并用于在指定的 URL 上对源进行补丁。上面的清单片段补丁crates-io源(例如 crates.io 本身)的foo箱和bar箱。它也用一个来自其他地方的my-branch分支修补了https://github.com/example/baz源。

可以用不存在的箱版本来修补源,也可以用已经存在的箱版本来修补源。如果用源中已经存在的箱版本对源进行修补,则会替换源的原始箱。

有关重写依赖关系的更多信息,可阅读本文档的重写依赖项章节和对于这一特性的RFC 1969技术规范说明。

The [replace] Section

[replace] 部分

这部分可以用来重写其他副本的依赖项。语法类似于[dependencies]部分:

[replace]
"foo:0.1.0" = { git = 'https://github.com/example/foo' }
"bar:1.0.2" = { path = 'my/local/bar' }

[replace]表格的每个字段都是包标识规范,它任意选择依赖图中的节点来重写。每个字段值与`[dependencies]指定依赖关系的语法是一样,除了不能指定特性。注意,当覆盖一个箱时,覆盖它的副本必须具有相同的名称和版本,但它可以来自不同的源(例如,git 或本地路径).

有关重写依赖关系的更多信息,可阅读本文档的重写依赖项章节。

配置

本文档将解释 Cargo 的配置系统如何工作,以及可用的字段或配置。有关通过其清单,来配置程序包的信息,请参阅清单格式.

Hierarchical structure

分层策略

Cargo 允许特定包,具有本地配置以及全局配置,就像 git 一样。Cargo 将其扩展为分层策略。例如,如果在/projects/foo/bar/baz调用 Cargo,然后将按以下顺序,探测和统一配置文件:

  • /projects/foo/bar/baz/.cargo/config
  • /projects/foo/bar/.cargo/config
  • /projects/foo/.cargo/config
  • /projects/.cargo/config
  • /.cargo/config
  • $HOME/.cargo/config

使用此结构,您可以为每个包指定配置,甚至可以将其检入版本控制。您还可以在主目录中,使用指定默认值的配置文件。

Configuration format

配置格式

所有配置目前都在TOML 格式(与 Cargo.toml 清单一样),在字段(表格)内部使用简单的键值对,它们都被合并在一起。

Configuration keys

配置字段

以下所有字段都是可选的,除非另有说明,否则它们的默认值将作为其值。

指定工具的键值可以给出,一个‘绝对路径,相对路径或无路径工具名称’。给定的绝对路径和无路径工具名称直接使用。相对路径,则解析相对于.cargo目录的父目录,配置文件就居住在里面。

# 路径数组,指向本地将要覆盖依赖项的存储库.
# 更多信息,请看 指定 依赖项 指南.
paths = ["/path/to/override"]

[cargo-new]
# 这是你 放 name/email 的地方, 在一个新Cargo.toml中的
#`authors` 表格就会生成。若不存在, 那 `git` 会去调查, 若还是不
# 存在,那 `$USER` 和 `$EMAIL` 会被使用.
name = "..."
email = "..."

# 默认来说, `cargo new` 会初始化一个新的  Git repository. 该字段若设为 `hg` ,就是新建一个 Mercurial repository, 或 `none` 禁用此行为.
vcs = "none"

# 接下来的 部分, $triple 是 一些有效目标 triple的引用, 而不是一个字面量"$triple"的意思, 这个引用是无论何时都能应用编译的。
# 'cfg(...)' 是参考 类Rust `#[cfg]` 语法 (条件语句)
[target.$triple]
#  linker 可传递参数给 rustc (通过 `-C linker=`) 当 `$triple`
# 要被编译. 默认不传递东东
linker = ".."
# 一样,但这是传递给 rustc 关于 库压缩的参数 ,通过 `-C ar=`.
ar = ".."
# 若 提供了 一个 runner , 编译`$triple`目标 会通过 执行 runner 执行文件来完成, 它会将真正的目标 作为第一参数.
# 这可运行 `cargo run`, `cargo test` 和 `cargo bench` 命令.
# 默认,编译目标是 直接执行的.
runner = ".."
# 自定义 全编译器,目标为 $triple
# 这些值会覆盖 build.rustflags
rustflags = ["..", ".."]

[target.'cfg(...)']
# 与 $triple 配置类似, 但使用的是 `cfg` 语法.
# 若有几个 `cfg` 和 $triple 目标作为备用, 那 rustflags
# 会被串联起来. 该 `cfg` 语法仅能应用到 rustflags, 而不能是
# linker.
rustflags = ["..", ".."]
# 与 $triple 配置类似 , 但使用的是 `cfg` 语法.
# 若 一个或多个 `cfg`s, 和一个 a $triple 目标作为备用,那 该$triple将会被使用
# 若有几个 `cfg` 和 备用的, 那构建会 error
runner = ".."

# 关联到 注册表 的配置字段
[registry]
index = "..."   # 注册表索引的URL(默认为中央存储库)
token = "..."   # 访问令牌(在中央回购网站上找到)
default = "..." # 要使用的默认备用注册表(可以使用--registry覆盖)

[http]
proxy = "host:port" # 用于HTTP请求的HTTP代理(默认为none)
                    # libcurl格式,例如“socks5h://host:port”
timeout = 30        # 每个HTTP请求的超时,以秒为单位
cainfo = "cert.pem" # 证书颁发机构(CA)包的路径(可选)
check-revoke = true # 指示是否检查SSL证书是否已废除
low-speed-limit = 5 # 限速 字节/秒(10 = 默认值,0 = 禁用)
multiplexing = true # 是否在可能的情况下使用 HTTP/2多路复用

# 此设置可用于帮助调试Cargo所发生的HTTP请求
# 当设置为“true”时,将填充Cargo的正常调试日志记录
# 关于HTTP的信息,您可以使用
# `RUST_LOG=cargo::ops::registry=debug`提取(和`trace`可能会打印更多)。
#
# 在将这些日志发布到其他地方时要小心,因可能存在这样的
# header中,有一个你不想泄露的身份验证令牌的情况!务必
# 在发布之前简要查看日志。
debug = false

[build]
jobs = 1                  # 并行作业数,默认为CPU数
rustc = "rustc"           # rust编译器工具
rustdoc = "rustdoc"       # doc生成器工具
target = "triple"         # build为目标 triple(被`cargo install`忽略)
target-dir = "target"     # 放置所有生成的工件的路径
rustflags = ["..", ".."]  # 自定义 传递给所有编译器调用 的参数
incremental = true        # 是否启用增量编译
dep-info-basedir = ".."   # depfiles中,目标的基本目录的完整路径

[term]
verbose = false        # Cargo否提供详细输出
color = 'auto'         # Cargo否着色输出

# 网络配置
[net]
retry = 2 # 失败 自动重试 次数
git-fetch-with-cli = false  # 若为 `true` 我们会使用 `git`命令行去 fetch git repos

# 别名 cargo 命令. 前 3 个aliases 是内置的. 如果你的命令 要求 整行命令,请使用 list 格式.
[alias]
b = "build"
t = "test"
r = "run"
rr = "run --release"
space_example = ["run", "--release", "--", "\"command list\""]

Environment variables

环境变量

除了上面的 TOML 语法之外,还可以通过环境变量配置 Cargo。对于上方的foo.bar表格的每个配置字段,也可以用环境变量CARGO_FOO_BAR来定义值。比如说build.jobs字段,也可以通过CARGO_BUILD_JOBS定义。

环境变量将优先于 TOML 配置,并且当前仅支持由环境变量定义的整数,布尔和字符串字段,这意味着来源更换,能由表格表示,却不能通过环境变量配置。

除上述系统外,Cargo 还认可其他一些特定的环境变量.

Environment Variables

Cargo 设置,并读取许多环境变量,代码可以检测或覆盖这些环境变量。以下是 Cargo 与它们交互时,组织的变量列表:

Environment variables Cargo reads

Cargo 会读取的环境变量

您可以重写这些环境变量来更改 Cargo 在系统中的行为:

名(ENV)
CARGO_HOME Cargo 在本地缓存注册表索引和箱子的 git 版本。默认情况下,这些存储在$HOME/.cargo,但是这个变量重写了这个目录的位置。一旦箱被缓存,它就不会被清除命令删除。
CARGO_TARGET_DIR 相对于当前工作目录,放置所有生成的工件的位置.
RUSTC Cargo 不运行rustc,而执行指定的编译器。
RUSTC_WRAPPER Cargo 将执行这个指定的包装器,而不是简单地运行rustc。将 rustc 调用 作为其命令行参数传递,第一个参数是 rustc.
RUSTDOC Cargo 将执行此指定的rustdoc实例,而不是rustdoc.
RUSTDOCFLAGS 空格分隔的自定义标志列表,用来传递给 Cargo 执行的所有rustdoc调用 。与cargo rustdoc不同,这对于传递一个参数给 全部的 rustdoc实例是有用的。
RUSTFLAGS 自定义参数的空格分隔列表,用来传递给 Cargo 执行的所有编译器调用。与cargo rustc不同,这对于传递一个标志 全部的 编译实例是有用的。
CARGO_INCREMENTAL 如果设置为 1,则 Cargo 将强制在当前编译中启用增量编译,而当设置为 0,则强制禁用增量编译。如果这个 ENV 不存在,否则将使用 Cargo 默认值。
CARGO_CACHE_RUSTC_INFO 如果这个设置为 0,那么 Cargo 将不尝试缓存编译器版本信息.

注意,Cargo 也会在.cargo/config配置中读取环境变量,如那份文件

Environment variables Cargo sets for crates

Cargo 为 crates 设置的环境变量

Cargo 在编译时,会将这些环境变量暴露在箱子中。请注意,这也适用于测试二进制文件。要在 RIST 程序中获得这些变量中的任何一个变量的值,请执行以下操作:


# #![allow(unused_variables)]
#fn main() {
let version = env!("CARGO_PKG_VERSION");
#}

version现在将包含了CARGO_PKG_VERSION值。

CARGO 执行构建的二进制cargo路径.
CARGO_MANIFEST_DIR 包含包的清单的目录.
CARGO_PKG_VERSION 您的包的完整版本.
CARGO_PKG_VERSION_MAJOR 你的软件包的主要版本.
CARGO_PKG_VERSION_MINOR 您的包的次要版本.
CARGO_PKG_VERSION_PATCH 包的补丁版本.
CARGO_PKG_VERSION_PRE 包的预发布版本.
CARGO_PKG_AUTHORS 从程序包的清单中,冒号分隔出作者列表.
CARGO_PKG_NAME 你的包的名字.
CARGO_PKG_DESCRIPTION 从包的清单中描述.
CARGO_PKG_HOMEPAGE 从包的清单中的主页.
CARGO_PKG_REPOSITORY 从包的清单中存储库.
OUT_DIR 如果包具有构建脚本,则将其设置为,构建脚本应该在其中放置其输出的文件夹。更多信息见下文.

Environment variables Cargo sets for build scripts

Cargo 为构建脚本设置的环境变量

当运行构建脚本时,Cargo 设置多个环境变量.因为编译生成脚本时还没有设置这些变量,所以上面的示例使用env!无法工作,而是在运行构建脚本时需要检索值:


# #![allow(unused_variables)]
#fn main() {
use std::env;
let out_dir = env::var("OUT_DIR").unwrap();
#}

out_dir现在将包含的价值OUT_DIR.

CARGO 执行构建的二进制cargo路径.
CARGO_MANIFEST_DIR 包含正在构建的包的清单的目录(包含构建脚本的包)。还要注意,这是生成脚本启动时,当前工作目录的值.
CARGO_MANIFEST_LINKS 清单links的值.
CARGO_FEATURE_<name> 对于正在构建的包的每个激活特性,此环境变量会让<name>功能名称存在,名称的-会转换成_.
CARGO_CFG_<cfg> 对正在构建包的每个配置选项,此环境变量将包含配置的值,其中<cfg>就是配置的名称,并将-翻译成_. 如果设置了布尔配置,则存在布尔配置,否则不存在。具有多个值的配置被连接到单个变量,该变量用分隔就好。
OUT_DIR 所有输出都应该放在这个文件夹。此文件夹位于正在构建的包的构建目录中,并且对于所讨论的包是唯一的。
TARGET 正在编译的目标三元组。该为这个三元组编译本机代码。关于目标三元组的更多信息在clang 自身文档中.
HOST Rust 编译器的主机三元组.
NUM_JOBS 指定为顶层并行的并行性。这可以传递一个-j参数到系统(像make). 注意,在解释这个环境变量时应该小心。出于历史目的,仍然提供此功能,但是例如,最新版本的 Cargo 不需要运行make -j,因为它会自动。Cargo 自行实现[JooServer],并且允许构建脚本继承这些信息,因此与 GNU 兼容的程序将使作业服务器已经具有适当配置的并行性。
OPT_LEVELDEBUG 为了分析,当前正在构建的相应变量值.
PROFILE 发布版本用release,而debug用于其他构建.
DEP_<name>_<key> 有关此组环境变量的更多信息,请参阅links.
RUSTCRUSTDOC Cargo 已经决定使用的编译器和文档生成器,传递给构建脚本,以便构建脚本也可以使用它.
RUSTC_LINKER 如果指定了,Cargo 为了当前目标,决定使用的链接器(二进制文件)的路径。这个链接器可以通过编辑.cargo/config更改,欲了解更多信息,请参阅有关文档Cargo 配置

Environment variables Cargo sets for 3rd party subcommands

Cargo 为 第三方子命令设置的环境变量

Cargo 将这个环境变量公开给第三方子命令(即,名为cargo-foobar放置在$PATH):

  • CARGO - 执行构建的二进制cargo路径。

Build Scripts

构建脚本

一些包需要编译第三方非 Rust 代码,例如 C 库。其他的包需要链接到 C 库,当然这些库既可以位于系统上,也可以从源代码构建。其他人或许还需要功能工具,比如构建之前的代码生成(想想解析生成器)。

Cargo 并不打算替换为这些能良好优化任务的其他工具,但是它与build配置选项.

[package]
# ...
build = "build.rs"

指定的build命令应执行的 Rust 文件(相对于包根),将在包编译其他内容之前,被编译和调用,从而具备 Rust 代码所依赖的构建或生成的工件。默认情况下 Cargo 在包根文件中寻找"build.rs"(即使您没有给build字段指定值)使用build = "custom_build_name.rs"指定自定义生成名,或build = false禁用对构建脚本的自动检测。

Build 命令的一些用例是:

  • 构建一个捆绑的 C 库.
  • 在主机系统上找到 C 库.
  • 从规范中生成 Rust 模块.
  • 为箱,执行所需的某平台特定配置.

下面将详细介绍每一个用例,以给出构建命令如何工作的示例.

Inputs to the Build Script

输入到构建脚本

当运行构建脚本时,存在许多构建脚本用到的输入,所有输入都以环境变量传入。

除了环境变量之外,构建脚本的当前目录是构建脚本包的源目录.

Outputs of the Build Script

构建脚本的输出

由构建脚本打印到 stdout 的所有行都被写入像target/debug/build/<pkg>/output这样的文件(精确的位置可能取决于你的配置)。如果您希望直接在终端中看到这样的输出,那么使用非常详细-vv标志。注意,如果既不修改构建脚本也不修改包源文件,下一次的-vv调用将打印重复输出到终端,因为没有执行新的构建。可执行cargo clean,如果希望确保输出始终显示在终端上,但要在每次 Cargo 调用之前执行。任何一行以cargo:开始的,直接由 Cargo 解释。行必须是cargo:key=value形式,就像下面的例子:

# specially recognized by Cargo
cargo:rustc-link-lib=static=foo
cargo:rustc-link-search=native=/path/to/foo
cargo:rustc-cfg=foo
cargo:rustc-env=FOO=bar
# arbitrary user-defined metadata
cargo:root=/path/to/foo
cargo:libdir=/path/to/foo/lib
cargo:include=/path/to/foo/include

另一方面,打印到 stderr 的行被写入像target/debug/build/<pkg>/stderr这样的文件,但不被 Cargo 解释。

Cargo 识别一些特殊的 key,其中一些影响箱的构造:

  • rustc-link-lib=[KIND=]NAME说明了,指定值是库名,且会作为-l标志传递给编译器。KIND可选为staticdylib(默认值),或framework的其中之一,用rustc --help见更多细节。

  • rustc-link-search=[KIND=]PATH说明了,指定值是库搜索路径,且会作为-L标志传递给编译器。KIND可选为dependencycratenativeframeworkall(默认值)的其中之一,使用rustc --help见更多细节.

  • rustc-flags=FLAGS是传递给编译器的一组标志,仅支持-l-L标志。

  • rustc-cfg=FEATURE说明了,指定的特性,且会作为--cfg标志传递给编译器。这通常对检测,执行各种特征的编译时间,是有用的。

  • rustc-env=VAR=VALUE说明了,指定的环境变量,且会被添加到编译器所在的环境中。然后,可以通过编译箱中的env!宏检索该值。这对于在箱的代码中嵌入额外的元数据很有用,比如 Git HEAD 的散列,或持续集成服务器的唯一标识符。

  • rerun-if-changed=PATH是文件或目录的路径,说明了如果构建脚本发生更改(由文件上最近修改的时间戳检测到),则应重新运行构建脚本。通常,如果箱根目录中的任何文件发生更改,则重新运行构建脚本,但这可用于将更改范围扩展到仅一小组文件。(如果这个路径指向一个目录,则不会遍历整个目录以进行更改——只对目录本身的时间戳进行更改(该时间戳对应于目录中的某些类型的更改,取决于平台),将触发重新构建。要请求重新运行整个目录中的任何更改,请递归地为该目录打印一行,为该目录内的所有内容打印另一行。)请注意,如果构建脚本本身(或其依赖项之一)更改,则无条件地重新构建和重新运行该脚本,因此,cargo:rerun-if-changed=build.rs几乎总是冗余(除非您想要忽略除了build.rs,所有其他文件的变化)

  • rerun-if-env-changed=VAR是环境变量的名称,说明了它指示如果环境变量的值发生变化,则应重新运行构建脚本。这基本上与rerun-if-changed是一样的,除了它与环境变量一起工作。注意,这里的环境变量用于全局环境变量,如CC这样的,对于 Cargo 所设的像TARGET,就不必使用它。还要注意,如果rerun-if-env-changed打印出来,然后 Cargo 将在,那些环境变量发生变化,或者打印出rerun-if-changed改变的文件的情况下,才重新运行构建脚本。

  • warning=MESSAGE是构建脚本运行完毕后,打印到主控制台的消息/警告只针对路径依赖项(即,您在本地工作的那些依赖项)显示,因此如, crates.io 的箱在默认情况下不会打印警告。

其他哪些元素都是用户定义的元数据,这些元数据传递给了依赖的。关于这个的更多信息可以在links部分查看.

Build Dependencies

构建依赖

构建脚本也可以依赖其他基于 Cargo 的箱。依赖关系通过清单的build-dependencies部分指定。

[build-dependencies]
foo = { git = "https://github.com/your-packages/foo" }

构建脚本可以访问dependenciesdev-dependencies部分列表中的依赖项(它们还没有建成!),除非明确声明,否则包本身也不能使用所有构建依赖项。

The links Manifest Key

links 清单 键

除了清单键build,Cargo 也支持一个,要链接到本地库的名称声明,那就是links清单键:

[package]
# ...
links = "foo"
build = "build.rs"

此清单说明了包会链接到本机库libfoo,并且它还具有定位和/或构建该本机库的构建脚本。Cargo 要求build如果有值,那links也要有值。

这个清单键的目的是,让 Cargo 了解包所具有的本地依赖项集合,并提供在包构建脚本之间,传递元数据的合适的系统.

首先,Cargo 要求一个包最多只有一个links值。换句话说,禁止两个包链接到同一个本机库。然而,这里也有约定位置的方式,用来缓解这个问题。

如上面在输出格式中提到的,每个构建脚本可以以键-值对的形式生成一组任意的元数据。此元数据传递给依赖的包。例如,如果libbar依赖libfoo,当libfoo生成key=value作为其元数据的一部分,那libbar的构建脚本会有DEP_FOO_KEY=value环境变量。

注意,元数据只传递给直接依赖项,而不是把依赖项串起来。此元数据传递的动机,会在接下来,关联到系统库案例研究中概述。

Overriding Build Scripts

覆盖 构建脚本

如果一个清单包含links关键字,那 Cargo 支持重写用自定义库指定的构建脚本。此功能的目的是防止完全运行有问题的构建脚本,而是提前提供下元数据。

要覆盖构建脚本,请将下列配置放在任何可接受的 Cargo 的配置位置中。

[target.x86_64-unknown-linux-gnu.foo]
rustc-link-search = ["/path/to/foo"]
rustc-link-lib = ["foo"]
root = "/path/to/foo"
key = "value"

本节说明目标x86_64-unknown-linux-gnu,命名为foo的库,具有指定的元数据。此元数据与构建脚本时生成的元数据相同,提供了许多键/值对,其中rustc-flagsrustc-link-searchrustc-link-lib有点特殊.

使用此配置,如果一个包声明它链接到此foo,那构建脚本将编译或运行,而会使用指定的元数据。

Case study: Code generation

案例学习: 代码生成

由于各种原因,一些 Cargo 包在编译之前需要生成代码。这里我们将介绍一个简单的示例,该示例把,’生成库调用’作为构建脚本的一部分.

首先,让我们看一下这个包的目录结构:

.
├── Cargo.toml
├── build.rs
└── src
    └── main.rs

1 directory, 3 files

在这里我们可以看到我们有一个build.rs构建脚本,和二进制文件main.rs。 接下来,让我们看一下清单:

# Cargo.toml

[package]
name = "hello-from-generated-code"
version = "0.1.0"
authors = ["you@example.com"]
build = "build.rs"

在这里,我们可以看到,我们已经指定了一个构建脚本build.rs,我们将使用它来生成一些代码。让我们看看构建脚本里面有什么:

// build.rs

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("hello.rs");
    let mut f = File::create(&dest_path).unwrap();

    f.write_all(b"
        pub fn message() -> &'static str {
            \"Hello, World!\"
        }
    ").unwrap();
}

这里有两点值得注意的地方:

  • 脚本使用OUT_DIR环境变量,以知道输出文件到哪里。它可以使用进程的当前工作目录,来查找输入文件应该到哪里,但是在这种情况下,我们是没有任何输入文件的。
  • 一般来说,构建脚本不应该修改OUT_DIR目录外的任何文件。 乍看之下,似乎不错,但当您使用这种箱子作为依赖项时,它确会带来问题,因为.cargo/registry源中的隐性的常量应该是不变的。cargo在打包时不会允许这样的脚本。
  • 这个脚本相对简单,只是写出一个小生成的文件。可以想象,其他更奇特的操作也可能发生,例如从 C 头文件或其他定义的语言生成 Rust 模块。

接下来,我们来看看库本身:

// src/main.rs

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

fn main() {
    println!("{}", message());
}

这就是真正的魔法发生的地方。该库正在使用 rustc 定义的 include!宏,它又结合concat!env!宏去包含生成文件(hello.rs),从而进入箱的编译。

使用此处所示的结构,箱可以包括(include)构建脚本在内的,任何数量的生成文件。

Case study: Building some native code

案例学习: 构建一些原生代码

有时需要建立一些本地 C 或 C++代码作为包的一部分。这是在用构建脚本到 Rust 箱本身之前,构建本机库的另一个极好用例。作为一个例子,我们将创建一个 Rust 库,它调用 C 来打印”Hello,World!”.

和上面一样,让我们先来看看包的布局:

.
├── Cargo.toml
├── build.rs
└── src
    ├── hello.c
    └── main.rs

1 directory, 4 files

很像之前的吧! 下一步,清单如下:

# Cargo.toml

[package]
name = "hello-world-from-c"
version = "0.1.0"
authors = ["you@example.com"]
build = "build.rs"

现在,我们不打算使用任何-构建的依赖项,所以现在让我们看一下构建脚本:

// build.rs

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

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

    // 请注意,这种方法存在许多缺点,
    // 下个代码展示,会详细介绍如何提高这些命令的可移植性。
    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");
}

此构建脚本首先将 C 文件编译为对象文件(通过调用gcc),然后将这个对象文件转换为静态库(通过调用ar),最后一步是反馈给 Cargo ,以表示我们的输出在out_dir和通过-l static=hello标志,编译器应该将箱静态链接到libhello.a

请注意,这种硬编码方法有许多缺点:

  • 这个gcc命令本身不是跨平台可移植的。如,在 Windows 平台不太可能gcc,甚至不是所有 UNIX 平台都可能有gcc。 这个ar命令也处于类似的情况。
  • 这些命令不考虑跨编译。如果我们为 Android 这样的平台进行跨编译,gcc就不太可能产生一个可执行的 ARM.

但不要害怕,这里build-dependencies就帮到你! Cargo 生态系统有许多包,为了使此类任务更加容易、可移植和标准化。构建脚本可以写成:

// build.rs

// 依赖于外部维护的`cc`包,管理
// 调用C编译器。
extern crate cc;

fn main() {
    cc::Build::new()
        .file("src/hello.c")
        .compile("hello");
}

添加cc箱,这样将构建,依赖cc就好啦,将下面的添加到您的Cargo.toml:

[build-dependencies]
cc = "1.0"

这个cc抽象了 C 代码构建,主要用于脚本需求范围:

  • 它调用适当的编译器(Windows 的 MSVC,gcc对 MinGW ,cc对 UNIX 平台等等).
  • 通过向正在使用的编译器传递适当的标志,获取TARGET变量.
  • 其他环境变量,如OPT_LEVELDEBUG等等,都是自动处理的.
  • stdout 输出和OUT_DIR位置也由cc库控制.

在这里,我们可以开始看到,将尽可能多的功能移植到公共构建依赖项,而不是在所有构建脚本之间复制来复制去,的一些主要好处!

回到案例研究,让我们快速浏览一下src目录中的内容:

// src/hello.c

#include <stdio.h>

void hello() {
    printf("Hello, World!\n");
}
// src/main.rs

// 注意缺少`#[link]`属性。 我们选择,将责任委派给
// 构建脚本的链接,而不是硬编码
// 它在源文件中.
extern { fn hello(); }

fn main() {
    unsafe { hello(); }
}

然后,就好啦! 这就完成了使用构建脚本,从 Cargo 包构建一些 C 代码的示例。这也说明了为什么在许多情况下使,用构建依赖项非常重要,甚至更加简洁!

我们还看到了构建脚本使用箱,纯粹作为用于构建过程的依赖项,而不是在运行时,用作箱本身的依赖项的简要示例。

Case study: Linking to system libraries

案例学习: 链接到系统库

这里的最后一个案例研究,将研究 Cargo 库如何链接到系统库,以及构建脚本如何支持这个用例。

通常,Rust 箱希望链接到系统上经常提供的本地库,以绑定其功能,或者只是将其用作实现细节的一部分。想以不管平台的方式执行这个操作,而这却是一个相当微妙的问题,再次说明下,构建脚本的目的是尽可能多地分配这些(微妙)内容,以便让消费者尽可能容易地使用它.

作为一个例子,让我们来看一个Cargo 本身的依赖libgit2。这个 C 库其实有许多约束条件:

  • 它可选为依赖 Unix 上的 OpenSSL ,来实现 https 传输.
  • 它可选为依赖所有平台上的 libssh2 ,来实现 ssh 传输.
  • 默认情况下,它通常不安装在所有系统上.
  • 它可以从源代码使用cmake构建.

为了可视化这里发生的事情,让我们看一下,链接本机 C 库的相关 Cargo 包的清单。

[package]
name = "libgit2-sys"
version = "0.1.0"
authors = ["..."]
links = "git2"
build = "build.rs"

[dependencies]
libssh2-sys = { git = "https://github.com/alexcrichton/ssh2-rs" }

[target.'cfg(unix)'.dependencies]
openssl-sys = { git = "https://github.com/alexcrichton/openssl-sys" }

# ...

正如上面的清单所显示的,我们指定了一个build脚本,但值得注意的是,该示例具有links项,说明该箱(libgit2-sys)链接到了这个本地库git2

在这里,我们还看到,我们选择让 Rust 箱有一个无条件的,通过libssh2-sys箱依赖libssh2(ssh2-rs),以及(有条件的)特定于平 unix 台的openssl-sys依赖(其他平台现在被漠视)。这似乎有点违反在 Cargo 清单C 依赖 的明确性,但这实际上是这’地方’中使用 Cargo 的一种约定.

*-sys Packages

*-sys 包们

为了减轻对系统库的链接,crates.io 有一个包命名和功能的惯例。比如包名foo-sys,它应该提供两个主要功能:

  • 库箱应链接到本地库libfoo。 在源代码最后构建之前,这将经常探测当前的系统的libfoo
  • 库箱应提供在libfoo声明函数,但是绑定或高级抽象。

一套*-sys包,提供了一组用于连接到本地库的公共依赖项。通过这种’本机库相关’的包约定,可以获得许多好处:

  • foo-sys的公共依赖,会减轻上面所说的,关于一个包的links的每个值规则。
  • 一个公共依赖关系,更能发现libfoo本身的集中逻辑(或者从源代码构建它).
  • 这些依赖关系很容易被重写.

Building libgit2

构建 libgit2 吧

现在我们已经整理了 libgit2 的依赖,我们需要实际编写下构建脚本。我们这里不讨论特定的代码片段,而只研究libgit2-sys构建脚本的高层细节。这并不是建议所有包都遵循这个策略,而仅概述一个特定的策略。

构建脚本应该做的第一步是查询 libgit2 是否已经安装在主机系统上。要做到这一点,我们将利用现有的工具pkg-config(当它可用时)。我们也会使用build-dependencies部分重构成pkg-config相关的所有代码(或者有人已经这样做了!)。

如果pkg-config找不到 libgit2,或者如果pkg-config只是没有安装,下一步就要从捆绑源代码构建 libgit2 (捆绑源码作为libgit2-sys本身的一部分)。然而,在这样做时有一些细微差别,我们需要加以考虑:

  • libgit2 的构建系统,cmake需要能够找到 libgit2 可选依赖 libssh2 。而我们确信我们已经构建了它(因它是一个 Cargo 依赖项),我们只需要传递这个信息。为此,我们利用元数据格式,在构建脚本之间传递信息。在这个例子中,打印出的 libssh2 包信息是cargo:root=...,它来告诉我们 libssh2 安装在哪里,然后我们可以通过CMAKE_PREFIX_PATH环境变量让 cmkae 知道。

  • 我们需要处理下,编译 C 代码时的一些CFLAGS值(也要告诉cmake关于这个信息)。我们想传递的一些标志是 64 位的-m64,32 位的-m32,或-fPIC也适用于 64 位。

  • 最后,我们调用cmake将所有输出放入环境变量OUT_DIR目录,然后打印必要的元数据,以指导 rustc 如何链接到 libgit2。

这个构建脚本的大部分功能,很容易就重构为常见的依赖项,因此我们的构建脚本不像这个描述那样长烦! 实际上,通过构建依赖项,构建脚本应该非常简单。

发布到 crates.io

一旦你有一个你想与世界分享的 crate,就该把它发布到crates.io! 发布是指,上载特定版本的,以让crates.io进行托管.

发布箱(crate)子时,要小心,因为发布是常驻。永远不能覆盖同版本,并且无法删除代码。但是,可以发布的版本数量没有限制。

在你开始发布前

首先,你需要一个crates.io帐户,用来获取 API 令牌。为此,访问主页,并通过 GitHub 帐户登录(现在需要)。在此之后,访问你的帐号设定页面,并运行cargo login命令联通账号。

$ cargo login abcdefghijklmnopqrstuvwxyz012345

此命令将告诉 Cargo 关于您的 API 令牌,并将其存储在您的本地~/.cargo/credentials(以前是~/.cargo/config)。请注意,此令牌是一个秘密,不应与其他任何人分享。如果因任何原因泄漏,您应立即重新生成。

在你创建新 crate 前

请记住crates.io上箱子的名字,会采取先到先得的方式分配。一旦获得箱子名称,它就不能用于另一个箱子.

打包一个 crate

下一步是,将您的包装箱打包成可供crates.io上传的格式。为此,我们将使用cargo package子命令。这将把我们的整个包装箱全部打包成一个*.crate文件,其在target/package目录中。

$ cargo package

作为一个额外的功能,*.crate将独立于当前源树进行验证。在*.crate创建之后,会解压到target/package目录,然后从头开始构建,以确保构建成功的所有必要文件。可以使用--no-verify参数禁用此行为。

现在是时候看看*.crate文件了,为了确保您不会意外地打包 2GB 视频资源,或用于代码生成,集成测试或基准测试的大型数据文件。目前存在 10MB 的*.crate文件上传大小限制。所以,如果testsbenches目录及其依赖项大小,最多只达 几 MB,您仍可以将它们保存在包; 不然的话,最好排除它们。

在打包时,Cargo 会自动忽略版本控制系统的忽略文件,但是如果要指定要额外的忽略文件集,则可以使用清单中的exclude字段:

[package]
# ...
exclude = [
    "public/assets/*",
    "videos/*",
]

这个数组中每个元素接受的语法是rust-lang/glob。如果您宁愿使用白名单,而不是黑名单,Cargo 也支持include字段,如果设置,则会覆盖exclude字段:

[package]
# ...
include = [
    "**/*.rs",
    "Cargo.toml",
]

上传该 crate

现在我们已经有了*.crate文件准备好了,可以上传到crates.io,接着使用cargo publish命令就好。就是这样,你现在已经发布了你的第一个箱子!

$ cargo publish

如果你想跳过cargo package那一步,cargo publish如果找不到副本,子命令将自动打包本地包.

一定要看看您可以指定的元数据确保您的箱子更容易被发现!

为已存在的 crate,发布一个新版本

要发布新版本,请在Cargo.toml更改version为您指定的值。记住semver 规则。然后可选择运行cargo package,如果你想检查一下*.crate发布前的新版本文件,然后运行cargo publish上传新版本.

管理一个 基于 crates.io 的 crate

箱的管理主要通过命令行完成cargo工具,而不是crates.io网络界面。为此,有一些子命令来管理包.

cargo yank

在您发布时,实际上可能会因某种原因,而最终破坏的箱子版本(语法错误,忘记包含文件等)的情况。对于诸如此类的情况,Cargo 支持一个箱子版本的”yank”。

$ cargo yank --vers 1.0.1
$ cargo yank --vers 1.0.1 --undo

一个 yank 不是删除任何代码。例如,此功能不用于删除意外上传的机密。如果发生这种情况,您必须立即重置这些秘密.

一个 yank 版本的语义是,不为该版本创建新的依赖项,但所有现有的依赖项继续有效。crates.io其中一个主要目标是作为永久存档的箱子库,不会随着时间的推移而改变,而允许删除版本将违背这一目标。所以基本上,一个 yank 意味着所有包的Cargo.lock不会被破坏,在任何的未来,Cargo.lock生成的文件不会列出 yank 的版本。(成为了一个孤岛版本)

cargo owner

箱子通常由不止一个人开发,或者主要维护者可能会随着时间而改变!箱子的所有者是唯一允许发布新版本箱子的人,但是所有者可以指定其他所有者。

$ cargo owner --add my-buddy
$ cargo owner --remove my-buddy
$ cargo owner --add github:rust-lang:owners
$ cargo owner --remove github:rust-lang:owners

给这些命令的所有者 ID 必须是 GitHub 用户名或 GitHub 团队.

如果用--add了一个用户名,那该用户成为”命名”所有者,拥有该箱子的完全权利。除了能够发布或 yank 箱子的版本,他们还能够添加或删除所有者,包含任命他们的所有者。毋庸置疑,你不应该让那些你不完全信任的人,作命名所有者。要成为命名所有者,用户必须之前就已登录crates.io

如果用--add了一个团队,那该团队成为”团队”所有者,对箱的权利受到限制。虽然他们有权发布或 yank 箱子的版本,但他们能够添加或删除所有者。除了更方便管理所有者群体之外,团队还可以更安全地防止恶意所有者。

团队的目前的语法是github:org:team(见上面的例子)。要将团队添加为所有者,必须是该团队的成员。删除所有者的团队就没有此类限制.

GitHub 权限

团队成员资格,不是 GitHub 提供的简单公共访问权限,并且在使用它时可能会遇到以下消息:

您似乎无权从 GitHub 查询必要的属性,来完成此请求。您可能需要在crates.io重新进行身份验证,并申请阅读 GitHub 组织成员资格的权限。去https://crates.io/login看看

这基本上是一个全查询防御,当”你试图查询团队信息,而其中的五级成员访问控制,拒绝了它”。这并不夸张。GitHub 对团队访问控制的支持是企业级.

最可能的原因是您最后一次登录是在添加此功能之前。我们最初没有要求 GitHub 对用户进行身份验证时的权限,因为我们实际上并没有将用户的令牌用于登录以外的任何其他内容。但是,为了代表您能够查询团队成员资格,我们现在需要read:org范围权限

您完全可以拒绝我们这个范围,且在团队介绍之前所做的一切,都将继续有效。但是,您永远无法将团队添加为所有者,或者将团队作为团队所有者发布。如果您尝试这样做,您将收到上述错误。如果你试图发布一个你根本不拥有的箱子,但恰好有一个团队,你可能也会看到这个错误。

如果你改变主意,或者只是不确定是否crates.io有足够的许可,你可以随时去https://crates.io/login查看,crates.io会提示您,它没有获得它想要的所有范围许可。

查询 GitHub 的另一个障碍,是组织可能会主动拒绝第三方访问。要检查这一点,您可以访问:

https://github.com/organizations/:org/settings/oauth_application_policy

这里的:org是组织的名称(例如 rust-lang)。你可能会看到类似的东西:

Organization Access Control

你可以选择,从您组织的黑名单中,明确删除crates.io,或只需按”删除限制(Remove Restrictions)”按钮,允许所有第三方应用程序访问此数据。

或者,当crates.io请求了read:org范围,您可以明确进入白名单的crates.io,能查询组织的问题,通过按其名称旁边的”授予访问权限(Grant Access)”按钮:

Authentication Access Control

Package ID Specifications

Package ID specifications

包 ID 规范

Cargo 的子命令经常需要引用依赖关系图中的特定包来进行各种操作,例如更新,清理,构建等。为了解决这个问题,Cargo 支持包 ID 规范。规范是一个字符串,用于唯一地引用依赖关系图中的一个包.

Specification grammar

规范语法

包 ID 规范的形式语法是:

pkgid := pkgname
       | [ proto "://" ] hostname-and-path [ "#" ( pkgname | semver ) ]
pkgname := name [ ":" semver ]

proto := "http" | "git" | ...

这里,括号表示内容是可选的.

Example specifications

规范示例

这些都可以是对foo包的引用,版本1.2.3,来自注册表crates.io

pkgid 名称 版本 网址
foo foo * *
foo:1.2.3 foo 1.2.3 *
crates.io/foo foo * *://crates.io/foo
crates.io/foo#1.2.3 foo 1.2.3 *://crates.io/foo
crates.io/bar#foo:1.2.3 foo 1.2.3 *://crates.io/bar
http://crates.io/foo#1.2.3 foo 1.2.3 http://crates.io/foo

Brevity of specifications

规范的简洁

这样做的目的是用简洁和详尽的语法来引用依赖图中的包。而不明确的引用可以指代一个或多个包。若使用相同的规范会引用多个包,那大多数命令都会生成错误。

来源 更换

本文档是关于更换 crate 索引(注册表)。您可以阅读有关重写依赖项的信息,它在本文档的重写依赖关系部分。

Cargo 支持用另一个来源更换一个来源的能力,可根据镜像或 vendoring 依赖关系来表达倾向。要配置这些,目前通过.cargo/config配置机制完成,像这样:

# `source` 表下,就是存储有关要更换的来源名称
[source]

# 在`source` 表格之下的,可为一定数量的有关来源名称. 示例下面就# 定义了一个新源, 叫 `my-awesome-source`, 其内容来自本地 # `vendor`目录 ,其相对于包含`.cargo/config`文件的目录
[source.my-awesome-source]
directory = "vendor"

# Git sources 也指定一个 branch/tag/rev
git = "https://example.com/path/to/repo"
# branch = "master"
# tag = "v1.0.1"
# rev = "313f44e8"

# The crates.io 默认源 在"crates-io"名称下, 且在这里我们使用 `replace-with` 字段指明 默认源更换成"my-awesome-source"源
[source.crates-io]
replace-with = "my-awesome-source"

使用此配置,Cargo 会尝试在”vendor”目录中,查找所有包,而不是 查询在线注册表 crates.io 。Cargo 有两种来源更换的表达 :

  • 供应(Vendoring) - 可以定义自定义源,它们表示本地文件系统上的包。这些源是它们正在更换的源的子集,并在需要时可以检入包中。

  • 镜像(Mirroring) - 可以更换为等效版本的源,行为表现为 crates.io 本身的缓存。

Cargo 有一个关于来源更换的核心假设,源代码从两个完全相同的源而来。在上面的例子中,Cargo 假设所有的箱子都来自my-awesome-source,与crates-io副本完全相同。请注意,这也意味着my-awesome-source,不允许有crates-io源不存在的箱。

因此,来源更换不适用于依赖项补丁(fix bug),或私有注册表等情况。Cargo 是通过使用[replace]字段支持依赖项补丁,计划为未来版本的 Cargo 提供私人注册表的支持。

配置

更换源的配置通过完成.cargo/config,下面为全套可用字段是:

# 每个源都有自己的表格,名称即是表名
[source.the-source-name]

# 命令 ,`the-source-name` 会被`another-source`取代
replace-with = "another-source"

# 有几种可用的源定义(接下来有所描述)
registry = "https://example.com/path/to/index"
local-registry = "path/to/registry"
directory = "path/to/vendor"

crates-io代表 crates.io 在线注册表(箱的默认来源),可以更换为:

[source.crates-io]
replace-with = 'another-source'

注册表源

“注册表源”与 crates.io 本身相同。也就是说,它也有一个在 git 存储库中提供的索引,该存储库匹配crates.io index的格式。然后该存储库具有指示从哪里下载包的配置。

目前还没有一个已经设置 crates.io 的镜像的可用项目。请继续关注!

中国用户,可搜索 ‘rust 换 中科大 源’

本地 注册表源

“本地注册表源”旨在成为另一个注册表源的子集,但可在本地文件系统(也称为 vendoring)上使用。本地注册表是提前下载,通常与一个 Cargo.lock同步,并由一组*.crate文件和像普通注册表一样的索引组成。

管理和创建本地注册表源的主要方法是通过cargo-local-registry子命令,可在 crates.io 上找到,并用cargo install cargo-local-registry安装。

本地注册表包含在一个目录,其中包含许多从 crates.io 下载的*.crate文件,以及index目录,它与 crates.io-index 项目目录具有相同格式(仅填充有存在的 crates).

目录 源

“目录源”类似于本地注册表源,其中包含本地文件系统上许多的可用包,适用于 vendoring 依赖项。与本地注册表一样,目录源主要由外部子命令管理cargo-vendor,可用cargo install cargo-vendor安装。

目录源与本地注册表不同,但它们包含*.crate文件的解压缩版本,使其在某些情况下,更适合检查所有内容到源代码控制工具。目录源只是一个包含许多其他目录的目录,其中包含 crates 的源代码(解压缩版本的*.crate文件)。目前,对每个目录的名称没有限制。

目录源中的每个包也有一个关联的元数据文件,指示包中每个文件的校验和,以防止意外修改。

External tools

外部工具

Cargo 的目标之一是与第三方工具(如 IDE 和其他构建系统)的简单集成。为了简化集成,Cargo 有几个设施:

  • 一个cargo metadata命令,以 JSON 格式输出包结构和依赖关系信息,

  • 一个--message-format标志,输出有关特定构建的信息,以及

  • 支持自定义子命令.

Information about package structure

包结构的资料

您可以使用cargo metadata命令,以获取有关包结构和依赖关系的信息。命令的输出如下所示:

{
  // Integer 版本格式数字.
  "version": integer,

  // 工作区包的列表, 包括 依赖项.
  "packages": [
    {
      // 包 识别id 队列.
      "id": PackageId,

      "name": string,

      "version": string,

      "source": SourceId,

      // 确认依赖的一个列表, 可看 `resolve` 字段中的真实依赖.
      "dependencies": [ Dependency ],

      "targets: [ Target ],

      //  Cargo.toml 路径
      "manifest_path": string,
    }
  ],

  "workspace_members": [ PackageId ],

  // 依赖 关系图.
  "resolve": {
     "nodes": [
       {
         "id": PackageId,
         "dependencies": [ PackageId ]
       }
     ]
  }
}

格式稳定且有版本化。调用cargo metadata时,你应该通过--format-version明确标记,以避免向前不兼容的危险。

如果你正在使用 Rust,这有个cargo_metadata箱.

Information about build

关于构建的资料

传递--message-format=json给,Cargo, 将在构建期间输出以下信息:

  • 编译器错误和警告,

  • 制作的工件,

  • 构建脚本的结果(例如,本机依赖项).

输出以每行格式的 JSON 对象转到 stdout。reason字段区分不同类型的消息.

有关 Makefile 兼容格式的依赖关系的信息存储在工件旁的.d文件中。

Custom subcommands

自定义的子命令

Cargo 设计为,可以使用新的子命令进行扩展,而无需修改 Cargo 本身。这是通过转化一个 cargo (?<command>[^ ]+)的命令调用,变化为调用外部工具cargo-${command}来实现的。外部工具必须存在于用户其中一个$PATH目录中.

当 Cargo 调用自定义子命令时,子命令的第一个参数将像往常一样是自定义子命令的文件名。第二个参数将是子命令名称本身。例如,在调用cargo-${command}时,第二个参数是${command}。命令行上的其他所有参数将保持不变.

Cargo 还可以用cargo help ${command}显示自定义子命令的帮助输出。Cargo 假定子命令将在第三个参数出现时,打印帮助消息--help.所以,cargo help ${command}会调用cargo-${command} ${command} --help.

自定义子命令可以使用CARGO环境变量回调 Cargo。或者,它可以链接到作为一个库的cargo箱,但这种方法有缺点:

  • Cargo 作为库是不稳定的:API 可能会更改,但不会弃用

  • 链接的 Cargo 库的版本可能与 Cargo 二进制文件不同

Unstable Features

不稳定的特性

实验性 Cargo 特性仅适用于夜间通道(Rust 的 nightly 版本)。您通常使用-Z带其中一个特性,以启用它们。运行cargo -Z help查看可用的标志列表。

-Z unstable-options是用于启用其他不稳定命令行标志的通用标志。需要的此选项将在下面列出。

某些不稳定的特性需要您,在Cargo.toml指定cargo-features字段。

Alternate Registries

替换(/备用)注册表

备用注册表,允许您使用 crates.io 以外的注册表。

注册表的名称定义在.cargo/config中,registries表格的下面:

[registries]
my-registry = { index = "https://my-intranet:8080/git/index" }

可以在.cargo/credentials添加备用注册表的身份验证信息:

[registries.my-registry]
token = "api-token"

Cargo.toml里面,您可以使用指定registry字段,让某个依赖项来自哪个注册表。但首先,您需要在文件的顶部包含适当的cargo-features:

cargo-features = ["alternative-registries"]

[package]
...

[dependencies]
other-create = { version = "1.0", registry = "my-registry"}

一个--registry标志 已添加到与注册表交互的publishlogin等命令中。示例:

cargo +nightly publish -Z unstable-options --registry my-registry

Cargo.tomlpublish 字段,已被扩展为接受限制为发布到这些注册表的注册表列表。

[package]
...
publish = ["my-registry"]

publish-lockfile

发布-锁文件

创建.crate文件分发时,Cargo 历史上不包括Cargo.lock文件。这可能会导致cargo install用于二进制文件的问题。您可以在cargo package要么cargo publish使用时,指定您的包应包含Cargo.lock,方法是在Cargo.toml中指定publish-lockfile字段。这也需要适当的cargo-features要求:

cargo-features = ["publish-lockfile"]

[package]
...
publish-lockfile = true

Offline Mode

离线模式

-Z offline标志 可防止 Cargo 因任何原因尝试访问网络。通常情况下,如果 Cargo 要访问网络但它不可用,则会因错误而停止。

请注意,这可能会导致与在线模式不同的依赖项解析。即使可能有索引的较新版本的本地副本,Cargo 也会将自己限制在本地可用的箱(crate)中。

no-index-update

无索引更新

-Z no-index-update标志 确保 Cargo 不会尝试更新注册表索引。这适用于测试(调制)许多 Cargo 命令的 Crater 等工具,并且您希望每次都避免更新索引的网络延迟。

avoid-dev-deps

阻止 开发依赖项

运行命令如cargo install要么cargo build时,Cargo 当前会需要下载 dev 依赖项,即使它们未被使用。而该-Z avoid-dev-deps标志 则让 Cargo 避免在不需要时下载 dev 依赖项。该Cargo.lock如果跳过了 dev-dependencies,将不会生成相关信息。

minimal-versions

最小的版本

当一个Cargo.lock文件被生成,-Z minimal-versions标志的使用, 将解析依赖关系为满足要求的最小 semver 版本(而不是最高版本)。

此标志的预期用例,是在持续集成期间,检查 Cargo.toml 中指定的版本是否是您实际使用的最低版本的正确反映。也就是说,如果 Cargo.toml 有foo = "1.0.0"说明,那您依赖该特性,不会意外地添加foo 1.5.0

out-dir

输出目录

此功能允许您指定,构建工件后,将复制到的目录。通常,工件只写入target/release要么target/debug目录。但是,确定明确的文件名可能很棘手,因为您需要解析 JSON 输出。而该--out-dir标志 可以更容易地预测访问工件。但是请注意,工件只是被复制,因此原件仍在target目录.例:

cargo +nightly build --out-dir=out -Z unstable-options

Profile Overrides

覆盖配置

可以为特定包和自定义生成脚本覆盖配置文件。一般格式如下:

cargo-features = ["profile-overrides"]

[package]
...

[profile.dev]
opt-level = 0
debug = true

# 这个 `image` 箱 会编译的等级 -Copt-level=3
[profile.dev.overrides.image]
opt-level = 3

# 所有 依赖项 (但 不是 箱本身 或 某些工作区成员)
# 会编译的等级 -Copt-level=2 。 其中包括 构建依赖项.
[profile.dev.overrides."*"]
opt-level = 2

# 构建 脚本和他们的 依赖项, 会编译的等级 -Copt-level=3
# 默认情况, 构建 脚本 对剩下的配置使用相同的选择
[profile.dev.build-override]
opt-level = 3

能只为 dev 和 release 配置文件指定覆盖。

Config Profiles

配置 配置文件 (第一个动词,第二个名词)

可以在.cargo/config文件中指定配置文件。该-Z config-profile命令行标志是使用此功能所必需的。格式与一个 Cargo.toml清单格式相同。如果在多个文件中找到相同的配置,则通过使用常规设置配置层次结构合并。配置设置优先于清单设置。

[profile.dev]
opt-level = 3
cargo +nightly build -Z config-profile

Namespaced features

特性的命名区间化

目前,不可能在清单中具有相同名称的特性和依赖项。但如果你设置namespaced-featurestrue,功能和依赖项的命名空间就会是分开的。这样做的结果是,在特性请求中,依赖项必须以crate:为前缀。像这样:

[package]
namespaced-features = true

[features]
bar = ["crate:baz", "foo"]
foo = []

[dependencies]
baz = { version = "0.1", optional = true }

为了防止不必要的,必须为每个可选依赖项显式声明特性的模版,将为任何不是定义为相同名称的特性,的可选依赖项创建隐式特性。但是,如果定义了与依赖项同名的特性,则该特性必须将依赖项作为必需项,正如foo = ["crate:foo"]

Build-plan

构建计划

build命令的--build-plan参数,将输出 JSON,其中包含有关将运行哪些命令,和不实际执行任何操作的信息。与其他构建工具集成时,这可能很有用。例:

cargo +nightly build --build-plan -Z unstable-options

default-run

默认运行

清单中[package]部分的该default-run选项,可用于指定cargo run选择的默认二进制文件。例如,当存在src/bin/a.rssrc/bin/b.rs两者时,选择前者:

[package]
default-run = "a"

Metabuild

元构建

Metabuild 是一个具有声明性构建脚本的特性。作为不去写一个build.rs脚本,而是您在Cargo.toml中的metabuild字段,指定构建依赖项列表。其将自动生成一个构建脚本,该脚本按顺序运行每个构建依赖项。然后,Metabuild 包可以从Cargo.toml中读取元数据,执行他们的指定行为。

需要包括cargo-featuresCargo.toml的顶部, 一个metadata字段在package下,列出build-dependencies依赖项,并添加 metabuild 包所需的任何元数据。例:

cargo-features = ["metabuild"]

[package]
name = "mypackage"
version = "0.0.1"
metabuild = ["foo", "bar"]

[build-dependencies]
foo = "1.0"
bar = "1.0"

[package.metadata.foo]
extra-info = "qwerty"

Metabuild 包应该有一个名metabuild为的公共函数,它会执行与常规build.rs脚本一样,执行相同操作。

Frequently Asked Questions

常问问题

Is the plan to use GitHub as a package repository?

是否有计划,使用 Github 作为一个包库 ?

不,Cargo 的计划是使用crates.io,像 NPM 或 RuuGuMes 对应 npmjs.org 和 rubygems.org。

我们计划永远(通过些配置)支持 git 存储库作为包的来源,因为它们可以用于早期开发和临时补丁(加了点灵活性),即便人们使用主要使用注册表作为包的来源。

Why build crates.io rather than use GitHub as a registry?

为啥,选 crates.io,而不是使用 Github 作为 注册表 ?

我们认为支持多种下载包的方式非常重要,包括从 GitHub 下载包,并将包复制到包本身.

也就是说,我们认为crates.io提供了许多重要的好处,并且预计其会成为人们在 Cargo 中,下载包的主要方式。

前车之鉴,Node.js 的npm和 Ruby 的bundler都支持中央注册中心模式,和基于 Git 的模式,而大多数包都是通过生态系统中的注册中心下载的,其中重要的少数包是使用基于 git 的包。

使中央注册中心,在其他语言中流行的一些优点包括:

  • 可发现性. 中央注册表提供了查找现有包的简单方式。结合标记(版本),这也使得注册中心能够提供生态系统的范围信息,例如最流行或最依赖的包的列表.
  • 速度. 中心注册中心使得可以快速有效地只获取包的元数据,然后只高效地下载已发布的包,而不会出现在存储库中的其他膨胀。这大大提高了依赖性解析和获取的速度。要知道随着依赖关系图的扩展,下载所有的 git 存储库会陷入困境。还要记住的是,并不是每个人都有高速、低延迟的互联网连接.

Will Cargo work with C code (or other languages)?

Cargo 可与 C 语言代码(或其他语言)一起工作吗?

可以的!

Cargo 处理编译 Rust 代码,但我们知道许多 Rust 包与 C 代码都有链接。我们还知道除 Rust 之外,在编译语言方面的工具,已建立了数十年。

我们的解决方案:Cargo 允许一个包可以指定脚本(用 Rust 编写),其在调用rustc之前运行。 利用 Rust 实现特定于平台的配置和重构包之间的常见构建功能。

Can Cargo be used inside of make (or ninja, or ...)

Cargo 能被用在 make(或 ninja或...) 中吗 ?

当然能。尽管我们希望, Cargo 是作为顶级编译 Rust 包的独立方式,但我们知道有些人希望从其他构建工具调用 Cargo。

我们已将 Cargo 设计成在这些环境中工作良好,并注意错误代码和机器可读输出模式等事项。在这些方面我们还有一些工作要做,但是在传统脚本上下文中使用 Cargo 是我们从一开始就设计的,并且将继续优先考虑。

Does Cargo handle multi-platform packages or cross-compilation?

Cargo 是怎么平衡 多平台或跨平台的包的?

Rust 本身提供了基于平台,配置代码段的工具。Cargo 也支持特定平台依赖关系,未来,我们计划为每个平台Cargo.toml支持更多的配置.

从长远来看,我们正在寻找使用 Cargo 方便地跨编译包的方法.

Does Cargo support environments, like production or test?

Cargo 有没支持像productiontest这样的环境?

我们通过使用profiles来支持这样的环境:

  • 特定环境标志(像 开发环境的 -g --opt-level=0和生产环境的--opt-level=3)。
  • 特定环境依赖性(像 测试断言 的hamcrest).
  • 特定环境变量 #[cfg]
  • 一个cargo test命令

Does Cargo work on Windows?

Windows 系统 呢,Cargo 能搞吗?

没问题!

所有提交的 Cargo 都需要通过 Windows 上的本地测试套件。但是,如果你发现一个 Windows 问题,我们认为它就是一个 bug,所以请提出一个问题.

Why do binaries have Cargo.lock in version control, but not libraries?

为啥,输出二进制的 Cargo 项目具有Cargo.lock,而单输出库的,就没有?

一个Cargo.lock文件的目的,是在于成功构建,能描述’世界’的状态。然后,它就能用来,通过确保编译完全相同的依赖项,就能跨任何机器上构建确定性的包。

这个属性对于,处在依赖链末端的应用程序和包(二进制文件)是最理想的。因此,建议所有二进制文件都在其Cargo.lock内部进行检查.

对于单库来说,情况有些不同。库不仅被库开发人员使用,而且被库的任何下游消费者使用。依赖库的用户不会检查库的Cargo.lock(即使它存在)。正是如此,库应该对库的所有用户进行确定性地重新编译。

如果一个库最终被多个依赖项传递使用,那么很可能只需要该库的一个副本(基于 semver 兼容性的版本)。如果 Cargo 使用了所有的 依赖项的Cargo.lock文件,那结果就是,使用库的多个副本,甚至可能存在版本冲突。

换句话说,库为它们的依赖项指定了 semver 版本,但是不用(无法)看到全部内容。只有像二进制文件这样的最终产品才需要有完整的图,来决定应该使用什么版本的依赖。

Can libraries use * as a version for their dependencies?

作为库的项目,可以使用*作为它们的依赖的版本号吗?

截至 2016 年 1 月 22 日,crates.io拒绝通配符*依赖约束的所有包(不只是库).

库是可以,但严格来说,他们不应该这样做。*版本要求,说明了”这将适用于任何版本”,而这永远不会是真的。库应该总是指定它们工作的范围,即使它和”每个 1.x.y 版本”一样。

Why Cargo.toml?

作为与 Cargo 最频繁的交互之一,为什么要命名配置文件叫Cargo.toml的问题不时出现。选择领先的大写—C,是为了确保清单与目录清单中的其他类似配置文件组合排序。对文件进行排序时,通常将大写字母放在小写字母之前,确保MakefileCargo.toml文件会放在一起。选择.toml结尾是强调文件是特定的配置文件格式.

Cargo 不允许其他名称(如cargo.tomlCargofile),来强调如何如何容易识别 Cargo 仓库。在历史上,许多可能的名称选择都导致了混乱,其中一个选项被选择了,而其他选项就被自然而然地遗忘。

How can Cargo work offline?

Cargo 能 离线 工作吗?

Cargo 通常用于网络访问有限,或没有网络访问的情况,如飞机、CI 环境或嵌入大型生产部署中。当 Cargo 试图从网络获取资源时,用户常常感到惊讶,因频繁出现 Cargo 离线工作的请求。

Cargo 的核心是不会试图访问网络,除非被告知这样做。也就是说,如果没有来自 crates.io、git 存储库或其他网络位置的箱,则 Cargo 永远不会尝试进行网络连接。因此,如果 Cargo 试图接触网络,那是因为它需要获取所需的资源。

Cargo 还非常积极地缓存信息,保持最小化的网络活动量。例如,它将保证cargo build(或类似的)运行到完成,那下一次cargo build保证不接触网络,只要Cargo.toml在此期间还没有被修改。网络的这种回避归结为,已存在Cargo.lock,和在 lock 文件中反映了,箱子的充分缓存。如果这些组件中的任何一个丢失,那么构建的成功就需要它们,并且必须远程获取它们。

对 Rust 1.11.0 打后的 Cargo ,可以看到新的(标志)参数--frozen,这是它不应该接触网络的断言。当传递给 Cargo,如果 Cargo 试图进行网络请求,它将立即返回一个错误。错误应该包括关于为什么进行网络请求(第一个地方),以帮助调试的上下文信息。注意这个标志是不改变 Cargo 的行为,它只是断言 Cargo 不应该触摸网络,这作为上一个命令已完成的保证,可以相同的网络活动是不必的。

上一个命令,如cargo build

有关版本管理的详细信息,请参阅文档来源更换.

Glossary

  • 词汇表

Artifact

  • 工件

一个Artifact是由编译过程创建的文件或文件集。这包括可链接库和可执行二进制文件.

Crate

  • 箱 (包/库, 又一抽象名)

包中的每个目标都是。Crates 是库或可执行二进制文件。它可能松散地引用目标的源代码或目标生成的编译工件。一个箱也可以指从注册表中提取的压缩包.

Edition

  • 版本

一个Rust Edition是 Rust 语言的开发里程碑。该一个包的版本Cargo.toml清单中指定,各个目标可以指定它们使用的版本。见版本指南欲获得更多信息.

Feature

  • 特性/特征/功能

一个特征是一个允许条件编译的命名标志参数。一个特性可以引用可选的依赖项,或者在 一个Cargo.toml 中定义的任意名称,可以键入(使用到)源代码中。

Cargo 有不稳定的特征标志,这可以用来实现 Cargo 本身的实验行为。Rust 编译器和 Rustdoc 也有自己的不稳定特征标志(参见不稳定的书Rustdoc 书).

Index

  • 索引是注册表中,可搜索的包的列表.

Lock file

  • (锁定/锁) 文件

Cargo.lock,名:锁定文件,是一个文件,用于捕获工作空间或包中使用的每个依赖项的确切版本,它由 Cargo 自动生成。看到Cargo.toml 与 Cargo.lock.

Manifest

  • 清单/元信息

一个清单是对包或工作空间的描述,名为Cargo.toml.

一个虚拟清单是一个Cargo.toml,仅描述工作空间的文件,不包含包。

Member

  • 会员/成员

一个成员是属于工作空间的一个包.

Package

一个是源文件和描述包的清单Cargo.toml的集合。包具有名称和版本,用于指定包之间的依赖关系。包中包含多个目标,这些目标是库或可执行二进制文件。

包根Cargo.toml清单位于的包的目录。

包 ID 规范, 要么SPEC,是一个字符串,用于辨识从特定源,引用特定版本的包唯一性。

Project

  • 包/项目

package的另一个名字.

Registry

  • 注册表

一个注册处是一种服务,包含可下载的包,可以安装或用作包的依赖项.默认注册表是crates.io。注册表有一个索引,其中包含所有包装箱的清单,并告诉 Cargo 如何下载所需的箱。

Source

  • 源/资源

一个是一个提供程序,包含箱子,这些箱子可当依赖项的包。有几种源:

看到来源更换欲获得更多信息.

Spec

Target

  • 目标

Target这个词的意思,取决于具体情况:

  • Cargo 目标 - Cargo 项目会有target,其具有对应将要生成的工件。项目可以包含库,二进制,示例,测试和基准目标。该目标列表配置在Cargo.toml清单,通常由源文件的目录布局自动推断的。
  • 目标架构- 构建工件的 OS 和机器体系结构,通常称为一个目标
  • 目标 三元(Triple)- 三元组是用于指定目标体系结构的特定格式。见clang 文档 了解详情。三元组可以称为 一个目标三元,此为产生的工件的架构,以及主机三元,此为编译器运行的体系结构。可以使用--target命令行选项或build.target [配置选项]指定目标三元组。
  • 目标目录- Cargo 将所有构建的工件和中间文件放入target目录。默认情况下,这是一个名为target的目录会在工作区根目录,或者包根(如果不使用工作空间)。目录随着--target-dir命令行选项,CARGO_TARGET_DIR 环境变量, 或者build.target-dir 配置选项改变而改变.

“target-triple (目标 三元)” 是 编译器的 专用术语,用 “ cpu- vendor- os” 来标识 交叉编译时的 系统类型

Test Targets

Cargo测试目标生成二进制文件,帮助验证代码的正确操作和正确性。有两种类型的测试工件:

  • 单元测试- 一个单元测试是直接从库或二进制目标编译的可执行二进制文件。它包含库或二进制代码的全部内容,并运行#[test]注释函数,用于验证各个代码单元。
  • 集成测试目标- 一个集成测试目标是一个来自测试目标的可执行的二进制文件,这是一个独特的箱子,其来源位于tests目录或由在Cargo.toml清单里面的[[test]]指定。它旨在仅测试库的公共 API,或执行二进制文件以验证其操作。

Workspace

  • 工作区

一个工作区是一个共享公共依赖项解析(具有共享Cargo.lock),输出目录和各种设置,如配置文件,的一个或多个包的集合。

一个虚拟工作区Cargo.toml清单根目录的工作空间, 没有定义包,只列出工作区成员。

工作区根是工作区的Cargo.toml清单位于的目录.