Rust : cargo簡介

http://wiki.jikexueyuan.com/project/rust-primer/cargo-detailed-cfg/cargo-detailed-cfg.html

cargo簡介

曾幾何時,對於使用慣了C/C++語言的猿們來說,項目代碼的組織與管理絕對是一場噩夢。爲了解決C/C++項目的管理問題,猿神們想盡了各種辦法,開發出了各種五花八門的項目管理工具,從一開始的automake到後來的cmake、qmake等等,但結果並不如人意,往往是解決了一些問題,卻引入了更多的問題,C/C++猿們經常會陷入在掌握語言本身的同時,還要掌握複雜的構建工具語法的窘境。無獨有偶,java的項目代碼組織與管理工具ant和maven也存在同樣的問題。複雜的項目管理配置參數,往往讓猿們不知所措。

作爲一門現代語言,rust自然要摒棄石器時代項目代碼管理的方法和手段。rust項目組爲各位猿提供了超級大殺器cargo,以解決項目代碼管理所帶來的干擾和困惑。用過node.js的猿們,應該對node.js中的神器npm、grunt、gulp等工具印象深刻。作爲新一代靜態語言中的翹楚,rust官方參考了現有語言管理工具的優點,於是就產生了cargo。

言而總之,作爲rust的代碼組織管理工具,cargo提供了一系列的工具,從項目的建立、構建到測試、運行直至部署,爲rust項目的管理提供儘可能完整的手段。同時,與rust語言及其編譯器rustc本身的各種特性緊密結合,可以說既是語言本身的知心愛人,又是rust猿們的貼心小棉襖,誰用誰知道。 廢話就不多說了,直接上例子和各種高清無馬圖。
cargo入門

首先,當然還是廢話,要使用cargo,自然首先要安裝cargo。安裝cargo有三種方法,前兩種方法請參見rust的安裝方法,因爲cargo工具是官方正統出身,當然包含在官方的分發包中。第三種方法即從cargo項目的源碼倉庫進行構建。Oh,My God。的確是廢話。

好了,假設各位已經安裝好了cargo,大家和我一起學一下起手式。當然了,猿的世界,起手式一般都千篇一律——那就是hello world大法。 在終端中輸入

$ cargo new hello_world --bin

上述命令使用cargo new在當前目錄下新建了基於cargo項目管理的rust項目,項目名稱爲hello_world,–bin表示該項目將生成可執行文件。具體生成的項目目錄結構如下:

$ cd hello_world
$ tree .
.
├── Cargo.toml
└── src
└── main.rs

1 directory, 2 files

大家可以在終端中輸入上述命令,敲出回車鍵之後即可看到上述結果,或者直接去編輯器或文件管理器中去觀察即可。 打開main.rs文件,可以看到,cargo new命令爲我們自動生成了hello_world運行所必須的所有代碼:

fn main() {
    println!("Hello, world!");
}

好了,心急的猿們可能已經迫不及待的脫褲子了,好吧,我們先來構建並看看cargo有多神奇,在終端中輸入:

$ cargo build

稍等片刻,cargo會自動爲我們構建好高清應用所需的一切,對於這個起手式來說,緩衝不會超過5秒,12秒88的選手要憋住了。

$ cargo run
Running target/debug/hello_world
Hello, world!

看到了什麼,看到了什麼,嚇尿了有木有,嚇尿了有木有。好了,cargo就是這麼簡單。

當然了,說cargo美,並不僅僅是簡單這麼簡單,cargo雖然簡單,但是很強大。有多麼強大??可以說,基本上rust開發管理中所需的手段,cargo都有。很小很強大,既強又有節操,不帶馬,學習曲線幾乎爲零。
基於cargo的rust項目組織結構

這次不說廢話了,先上高清無馬圖:

cargo項目組織結構

對上述cargo默認的項目結構解釋如下:

cargo.toml和cargo.lock文件總是位於項目根目錄下。
 源代碼位於src目錄下。    
 默認的庫入口文件是src/lib.rs。 
 默認的可執行程序入口文件是src/main.rs。    
 其他可選的可執行文件位於src/bin/*.rs(這裏每一個rs文件均對應一個可執行文件)。 
 外部測試源代碼文件位於tests目錄下。   
  示例程序源代碼文件位於examples。 
  基準測試源代碼文件位於benches目錄下。

好了,大家一定謹記這些默認規則,最好按照這種模式來組織自己的rust項目。
cargo.toml和cargo.lock

cargo.toml和cargo.lock是cargo項目代碼管理的核心兩個文件,cargo工具的所有活動均基於這兩個文件。

cargo.toml是cargo特有的項目數據描述文件,對於猿們而言,cargo.toml文件存儲了項目的所有信息,它直接面向rust猿,猿們如果想讓自己的rust項目能夠按照期望的方式進行構建、測試和運行,那麼,必須按照合理的方式構建’cargo.toml’。

而cargo.lock文件則不直接面向猿,猿們也不需要直接去修改這個文件。lock文件是cargo工具根據同一項目的toml文件生成的項目依賴詳細清單文件,所以我們一般不用不管他,只需要對着cargo.toml文件擼就行了。

[package]
name = "hello_world"
version = "0.1.0"
authors = ["fuying"]

[dependencies]

toml文件是由諸如[package]或[dependencies]這樣的段落組成,每一個段落又由多個字段組成,這些段落和字段就描述了項目組織的基本信息,例如上述toml文件中的[package]段落描述了hello_world項目本身的一些信息,包括項目名稱(對應於name字段)、項目版本(對應於version字段)、作者列表(對應於authors字段)等;[dependencies]段落描述了hello_world項目的依賴項目有哪些。

下面我們來看看toml描述文件中常用段落和字段的意義。
package段落

[package]段落描述了軟件開發者對本項目的各種元數據描述信息,例如[name]字段定義了項目的名稱,[version]字段定義了項目的當前版本,[authors]定義了該項目的所有作者,當然,[package]段落不僅僅包含這些字段,[package]段落的其他可選字段詳見cargo參數配置章節。
定義項目依賴

使用cargo工具的最大優勢就在於,能夠對該項目的各種依賴項進行方便、統一和靈活的管理。這也是使用cargo對rust 的項目進行管理的重要目標之一。在cargo的toml文件描述中,主要通過各種依賴段落來描述該項目的各種依賴項。toml中常用的依賴段落包括一下幾種:

基於rust官方倉庫crates.io,通過版本說明來描述:
基於項目源代碼的git倉庫地址,通過URL來描述:
基於本地項目的絕對路徑或者相對路徑,通過類Unix模式的路徑來描述: 這三種形式具體寫法如下:

[dependencies]
typemap = "0.3"
plugin = "0.2*"
hammer = { version = "0.5.0"}
color = { git = "https://github.com/bjz/color-rs" }
geometry = { path = "crates/geometry" }

上述例子中,2-4行爲方法一的寫法,第5行爲方法二的寫法,第6行爲方法三的寫法。 這三種寫法各有用處,如果項目需要使用crates.io官方倉庫來管理項目依賴項,推薦使用第一種方法。如果項目開發者更傾向於使用git倉庫中最新的源碼,可以使用方法二。方法二也經常用於當官方倉庫的依賴項編譯不通過時的備選方案。方法三主要用於源代碼位於本地的依賴項。
定義集成測試用例

cargo另一個重要的功能,即將軟件開發過程中必要且非常重要的測試環節進行集成,並通過代碼屬性聲明或者toml文件描述來對測試進行管理。其中,單元測試主要通過在項目代碼的測試代碼部分前用#[test]屬性來描述,而集成測試,則一般都會通過toml文件中的[[test]]段落進行描述。 例如,假設集成測試文件均位於tests文件夾下,則toml可以這樣來寫:

[[test]]
name = "testinit"
path = "tests/testinit.rs"

[[test]]
name = "testtime"
path = "tests/testtime.rs"

上述例子中,name字段定義了集成測試的名稱,path字段定義了集成測試文件相對於本toml文件的路徑。 看看,定義集成測試就是如此簡單。 需要注意的是:

如果沒有在Cargo.toml裏定義集成測試的入口,那麼tests目錄(不包括子目錄)下的每個rs文件被當作集成測試入口.
如果在Cargo.toml裏定義了集成測試入口,那麼定義的那些rs就是入口,不再默認指定任何集成測試入口.

定義項目示例和可執行程序

上面我們介紹了cargo項目管理中常用的三個功能,還有兩個經常使用的功能:example用例的描述以及bin用例的描述。其描述方法和test用例描述方法類似。不過,這時候段落名稱’[[test]]‘分別替換爲:’[[example]]‘或者’[[bin]]’。例如:

[[example]]
name = "timeout"
path = "examples/timeout.rs"

[[bin]]
name = "bin1"
path = "bin/bin1.rs"

對於’[[example]]‘和’[[bin]]'段落中聲明的examples和bins,需要通過’cargo run --example NAME’或者’cargo run --bin NAME’來運行,其中NAME對應於你在name字段中定義的名稱。
構建、清理、更新以及安裝

領會了toml描述文件的寫法,是一個重要的方面。另一個重要的方面,就是cargo工具本身爲我們程序猿提供的各種好用的工具。如果大家感興趣,自己在終端中輸入’cargo --help’查看即可。其中開發時最常用的命令就是’cargo build’,用於構建項目。此外,'cargo clean’命令可以清理target文件夾中的所有內容;'cargo update’根據toml描述文件重新檢索並更新各種依賴項的信息,並寫入lock文件,例如依賴項版本的更新變化等等;'cargo install’可用於實際的生產部署。這些命令在實際的開發部署中均是非常有用的。

筒子們好,我們又見面了。之前第5章,我們一起探討了cargo的一些常用的基本技能。通過第5章的學習,大家基本能解決日常項目開發中遇到的大多數問題。但實際上,cargo提供給我們所使用的功能不僅限於此。我只想說一個字:cargo很好很強大,而且遠比你想象的強大。 本章將深入探討cargo的一些細節問題,這包括以下幾個方面:

基於語義化版本的項目版本聲明與管理
cargo的toml描述文件配置字段詳細參考

基於語義化版本的項目版本聲明與管理

我們在使用toml描述文件對項目進行配置時,經常會遇到項目版本聲明及管理的問題,比如:

[package]
name = "libevent_sys"
version = "0.1.0"

[dependencies]
libc = "0.2"

這裏package段落中的version字段的值,以及dependencies段落中的libc字段的值,這些值的寫法,都涉及到語義化版本控制的問題。語義化版本控制是用一組簡單的規則及條件來約束版本號的配置和增長。這些規則是根據(但不侷限於)已經被各種封閉、開放源碼軟件所廣泛使用的慣例所設計。簡單來說,語義化版本控制遵循下面這些規則:

版本格式:主版本號.次版本號.修訂號,版本號遞增規則如下:

主版本號:當你做了不兼容的 API 修改,
次版本號:當你做了向下兼容的功能性新增,
修訂號:當你做了向下兼容的問題修正。

先行版本號及版本編譯信息可以加到“主版本號.次版本號.修訂號”的後面,作爲延伸。

關於語義化版本控制的具體細節問題,大家可以參考這裏,我不再贅述。
cargo的toml描述文件配置字段詳細參考
[package]段落

啥也不多說了,直接上例子,大家注意我在例子中的中文解釋,個人覺得這樣比較一目瞭然:

[package]
 # 軟件包名稱,如果需要在別的地方引用此軟件包,請用此名稱。
name = "hello_world"

# 當前版本號,這裏遵循semver標準,也就是語義化版本控制標準。
version = "0.1.0"    # the current version, obeying semver

# 軟件所有作者列表
authors = ["[email protected]"]

# 非常有用的一個字段,如果要自定義自己的構建工作流,
# 尤其是要調用外部工具來構建其他本地語言(C、C++、D等)開發的軟件包時。
# 這時,自定義的構建流程可以使用rust語言,寫在"build.rs"文件中。
build = "build.rs"

# 顯式聲明軟件包文件夾內哪些文件被排除在項目的構建流程之外,
# 哪些文件包含在項目的構建流程中
exclude = ["build/**/*.o", "doc/**/*.html"]
include = ["src/**/*", "Cargo.toml"]

