Rust实现按需环境控制,Cargo.toml中的特性配置说明,跨平台,可代替环境变量

2023年 12月 27日 36.3k 0

Cargo的配置术语:特性 features

Cargo 的Cargo.toml 文件的语法 "特性" features提供了一种表达 条件编译[1] 和 可选依赖项 的机制。包在 Cargo.toml 中的 [features] 表中定义了一组具有名称的特征,每个特征可以被启用或禁用。在构建包时,可以通过命令行参数(如 --features)来启用包中的特征。对于依赖项,可以在 Cargo.toml 中的依赖项声明中启用特征。

Cargo.toml 文件的 [features] 设置

特性是在 Cargo.toml 中的 [features] 表中定义的。每个特性都定义了一个数组,其中包含其他特征或可选依赖项,它们被该特性启用。以下示例展示了如何使用特性来实现一个支持不同图像格式的 2D 图像处理库。

[features]
# 定义了一个名为webp的 特性,它内部暂不定义任何配置项。
webp = []

这个特性启用后,可以在编译时通过 cfg 表达式 (即 cfg-macro 语法)选择性地包含支持该特性的代码。例如,在包的 lib.rs 中可以:

// 这个条件编译,包含了一个模块,实现 WEBP 支持。
// 代码中可根据是否启用了 "webp" 特性来选择是否包含 WEBP 支持。若支持,则`pub mod webp`有效。
#[cfg(feature = "webp")]
pub mod webp;

cargo 通过使用rustc [--cfg flag] 来帮助代码判断某个特性是否支持;代码中通过[cfg attribute] 或 [cfg macro]实现在符合特性的时候执行代码段。

特性可以列出其他特性来启用。例如,ICO 图像格式可以包含 BMP 和 PNG 图像,所以当它被启用时,它应该确保其他特性也被启用。

[features]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []

Cargo 使用 rustc 的 cfg-expressions 来设置包中的特性。代码可以使用 cfg-macro 来测试特性是否可用,以执行紧跟的相关代码(仅在特性启用的情况下编译和运行的代码)。

例如,ICO 图像格式可以包含 BMP 和 PNG 图像,因此当它被启用时,它应该确保其他特性也被启用。

特性名称允许包含来自 https://unicode.org/reports/tr31/ 的字符(包括大多数字母),此外还允许从 _ 或数字 0 到 9 开始,并在第一个字符之后可能包含 -、+ 或 .。

default 特性是自带的

默认情况下,所有特性都处于禁用状态,除非明确启用。可以通过指定 default 特性来更改此行为:

[features]
default = ["ico", "webp"]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []

当包被构建时,default 特性被启用,从而启用了列出的特性。这种行为可以通过:

  • --no-default-features 命令行选项禁用包的默认特性。
  • default-features = false 选项可以在 Cargo.toml的 依赖声明 (dependency-features) 中指定。

注意:选择默认特性集时要小心。默认特性集是方便用户不用费心选择哪些特性被启用,但也有缺点。依赖项会自动启用默认特性,除非 default-features = false 被指定。这在希望 默认特性不被启用时可能要额外告诉编译器,尤其是在依赖图中有多个依赖项时尤其如此。每个包必须确保default-features = false 被指定,以避免启用它们。

另一个问题是在从默认特性集中移除特性时,这可能会破坏 SemVer 兼容性,因此你必须确保 你不会移除这些特性。

可选依赖

依赖的特性可被标记为可选的(optional),这表示它们不会被默认编译。例如,让我们假设我们的 2D 图像处理库使用一个外部包来处理 GIF 图像。这可以用以下方式表达:

[dependencies]
gif = { version = "0.11.1", optional = true }

可选特性会隐式定义为与依赖同名的特性。这意味着代码中可以使用相同的 cfg(feature = "gif") 语法,并且依赖可以像特性一样启用,例如 --features gif。

注意:[feature]表中的特性不能与依赖同一名称。在rust的 nightly渠道上才有,可以在nightly版的rust中启用 [namespaced 特性],注意这个是rust试验阶段的功能。

额外的特性可以启用可选依赖,只要在特性列表中包含可选依赖的名字。例如,假设为了支持AVIF图像格式,我们的库需要两个其他的依赖:

[dependencies]
ravif = { version = "0.6.3", optional = true }
rgb = { version = "0.8.25", optional = true }

[features]
avif = ["ravif", "rgb"]

本例中,avif特性会启用两个指定的依赖。

注意:另外的一种可选依赖的方法是使用 [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 一样,这里的语法支持not,any,和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" }

依赖的特性

可在依赖声明中启用依赖的特性。features键指示要启用的特性:

[dependencies]
# cargo.toml的依赖声明中启用 serde包的 `derive` 特性.
serde = { version = "1.0.118", features = ["derive"] }

default默认特性 可以用default-features = false声明实现禁用,完整的示例如下:

[dependencies]
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }

注意:这可能无法确保默认特性被禁用。如果另一个依赖项依赖了flate2且它未声明default-features = false,则flate2的默认特性将被启用。

依赖包的特性也可以在[features]表中启用,语法为"package-name/feature-name"。例如:

[dependencies]
jpeg-decoder = { version = "0.1.20", default-features = false }

[features]
# 通过启用jpeg-decoder的`rayon`特性,打开并行处理支持
parallel = ["jpeg-decoder/rayon"]

注意:"package-name/feature-name"语法也会启用package-name,即使它是一个可选依赖项。

通过cargo的命令行参数控制

cargo build 命令支持控制是否启用指定的feature,有3个相关参数:

-F, --features   Space or comma separated list of features to activate
    --all-features         Activate all available features
    --no-default-features  Do not activate the `default` feature

通过命令行控制特性的启用:

  • --features FEATURES: 参数启用所指定FEATURES特性是否启用。多个特性可以用逗号或空格分隔。如果使用空格,请确保在运行Cargo从shell(例如--features "foo bar")。如果在一个[工作区]中构建多个包,则可以使用package-name/feature-name语法来指定 特定工作区成员的特性。
  • --all-features参数,启用指定的包的所有特性。
  • --no-default-features参数,指定不启用指定包的default特性。

参考资料

[1]条件编译: https://doc.rust-lang.org/cargo/reference/features.html#conditional-compilation

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论