Cargo 中的本开发周期:1.80

2024 年 6 月 19 日 · Ed Page 代表 Cargo 团队

Cargo 中的本开发周期:1.80

这是对过去 6 周左右(大约是 Rust 1.80 的合并窗口)Cargo 开发进展的总结。

本周期的插件

Cargo 不可能满足所有人的需求,如果说仅仅是因为它必须遵守兼容性保证。插件在 Cargo 生态系统中扮演着重要的角色,我们希望对它们进行表彰。

我们本周期的插件是 cargo-expand,它是 cargo rustc 的一个更方便、更容易记住的包装器,用于查看 crate 中所有展开的宏。对于宏作者来说,这是一个很大的调试帮助。对于宏用户来说,这可以帮助打破宏调用带来的不透明性。

感谢 LukeMathWalker 的建议!

请提交您对下一篇文章的建议。

实现

-Zcheck-cfg

来自 1.77 的更新

回顾一下,这是一个 rustc 特性,它会根据已知名称和值的列表检查 #[cfg]。当与 Cargo 一起使用时,名称和值来自

在 5 月初,Cargo 对 --check-cfg 的支持已稳定(#13571)。在稳定时,该团队权衡了 crater 结果 和来自多轮 测试请求 的反馈(其中包括 调整了 This Week in Rust 的布局 以提高可见性)。与稳定化齐头并进,urgau 发布了一篇 博文,以帮助那些通过升级 nightly 首次接触到此功能的人。

在此功能进入 nightly 后不久,rust-lang/rust#124800(以及相关问题)被提出。人们的担忧包括:

  • 误报率
  • 必须使用 build.rs 来指定自定义 --cfg,而以前没有使用过
  • build.rs 本身中的 #[cfg] 没有办法为自身指定 --check-cfg
  • 对 lint 消息和文档的一些困惑

大量的讨论都花在了试图让参与对话的所有人(包括我们)在以下方面达成共识:此功能是什么,使用它的选项有哪些,以及每个选项的影响。这无疑是一个令人沮丧的过程,因为那些受到负面影响的人现在正在感受到痛苦。但是,我们需要确保找到正确的解决方案,而不是第一个出现的解决方案。

一个积极的一面是,nightly 在帮助收集关键反馈方面做得很好,这远在我们达到稳定版本之前!为了给我们充足的时间收集反馈,我们故意将稳定化 PR 推迟到前一个 nightly 分支到 beta 之后,这样我们就有完整的 12 周时间来收集反馈并改进此功能。由于这是一个 lint(非阻塞),并且我们有信心在发布前的 12 周内进行任何必要的润色,因此我们决定将其保留在 nightly 中,只有在我们即将达到发布截止日期时才会回滚。

我们认识到文档是一个问题。我们共同努力寻找改进它的方法(例如 #13869 #13937 rust-lang/rust#124209 )。限制这项工作的一个挑战是找到 Cargo 文档的存放位置,因为没有以用户为中心的 Cargo 功能来围绕此文档进行组织。

至于改进人们与此功能的交互方式,从长远来看,我们认为 私有特性互斥的全局特性 将有助于替换许多自定义 cfg 的情况。这仍然留下了短期问题。

多次出现的一个简单直接的答案是在 Cargo.toml 中添加一个 [cfg] 表(#11631)。这在功能开发期间提出过,但被 Cargo 团队拒绝,因为它设计了一个与 Cargo 的其余部分不一致的全新系统(另请参阅 rustc 的泄漏抽象)。

通过 GitHub、Cargo 办公时间和在会议上的面对面交谈,我们最终确定了使用来自 RFC 3389: [lints] 的“未来可能性”中的 lint 配置的解决方案。感谢 wesleywiser 提供的 想法urgau#13913 中的实现。

例如:

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_foo)'] }

(有关更多详细信息,请参阅 Cargo 特定 - 检查条件配置

我们不一定已经完成,还需要解决以下问题:

  • 我们是否应该阻止人们扩展 Cargo check-cfg 值?
  • 我们是否应该在 1.79 中减少警告噪音,作为向此功能过渡的一部分?是的,在 #13925 中。
  • 我们是否可以接受 lints.rust.unexpected_cfgs.check-cfg 中不一致的破折号?是的,它与依赖项名称和字段的差别不大。
  • 我们是否应该允许 level 是可选的?由于无论如何这都是一个 MSRV 升级,我们决定推迟对此做出决定。有些人倾向于偏爱显式。

从这得到的一个教训是,Cargo 和编译器团队应该更好地协调此类更改。特别是,公共-私有依赖项 似乎会对生态系统产生类似的影响。

用户控制的 Cargo 诊断

来自 1.79 的更新

本开发周期,诊断主要进行了润色。

我们在 Cargo 团队中进行了一次前瞻性对话:何时应该评估 lints?

今天,大多数清单错误和警告是在解析文档时处理的。警告会被捕获,并且仅在某些命令中稍后报告,并且仅在我们没有另外限制该包的 lints 的情况下。或者换句话说,我们会分析你的依赖项树中每个包的警告,然后丢弃几乎所有的工作。

如果我们将其中一些错误和警告移到我们运行新诊断的时候,我们可以停止做丢弃式的工作,并且可能会改进代码组织的一些方面。作为副作用,今天报告的一些错误可能根本不会报告。我们以后是否可以在不打破我们对兼容性的期望的情况下更改此行为,或者,如果不能,我们是否可以接受这种新行为?经过一番讨论,我们认为我们可以朝着这个方向前进,但我们需要逐个案例地查看。

-Ztrim-paths

来自 1.76 的更新

rust-lang/rust#107099 已合并,允许 rustdoc 参与 trim-paths。Cargo 仍然需要更新。

Cargo 遇到的另一个挑战是如何访问在平台之间稳定且不仅仅是每次运行都稳定的哈希算法。Urgau 正在努力提取 rustc 的稳定哈希器以供 Cargo 重用(rust-lang/rustc-stable-hash#1)。

支持 MSRV 的 Cargo

来自 1.79 的更新

目前的主要重点是支持 MSRV 的解析器。

我们运行了 测试请求,到目前为止,我们没有收到任何可操作的反馈。与该测试请求分开,我们确实收到了关于 cargo updatelatest 注释的反馈(#13908)。讨论仍在进行中。

据我们所知,稳定化之前的最后一个步骤是重命名配置(#13540

[resolver]
something-like-precedence = "something-like-rust-version"

我们希望记住这如何与未来的可能性保持一致,例如:

对于最小与最大版本解析,我们看到这很可能是一个未命名键下的枚举值,因此我们可以将其搁置。

对于其余部分,我们正在讨论以下模式:

模式 MSRV 撤销 预发布
这是另一个候选方案 必需 永不? 永不?
降低此版本相对于其他版本的优先级 必需 可能 可能
如果已在使用,则不解析为此 可能 必需 必需

这有助于表明我们可能想要将字段命名为 incompatible-rust-version,以明确我们正在讨论如何处理这些包,并为 resolver.rust-version 留下空间,以便在解析依赖项时覆盖使用的版本。

挑战在于提供一种清晰的方式来传达“模式”。对于降低优先级,我们考虑了

  • 不鼓励
  • 避开
  • 避免
  • 回退

在这些词中,fallback 最能捕捉细微的差别,但它是一个名词,而我们之前考虑的其他模式的 allowdeny 都是动词。

我们曾考虑过一个短期解决方案,即添加一个“优先级策略”字段,该字段可以接受一个策略名称,就像我们有包含许多编译器设置的配置文件名称一样。但这只是一个短期方案,可能会因产生混淆而导致自身的问题,因此我们决定专注于“正确”的解决方案。

移除隐式特性

来自 1.781.79 的更新

一个 crater 运行暴露了当将隐式特性迁移到显式特性时,cargo fix 存在一些错误 (#14010)。

这暴露了一个误解,即 ["dep_name/feature_name"] 并不总是与 ["dep:dep_name", "dep_name?/feature_name"] 相同,因为前者不会阻止隐式特性的创建。作为我们在 2024 Edition 中阻止创建隐式特性的副作用,在某些极端情况下,["dep_name/feature_name"] 语法会报错 (#14016)。我们考虑是否应该弃用该语法(我们之前已推迟),或者是否应该隐式注入 "dep:dep_name"。经过一些讨论,我们最终选择了后者。

规范化已发布的清单文件

来自 1.79 的更新

现在已发布的 Cargo.toml 文件列出了所有目标,我们可以避免使用 #13849 读取文件系统。

感谢 stormshield-guillaumed 测试 nightly 版本,我们发现这些更改使 vendored 的 Cargo.toml 文件不确定,并在 #14004 中修复了该问题。

cargo upgrade 合并到 cargo update

来自 1.77 的更新

#13979 中,torhovland 添加了对带有以下策略的 cargo update --breaking 的支持

  1. 在内存中,将某些工作区成员依赖项升级到最新的破坏性版本
  • 必须使用 ^ 版本要求运算符(默认)
  • 不得重命名,因为重命名通常用于允许多个相同包的版本,并且用户可能不希望更改版本
  • 可以通过命名进一步限制到特定的依赖项
  1. 使用这些新的版本要求协调 lockfile
  2. 写出新的版本要求,保留原始要求中使用的精度(例如,1.0 将升级到 2.0,而不是 2.0.5

从 2024-06-09 的 nightly 版本开始可以使用此功能。

.crate 来源

随着 xz 后门的出现,人们对验证已发布的 .crate 是否与仓库匹配的兴趣日益浓厚。Cargo 已经在 .crate 中包含一个 cargo_vcs_info.json 文件,以标识运行 cargo publish 的内容。一个问题是,如果使用了 --allow-dirty,则不会生成此文件 (#13695)。

经过一些讨论,torhovland 创建了 #13960,以便我们始终生成 cargo_vcs_info.json,但在使用 --allow-dirty 时包含一个 dirty: "true" 字段。

有关使用此文件的更多调查,请参阅 999 crates of Rust on the wall

cargo publish --workspace

torhovlandjneem 挺身而出,分析了在 #1169#10948 中添加 --workspace 需要做的事情。他们开始着手支持 cargo package --workspace 的里程碑。

要解决的第一个问题是如何为尚未上传的包生成有效的 lockfile。我们在 Office Hours 中讨论了这个问题,并决定在 #13926 中实现一个内部的包源覆盖系统。这将允许本地包假装在 crates.io 上。这听起来像一个非常有用的未来功能,但是我们有意决定仅在内部使用它,并且只是非常犹豫地实现了它,因为该功能的通用版本将成为 依赖混淆攻击 的源头。

现在覆盖源已实现,#13947 的工作仍在继续。

快照测试

来自 1.78 的更新

Cargo 有一个自制的 CLI 断言框架,支持以下功能

  • 修订
  • Jsonlines
  • 无序文本和 jsonlines
  • 失败时的漂亮 diff

由于围绕这个框架的社区较少,因此知识更加专业化,不太可能被记录下来,并且我们在功能开发方面需要靠自己。

epageMuscraft 一直在将 cargo-test-support 中的概念泛化为 snapbox。自 cargo add 以来,这已被用于 cargo 中的 UI 测试。

由于今年多次手动编辑几乎每个测试而感到沮丧,epage 深入研究了使用 cargo-test-support 弥补功能差距,并在 0.5.11 和 0.6.0 版本中发布了此功能。

我们首先在 #13980 中移植了非 CLI 断言,然后在 #14031 中移植了 CLI 断言。

对 cargo 贡献者的直接好处

  • 通过添加 env SNAPSHOTS=overwrite 更新测试结果
  • 自动修订非确定性的快照值(例如,[ELAPSED])或特定于平台的快照值(例如,[BROKEN_PIPE]

我们已经记录了这个过程,并在 #14039 中开放了移植工作,供任何人贡献,就像 weihanglo#14041 中所做的那样。

设计讨论

RFC 分流

在讨论 2024 Edition 时,有人提出 RFC 部分延迟是因为作者和审查团队都认为他们在等待对方。RFC 作者有真正的需求促使他们编写提案,并投入大量时间来编写 RFC 并推动讨论。出于对 RFC 作者投入的尊重的考虑,epage 希望再次尝试确保每个 RFC 的后续步骤都有明确的所有者。这甚至可能包括关闭 RFC,这是一个敏感的话题。

作为我们的第一个例子,epage 提醒我们,模板化 CARGO_TARGET_DIR 已被提议合并,并且正在等待团队成员审查。

RFC #3383:recommended-bins:当上次我们讨论 何时使用包或工作区 时,曾简要提到过这一点。此后,#[diagnostic] 属性在 1.78 中稳定下来。在 Cargo 中镜像这个概念的准入门槛要比完全设计的功能要低得多。Cargo 团队支持这个想法,并在 RFC 中分享了这一点。

RFC 3310:root rustflags:最初是为覆盖率报告而编写的,后来作者编写了 #3287。也就是说,即使有危险(请参阅缺点),该功能似乎也普遍有用。最后,我们决定推迟这个功能,因为作者没有回应。我们包含了供某人接手此功能的后续步骤。

RFC 3416:将特性作为表,而不仅仅是数组:总的来说,我们赞成这个功能继续推进。主要关注的问题是命名(我们已经解决了)和确保第三方了解 Cargo.toml 的潜在更改 (在 Zulip 上宣布)。提醒一下,我们提供了 TomlManifest 的定义,供其他人提取和使用。

自定义测试框架和 panic = "abort"

#11214 正在寻找一种方法来允许在测试和基准测试中使用 panic = "abort"。例如,理想情况下,基准测试应反映生产代码的行为,但如果生产代码使用 panic = "abort" 构建,而测试使用 panic = "unwind" 构建,那么它们可能具有不同的性能特征。

panic 是一种配置文件设置,会应用于所有构建目标。我们不希望有人更改其 bin 的此值,从而意外影响其测试。

我们还需要牢记向后兼容性。更改此设置会立即破坏人们的代码。

我们没有就如何推进此问题达成任何明确的结论。

简写清单语法

RFC #3502 最近已合并。在其整个生命周期中,我们偶尔会集思广益地寻找方法来进一步简化该功能,例如 支持将 package.edition 作为数字而不仅仅是字符串

删除对 [package] 标题的需求:相反,每个 package 字段都可以在清单的顶层工作。对于 cargo-script,这意味着你可以执行以下操作

edition = "2024"

而不是

package.edition = "2024"

[package]
edition = "2024"

提出的主要问题是

  • 在使用 [workspace] 时检测包是否存在
  • 潜在的实现复杂性,尤其是在良好的错误报告方面
  • 如果我们通过将其限制为 cargo-script 来简化此过程,则会增加手动将 cargo-script 转换为多文件包的步骤

总的来说,这是现在不必决定的事情。相反,我们可以观察人们如何使用 cargo-script,并确定需要哪些改进。

嵌入式 build.rs提出了以下 Cargo.toml 替代方案,以替代独立的 build.rs

build.rs = '''
fn main() {
  ...
}
'''
# or
build.rs.output = '''
cargo::directive=...
'''

虽然 build.rs.output 可能在添加 lint 配置之前帮助了 check-cfg,但似乎很少会用到它。

在清单中嵌入 Rust 源代码将允许 cargo-script 具有 build.rs。虽然对于一般情况,拥有完全独立的 -sys 包是合理的,但在某些一次性“我只是想要一个小库”的用例中可能会用到。我们之前决定在 cargo-script 中嵌入其他内容(构建脚本、配置、proc-macros、附加源代码和包)是一种反功能,我们需要重新审视的不仅仅是这一点。相反,我们希望在这种情况下鼓励使用多文件包。

作为替代方案,我们讨论了 metabuild,它由于缺乏实际应用以及像 system-deps 这样的填充物而一直处于不稳定的状态。

同样,这可能应该等到 cargo-script 稳定并使用一段时间后再考虑。

rustc 的泄漏抽象

Cargo 依赖于 rustc 的行为,但用户经常需要访问尚未被抽象化的 rustc 功能 (#12739),从而导致使用 RUSTFLAGScargo rustc

这里潜藏着风险。最近一个导致用户困惑的例子是,RUSTFLAGS=-Copt-level=3 cargo test 会禁用调试断言,而 profile.test.opt-level = 3 则不会 (#14033)。

类似地,cargo build --message-format=json 不会报告编译器返回的所有 json 消息。这使得 cargo show-asm 的工作更加困难,因为他们希望指示编译器发出汇编代码,并让编译器告诉他们在哪里可以找到汇编文件,而不是尝试猜测 (#13672)。Cargo 过滤掉了与实现细节相关的消息(例如,发出的文件和 target-dir 的不稳定方面),但不知道何时返回消息是因为它与实现细节相关还是用户通过 RUSTFLAGS 请求的。虽然人们可以找到依赖这些相同实现细节的方法,但用户有意识地偏离既定路径与我们认可它在未来发展方面给我们带来的负担之间存在差异。在团队会议期间,我们没有找到令我们满意的解决方案,并在该问题上报告了我们不尽人意的想法。然而,在办公时间,一个关于此的 rustc 功能的想法是允许指定一个根目录,而不仅仅是一个文件,用于输出。

其他

  • zulip 上简要讨论了 zstd .crate 文件,另请参阅 #2526
  • 每日报告Eh2406 提供,关于 PubGrub 版本解决算法的 Rust 实现的进展情况
  • #13709 上为 RFC 3553:SBOM 取得了更多进展
  • 我们已经确定了 cargo update --precise <prerelease> 的更多极端情况 (#13290)

没有进展的重点领域

这些是 Cargo 团队成员感兴趣的领域,但在本次开发周期中没有可报告的进展。

准备开发

需要设计和/或实验

计划中

您可以如何提供帮助

如果您有改进 cargo 的想法,我们建议首先查看 我们的积压,然后在 Internals 上探索这个想法。

如果您希望解决某个未在此处讨论的特定问题,您可以采取以下一些步骤来帮助推动它:

  • 总结现有对话(示例:更好地支持 Docker 层缓存Cargo.lock 策略的更改MSRV 感知解析器
  • 记录其他生态系统的现有技术,以便我们可以在其他人已完成的工作的基础上构建,并在有意义的地方为用户提供熟悉的东西
  • 记录 Cargo 中的相关问题和解决方案,以便我们了解是否在正确的抽象层进行解决
  • 在这些帖子的基础上,提出一个解决方案,其中考虑到上述信息和 cargo 的兼容性要求 (示例)

我们可以在 zulip 上帮助指导人们解决 S-accepted 问题,并且您可以在 贡献者办公时间 与我们实时交谈。如果您想帮助解决此处提到的大型项目之一,并且刚开始,修复一些问题 将有助于您熟悉该流程和期望,使事情进展得更加顺利。如果您想在没有导师的情况下解决某些问题,那么对您需要自己完成的工作的期望会更高。