# 當軟件包在向公共倉庫發佈時出現錯誤時,使能此字段可以阻止此錯誤。
publish = false

# 關於軟件包的一個簡短介紹。
description = "..."

# 下面這些字段標明瞭軟件包倉庫的更多信息
documentation = "..."
homepage = "..."
repository = "..."

# 顧名思義,此字段指向的文件就是傳說中的ReadMe,
# 並且,此文件的內容最終會保存在註冊表數據庫中。
readme = "..."

# 用於分類和檢索的關鍵詞。
keywords = ["...", "..."]

# 軟件包的許可證,必須是cargo倉庫已列出的已知的標準許可證。
license = "..."

# 軟件包的非標許可證書對應的文件路徑。
license-file = "..."

依賴的詳細配置

最直接的方式在之前第五章探討過,這裏不在贅述,例如這樣:

[dependencies]
hammer = "0.5.0"
color = "> 0.6.0, < 0.8.0"

與平臺相關的依賴定義格式不變,不同的是需要定義在[target]字段下。例如:

# 注意,此處的cfg可以使用not、any、all等操作符任意組合鍵值對。
# 並且此用法僅支持cargo 0.9.0(rust 1.8.0)以上版本。
# 如果是windows平臺,則需要此依賴。
[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"

[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"

#如果是32位平臺,則需要此依賴。
[target.'cfg(target_pointer_width = "32")'.dependencies]
native = { path = "native/i686" }

[target.'cfg(target_pointer_width = "64")'.dependencies]
native = { path = "native/i686" }

# 另一種寫法就是列出平臺的全稱描述
[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" }
openssl = "1.0.1"
native = { path = "native/x86_64" }

# [dev-dependencies]段落的格式等同於[dependencies]段落,
# 不同之處在於,[dependencies]段落聲明的依賴用於構建軟件包,
# 而[dev-dependencies]段落聲明的依賴僅用於構建測試和性能評估。
# 此外,[dev-dependencies]段落聲明的依賴不會傳遞給其他依賴本軟件包的項目
[dev-dependencies]
iron = "0.2"

自定義編譯器調用方式模板詳細參數

cargo內置五種編譯器調用模板,分別爲dev、release、test、bench、doc,分別用於定義不同類型生成目標時的編譯器參數,如果我們自己想改變這些編譯模板,可以自己定義相應字段的值,例如(注意:下述例子中列出的值均爲此模板字段對應的系統默認值):

# 開發模板, 對應`cargo build`命令

    [profile.dev]
    opt-level = 0  # 控制編譯器的 --opt-level 參數,也就是優化參數
    debug = true   # 控制編譯器是否開啓 `-g` 參數
    rpath = false  # 控制編譯器的 `-C rpath` 參數
    lto = false    # 控制`-C lto` 參數,此參數影響可執行文件和靜態庫的生成,
    debug-assertions = true  # 控制調試斷言是否開啓
    codegen-units = 1 # 控制編譯器的 `-C codegen-units` 參數。注意,當`lto = true`時,此字段值被忽略

# 發佈模板, 對應`cargo build --release`命令
[profile.release]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 1

# 測試模板,對應`cargo test`命令
[profile.test]
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 1

# 性能評估模板,對應`cargo bench`命令
[profile.bench]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 1

# 文檔模板,對應`cargo doc`命令
[profile.doc]
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 1

需要注意的是,當調用編譯器時,只有位於調用最頂層的軟件包的模板文件有效,其他的子軟件包或者依賴軟件包的模板定義將被頂層軟件包的模板覆蓋。
[features]段落

[features]段落中的字段被用於條件編譯選項或者是可選依賴。例如:

[package]
name = "awesome"

[features]
# 此字段設置了可選依賴的默認選擇列表,
# 注意這裏的"session"並非一個軟件包名稱,
# 而是另一個featrue字段session
default = ["jquery", "uglifier", "session"]

# 類似這樣的值爲空的feature一般用於條件編譯,
# 類似於`#[cfg(feature = "go-faster")]`。
go-faster = []

# 此feature依賴於bcrypt軟件包,
# 這樣封裝的好處是未來可以對secure-password此feature增加可選項目。
secure-password = ["bcrypt"]

# 此處的session字段導入了cookie軟件包中的feature段落中的session字段
session = ["cookie/session"]

[dependencies]
# 必要的依賴
cookie = "1.2.0"
oauth = "1.1.0"
route-recognizer = "=2.1.0"

# 可選依賴
jquery = { version = "1.0.2", optional = true }
uglifier = { version = "1.5.3", optional = true }
bcrypt = { version = "*", optional = true }
civet = { version = "*", optional = true }

如果其他軟件包要依賴使用上述awesome軟件包,可以在其描述文件中這樣寫:

[dependencies.awesome]
version = "1.3.5"
default-features = false # 禁用awesome 的默認features
features = ["secure-password", "civet"] # 使用此處列舉的各項features

使用features時需要遵循以下規則:

feature名稱在本描述文件中不能與出現的軟件包名稱衝突
除了default feature,其他所有的features均是可選的
features不能相互循環包含
開發依賴包不能包含在內
features組只能依賴於可選軟件包

features的一個重要用途就是,當開發者需要對軟件包進行最終的發佈時,在進行構建時可以聲明暴露給終端用戶的features,這可以通過下述命令實現:

$ cargo build --release --features "shumway pdf"

關於測試

當運行cargo test命令時,cargo將會按做以下事情:

編譯並運行軟件包源代碼中被#[cfg(test)] 所標誌的單元測試
編譯並運行文檔測試
編譯並運行集成測試
編譯examples

配置構建目標

所有的諸如[[bin]], [lib], [[bench]], [[test]]以及 [[example]]等字段,均提供了類似的配置,以說明構建目標應該怎樣被構建。例如(下述例子中[lib]段落中各字段值均爲默認值):

[lib]
# 庫名稱,默認與項目名稱相同
name = "foo"

# 此選項僅用於[lib]段落,其決定構建目標的構建方式,
# 可以取dylib, rlib, staticlib 三種值之一,表示生成動態庫、r庫或者靜態庫。
crate-type = ["dylib"]

# path字段聲明瞭此構建目標相對於cargo.toml文件的相對路徑
path = "src/lib.rs"

# 單元測試開關選項
test = true

# 文檔測試開關選項
doctest = true

# 性能評估開關選項
bench = true

# 文檔生成開關選項
doc = true

# 是否構建爲編譯器插件的開關選項
plugin = false

# 如果設置爲false,`cargo test`將會忽略傳遞給rustc的--test參數。
harness = true
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章