文件列表見:Rust 移動端跨平臺複雜圖形渲染專案開發系列總結(目錄)
一句話概括:macOS/Linux使用者首選CLion + Rust外掛,折騰VSCode收益太低。以下內容來自參與開發gfx-rs/hal、gfx-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 複製程式碼
- 安裝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 複製程式碼
- 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編譯、單元測試配置。
- 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 複製程式碼
- 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上搭配。
- 安裝
rustup component add rustfmt-preview
- 更新rustfmt版本,使用
rustup update
命令可更新所有rustup已安裝的元件。 - 使用
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.
- 安裝sccache
cargo install sccache
- 配置sccache環境變數
export RUSTC_WRAPPER=sccache
- (最重要的一步) 配置sccache全域性編譯快取路徑
提高Rust與C介面互動的開發效率
cbindgen 給Rust程式碼自動生成C標頭檔案
給iOS/Android等編寫跨平臺C++/Rust專案最終還是以C介面方式讓外部使用,當提供較多介面時,手寫容易出錯、開發慢,此時用自動標頭檔案生成器是更合理的選擇,cbindgen可幫我們實現這一目標。
- 安裝
cargo install cbindgen
- 更新cbindgen
cargo install --force cbindgen
- 使用方式一:命令列執行
cbindgen crate/ -o crate/bindings.h 複製程式碼
- 使用方式二:作為專案的預處理使用方式寫成
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"
時會出錯,詳細說明見官方文件。
- 安裝
cargo install bindgen
- 使用
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。
-
配置Cargo.toml
[package] build = "build.rs" [build-dependencies] built = "0.3" 複製程式碼
-
新增build.rs,與Cargo.toml同級。更詳細的資訊需要配置built。
extern crate built; fn main() { built::write_built_file().expect("Failed to acquire build-time information"); } 複製程式碼
-
使用示例
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管理效率
一鍵更新Cargo工具
cargo-update一鍵更新本地安裝的Cargo工具,全部或指定部分。
- 安裝cargo-update
cargo install cargo-update
- 使用,一鍵更新所有cargo工具
cargo install-update -a
檢查並自動更新已安裝的cargo元件中需要更新的部分 - 只更新指定的工具
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靜態庫。
- 安裝
cargo install cargo-lipo
- 在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
複製程式碼
Rust開發Android JNI專案的效率工具
cargo-rumo
支援編譯Android/iOS跨平臺庫。
- 安裝cargo-rumo
cargo install rumo
- 編譯當前專案為APK
rumo build
- 安裝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程式碼呼叫。
- 配置Cargo.toml
[target.'cfg(target_os = "android")'.dependencies] android_logger = "0.5" 複製程式碼
- 使用示例
#[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); } 複製程式碼