Rust 2018開發環境配置與開發效率工具集

熊皮皮發表於2018-11-13

文件列表見:Rust 移動端跨平臺複雜圖形渲染專案開發系列總結(目錄)

一句話概括:macOS/Linux使用者首選CLion + Rust外掛,折騰VSCode收益太低。以下內容來自參與開發gfx-rs/halgfx-rs/wgpu等Rust主流開源圖形專案的經歷總結。

配置Rust編譯環境

使用Rust開發macOS、iOS、Android等跨平臺共享原始碼的專案,開發環境避免不了這些系統所要求的開發環境,即:

  • macOS、iOS需要安裝Xcode
  • Android需要Android Studio、Android SDK、Android NDK,並且配置SDK、NDK到環境變數。如果不想手工配置SDK、NDK變數,對於macOS,推薦先安裝Android Studio到Application,之後通過Android Studio安裝Android SDK、NDK,然後向profile、zsh配置檔案等寫入SDK、NDK變數。
  • 修改Rust軟體更新源為中科大站點對國內使用者而言可以提高下載速度,已翻牆可不考慮。
    // 1. 開啟環境變數配置檔案
    vi ~/.bashrc
    // 2. 加入如下內容
    export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
    export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
    // 3. 啟用新配置內容
    source ~/.bashrc
    複製程式碼
  1. 安裝Rust,如果要安裝nightly編譯工具鏈才加--channel=nightly
    根據朋友反饋,2018年11月21日用下面的中科大源安裝會報錯,官方源沒問題。
    // !!! 以下命令二選一 !!!
    // 中科大源
    curl -sSf https://mirrors.ustc.edu.cn/rust-static/rustup.sh | 
    sh # -s -- --channel=nightly
    // 官方源
    curl https://sh.rustup.rs -sSf | sh
    複製程式碼
  2. cargo環境變數設定
    根據朋友反饋,安裝Rust 1.30以上版本,cargo配置是自動完成的,在terminal中輸入cargo --version測試cargo是否已配置,若結果類似cargo 1.32.0-nightly (1fa308820 2018-10-31)表明一切正常,後面這些操作可跳過。當前版本(1.26)的cargo裝好後,並不自動設定環境變數。在此進行手動配置,方便後面使用cargo安裝的效率工具。在此以macOS為例,mac上的cargo一般安裝在~/.cargo/bin下。
    export PATH="$HOME/.cargo/bin:$PATH"
    複製程式碼

IDE配置

CLion與推薦外掛

  • Rust外掛(最關鍵的外掛)
    提供程式碼提示、補全、跳轉等功能,比Rust Language Server(RLS)穩定、好用,外掛功能的更新速度快。
  • Toml
    方便編寫Cargo.toml檔案。
  • Active Intellij Tab Hightlighter
    高亮當前開啟的Tab頁。
  • Dash
    查文件方便
  • Git Conflict
    在原始檔中用顏色區分程式碼衝突,比Intellij系列產品原生做法更直觀。
  • Grep Console
    過濾控制檯輸出,可配置色彩輸出,比預設功能更強。
  • HighlightBracketPair
    高亮顯示游標所在的區域,比如在某個{}()[]內。

值得注意的是,由於gfx-rs專案組的幾個核心成員都沒用CLion,導致gfx-rs系列專案的examples等測試專案用CLion開啟後都沒有hal和各backend資料結構跳轉功能。

新建引用gfx的專案時,加上正確的#[cfg(any(feature = "vulkan", feature = "metal"))],CLion也可以程式碼跳轉到gfx裡面,這個問題坑了我幾個月,淚奔。

小八卦,他們用什麼編輯工具?我在gitter上私聊過他們,Dzmitry Malyshau(kvark,火狐WebRender團隊成員) 用Sublime Text,Josh Groves(grovesNL)用Visual Studio Code。

不推薦Visual Studio Code的原因

RLS不穩定導致程式碼跳轉經常失效是最重要的原因,但是,VSCode的優勢是,在沒有程式碼跳轉的情況下還能提供比CLion更強的程式碼提示,這讓我感到意外。

