likes
comments
collection
share

Rust 2018开发环境配置与开发效率工具集

作者站长头像
站长
· 阅读数 7

一句话概括: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的可见性等,非常好用,在此只放张官方动图,详情见项目文档。

Rust 2018开发环境配置与开发效率工具集

提高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);
    }
    
转载自:https://juejin.cn/post/6844903712670875661
评论
请登录