Cargo 依賴指定

2021-09-27 14:06 更新

依賴指定

您的箱子,可以依賴多個(gè)來(lái)源的庫(kù),如crates.io,git的存儲(chǔ)庫(kù)或本地文件系統(tǒng)上的子目錄。您還可以臨時(shí)覆蓋依賴項(xiàng)的位置 - 例如, 便于能夠測(cè)試您在本地工作的依賴項(xiàng)中的錯(cuò)誤修復(fù)。您可以為不同的平臺(tái),和或僅在開(kāi)發(fā)期間使用不同的依賴項(xiàng)。我們來(lái)看看如何做到這些.

指定依賴,來(lái)自 crates.io

默認(rèn)情況下,Cargo 是準(zhǔn)備好,在crates.io上查找依賴項(xiàng)。在這種情況下,只需要名稱和版本字符串。在Cargo 指南,我們選擇了一個(gè)依賴項(xiàng)-time箱:

[dependencies]
time = "0.1.12"

字符串"0.1.12"是一個(gè)semver版本格式字符串。由于此字符串中沒(méi)有任何運(yùn)算符,因此它的解釋方式與我們指定的"^0.1.12"方式相同,而^被稱為跳脫條件.

Caret requirements(跳脫條件)

跳脫條件: 允許 SemVer 兼容更新指定版本。新的版本允許更新的條件是,不修改最左邊的非零數(shù)字(無(wú)論major,minor,patch)。在這種情況下,如果我們執(zhí)行了cargo update -p time,Cargo 應(yīng)該更新我們的0.1.13版本(如果是最新的0.1.z發(fā)布),但不會(huì)更新為0.2.0。相反,我們?nèi)魧姹咀址付閊1.0,Cargo 應(yīng)更新至1.1,如果是最新的1.y發(fā)布,但不是2.0版本。0.0.x并不與任何其他版本兼容.

以下是一些跳脫條件的例子以及它們?cè)试S的版本:

^1.2.3 := >=1.2.3 <2.0.0
^1.2 := >=1.2.0 <2.0.0
^1 := >=1.0.0 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.2 := >= 0.2.0 < 0.3.0
^0.0.3 := >=0.0.3 <0.0.4
^0.0 := >=0.0.0 <0.1.0
^0 := >=0.0.0 <1.0.0

此兼容性約定與 SemVer ,在處理 1.0.0 之前的版本方面有所不同。雖然 SemVer 說(shuō)在 1.0.0 之前沒(méi)有兼容性,但 Cargo 認(rèn)為0.x.y是兼容0.x.z,這里y ≥ z和x > 0.

Tilde 條件

Tilde 條件指定具有更新最小版本的一定能力。如果指定 major 版本,minor 版本和 patch 程序版本,或僅指定 major 版本和 minor 版本,則僅允許 patch 程序級(jí)別更改。如果僅指定 major 版本,則允許進(jìn)行 minor 和 patch 級(jí)別更改.

~1.2.3是 Tilde 條件的一個(gè)例子.

~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0

通配符要求

通配符條件允許任何通配符所在的版本.

*,1.*和1.2.*是通配符條件的示例.

* := >=0.0.0
1.* := >=1.0.0 <2.0.0
1.2.* := >=1.2.0 <1.3.0

Inequality requirements(范圍條件)

范圍條件允許手動(dòng)指定要依賴的版本范圍或確切版本.

以下是范圍條件的一些示例:

>= 1.2.0
> 1
< 2
= 1.2.3

多版本條件

多個(gè)版本,要求用逗號(hào)分隔,例如>= 1.2, < 1.5.

依賴指定,來(lái)自 git 存儲(chǔ)庫(kù)

依賴于位于git存儲(chǔ)庫(kù)的庫(kù),您需要指定的最小信息,為一個(gè)git字段,其是存儲(chǔ)庫(kù)的github位置:

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand" }

Cargo 將取得git,然后在這個(gè)位置找到一個(gè)存儲(chǔ)庫(kù)的請(qǐng)求箱子的Cargo.toml。方式是對(duì)git存儲(chǔ)庫(kù)里面的任何地方(不一定在根目錄) - 例如,指定工作區(qū)中的成員包名稱,和設(shè)置git到包含工作區(qū)的存儲(chǔ)庫(kù)).

由于我們尚未指定任何其他信息,因此 Cargo 假定我們打算使用最新的提交master分支,來(lái)構(gòu)建我們的包。你可以將git字段和rev,tag, 還有branch,這些用于指定其他內(nèi)容的字段組合起來(lái)。這是一個(gè)指定您希望在名為next分支上,使用最新提交的示例:

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand", branch = "next" }