另外,由於我個人很少使用VSCode,VSCode配置起來對我而言是比較麻煩的,而且Rust有幾個元件要正常配置才能在VSCode上實現單步除錯,個人認為對Rust新手不友好。
其實,我在7月剛學習Rust時用的就是VSCode,跟著老外的部落格配了半天才弄好LLDB單步除錯,我覺得不值得,我當時的核心任務應該是環境就緒情況下學習Rust語法以及用它合理地解決問題,而不是折騰開發環境。

提高開發維護效率的工具集

CI配置、程式碼風格化與編譯快取

CI配置appveyor與travis

appveyor與travis都支援GitHub專案,在此給出它們的Rust編譯、單元測試配置。

  1. appveyor配置檔案appveyor.yml
    language: rust
    sudo: false
    
    matrix:
      include:
    
      - rust: stable
        script:
        - cargo test --all --locked
        - rustup component add rustfmt-preview
        - cargo fmt -- --write-mode=diff
    複製程式碼
  2. travis配置檔案.travis.yml
    language: rust
    rust:
      - stable
      - nightly
    
    branches:
      except:
        - staging.tmp
    
    before_install:
      # Do not run bors builds against the nightly compiler.
      # We want to find out about nightly bugs, so they're done in master, but we don't block on them.
      - if [[ $TRAVIS_RUST_VERSION == "nightly" && $TRAVIS_BRANCH == "staging" ]]; then exit; fi
    
    script:
      - cargo test
      - cargo build #--manifest-path your_project_path/Cargo.toml --features remote
      - cargo build
      #- (cd examples && make) #TODO
    複製程式碼

rustfmt 統一程式碼風格

為避免無意義的風格爭論,推薦使用Rust官方出品的統一程式碼風格元件rustfmt。以下所有命令都需要在已配置好Rust環境的終端上執行。可在CI上搭配。

  1. 安裝 rustup component add rustfmt-preview
  2. 更新rustfmt版本,使用rustup update命令可更新所有rustup已安裝的元件。
  3. 使用 cargo fmt

自定義rustfmt程式碼風格

不建議自定義程式碼風格,最好和官方預設程式碼保持一致。 定製風格規則參考rustfmt#configuring-rustfmt

sccahe 多工作區共享編譯快取

目前Rust只支援工作區workspace內部多個專案間的編譯快取,不支援workspace之間的快取。對於多個workspace引用了部分相同版本的元件,每個工作區或獨立專案都要編譯這些相同版本的元件,花費了多餘的編譯時間,沒意義。藉助第三方工具mozilla/sccache 可解決此問題,它支援遠端伺服器編譯共享,也支援本地共享。

Sccache is a ccache-like tool. It is used as a compiler wrapper and avoids compilation when possible, storing a cache in a remote storage using the Amazon Simple Cloud Storage Service (S3) API, the Google Cloud Storage (GCS) API, or Redis.

Sccache can also be used with local storage instead of remote.

  1. 安裝sccache cargo install sccache
  2. 配置sccache環境變數 export RUSTC_WRAPPER=sccache
  3. (最重要的一步) 配置sccache全域性編譯快取路徑

提高Rust與C介面互動的開發效率

cbindgen 給Rust程式碼自動生成C標頭檔案

給iOS/Android等編寫跨平臺C++/Rust專案最終還是以C介面方式讓外部使用,當提供較多介面時,手寫容易出錯、開發慢,此時用自動標頭檔案生成器是更合理的選擇,cbindgen可幫我們實現這一目標。

  1. 安裝 cargo install cbindgen
  2. 更新cbindgen cargo install --force cbindgen
  3. 使用方式一:命令列執行
    cbindgen crate/ -o crate/bindings.h
    複製程式碼
  4. 使用方式二:作為專案的預處理使用方式寫成build.rs,對於複雜專案,推薦用此方案。
    extern crate cbindgen;
    
    use std::env;
    
    fn main() {
        let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    
        cbindgen::Builder::new()
          .with_crate(crate_dir)
          .generate()
          .expect("Unable to generate bindings")
          .write_to_file("bindings.h");
    }
    複製程式碼

bindgen 給C標頭檔案生成Rust繫結程式碼

