Cargo 提供了完善的機制管理 Rust 專案的相依性,讓開發者能有效控制外部函式庫的整合。透過功能旗標,我們可以根據需求選擇性地引入相依性,並藉由條件式編譯調整程式碼行為。當需要修改上游函式庫時,Cargo 的 patch 機制提供更彈性的本地修補方案,避免直接 fork 上游專案的繁瑣流程。此外,Cargo 也簡化了 crate 的釋出流程,讓開發者能輕鬆分享自己的程式函式庫,並透過 CI/CD 整合自動化釋出流程,提升開發效率。文章也涵蓋瞭如何在不同平台編譯 Rust 二進位制檔,以及如何使用 rustdoc 生成檔案,提供更全面的 Rust 專案管理。
使用 Cargo 管理專案與相依性控制
Cargo 為 Rust 專案提供了強大的相依性管理功能,讓開發者能夠輕鬆處理外部函式庫的整合與維護。本章節將探討 Cargo 的進階功能,包括如何使用功能旗標(feature flags)控制相依性、如何修補上游函式庫(patching dependencies)等實務操作。
功能旗標與條件式編譯
在 Cargo.toml 中,我們可以定義不同的功能旗標來控制專案的編譯行為。以下是一個範例:
[dependencies]
base64 = { version = "0.13", optional = true }
serde = { version = "1.0", optional = true, features = ["derive"] }
[features]
default = ["u64_backend"]
simd_backend = ["curve25519-dalek/simd_backend", "sha2/asm"]
u64_backend = ["x25519-dalek/u64_backend"]
內容解密:
optional = true表示該相依性是可選的,只有在特定功能旗標啟用時才會被包含。[features]區塊定義了不同的功能旗標,例如default、simd_backend和u64_backend。- 這些功能旗標可以控制相依性的啟用與否,以及編譯行為的差異。
在程式碼中,我們可以使用 #[cfg(feature = "serde")] 來進行條件式編譯:
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Message(pub Box<InputBase>);
內容解密:
#[cfg(feature = "serde")]表示只有在serde功能旗標啟用時,才會編譯相關程式碼。#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]表示只有在serde功能旗標啟用時,才會為Message結構體衍生Serialize和Deserialize特性。
修補上游函式庫
當我們需要修改上游函式庫的程式碼時,可以透過以下步驟進行修補:
- 在 GitHub 上 fork 原始函式庫。
- 在 fork 的函式庫中進行必要的修改。
- 提交 pull request 給上游專案。
- 在自己的專案中,將 Cargo.toml 中的相依性指向 fork 的函式庫。
Cargo 提供了 [patch] 區塊,讓我們可以在本地端進行函式庫的修補,而不需要直接 fork 上游專案。以下是一個範例:
[dependencies]
num_cpus = { path = "num_cpus" }
內容解密:
path = "num_cpus"表示使用本地端的num_cpus函式庫,而不是從 crates.io 下載。
假設我們建立了一個新的函式庫 num_cpus,並實作了相同的 API:
// num_cpus/src/lib.rs
pub fn get() -> usize {
100
}
內容解密:
- 這個範例中,我們將
num_cpus::get()函式的回傳值改為固定的 100。
在主專案中,我們可以正常使用 num_cpus 函式庫:
// src/main.rs
fn main() {
println!("There are {} CPUs", num_cpus::get());
}
執行結果將會輸出 There are 100 CPUs,表示我們成功地使用了修補後的 num_cpus 函式庫。
使用Cargo進行專案管理:相依性修補與釋出
在Rust開發中,Cargo是不可或缺的工具,用於專案管理、相依性控制以及釋出crate。本章節將探討如何使用Cargo進行相依性修補以及如何釋出crate至crates.io。
修補相依性
在某些情況下,我們需要對專案的相依性進行修補。Cargo提供了多種方式來實作這一點。
直接修補相依性
假設我們需要使用某個crate的fork版本,可以直接在Cargo.toml中指定GitHub倉函式庫地址和特定的commit或tag。例如:
[dependencies]
num_cpus = { git = "https://github.com/brndnmtthws/num_cpus", rev = "b423db0a698b035914ae1fd6b7ce5d2a4e727b46" }
間接修補相依性
有時,我們需要修補的是某個相依性的相依性。例如,num_cpus相依於libc,我們可以透過在Cargo.toml中新增[patch.crates-io]段落來修補libc:
[patch.crates-io]
libc = { git = "https://github.com/rust-lang/libc", tag = "0.2.88" }
這種方法可以替換所有來自crates.io的libc相依性,但不會影響下游的相依性。
修補相依性的最佳實踐
- 盡量避免修補相依性,因為這會增加維護的難度。
- 當不得不進行修補時,應盡快將修改推播至上游專案,尤其是當涉及許可證要求時。
- 避免長期維護fork版本,應盡快將修改合併至主分支。
釋出Crate
當crate準備就緒後,可以透過執行cargo publish命令將其釋出至crates.io。釋出前需要確保crate滿足一定的要求,例如指定許可證、提供檔案和倉函式庫URL等。
CI/CD整合
為了自動化釋出流程,通常會使用CI/CD系統。對於大多數crate,會設定CI系統來驗證每次提交,並使用CD系統在標籤發布時自動釋出至crates.io。
以dryoc專案為例,它使用了GitHub Actions來實作CI/CD流程。其中,.github/workflows/build-and-test.yml用於構建和測試,而.github/workflows/publish.yml則用於在標籤發布時釋出crate。
name: Build & test
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
matrix:
rust:
- stable
- beta
- nightly
features:
- serde
- base64
- simd_backend
- default
os:
- ubuntu-latest
- macos-latest
- windows-latest
exclude:
- rust: stable
features: simd_backend
- rust: beta
features: simd_backend
- os: windows-latest
features: simd_backend
釋出流程
- 更新
Cargo.toml中的版本號。 - CI系統執行,驗證測試和檢查是否透過。
- 建立並推播標籤(例如:
git tag -s vX.Y.Z)。 - CD系統執行,構建並釋出標籤版本至crates.io。
- 更新
Cargo.toml中的版本號以準備下一次發布。
注意事項
- 釋出至crates.io的crate是不可變的,因此任何更改都需要透過新的版本來實作。
使用Cargo進行專案管理與C函式庫連結
在Rust開發中,Cargo是不可或缺的專案管理工具。本章節將探討如何使用Cargo進行專案管理,並介紹如何連結C函式庫以擴充套件Rust的功能。
自動化建置與測試
在現代軟體開發中,自動化建置與測試是確保程式碼品質的重要步驟。以下是一個使用GitHub Actions自動化建置、測試和發布Rust專案的範例:
name: 建置與測試
on:
push:
branches:
- main
env:
FEATURES: >
${{ matrix.rust != 'nightly' && matrix.features
|| format('{0},nightly', matrix.features) }}
jobs:
建置與測試:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
rust: [stable, nightly]
features: [feature1, feature2]
steps:
- uses: actions/checkout@v3
- name: 設定Rust工具鏈
uses: brndnmtthws/rust-action@v1
with:
toolchain: ${{ matrix.rust }}
- run: cargo build --features ${{ env.FEATURES }}
- run: cargo test --features ${{ env.FEATURES }}
env:
RUST_BACKTRACE: 1
- run: cargo fmt --all -- --check
if: ${{ matrix.rust == 'nightly' && matrix.os == 'ubuntu-latest' }}
- run: cargo clippy --features ${{ env.FEATURES }} -- -D warnings
內容解密:
- 觸發條件:當推播到
main分支時,自動觸發建置與測試工作流程。 - 環境變數:根據所使用的Rust版本和功能特性,動態設定
FEATURES環境變數。 - 矩陣策略:在不同的作業系統(Ubuntu、Windows、macOS)、Rust版本(穩定版、夜間版)和功能特性下執行建置與測試。
- 步驟:
- 簽出程式碼:使用
actions/checkout@v3簽出倉函式庫中的程式碼。 - 設定Rust工具鏈:使用
brndnmtthws/rust-action@v1設定指定的Rust工具鏈。 - 建置:執行
cargo build命令,根據設定的功能特性進行建置。 - 測試:執行
cargo test命令,執行測試案例。 - 格式檢查:在夜間版Rust和Ubuntu環境下,執行
cargo fmt檢查程式碼格式。 - Clippy檢查:執行
cargo clippy命令,進行程式碼品品檢查。
- 簽出程式碼:使用
連結C函式庫
在某些情況下,Rust專案需要呼叫C函式庫中的函式。這可以透過外部函式介面(FFI)來實作。以下是一個連結zlib函式庫的範例:
use libc::{c_int, c_ulong};
#[link(name = "z")]
extern "C" {
fn compress(
dest: *mut u8,
dest_len: *mut c_ulong,
source: *const u8,
source_len: c_ulong,
) -> c_int;
fn compressBound(source_len: c_ulong) -> c_ulong;
fn uncompress(
dest: *mut u8,
dest_len: *mut c_ulong,
source: *const u8,
source_len: c_ulong,
) -> c_int;
}
pub fn zlib_compress(source: &[u8]) -> Vec<u8> {
unsafe {
let source_len = source.len() as c_ulong;
let mut dest_len = compressBound(source_len);
let mut dest = Vec::with_capacity(dest_len as usize);
compress(
dest.as_mut_ptr(),
&mut dest_len,
source.as_ptr(),
source_len,
);
dest.set_len(dest_len as usize);
dest
}
}
內容解密:
- 外部函式宣告:使用
extern "C"宣告zlib函式庫中的函式,包括compress、compressBound和uncompress。 - 連結屬性:使用
#[link(name = "z")]屬性指示編譯器連結zlib函式庫。 - Rust包裝函式:定義
zlib_compress函式,呼叫底層的C函式進行壓縮操作。 - 安全性:由於呼叫C函式涉及指標操作,因此需要在
unsafe區塊中進行。
發布Crate到crates.io
發布Crate到crates.io可以讓其他開發者輕鬆地使用您的程式函式庫。以下是發布流程的簡要說明:
name: 發布到crates.io
on:
push:
tags:
- v*
env:
CARGO_TERM_COLOR: always
jobs:
建置測試發布:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: brndnmtthws/rust-action@v1
with:
toolchain: stable
- run: cargo build
- run: cargo test
- run: cargo login -- ${{ secrets.CRATES_IO_TOKEN }}
- run: cargo publish
內容解密:
- 觸發條件:當推播帶有
v*標籤的提交時,觸發發布流程。 - 環境變數:設定
CARGO_TERM_COLOR以保持Cargo輸出的色彩。 - 步驟:
- 簽出程式碼 和 設定Rust工具鏈:與建置測試流程相同。
- 建置和測試:確保程式碼在發布前能夠正確建置和透過測試。
- 登入crates.io:使用儲存在GitHub秘密中的crates.io令牌登入。
- 發布Crate:執行
cargo publish命令,將Crate發布到crates.io。
本章節介紹瞭如何使用Cargo進行專案管理,包括自動化建置與測試、連結C函式庫以及發布Crate到crates.io。這些技術對於開發高效、可靠且可重用的Rust程式碼至關重要。
Rust 專案的二進位制發行與檔案檔案
2.7 二進位制發行
Rust 的二進位制檔包含了給定平台的所有 Rust 相依套件,但不包含 C 執行環境,同時也包含任何動態連結的非 Rust 函式庫。你可以建置靜態連結到 C 執行環境的二進位制檔,但預設情況下這是可選的。因此,在發行 Rust 二進位制檔時,你需要考慮是否要靜態連結 C 執行環境或依賴系統的執行環境。
2.7.1 跨平台編譯
你可以使用 Cargo 來跨平台編譯二進位制檔,但前提是編譯器支援該目標平台。例如,你可以在 Windows 上輕鬆編譯 Linux 的二進位制檔,但反之則較為困難。
你可以使用 rustup target list 列出主機平台上可用的目標平台,並使用 rustup target add <target> 安裝不同的目標平台。然後,使用 cargo build --target <target> 為特定的目標平台建置。
$ rustup target list
$ rustup target add aarch64-apple-darwin
$ cargo build --target aarch64-apple-darwin
2.7.2 建置靜態連結的二進位制檔
一般的 Rust 二進位制檔包含了所有編譯後的相依套件,但不包含 C 執行環境函式庫。在 Windows 和 macOS 上,通常會發行預先編譯好的二進位制檔並連結到作業系統的 C 執行環境函式庫。但在 Linux 上,大多數套件是由發行版的維護者從原始碼編譯的,並且由發行版負責管理 C 執行環境。
在 Linux 上發行 Rust 二進位制檔時,你可以使用 glibc 或 musl,取決於你的偏好。Glibc 是大多數 Linux 發行版的預設 C 函式庫執行環境。然而,我建議在發行 Linux 二進位制檔時靜態連結到 musl,以獲得最大的可攜性。
你可以使用 RUSTFLAGS="-C target-feature=+crt-static" 指示 rustc 使用靜態 C 執行環境。
$ RUSTFLAGS="-C target-feature=+crt-static" cargo build --target x86_64-unknown-linux-musl
2.8 檔案檔案化 Rust 專案
Rust 預設提供的用於檔案檔案化的工具稱為 rustdoc。如果你曾經使用過其他專案的檔案檔案化工具(例如 Javadoc、docstring 或 RDoc),那麼 rustdoc 將會很自然。
使用 rustdoc 就像在程式碼中新增註解並產生檔案一樣簡單。讓我們來快速示範一下。首先,建立一個函式庫:
$ cargo new rustdoc-example --lib
現在,讓我們編輯 src/lib.rs 以新增一個名為 mult 的函式,它接受兩個整數(a 和 b)並將它們相乘。我們還會新增一個測試:
/// 將兩個整數相乘。
///
/// # 例子
///
/// ```
/// assert_eq!(mult(2, 3), 6);
/// ```
pub fn mult(a: i32, b: i32) -> i32 {
a * b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mult() {
assert_eq!(mult(2, 3), 6);
}
}
使用 rustdoc 生成檔案
要生成檔案,你可以在專案目錄中執行以下命令:
$ cargo doc
這將在 target/doc 目錄中生成檔案。你可以在瀏覽器中開啟 target/doc/rustdoc_example/index.html 以檢視檔案。
檔案註解的最佳實踐
- 使用 Markdown 格式撰寫檔案註解。
- 在檔案註解中提供範例程式碼,以說明如何使用你的 API。
- 使用
# 例子標題來標記範例程式碼區塊。 - 使用測試來驗證範例程式碼的正確性。