路徑,依賴指定

隨著時(shí)間的推移,我們來(lái)自指南的hello_world示例已大幅增長(zhǎng)! 它已經(jīng)到了我們可能想分出一個(gè)單獨(dú)的箱子供其他人使用的地步。為此,Cargo 支持路徑依賴通常是位于一個(gè)存儲(chǔ)庫(kù)中的子箱。讓我們開(kāi)始在hello_world包的內(nèi)部制作一個(gè)新的箱子:

# inside of hello_world/
$ cargo new hello_utils

這將創(chuàng)建一個(gè)新文件夾hello_utils,里面有一個(gè)Cargo.toml和src文件夾已準(zhǔn)備好進(jìn)行配置。為了告訴 Cargo,請(qǐng)打開(kāi)hello_world/Cargo.toml,并添加你的hello_utils依賴:

[dependencies]
hello_utils = { path = "hello_utils" }

這告訴 Cargo 我們依賴于一個(gè)叫做hello_utils的箱子,這能在hello_utils文件夾找到(相對(duì)于,寫(xiě)在Cargo.toml路徑).

就是這樣! 下一步cargo build將自動(dòng)構(gòu)建hello_utils,以及它自己的所有依賴項(xiàng),其他人也可以開(kāi)始使用它。但是,crates.io不允許僅使用 路徑指定依賴項(xiàng) 的包。如果我們想發(fā)布我們的hello_world箱子,我們需要發(fā)布一個(gè)版本hello_utils至crates.io,并在依賴項(xiàng)行中指定其版本:

[dependencies]
hello_utils = { path = "hello_utils", version = "0.1.0" }

依賴覆蓋

Cargo 中有許多方法支持,覆蓋依賴關(guān)系以及控制依賴關(guān)系圖。但是,這些選項(xiàng)通常僅在工作區(qū)級(jí)別可用,并且不通過(guò)依賴項(xiàng)傳播。換句話說(shuō),"應(yīng)用程序"具有覆蓋依賴關(guān)系的能力,但"庫(kù)"卻沒(méi)有。

許多場(chǎng)景,會(huì)產(chǎn)生想,覆蓋依賴性或以其他方式改變某些依賴關(guān)系的愿望。然而,他們中的大多數(shù)都可以歸結(jié)為,將箱子發(fā)布到 crates.io 之前使用箱子(覆蓋依賴)的能力。例如:

  • 您編寫(xiě)的 crate ,也用于您編寫(xiě)的更大應(yīng)用程序中,并且您希望測(cè)試在更大應(yīng)用程序內(nèi),crate的錯(cuò)誤修復(fù)情況。
  • 不是你編寫(xiě)的上游包,現(xiàn)在其 git 存儲(chǔ)庫(kù)的主分支上,有一個(gè)新功能或錯(cuò)誤修復(fù),您要測(cè)試它。
  • 您即將發(fā)布新版本的 major 版本,但您希望在整個(gè)軟件包中進(jìn)行集成測(cè)試,以確保新的主要版本能夠正常運(yùn)行.
  • 您已經(jīng)為上游的軟件包提交了一個(gè)針對(duì)您找到的錯(cuò)誤的修復(fù)程序,但是您希望立即讓您的應(yīng)用程序依賴,此程序包的固定修復(fù)版本,以避免錯(cuò)誤修復(fù)程序被拒絕合并.

這些場(chǎng)景目前都是通過(guò)[patch] 清單部分 解決的,從歷史上看,其中一些方案是該[replace]部分解決的,但我們?cè)谶@里會(huì)記錄[patch]解決的部分。

測(cè)試一個(gè)錯(cuò)誤修復(fù)

假設(shè)你正在使用uuid crate,但是當(dāng)你正在研究它時(shí),你會(huì)發(fā)現(xiàn)一個(gè)錯(cuò)誤.但是,你很有進(jìn)取心,所以你決定嘗試修復(fù)這個(gè) bug! 最初你的清單看起來(lái)像:

[package]
name = "my-library"
version = "0.1.0"
authors = ["..."]
[dependencies]
uuid = "1.0"

我們要做的第一件事是克隆uuid存儲(chǔ)庫(kù),到本地:

$ git clone https://github.com/rust-lang-nursery/uuid

接下來(lái)我們將編輯my-library-Cargo.toml,為:

[patch.crates-io]
uuid = { path = "../path/to/uuid" }

在這里,我們宣布我們是*修補(bǔ)(patch)*來(lái)源crates-io,其有一個(gè)新的依賴,這將有效地添加本地(簽出 checkout)版本uuid到 crates.io 注冊(cè)表,指向本地包。

接下來(lái)我們需要確保我們的鎖(lock)文件已更新為,使用此新版本uuid,所以我們的包使用本地簽出的副本,而不是 crates.io 中的副本。[patch]工作方式是它將從../path/to/uuid加載依賴,然后每當(dāng) crates.io 查詢uuid的版本時(shí),它也會(huì)返回本地版本.

這意味著本地簽出的版本號(hào)很重要,會(huì)影響是否使用該補(bǔ)丁。我們的清單宣布uuid = "1.0",這意味著我們只會(huì)解析>= 1.0.0, < 2.0.0,和 Cargo 的貪婪解析算法,也意味著我們將解析到該范圍內(nèi)的最大版本。通常情況下這并不重要,因?yàn)?git 存儲(chǔ)庫(kù)的版本已經(jīng)更大,或與 crates.io 上發(fā)布的最大版本相匹配,但重要的是要記住這一點(diǎn)!

無(wú)論如何,通常您現(xiàn)在需要做的就是:

$ cargo build
   Compiling uuid v1.0.0 (.../uuid)
   Compiling my-library v0.1.0 (.../my-library)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs

就是這樣! 您現(xiàn)在正在使用本地版本uuid構(gòu)建(注意構(gòu)建輸出中括號(hào)中的路徑)。如果您沒(méi)有看到構(gòu)建本地路徑版本,那么您可能需要運(yùn)行cargo update -p uuid --precise $version,這里$version是本地簽出版本的uuid副本。

一旦你修復(fù)了你最初發(fā)現(xiàn)的錯(cuò)誤,你要做的下一件事就是將其作為拉取請(qǐng)求提交給uuid箱子本身。一旦你完成了這個(gè),你也可以更新下[patch]部分。[patch]里面的內(nèi)容列表就像是[dependencies]部分,所以一旦你的拉動(dòng)請(qǐng)求合并,你就可以改變你的path依賴:

[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

Working with an unpublished minor version

與 一個(gè)未發(fā)布的次要版本,一起工作

現(xiàn)在讓我們稍微改變一下,從錯(cuò)誤修復(fù),變成要添加功能。在努力my-library的同時(shí),你發(fā)現(xiàn)需要uuid箱的一個(gè)全新的功能。而您已實(shí)現(xiàn)uuid此功能,并在[patch]上面進(jìn)行本地測(cè)試,并提交了拉取請(qǐng)求。讓我們來(lái)看看在實(shí)際發(fā)布之前,你如何繼續(xù)使用和測(cè)試它。

我們也說(shuō)當(dāng)前版本的uuid,在 crates.io 上是1.0.0版本,但從提交那時(shí)起,git 存儲(chǔ)庫(kù)的主分支已更新為1.0.1。此分支包含您之前提交的新功能。要使用此存儲(chǔ)庫(kù),我們將編輯我們的Cargo.toml,看起來(lái)像

[package]
name = "my-library"
version = "0.1.0"
authors = ["..."]
[dependencies]
uuid = "1.0.1"
[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

注意我們對(duì)本地uuid的依賴已更新為1.0.1,因?yàn)檫@是我們?cè)谙渥影l(fā)布后實(shí)際需要的東西。但是,這個(gè)版本在 crates.io 上不存在,所以我們提供給它清單的[patch]部分.

現(xiàn)在,當(dāng)我們的庫(kù)被構(gòu)建時(shí),它將uuid從 git 存儲(chǔ)庫(kù)取出,并解析到存儲(chǔ)庫(kù)中的 1.0.1 ,而不是嘗試從 crates.io 下載版本。一旦 1.0.1 發(fā)布在 crates.io 上,那[patch]部分就可以刪除了。

值得注意的是,[patch]是連帶關(guān)系。假設(shè)您在更大的包中使用my-library,例如:

[package]
name = "my-binary"
version = "0.1.0"
authors = ["..."]
[dependencies]
my-library = { git = 'https://example.com/git/my-library' }
uuid = "1.0"
[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

記住這[patch]是連帶關(guān)系,但只能在頂層,所以我們的my-library消費(fèi)者不得不重寫(xiě)[patch]部分(如有必要的話)。不過(guò),在這里,新的uuid箱子會(huì)適用對(duì)uuid的依賴和my-library -> uuid的依賴,兩個(gè)依賴都指定了。該uuid箱 將被解析為整個(gè) crate 關(guān)系圖 的 1.0.1 版本,并且它是將從 git 存儲(chǔ)庫(kù)中提取。

Overriding repository URL

覆蓋 注冊(cè)表 URL

如果要覆蓋的依賴項(xiàng)不是加載自crates.io,你將不得不改變一下你的[patch]使用方式:

[patch."https://github.com/your/repository"]
my-library = { path = "../my-library/path" }

就是這樣!

Prepublishing a breaking change

預(yù)發(fā)布一個(gè)重要變化

讓我們來(lái)看看最后一個(gè)場(chǎng)景。若要使用一個(gè)新的主要版本的箱子,其通常伴隨著重大變化。而要堅(jiān)持使用我們以前的箱,這意味著我們將創(chuàng)建 2.0.0 版本uuid箱。在我們提交了所有上游更改后,我們可以更新我們的my-library清單,看起來(lái)像:

[dependencies]
uuid = "2.0"
[patch.crates-io]
uuid = { git = "https://github.com/rust-lang-nursery/uuid", branch = "2.0.0" }

就是這樣!與前面的示例一樣,2.0.0 版本實(shí)際上,并不存在于 crates.io 上,但我們?nèi)匀豢梢酝ㄟ^(guò)[patch]部分使用。作為一個(gè)思考練習(xí),讓我們?cè)倏纯磎y-binary(被使用)的再次表現(xiàn):

[package]
name = "my-binary"
version = "0.1.0"
authors = ["..."]
[dependencies]
my-library = { git = 'https://example.com/git/my-library' }
uuid = "1.0"
[patch.crates-io]
uuid = { git = 'https://github.com/rust-lang-nursery/uuid', branch = '2.0.0' }

請(qǐng)注意,這實(shí)際上將解析為兩個(gè)版本的uuid箱。該my-binary箱子將繼續(xù)使用 1.x.y 系列的uuid箱子,但是my-library箱 會(huì)使用 2.0.0 版本uuid。這將允許您通過(guò)依賴關(guān)系圖逐步推出對(duì)包的更改,而無(wú)需一次性更新所有內(nèi)容。

Overriding with local dependencies

覆蓋 本地依賴項(xiàng)

有時(shí)你只是暫時(shí)在一個(gè)箱子上工作,而你不想修改Cargo.toml中像上訴的[patch]部分。對(duì)于這個(gè)用例,Cargo 提供了更為有限的覆蓋版本路徑覆蓋.

路徑覆蓋是通過(guò).cargo/config指定,而不是Cargo.toml,你可以尋找有關(guān)此配置的更多文檔。在.cargo/config內(nèi),你要指定的是一個(gè)名為paths字段:

paths = ["/path/to/uuid"]

該數(shù)組應(yīng)填充包含Cargo.toml的目錄。在這種情況下,我們只是添加uuid,所以它將是唯一一個(gè)被覆蓋的。此路徑可以是包含該路徑的絕對(duì)路徑或相對(duì).cargo文件夾的路徑.

路徑覆蓋,比[patch]部分的限制更嚴(yán)格,但是,路徑覆蓋不能改變依賴圖的結(jié)構(gòu)。而當(dāng)使用路徑替換時(shí),前一組依賴項(xiàng)必須完全匹配新的Cargo.toml規(guī)格。如此,就意味著路徑覆蓋不能用于向箱添加依賴項(xiàng)的測(cè)試,而換成[patch]在該種情況下使用。因此,路徑覆蓋的使用,通常會(huì)與快速錯(cuò)誤修復(fù)分隔開(kāi)來(lái),而不是大更新分開(kāi)。

注意:使用本地配置覆蓋路徑,僅適用于已發(fā)布到crates.io的包。您無(wú)法使用此功能告訴 Cargo 如何查找本地未發(fā)布的箱。

Platform specific dependencies

平臺(tái)決定依賴

特定于平臺(tái)的依賴項(xiàng)采用相同的格式,但在target下列出。像正常 Rust 一樣的#[cfg]語(yǔ)法,將用于定義這些部分:

[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 一樣,這里的語(yǔ)法支持not,any,和all運(yùn)算符組合各種 cfg 名稱/值對(duì)。請(qǐng)注意cfg語(yǔ)法僅在 Cargo 0.9.0(Rust 1.8.0)之后可用.

除了#[cfg]語(yǔ)法,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ī)范,請(qǐng)引用完整路徑和文件名:

[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" }

Development dependencies

開(kāi)發(fā)(Dev)依賴項(xiàng)

你可以添加一個(gè)[dev-dependencies]表格到Cargo.toml,其格式相當(dāng)于[dependencies]:

[dev-dependencies]
tempdir = "0.3"

編譯用于構(gòu)建的包時(shí),不會(huì)使用 Dev 依賴,但用于編譯測(cè)試,示例和基準(zhǔn)。

這些依賴關(guān)系是不會(huì)傳播到依賴于此包的其他包.

您還可以讓dev-dependencies具有特定目標(biāo)的開(kāi)發(fā)依賴項(xiàng),而不是dependencies標(biāo)題。例如:

[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"

Build dependencies

構(gòu)建 依賴項(xiàng)

您可以在構(gòu)建腳本中使用,依賴其他基于 Cargo 的箱。依賴關(guān)系是由清單的build-dependencies部分定義:

[build-dependencies]
cc = "1.0.3"

構(gòu)建腳本并不是有權(quán)訪問(wèn)中 dependencies要么dev-dependencies部分列出的依賴項(xiàng)。除非也在dependencies部分下面列出,否則構(gòu)建依賴項(xiàng)同樣不可用于包本身。包本身及其構(gòu)建腳本是分開(kāi)構(gòu)建的,因此它們的依賴關(guān)系不重合。通過(guò)將獨(dú)立依賴用于獨(dú)立目的,使 Cargo 更簡(jiǎn)單,更清潔。

Choosing features

選擇 特性

如果您依賴的包提供條件特性,您可以指定使用哪個(gè):

[dependencies.awesome]
version = "1.3.5"
default-features = false # 不會(huì)包括默認(rèn)特性, 和 任君選
                        # 單特性
features = ["secure-password", "civet"]

有關(guān) features 的更多信息,請(qǐng)參閱清單文檔.

Renaming dependencies in Cargo.toml

在Cargo.toml中的重命名依賴項(xiàng)

寫(xiě)Cargo.toml的[dependencies]部分的時(shí)候,您為依賴項(xiàng)編寫(xiě)的字段通常與您在代碼中導(dǎo)入的包的名稱相匹配。但是,對(duì)于某些項(xiàng)目,您可能希望在代碼中引用具有不同名稱的包,而不管它是如何在 crates.io 上發(fā)布的。例如,您可能希望:

  • 避免在 Rust 代碼常用use foo as bar.
  • 依賴箱子的多個(gè)版本.
  • 依賴來(lái)自不同注冊(cè)表管理機(jī)構(gòu)的同名箱.

為了支持這個(gè) ,Cargo 在[dependencies]部分使用 一個(gè)package字段,決定應(yīng)該依賴哪個(gè)包:

[package]
name = "mypackage"
version = "0.0.1"

[dependencies]
foo = "0.1"
bar = { git = "https://github.com/example/project", package = "foo" }
baz = { version = "0.1", registry = "custom", package = "foo" }

在此示例中,Rust 代碼中現(xiàn)在提供了三個(gè)包:

extern crate foo; // crates.io
extern crate bar; // git repository
extern crate baz; // registry `custom`

所有這三個(gè)箱的包名稱在他們自己Cargo.toml,都是foo,所以我們明確地告知 Cargo ,使用的是我們想要的package字段(如 package = "foo"包名,即我們?cè)诒镜卣{(diào)用其他東西)。如果沒(méi)有指定package,則默認(rèn)為所請(qǐng)求的依賴項(xiàng)的名稱。

請(qǐng)注意,如果您有一個(gè)可選的(optional)依賴項(xiàng),例如:

[dependencies]
foo = { version = "0.1", package = 'bar', optional = true }

你依賴于一個(gè)bar箱子,其來(lái)自 crates.io,但你箱子有一個(gè)foo特性,取代了一個(gè)bar特性。也就是說(shuō),在重命名時(shí),特性的名稱拿掉了依賴項(xiàng)的名稱,而不是包名稱。

啟用傳遞依賴項(xiàng)的工作方式類(lèi)似,例如我們可以將以下內(nèi)容,添加到上面的清單中:

[features]
log-debug = ['foo/log-debug'] # 使用 'bar/log-debug' 就會(huì)出現(xiàn)一個(gè)錯(cuò)誤!


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)