和cbindgen相反,bindgen可生成Rust呼叫C函式所需的FFI繫結程式碼,但是這個工具在遇到多重包含如#include "other_file.h"時會出錯,詳細說明見官方文件

  1. 安裝 cargo install bindgen
  2. 使用bindgen input.h -o bindings.rs
    • --rust-target 指定Rust版本,如--rust-target 1.30
    • --rust-target nightly 使用nightly工具鏈

提高Rust開發SDK專案的維護效率

用built輸出Rust專案構建資訊

built是個構建依賴(build-dependencies)型別的開源專案,詳細用法見專案說明。
built 0.3.0預設不支援built_info::DEPENDENCIES_STR。

  1. 配置Cargo.toml

    [package]
    build = "build.rs"
    
    [build-dependencies]
    built = "0.3"
    複製程式碼
  2. 新增build.rs,與Cargo.toml同級。更詳細的資訊需要配置built。

    extern crate built;
    fn main() {
        built::write_built_file().expect("Failed to acquire build-time information");
    }
    複製程式碼
  3. 使用示例

    pub mod built_info {
        include!(concat!(env!("OUT_DIR"), "/build.rs"));
    }
    
    info!("Version {}{}, built for {} by {}.",
          built_info::PKG_VERSION,
          built_info::GIT_VERSION.map_or_else(|| "".to_owned(),
                                              |v| format!(" (git {})", v)),
          built_info::TARGET,
          built_info::RUSTC_VERSION);
    trace!("Built with profile \"{}\", features \"{}\" on {} using {}",
           built_info::PROFILE,
           built_info::FEATURES_STR,
           built_info::BUILT_TIME_UTC,
           built_info::DEPENDENCIES_STR);
    複製程式碼

輸出資訊:

Version 0.1.0 (git 62eb1e2), built for x86_64-apple-darwin
by rustc 1.32.0-nightly (6b9b97bd9 2018-11-15).

Built with profile "debug", features "DEFAULT, ERR_PRINTLN"
on Thu, 23 Nov 2018 19:00:08 GMT using bitflags 0.7.0, block 0.1.6, built 0.1.0, byteorder 0.5.3,
bytes 0.3.0, cgmath 0.7.0, ...
複製程式碼

tokei統計專案程式碼行數,支援C/C++/Rust等語言

tokei幾乎支援所有程式語言或指令碼的行數統計(有效程式碼與註釋都正常區分開),非常好用,下面給個我們專案的統計。

--------------------------------------------------------------------------------
 Language             Files        Lines         Code     Comments       Blanks
--------------------------------------------------------------------------------
 C Header                 1            5            5            0            0
 GLSL                    10          147          113            6           28
 Makefile                 1           83           58           12           13
 Markdown                13          288          288            0            0
 Rust                    96        51261        41749         4316         5196
 SVG                      1           33           33            0            0
 TOML                    12          338          293            2           43
 YAML                     1           39           31            4            4
--------------------------------------------------------------------------------
 Total                  135        52194        42570         4340         5284
--------------------------------------------------------------------------------
複製程式碼

提高Rust專案多模組管理效率

cargo-modules是檢視多個模組資訊的有效工具,比如樹形顯示crate的mod、mod的可見性等,非常好用,在此只放張官方動圖,詳情見專案文件。

cargo-modules功能演示

提高Cargo管理效率

一鍵更新Cargo工具

cargo-update一鍵更新本地安裝的Cargo工具,全部或指定部分。

  1. 安裝cargo-update cargo install cargo-update
  2. 使用,一鍵更新所有cargo工具 cargo install-update -a 檢查並自動更新已安裝的cargo元件中需要更新的部分
  3. 只更新指定的工具cargo install-update crate1 crate2

cargo install-update -a為例,以下為它的具體執行過程。

cargo install-update -a
    Updating registry 'https://github.com/rust-lang/crates.io-index'

Package       Installed      Latest   Needs update
bat           v0.6.1         v0.9.0   Yes
bindgen       v0.37.4        v0.43.1  Yes
cargo-lipo    v2.0.0-beta-3  v2.0.0   Yes
cargo-apk     v0.4.0         v0.4.0   No
cargo-update  v1.7.0         v1.7.0   No
exa           v0.8.0         v0.8.0   No
sccache       v0.2.7         v0.2.7   No

