返回文章列表

Rust專案相依性管理與釋出技巧

本文探討 Rust 專案的相依性管理,涵蓋功能旗標的運用、上游函式庫的修補、以及 crate 的釋出流程。文章以 Cargo 工具為核心,講解如何透過功能旗標控制編譯行為、使用 patch 區塊修改相依性,並搭配程式碼範例說明如何在 Cargo.toml

程式語言 開發工具

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] 區塊定義了不同的功能旗標,例如 defaultsimd_backendu64_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 結構體衍生 SerializeDeserialize 特性。

修補上游函式庫

當我們需要修改上游函式庫的程式碼時,可以透過以下步驟進行修補:

  1. 在 GitHub 上 fork 原始函式庫。
  2. 在 fork 的函式庫中進行必要的修改。
  3. 提交 pull request 給上游專案。
  4. 在自己的專案中,將 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

釋出流程

  1. 更新Cargo.toml中的版本號。
  2. CI系統執行,驗證測試和檢查是否透過。
  3. 建立並推播標籤(例如:git tag -s vX.Y.Z)。
  4. CD系統執行,構建並釋出標籤版本至crates.io。
  5. 更新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

內容解密:

  1. 觸發條件:當推播到main分支時,自動觸發建置與測試工作流程。
  2. 環境變數:根據所使用的Rust版本和功能特性,動態設定FEATURES環境變數。
  3. 矩陣策略:在不同的作業系統(Ubuntu、Windows、macOS)、Rust版本(穩定版、夜間版)和功能特性下執行建置與測試。
  4. 步驟
    • 簽出程式碼:使用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
    }
}

內容解密:

  1. 外部函式宣告:使用extern "C"宣告zlib函式庫中的函式,包括compresscompressBounduncompress
  2. 連結屬性:使用#[link(name = "z")]屬性指示編譯器連結zlib函式庫。
  3. Rust包裝函式:定義zlib_compress函式,呼叫底層的C函式進行壓縮操作。
  4. 安全性:由於呼叫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

內容解密:

  1. 觸發條件:當推播帶有v*標籤的提交時,觸發發布流程。
  2. 環境變數:設定CARGO_TERM_COLOR以保持Cargo輸出的色彩。
  3. 步驟
    • 簽出程式碼設定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。
  • 使用 # 例子 標題來標記範例程式碼區塊。
  • 使用測試來驗證範例程式碼的正確性。