Cargo 和编译器团队很高兴地宣布,从 Rust 1.80(或 nightly-2024-05-05)开始,每个可达到的 #[cfg]
都将自动检查它们是否与预期的配置名称和值匹配。
这有助于验证 crate 是否正确处理不同目标平台或功能的条件编译。它确保 cfg 设置在预期和使用之间保持一致,有助于在开发过程的早期捕获潜在的错误或问题。
这解决了新手和高级用户的常见陷阱。
这是我们致力于提供以用户为中心的工具的又一步,我们很兴奋地看到它最终得到修复,距离最初的 RFC 30131 已经过去两年多了。
特性概览
每次声明 Cargo feature 时,该 feature 都会被转换为一个配置,传递给 rustc
(Rust 编译器),以便它可以使用该配置以及众所周知的 cfgs来验证是否有任何 #[cfg]
、 #![cfg_attr]
和 cfg!
具有意外的配置,并使用 unexpected_cfgs
lint 报告警告。
Cargo.toml
:
[package]
name = "foo"
[features]
lasers = []
zapping = []
src/lib.rs
:
#[cfg(feature = "lasers")] // This condition is expected
// as "lasers" is an expected value
// of the `feature` cfg
fn shoot_lasers() {}
#[cfg(feature = "monkeys")] // This condition is UNEXPECTED
// as "monkeys" is NOT an expected
// value of the `feature` cfg
fn write_shakespeare() {}
#[cfg(windosw)] // This condition is UNEXPECTED
// it's supposed to be `windows`
fn win() {}
cargo check
:
期望自定义 cfgs
更新:此部分在 nightly-2024-05-19 发布时添加。
在 Cargo 的角度来看:自定义 cfg 是既不由
rustc
定义,也不由 Cargo feature 定义的 cfg。可以理解为tokio_unstable
,has_foo
, ... 但不是feature = "lasers"
,unix
或debug_assertions
某些 crate 可能会使用自定义 cfgs,例如 loom
、fuzzing
或 tokio_unstable
,它们期望从环境(RUSTFLAGS
或其他方式)获取,并且在编译时始终是静态已知的。对于这些情况,Cargo 通过 [lints]
表提供了一种将这些 cfgs 静态声明为预期的方式。
将这些自定义 cfgs 定义为预期是通过 [lints.rust.unexpected_cfgs]
下的特殊 check-cfg
配置完成的。
Cargo.toml
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)', 'cfg(fuzzing)'] }
构建脚本中的自定义 cfgs
另一方面,一些 crate 使用的自定义 cfgs 是由 crate build.rs
中的某些逻辑启用的。对于这些 crate,Cargo 提供了一个新指令: cargo::rustc-check-cfg
2 (或对于较旧的 Cargo 版本,使用 cargo:rustc-check-cfg
)。
使用的语法在 rustc book 的 检查配置 部分中进行了描述,但简而言之,--check-cfg
的基本语法是
cfg(name, values("value1", "value2", ..., "valueN"))
请注意,每个自定义 cfgs 都必须始终是预期的,无论 cfg 是否处于活动状态!
build.rs
示例
build.rs
:
fn main() {
println!("cargo::rustc-check-cfg=cfg(has_foo)");
// ^^^^^^^^^^^^^^^^^^^^^^ new with Cargo 1.80
if has_foo() {
println!("cargo::rustc-cfg=has_foo");
}
}
每个
cargo::rustc-cfg
都应该附带一个无条件的cargo::rustc-check-cfg
指令,以避免出现类似这样的警告:unexpected cfg condition name: has_foo
。
等效表
cargo::rustc-cfg |
cargo::rustc-check-cfg |
---|---|
foo |
cfg(foo) 或 cfg(foo, values(none())) |
foo="" |
cfg(foo, values("")) |
foo="bar" |
cfg(foo, values("bar")) |
foo="1" 和 foo="2" |
cfg(foo, values("1", "2")) |
foo="1" 和 bar="2" |
cfg(foo, values("1")) 和 cfg(bar, values("2")) |
foo 和 foo="bar" |
cfg(foo, values(none(), "bar")) |
更多详细信息可以在 rustc
book 中找到。
常见问题
可以禁用吗?
对于 Cargo 用户,此功能始终启用,无法禁用,但是像任何其他 lint 一样,它可以被控制:#![warn(unexpected_cfgs)]
。
lint 会影响依赖项吗?
不会,像大多数 lint 一样,由于 cap-lints,unexpected_cfgs
只会为本地包报告。
RUSTFLAGS
环境变量交互?
它如何与 您应该可以像以前一样使用 RUSTFLAGS
环境变量。目前只检查代码中的使用情况,而不检查 --cfg
参数。
这意味着执行 RUSTFLAGS="--cfg tokio_unstable" cargo check
不会报告任何警告,除非在您的本地 crate 中使用了 tokio_unstable
,在这种情况下,crate 作者需要确保使用该 crate 的 build.rs
中的 cargo::rustc-check-cfg
来声明该自定义 cfg 是预期的。
build.rs
的情况下期望自定义 cfgs?
如何在没有 更新:Cargo with nightly-2024-05-19 现在提供了 [lints.rust.unexpected_cfgs.check-cfg]
配置来处理静态已知的自定义 cfgs。
目前没有其他方法来声明自定义 cfg 是预期的,除了使用 build.rs
中的 cargo::rustc-check-cfg
。
不希望使用 build.rs
并且不能使用 [lints.rust.unexpected_cfgs.check-cfg]
的 crate 作者,建议改用 Cargo features。
它如何与其他构建系统交互?
默认情况下,非基于 Cargo 的构建系统不受该 lint 的影响。希望具有相同功能的构建系统作者应查看 rustc
文档中关于 --check-cfg
标志的详细说明,以了解如何实现相同的功能。