Updating cargo-lipo
    Updating crates.io index
  Downloaded cargo-lipo v2.0.0
  Downloaded 1 crates (9.4 KB) in 3.14s
  Installing cargo-lipo v2.0.0
   Compiling num-traits v0.2.6
   Compiling libc v0.2.44
   Compiling vec_map v0.6.0
   Compiling ansi_term v0.7.5
   Compiling strsim v0.4.1
   Compiling serde v0.7.15
   Compiling itoa v0.1.1
   Compiling bitflags v0.5.0
   Compiling unicode-width v0.1.5
   Compiling clap v2.2.6
   Compiling num-traits v0.1.43
   Compiling serde_json v0.7.4
   Compiling cargo-lipo v2.0.0
    Finished release [optimized] target(s) in 22.19s
   Replacing /Users/你的使用者名稱/.cargo/bin/cargo-lipo

// ...

Updated 3 packages.
複製程式碼

以macOS為例,所有cargo工具預設都安裝在/Users/你的使用者名稱/.cargo/bin/下,比如/Users/你的使用者名稱/.cargo/bin/cargo-lipo。

Rust開發iOS專案的效率工具

cargo-lipo

cargo lipo一個命令可編譯出iOS目前支援的5個CPU架構靜態庫,且自動合併成一個多合一的universal靜態庫。

  1. 安裝 cargo install cargo-lipo
  2. 在Rust專案任意位置執行cargo lipo即可開始編譯iOS靜態庫

Rust專案開啟Bitcode編譯

RUSTFLAGS="-C llvm-args=\"-fembed-bitcode\"" cargo build
複製程式碼

You can tell cargo to pass any argument you wish to the Rust compiler by setting the RUSTFLAGS environment variable. The Rustc compiler has a flag -C llvm-args=val that you can use to pass additional arguments to llvm.

參考:Enable Bitcode Output in Cargo Build for iOS Targets?

cargo build指定需要的iOS版本

IPHONEOS_DEPLOYMENT_TARGET=7.0 cargo build
複製程式碼

參考:libc .travis.yml檔案

Rust開發Android JNI專案的效率工具

cargo-rumo

支援編譯Android/iOS跨平臺庫。

  1. 安裝cargo-rumo cargo install rumo
  2. 編譯當前專案為APK rumo build
  3. 安裝APK到模擬器或手機 rumo device-install

jni-rs在Rust中呼叫JNI介面與暴露JNI介面給Java

jni-rs/jni-rs,顧名思義給JNI介面編寫Rust繫結,讓我們在Rust中呼叫JNI函式,在Java程式碼中直接載入so即可呼叫我們公開的API,無需再加一層標頭檔案。下面是給一個示例。

/// Expose the JNI interface for android below
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
pub mod android {
    extern crate jni;
    use self::jni::objects::{JClass, JString};
    use self::jni::sys::{jlong, jstring};
    use self::jni::JNIEnv;

    #[no_mangle]
    pub unsafe extern "C" fn Java_com_example_Rust_opengl_init(
        env: JNIEnv,
        _: JClass,
    ) -> jlong {
        let res = Box::into_raw(rust_opengl_backend_init());
        res as jlong
    }

    #[no_mangle]
    pub unsafe extern "C" fn Java_com_example_Rust_opengl_draw_frame(
        env: JNIEnv,
        _: JClass,
        handle: jlong,
    ) {
        rust_opengl_backend_draw(&mut (*(handle as *mut OpenGLBackend)));
    }
}
複製程式碼

android_logger在Rust中呼叫Android Log函式

同上,Nercury/android_logger-rs對映Android Log函式到Rust,方便Rust程式碼呼叫。

  1. 配置Cargo.toml
    [target.'cfg(target_os = "android")'.dependencies]
    android_logger = "0.5"
    複製程式碼
  2. 使用示例
    #[macro_use] 
    extern crate log;
    extern crate android_logger;
    
    use android_logger::Filter;
    
    fn native_activity_create() {
        android_logger::init_once(Filter::default().with_min_level(Level::Trace),
                                   None);
    }
    複製程式碼

相關文章