Rust實(shí)現(xiàn)按需環(huán)境控制,Cargo.toml中的特性配置說明,跨平臺(tái),可代替環(huán)境變量
Cargo的配置術(shù)語:特性 features
Cargo 的Cargo.toml 文件的語法 "特性" features提供了一種表達(dá) 條件編譯[1] 和 可選依賴項(xiàng) 的機(jī)制。包在 Cargo.toml 中的 [features] 表中定義了一組具有名稱的特征,每個(gè)特征可以被啟用或禁用。在構(gòu)建包時(shí),可以通過命令行參數(shù)(如 --features)來啟用包中的特征。對于依賴項(xiàng),可以在 Cargo.toml 中的依賴項(xiàng)聲明中啟用特征。
Cargo.toml 文件的 [features] 設(shè)置
特性是在 Cargo.toml 中的 [features] 表中定義的。每個(gè)特性都定義了一個(gè)數(shù)組,其中包含其他特征或可選依賴項(xiàng),它們被該特性啟用。以下示例展示了如何使用特性來實(shí)現(xiàn)一個(gè)支持不同圖像格式的 2D 圖像處理庫。
[features]
# 定義了一個(gè)名為webp的 特性,它內(nèi)部暫不定義任何配置項(xiàng)。
webp = []
這個(gè)特性啟用后,可以在編譯時(shí)通過 cfg 表達(dá)式 (即 cfg-macro 語法)選擇性地包含支持該特性的代碼。例如,在包的 lib.rs 中可以:
// 這個(gè)條件編譯,包含了一個(gè)模塊,實(shí)現(xiàn) WEBP 支持。
// 代碼中可根據(jù)是否啟用了 "webp" 特性來選擇是否包含 WEBP 支持。若支持,則`pub mod webp`有效。
#[cfg(feature = "webp")]
pub mod webp;
cargo 通過使用rustc [--cfg flag] 來幫助代碼判斷某個(gè)特性是否支持;代碼中通過[cfg attribute] 或 [cfg macro]實(shí)現(xiàn)在符合特性的時(shí)候執(zhí)行代碼段。
特性可以列出其他特性來啟用。例如,ICO 圖像格式可以包含 BMP 和 PNG 圖像,所以當(dāng)它被啟用時(shí),它應(yīng)該確保其他特性也被啟用。
[features]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []
Cargo 使用 rustc 的 cfg-expressions 來設(shè)置包中的特性。代碼可以使用 cfg-macro 來測試特性是否可用,以執(zhí)行緊跟的相關(guān)代碼(僅在特性啟用的情況下編譯和運(yùn)行的代碼)。
例如,ICO 圖像格式可以包含 BMP 和 PNG 圖像,因此當(dāng)它被啟用時(shí),它應(yīng)該確保其他特性也被啟用。
特性名稱允許包含來自 https://unicode.org/reports/tr31/ 的字符(包括大多數(shù)字母),此外還允許從 _ 或數(shù)字 0 到 9 開始,并在第一個(gè)字符之后可能包含 -、+ 或 .。
default 特性是自帶的
默認(rèn)情況下,所有特性都處于禁用狀態(tài),除非明確啟用??梢酝ㄟ^指定 default 特性來更改此行為:
[features]
default = ["ico", "webp"]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []
當(dāng)包被構(gòu)建時(shí),default 特性被啟用,從而啟用了列出的特性。這種行為可以通過:
- --no-default-features 命令行選項(xiàng)禁用包的默認(rèn)特性。
- default-features = false 選項(xiàng)可以在 Cargo.toml的 依賴聲明 (dependency-features) 中指定。
注意:選擇默認(rèn)特性集時(shí)要小心。默認(rèn)特性集是方便用戶不用費(fèi)心選擇哪些特性被啟用,但也有缺點(diǎn)。依賴項(xiàng)會(huì)自動(dòng)啟用默認(rèn)特性,除非 default-features = false 被指定。這在希望 默認(rèn)特性不被啟用時(shí)可能要額外告訴編譯器,尤其是在依賴圖中有多個(gè)依賴項(xiàng)時(shí)尤其如此。每個(gè)包必須確保default-features = false 被指定,以避免啟用它們。
另一個(gè)問題是在從默認(rèn)特性集中移除特性時(shí),這可能會(huì)破壞 SemVer 兼容性,因此你必須確保 你不會(huì)移除這些特性。
可選依賴
依賴的特性可被標(biāo)記為可選的(optional),這表示它們不會(huì)被默認(rèn)編譯。例如,讓我們假設(shè)我們的 2D 圖像處理庫使用一個(gè)外部包來處理 GIF 圖像。這可以用以下方式表達(dá):
[dependencies]
gif = { version = "0.11.1", optional = true }
可選特性會(huì)隱式定義為與依賴同名的特性。這意味著代碼中可以使用相同的 cfg(feature = "gif") 語法,并且依賴可以像特性一樣啟用,例如 --features gif。
注意:[feature]表中的特性不能與依賴同一名稱。在rust的 nightly渠道上才有,可以在nightly版的rust中啟用 [namespaced 特性],注意這個(gè)是rust試驗(yàn)階段的功能。
額外的特性可以啟用可選依賴,只要在特性列表中包含可選依賴的名字。例如,假設(shè)為了支持AVIF圖像格式,我們的庫需要兩個(gè)其他的依賴:
[dependencies]
ravif = { version = "0.6.3", optional = true }
rgb = { version = "0.8.25", optional = true }
[features]
avif = ["ravif", "rgb"]
本例中,avif特性會(huì)啟用兩個(gè)指定的依賴。
注意:另外的一種可選依賴的方法是使用 [platform-specific dependencies],這個(gè)是條件依賴,根據(jù)目標(biāo)平臺(tái)。
特定于平臺(tái)的依賴項(xiàng)采用相同的格式,但在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運(yùn)算符組合各種 cfg 名稱/值對。請注意cfg語法僅在 Cargo 0.9.0(Rust 1.8.0)之后可用.
除了#[cfg]語法,Cargo 還支持列出依賴關(guān)系適用的完整目標(biāo):
[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"
[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"
如果您使用的是自定義目標(biāo)規(guī)范,請引用完整路徑和文件名:
[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默認(rèn)特性 可以用default-features = false聲明實(shí)現(xiàn)禁用,完整的示例如下:
[dependencies]
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
注意:這可能無法確保默認(rèn)特性被禁用。如果另一個(gè)依賴項(xiàng)依賴了flate2且它未聲明default-features = false,則flate2的默認(rèn)特性將被啟用。
依賴包的特性也可以在[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"語法也會(huì)啟用package-name,即使它是一個(gè)可選依賴項(xiàng)。
通過cargo的命令行參數(shù)控制
cargo build 命令支持控制是否啟用指定的feature,有3個(gè)相關(guān)參數(shù):
-F, --features <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: 參數(shù)啟用所指定FEATURES特性是否啟用。多個(gè)特性可以用逗號(hào)或空格分隔。如果使用空格,請確保在運(yùn)行Cargo從shell(例如--features "foo bar")。如果在一個(gè)[工作區(qū)]中構(gòu)建多個(gè)包,則可以使用package-name/feature-name語法來指定 特定工作區(qū)成員的特性。
- --all-features參數(shù),啟用指定的包的所有特性。
- --no-default-features參數(shù),指定不啟用指定包的default特性。
參考資料
[1]條件編譯: https://doc.rust-lang.org/cargo/reference/features.html#conditional-compilation