常见问题解答
计划使用 GitHub 作为软件包存储库吗?
不。货物计划使用 crates.io,就像 npm 或 Rubygems 对 npmjs.com 和 rubygems.org 所做的那样。
我们计划永远支持将 git 存储库作为软件包来源,因为即使人们将注册表用作软件包的主要来源,它们也可以用于早期开发和临时补丁。
为什么构建 crates.io 而不是使用 GitHub 作为注册表?
我们认为支持多种下载软件包的方式非常重要,包括从 GitHub 下载和将软件包复制到您的软件包本身。
也就是说,我们认为 crates.io 提供了许多重要的优势,并且很可能成为人们在 Cargo 中下载软件包的主要方式。
作为先例,Node.js 的 npm 和 Ruby 的 bundler 都支持集中式注册表模型和基于 Git 的模型,大多数软件包都是通过注册表在这些生态系统中下载的,一小部分重要的软件包使用基于 git 的软件包。
在其他语言中使集中式注册表流行的一些优势包括
- 可发现性。集中式注册表提供了一个查找现有软件包的简便位置。结合标记,这也有可能使注册表提供整个生态系统的信息,例如最受欢迎或依赖最多的软件包列表。
- 速度。集中式注册表可以轻松快速有效地获取软件包的元数据,然后有效地仅下载已发布的软件包,而不是存储库中碰巧存在的其他膨胀内容。这加起来显著提高了依赖项解析和获取的速度。随着依赖关系图的扩大,下载所有 git 存储库会很快变得缓慢。还要记住,并非每个人都拥有高速、低延迟的互联网连接。
货物是否与 C 代码(或其他语言)一起使用?
是的!
货物处理编译 Rust 代码,但我们知道许多 Rust 软件包链接到 C 代码。我们还知道,除了 Rust 之外,还有数十年的工具围绕编译其他语言而构建。
我们的解决方案:货物允许软件包 指定一个脚本(用 Rust 编写)在调用 rustc
之前运行。Rust 被用来实现特定于平台的配置,并在软件包之间重构常见的构建功能。
货物是否可以在 make
(或 ninja
,或…)中使用?
确实。虽然我们希望货物成为在顶层编译 Rust 软件包的独立方式,但我们知道有些人希望从其他构建工具调用货物。
我们设计了货物以在这些上下文中良好运行,注意错误代码和机器可读输出模式等事项。我们在这方面还有工作要做,但在传统脚本的上下文中使用货物是我们从一开始就设计的东西,并将继续优先考虑。
货物是否处理多平台软件包或交叉编译?
Rust 本身提供了根据平台配置代码部分的功能。货物还支持 特定于平台的依赖项,我们计划在将来支持 Cargo.toml
中更多特定于平台的配置。
从长远来看,我们正在研究使用货物方便地交叉编译软件包的方法。
货物是否支持环境,例如 production
或 test
?
我们通过使用 配置文件 来支持环境,以支持
- 特定于环境的标志(例如,开发时的
-g --opt-level=0
和生产时的--opt-level=3
)。 - 特定于环境的依赖项(例如,用于测试断言的
hamcrest
)。 - 特定于环境的
#[cfg]
- 一个
cargo test
命令
货物是否在 Windows 上运行?
是的!
对货物的每次提交都需要在 Windows 上通过本地测试套件。如果您在 Windows 上运行时遇到问题,我们认为这是一个错误,因此请 提交问题。
为什么在版本控制中使用 Cargo.lock
?
虽然 cargo new
默认情况下会在版本控制中跟踪 Cargo.lock
,但您是否这样做取决于您的软件包的需求。
Cargo.lock
锁定文件的作用是描述成功构建时世界的状态。货物使用锁定文件在不同的时间和不同的系统上提供确定性构建,方法是确保使用与最初生成 Cargo.lock
文件时完全相同的依赖项和版本。
确定性构建有助于
- 运行
git bisect
以找到错误的根本原因 - 确保 CI 仅因新提交而失败,而不是外部因素
- 当贡献者看到与其他贡献者或 CI 相比不同的行为时,减少混淆
拥有此依赖项快照也有助于在项目需要针对一致的依赖项版本进行验证时,例如在
- 验证低于依赖项最新版本支持的最低支持 Rust 版本 (MSRV)
- 验证不可读的输出,这些输出将没有兼容性保证(例如,对错误消息进行快照测试以确保它们“可理解”,这是一个过于模糊而无法自动化的指标)
但是,这种确定性可能会给人一种错误的安全感,因为 Cargo.lock
不会影响您软件包的使用者,只有 Cargo.toml
会这样做。例如
cargo install
将选择最新的依赖项,除非传递--locked
。- 新的依赖项,例如使用
cargo add
添加的依赖项,将被锁定到最新版本
锁定文件也可能是合并冲突的来源。
有关通过 CI 验证依赖项的更新版本的策略,请参阅 验证最新依赖项。
库可以使用 *
作为其依赖项的版本吗?
截至 2016 年 1 月 22 日,crates.io 拒绝所有具有通配符依赖项约束的软件包(不仅仅是库)。
虽然库严格来说可以,但它们不应该。*
的版本要求表示“这将与所有版本一起使用”,这永远不会是真的。库应该始终指定它们确实可以使用的范围,即使它像“所有 1.x.y 版本”一样通用。
为什么是 Cargo.toml
?
作为与 Cargo 最常见的交互之一,关于配置文件为什么名为 Cargo.toml
的问题不时出现。首字母大写的 C
的选择是为了确保清单在目录列表中与其他类似的配置文件分组。排序文件通常将大写字母放在小写字母之前,确保像 Makefile
和 Cargo.toml
这样的文件放在一起。尾部的 .toml
被选择来强调该文件是 TOML 配置格式 的事实。
Cargo 不允许其他名称,例如 cargo.toml
或 Cargofile
,以强调 Cargo 存储库如何轻松识别。许多可能名称中的一个选项在历史上导致混淆,其中一个情况得到处理,而其他情况则意外被遗忘。
Cargo 如何离线工作?
Cargo 通常用于网络访问有限或没有网络访问的情况,例如飞机、CI 环境或嵌入大型生产部署中。用户经常对 Cargo 尝试从网络获取资源感到惊讶,因此经常要求 Cargo 离线工作。
Cargo 的核心不会尝试访问网络,除非被告知这样做。也就是说,如果没有任何箱子来自 crates.io、git 存储库或其他网络位置,Cargo 永远不会尝试建立网络连接。因此,如果 Cargo 尝试接触网络,那么是因为它需要获取所需的资源。
Cargo 还非常积极地缓存信息以最大限度地减少网络活动。例如,它将保证,如果 cargo build
(或等效命令)运行完成,那么下一个 cargo build
将保证不会接触网络,只要 Cargo.toml
在此期间没有被修改。这种避免网络归结为 Cargo.lock
的存在以及锁定文件中反映的箱子的填充缓存。如果这两个组件中的任何一个丢失,那么它们是构建成功所必需的,并且必须从远程获取。
从 Rust 1.11.0 开始,Cargo 理解一个新的标志 --frozen
,它断言它不应该接触网络。当传递时,如果 Cargo 否则尝试网络请求,它将立即返回错误。错误应该包含有关为什么首先进行网络请求的上下文信息,以帮助调试。请注意,此标志 *不会更改 Cargo 的行为*,它只是断言 Cargo 不应该接触网络,因为之前已经运行了一个命令以确保网络活动不应是必要的。
--offline
标志是在 Rust 1.36.0 中添加的。此标志告诉 Cargo 不要访问网络,并尝试尽可能使用可用的缓存数据。您可以使用 cargo fetch
在一个项目中下载依赖项,然后在另一个项目中使用 --offline
标志(或 配置值)使用相同的依赖项。
有关供应商的更多信息,请参阅有关 源替换 的文档。
为什么 Cargo 重新构建我的代码?
Cargo 负责增量编译项目中的箱子。这意味着,如果您两次键入 cargo build
,第二次不应该重新构建您的 crates.io 依赖项,例如。然而,错误会发生,Cargo 有时会在您不期望的时候重新构建代码!
我们很早就 想要提供关于此的更好的诊断,但不幸的是,我们很长时间以来一直无法在这方面取得进展。然而,在此期间,您可以通过设置 CARGO_LOG
环境变量来至少稍微调试一下重建。
$ CARGO_LOG=cargo::core::compiler::fingerprint=info cargo build
这将导致 Cargo 打印出大量关于诊断和重建的信息。这通常包含关于为什么您的项目正在被重建的线索,尽管您通常需要自己连接一些点,因为此输出目前还不太容易阅读。请注意,CARGO_LOG
需要为当您认为它不应该重建时重建的命令设置。不幸的是,Cargo 目前没有办法事后调试“为什么重建了?”
我们历史上看到的一些可能导致箱子被重新构建的问题是
-
构建脚本打印
cargo::rerun-if-changed=foo
,其中foo
是一个不存在的文件,并且没有任何东西生成它。在这种情况下,Cargo 将继续运行构建脚本,认为它将生成该文件,但实际上什么也没有。解决方法是在这种情况下避免打印rerun-if-changed
。 -
两个连续的 Cargo 构建可能在为某些依赖项启用的功能集中有所不同。例如,如果第一个构建命令构建整个工作区,而第二个命令只构建一个箱子,这可能会导致 crates.io 上的依赖项具有不同的功能集,从而导致它以及所有依赖它的东西被重新构建。不幸的是,对此没有很好的解决方法,尽管如果可能,最好让箱子上的功能集保持不变,无论您在工作区中构建什么。
-
某些文件系统在时间戳方面表现出异常行为。Cargo 主要使用文件上的时间戳来控制是否需要重新构建,但是如果您使用的是非标准文件系统,它可能会以某种方式影响时间戳(例如,截断它们,导致它们漂移等)。在这种情况下,请随时打开一个问题,我们可以看看是否可以以某种方式适应文件系统。
-
并发构建进程正在删除工件或修改文件。有时您可能有一个后台进程试图构建或检查您的项目。这些后台进程可能会意外地删除一些构建工件或触摸文件(或者可能只是意外地),这会导致重建看起来很奇怪!这里最好的解决方法是整理后台进程以避免与您的工作冲突。
但是,如果您在尝试调试问题后仍然遇到问题,请随时 打开一个问题!
“版本冲突”是什么意思以及如何解决?
无法为
x
选择一个版本来解决此冲突
您是否见过上面的错误消息?
这是 Cargo 用户最恼人的错误消息之一。有几种情况可能会导致版本冲突。下面我们将逐步介绍可能的原因,并提供诊断技术来帮助您解决问题。
-
项目及其依赖项使用 链接 来重复链接本地库。Cargo 禁止将两个具有相同本地库的包链接在一起,因此即使在多层依赖项的情况下也不允许。在这种情况下,错误消息将提示:
依赖关系图中只有一个包可以指定相同的链接值
,您可能需要手动检查并删除重复的链接值。社区也有 约定 来缓解这种情况。 -
当依赖于项目中的不同箱子时,如果这些箱子使用相同的依赖库,但使用的版本受到限制,使得无法确定正确的版本,也会导致冲突。错误消息将提示:
所有可能的版本都与先前选择的包冲突
。您可能需要修改版本要求以使其一致。 -
如果项目中存在多个版本的依赖项,当使用
direct-minimal-versions
时,无法满足最小版本要求,这将导致冲突。您可能需要修改直接依赖项的版本要求以满足相应的最小 SemVer 版本。 -
如果依赖箱子没有您选择的功能,也会导致冲突。此时,您需要检查依赖版本及其功能。
-
合并分支或 PR 时可能会发生冲突,如果存在非平凡的冲突,您可以重置所有“您的”更改,修复分支中的所有其他冲突,然后运行一些 cargo 命令(如
cargo tree
或cargo check
),这应该使用您自己的本地更改重新更新锁定文件。如果您之前在分支中运行了一些cargo update
命令,您可以重新运行它们。社区一直在寻找使用 自定义合并工具 解决Cargo.lock
和Cargo.toml
的合并冲突。