Rust 之二 各组件工具的源码、构建、配置、使用

概述

Rust 是一种预编译静态类型(ahead-of-time compiled)的编程语言,除了有基本的 Rust 编程语言标准之外,Rust 官方还提供了一系列适用于各个平台的开发辅助工具以及编译工具链来帮助我们处理 Rust 代码。

rustup

rustup 是 Rust 官方提供的所有相关开发辅助工具以及编译工具链的安装及管理器,使用 rustup 这个工具就可以自动为我们下载安装官方的 Rust 各工具的预编译发行版,并且可以在不同的发行版之间自由切换。

使用

rustup 是一个命令行工具,对应的可执行程序名为 rustup,详细的使用方法见下图中的命令行参数说明即可。

  1. rustup 本身就是代理的原型,它自身没有代理!

  2. 为了方便管理各种工具,rustup 引入了 component、profile、proxy 等基本概念来组织和管理所有相关工具的发布,详见之前博文 Rust 之一 组件介绍、版本发布、开发环境搭建 中的详细介绍!

配置

rustuprustup-init) 在运行时会自动读取 CARGO_HOMERUSTUP_HOME 这两个环境变量的值,其中,CARGO_HOME 指定了 Cargo 相关工具以及缓存的存放位置, 而 RUSTUP_HOME 则指定了编译工具链的位置。

  1. RUSTUP_DIST_SERVER(之前叫 RUSTUP_DIST_ROOT,现已弃用 )指定了编译工具链的下载的地址,默认是 https://static.rust-lang.org

  2. RUSTUP_UPDATE_ROOT 指定了 rustup 自更新的地址,默认值是 https://static.rust-lang.org/rustup

  3. 其他配置项见 https://rust-lang.github.io/rustup/environment-variables.html

源码

rustup 的源码仓库为 https://github.com/rust-lang/rustup,可以直接 git clone https://github.com/rust-lang/rustup.git 获取。rustup 是 multirust 的继承者。Rustup 最初是 multiust-rs,由 Diggory Blake 将 multirust 从 shell 脚本改写为 Rust,现在由 Rust 项目维护。

  1. rustup 源码中实现了根据名字的不同而实现不一样的功能,.cargo/bin 中的各个工具以及我们安装使用的 rustup-init 实际上都是它改成了不同名字。 详见 src\bin\rustup-init.rs 中函数 main()run_rustup()run_rustup_inner() 的内容

  2. 对于类 Unix 系统,官方推荐的 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 安装方法实际上就是下载并执行源码中的 rustup-init.sh 这个脚本

  3. 源码下的 doc 文件夹中是文档的源码,分别对应 The rustup bookThe Rustup developer guide。文档使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍

  4. 源码下的 www 文件夹中是 https://rustup.rs/ 网站的源码,这个网站貌似很少使用。

    1. 直接在 www 文件夹执行 python -m http.server 8000 可以本地部署

    2. 这个不是 https://rust-lang.org/ 的源码,https://rust-lang.org/ 的源码是独立仓库 https://github.com/rust-lang/www.rust-lang.org

构建

rustup 的源码本身就是一个标准的 Rust 项目工程(在 Rust 之三 项目管理(Package、Crate、Workspace 等)、文档规范、编码规范 中详细介绍),因此,我们可以使用 cargo build 命令一键编译出名为 rustup-init 可执行程序,这个是 rustup-init 实际上就是 rustup,详见下面的说明。

  1. 如果在 Windows 下构建,则必须要安装 Visual Studio Build Tools 2017 or later(必选 Workloads select Visual C++ build tools 以及 Individual componenets 中的 C++/CLI support、C++ CMake tools for Windows)、NASMCMakeninja

cargo

Cargo 是 Rust 的包管理器,可以用于依赖包的下载、编译、更新、分发等操作。同时, Cargo 还是一个命令行项目管理工具,它有一套自己的项目结构(在 Rust 之三 项目管理(Package、Crate、Workspace 等)、文档规范、编码规范 中详细介绍),可以帮助我们自动调用编译工具链来编译 Rust 项目源码。

使用

cargo 是一个命令行工具,对应的可执行程序名为 cargo,详细的使用方法见下图中的命令行参数说明即可。

  1. 当我们调用 cargo 命令时实际上是通过 .cargo/bin/cargo 这个代理来调用 .rustup/toolchains/发行版/bin/cargo 这个真正的可执行程序。

  2. cargo 默认的工作目录为 $HOME/.cargo,其中的目录说明如下:

    • bin:包含了通过 cargo install(只有拥有二进制目标文件的包能够被安装,并会被安装到 $HOME/.cargo/bin 中) 或 rustup 下载的包编译出的可执行文件。可以(默认)将该目录加入到 $PATH 环境变量中,以实现对这些可执行文件的直接访问
    • registry:这个是 Cargo 下周的各种 Crate 的缓存目录,包含了注册中心(例如 crates.io)的元数据和 Packages
      • index: 包含了注册中心中所有可用包的元数据( 版本、依赖等 )
      • cache: 保存了已下载的依赖,这些依赖包以 gzip 的压缩档案形式保存,后缀名为 .crate
      • src: 若一个已下载的 .crate 被一个 package 所需要,该档案会被解压缩到此目录下,最终 rustc 可以在其中找到所需的 .rs 文件
  3. Cargo 管理的项目的基本单位被称为 Package。一个 Package 中至少包含一个 Crate;一个 Package 中最多只能包含一个库 Library Crate;一个 Package 中可以包含任意多个 Binary Crate,详见之后的博文 Rust 之三 项目管理(Package、Crate、Workspace 等)、文档规范、编码规范

  4. cargo 内置的子命令只有 buildcheckdocruntestremove 等几个,当输入其他子命令时,它支持自动查找 cargo-xxx 的其他可执行文件,详见后文源码章节的说明!

  5. 绝大多数情况下,我们都是使用 cargo 命令来编译项目,而不是直接使用 rustc

配置

cargo 在工作时会自动读取 Windows 平台下的 %USERPROFILE%\.cargo\config.toml 或者类 Unix 系统下的 $HOME/.cargo/config.toml 或者当前项目目录下的 .cargo/config.toml 中的配置信息(在 cargo 1.38 及以前的配置版本使用文件名 config)。

  1. 当前项目目录下的 .cargo/config.toml 优先级高于系统用户目录下的配置文件!

  2. config.toml 中的内容最常用的就是更改软件包的镜像源地址。

  3. crates.io 之外添加新的注册服务,配置如下:

    复制代码
    [registries]
    ustc = { index = "https://mirrors.ustc.edu.cn/crates.io-index/" }

    对于这种方式,我们的项目的 Cargo.toml 中的依赖包引入方式也得对应的改

    复制代码
    [dependencies]
    time = {  registry = "ustc" }
  4. 直接使用新注册服务来替代默认的 crates.io,配置如下

    复制代码
    [source.crates-io]
    replace-with = 'ustc'
    [source.ustc]
    registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"

    示例:

    c 复制代码
    [source.crates-io] 
    registry = "https://github.com/rust-lang/crates.io-index" 
    replace-with = "ustc" # 或 rustcc、sjtu、tuna  
    # 根据 replace-with 配置的镜像源,以下只需配置一个对应的源即可 
    # 中国科大 
    [source.ustc] 
    registry = "git://mirrors.ustc.edu.cn/crates.io-index"  
    # rustcc 社区 
    [source.rustcc] 
    registry = "https://code.aliyun.com/rustcc/crates.io-index.git"  
    # 上海交通大学 
    [source.sjtu] 
    registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"  
    # 清华大学 
    [source.tuna] 
    registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
  5. CARGO_TARGET_DIR 定义了构建输出目录,默认是 target

  6. CARGO_HOME 这个环境变量定义了 cargo 默认所在的目录,其中包含了 Cargo 可执行程序、配置文件、缓存文件等等,我们通过修改这个环境变量就可以重定位 Cargo 的位置。

    • Windows 平台下默认值是 %USERPROFILE%\.cargo;Unix 类系统下默认值是 $HOME/.cargo

    • 当我们使用 Cargo 构建项目时,下载的各种 crates 就位于 .cargo/registry 目录中

cargo-binutils

cargo-binutils 是由 Rust 官方的嵌入式工作组(Rust on Embedded Devices Working Group)中的工具组(Embedded WG Tools team)维护的一个非官方项目,这个项目可以为我们的 cargo 命令增加一些子命令,使用这些子命令可以直接调用指定编译工具链对应的 LLVM 的相关工具。

安装
  1. cargo install cargo-binutils

  2. rustup component add llvm-tools

示例

当安装了 cargo-binutils 和 llvm-tools 之后,我们就可以直接使用如下命令了(cargo xxx 就是通过直接查找 cargo-xxx 可执行程序来实现的):

  • cargo objcopy 用于将 ELF 转换为 BIN
  • cargo nm 输出符号表
  • cargo objdump 反汇编

cargo build

cargo build 会编译我们的项目代码,结果会被放入项目根目录下的 target 文件夹中,target 目录的结构取决于是否使用 --target 标志为特定的平台构建。target 的位置可以通过设置 CARGO_TARGET_DIR 环境变量、build.target-dir 配置项以及 --target-dir 命令行参数这三种方式来更改。

  1. cargo build --verbose 可以看到调用 rustc 的详细参数

  2. 编译库时,Cargo 会自动使用 rustc--crate-type lib 选项,进而生成 .rlib 的库文件;编译可执行程序时,Cargo 会自动使用 rustc--crate-type bin,进而生成可执行文件

  3. 对于每个 Crate 的编译,Cargo 都会为 rustc 指定 --extern 选项,给出当前 crate 将使用的每个库的文件名,Rust 会将代码静态链接到最终的可执行文件中

不使用 --target

若不使用 --target 标志,则 Cargo 会根据宿主机架构进行构建,构建结果会放入项目根目录下的 target 目录中,target 下每个子目录中包含了相应的发布配置 profile 的构建结果,例如 release、debug 是自带的 profile,前者往往用于生产环境,因为会做大量的性能优化,而后者则用于开发环境,此时的编译效率和报错信息是最好的。

目录 描述
target/debug/ 包含了 dev profile 的构建输出(cargo build 或 cargo build --debug)
target/release/ release profile 的构建输出,cargo build --release
target/foo/ 自定义 foo profile 的构建输出,cargo build --profile=foo

除此之外我们还可以定义自己想要的 profile ,例如用于测试环境的 profile: test,用于预发环境的 profile :pre-prod 等。出于历史原因:

  • dev 和 test profile 的构建结果都存放在 debug 目录下
  • release 和 bench profile 则存放在 release 目录下
  • 用户定义的 profile 存在同名的目录下
使用 --target

当使用 --target XXX 为特定的平台编译后,输出会放在 target/XXX/ 目录下:

target 子目录说明

在 profile 文件夹中(例如 debug 或 release)包含编译后的最终成果:

目录 描述
target/debug/ 包含编译后的输出,例如二进制可执行文件、库对象( library target )
target/debug/examples/ 包含示例对象( example target )

还有一些命令会在 target 下生成自己的独立目录:

目录 描述
target/doc/ 包含通过 cargo doc 生成的文档
target/package/ 包含 cargo package 或 cargo publish 生成的输出

Cargo 还会创建几个用于构建过程的其它类型目录,它们的目录结构只应该被 Cargo 自身使用,因此可能会在未来发生变化:

目录 描述
target/debug/deps 依赖和其它输出成果
target/debug/incremental rustc 增量编译的输出,该缓存可以用于提升后续的编译速度
target/debug/build/ 构建脚本的输出

源码

cargo 的源码仓库为 https://github.com/rust-lang/cargo,直接使用 git clone https://github.com/rust-lang/cargo.git 获取。

  1. cargo 会自动查找 .cargo/bin/ 中以 cargo- 开头的其他可执行文件,并把他们作为子命令来使用!
  2. 源码的 /src/doc 目录下是文档 The Cargo Book 的源码,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍

构建

cargo 的源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build 命令一键编译出名为 cargo 可执行程序。

Clippy

Clippy 是Rust 官方开发的一个基于 LLVM 的 Rust 代码检测及优化工具(类似于 PC-lint 这类工具),它可以自动检测 Rust 代码中的潜在问题,并提供相应的建议。Clippy 使用 Lint 规则来检查代码,这些规则基于 Rust 的编译器插件系统。

cargo-clippy 是一个命令行工具,对应的可执程序为 cargo-clippy。当我们使用时实际上是通过 .cargo/bin/cargo-clippy 这个代理来调用 .rustup/toolchains/发行版/bin/cargo-clippy 这个真正的可执行程序。

cargo clippy

通常,cargo-clippy 是作为 cargo 命令的一个子命令 cargo clippy 来间接使用

clippy-driver

Clippy 可以抛开 Cargo 来独立使用,此时需要使用 clippy-driver 命令,其参数则需要与 rustc 的参数相同,例如 clippy-driver --edition 2018 -Cpanic=abort foo.rs

配置

默认情况下,cargo-clippy 会自动查找名为 clippy.toml.clippy.toml 的文件,搜索的位置优先级由高到低为 CLIPPY_CONF_DIRCARGO_MANIFEST_DIR、当前项目目录,并执行其中的规则。

  1. 除了配置文件中存放的部分 Lint 规则外,还可以直接在 cargo-clippy 的命令行中指定一些规则。

  2. 目前,Clippy 中包含了超过 700 条 lints,这些 lints 被划分为了多个类别,每个类别都对应一个默认的 lint level。在 rustc 中,Lint 被分为了 allow、warn、force-warn、deny、forbid 这 5 level。

  3. Cargo.toml 中的 [lints.clippy] 中也可以指定一些规则

  4. 在源代码中也可以使用特定的属性,例如 #![allow(clippy::all)] 来指定规则

源码

Clippy 的源码仓库为 https://github.com/rust-lang/rust-clippy,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build 命令一键编译出名为 cargo-clippyclippy-driver 的可执行程序。

文档

  1. 官方文档 Clippy Documentation

  2. 文档的源码位于 Clippy 源码的 book 目录下,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍

Rustfmt

Rustfmt 是 Rust 官方提供的格式化 Rust 代码的工具(类似于 LLVM 的 clang-format 这类工具),它根据一些预定义的规则(括号的使用、缩进的级别、空格的添加等)来自动将 Rust 代码格式化为统一的风格。

rustfmt 是一个命令行工具,对应的可执程序为 rustfmt,使用命令 rustfmt [options] <file>... 来对指定的源码文件来进行格式化。当我们使用时实际上是通过 .cargo/bin/rustfmt 这个代理来调用 .rustup/toolchains/发行版/bin/rustfmt 这个真正的可执行程序。

cargo fmt

通常 rustfmt 会被作为 cargo 命令的一个子命令 cargo fmt 来间接使用,完整格式是 cargo fmt [OPTIONS] [-- <rustfmt_options>...],其中的 [-- <rustfmt_options>...] 会直接传递给 rustfmt 来进行使用,因此它的取值就是 rustfmt 的选项。

cargo-fmt

此外,rustfmt 默认还会提供一个名为 cargo-fmt 的可执行程序,这个工具的作用于 cargo fmt 类似,不过它不依赖于 Cargo 这个工具进而实现类似的功能,因此,在不用 Cargo 时,我们可以直接使用 cargo-fmt 来一键格式化整个项目的源文件。

配置

在默认情况下,rustfmt 会自动读取当前项目各级目录下的 rustfmt.toml.rustfmt.toml 文件,并根据其中的规则来进行格式化配置文件所在的目录的代码。如果子目录下没有配置文件,但是它的父级目录有,则根据父级目录的配置文件格式化。

  1. 如果项目目录下不存在 rustfmt.toml.rustfmt.toml,则会尝试读取用户目录
  2. 如果用户目录下也没有,则读取 rustfmt 全局配置目录下的全局配置文件
  3. 配置文件中的各种规则直接查看源码中的 Configurations.md 或者使用在线 https://rust-lang.github.io/rustfmt/ 版本,两者是一样的!

源码

Rustfmt 的源码仓库为 https://github.com/rust-lang/rustfmt,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build 命令一键编译出名为 cargo-fmtrustfmt 的可执行程序。

文档

目前,Rustfmt 的文档就是源码里的 Configurations.md,源码目录的 docs 中的 index.thml 中就是通过 JavaScript 调用的 Configurations.md 的内容!

rust-analyzer

rust-analyzer 是 Rust 编程语言的语言服务器协议的实现。它为许多代码编辑器(VS Code、 Emacs 和 Vim)提供了代码完成、语法高亮和转到定义等特性。它也是一个命令行工具,不过,我们通常不会直接使用它,而是由代码编辑器插件来帮我们调用。

  1. 由于 rust-analyzer 通常不会由我们直接使用,所以,默认安装 Rust 后没有 rust-analyzer

  2. 实际上就是一个模块化编译器前端或者说一个动态分析库,通过 LSP(Language Server Protocol)与 VS Code、 Emacs 和 Vim 等通信

  3. rust-analyzer 依赖 Rust 标准库的源码,通常需要手动执行 rustup component add rust-src 来安装,安装后的位置为 .rustup\toolchains\toolchain_name\lib\rustlib\src\rust

  4. rust-analyzer 提供的功能都是通过调用其他 Rust 工具来实现的,例如,代码格式就会调用 rustfmt

源码

rust-analyzer 的源码仓库为 https://github.com/rust-lang/rust-analyzer,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build 命令一键编译出名为 rust-analyzerrust-analyzer-proc-macro-srv 的可执行程序。

文档

官方在线文档 。

  1. 官方在线文档
  2. 文档的源码位于 rust-analyzer 源码的 docs 目录下,,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍

配置

配置项众多,参见 https://rust-analyzer.github.io/manual.html#configuration 即可。

mdBook

mdBook 是一个使用 Markdown 创建出在线文档网站的命令行工具。它是创建产品或 API 文档、教程、课程材料或任何需要简洁、易于导航和可定制的演示文稿的理想工具。Rust 提供的绝大多数文档都是使用 mdBook 来构建的!

安装

默认安装 Rust 后比不会安装 mdbook,我们需要使用 cargo install mdbook 手动进行安装,mdbook 可执行文件会被放到 .cargo/bin 目录中。也可以直接在 https://github.com/rust-lang/mdBook/releases 上下载预编译好的可执行文件来使用!或者直接从源码构建出可执行程序!

目录结构

使用 mdbook init xxx 命令就会创建出名为 xxx 的文档目录结构,自动生成如下所示的目录结构

  • book.toml 文件按是配置文件

  • 新增的 Markdown文章都要放到 src 目录下,可以自行创建子目录来进行分类存放

  • src/SUMMARY.md 是所有文章的目录,新增的 Markdown文章都需要在这里面写一下

  • book 文件夹用于存放 mdBook 构建后产生的 HTML 的文档

发布文档

在文档目录中,执行 mdbook build 就可以一键生成 HTML 格式的静态网页文档,默认存到当前目录的 book 文件夹中。在编写时,可以使用 mdbook serve 命令可以启动本地 Web 服务器,我们可以直接在浏览器中以 localhost 进行预览

源码

mdBook 的源码仓库为 https://github.com/rust-lang/mdBook,源码本身就是一个 Rust 项目工程,因此,我们可以使用 cargo build 命令一键编译出名为 mdbook 的可执行程序。

文档

  1. 官方在线文档
  2. 文档的源码位于 mdBook 源码的 guide 目录下,本身也是使用的 mdBook 文档系统。首先安装 cargo install mbook 或者自己构建后,在 doc\guide中执行 mbook build 或者在 mbook build 指定路径 就可以构建出上面的官方在线文档

rustdoc

rustdoc 是 Rust 官方提供的随附编译工具链发布的文档处理工具,这个工具主要是用来自动收集当前项目源码文件中的各种注释来生成对应文档 ,它接受一个 crate root 文件或者一个 Markdown 文件作为参数,然后生成 HTML,CSS 和 JavaScript 文件等称为一套以静态网页为主的文档。

rustdoc 是一个命令行工具,对应的可执程序为 rustdoc,使用命令 rustfmt [options] <input> 自动收集源码中的注释形成文档。当我们使用时实际上是通过 .cargo/bin/rustdoc 这个代理来调用 .rustup/toolchains/发行版/bin/rustdoc 这个真正的可执行程序。

  1. rustdoc 以 crate root 文件为入口自动遍历所以使用到的源码文件,不用我们手动指定所有源码文件。
  2. 默认的文档名字是 Crate 的名字,可以使用 --crate-name xxx 来进行更改
  3. 文档将存放到 target\doc 目录下,每个 Crate 一个子目录,他们之间会互相引用

cargo doc

通常 rustdoc 会被作为 cargo 命令的一个子命令 cargo doc 来间接使用,完整格式是 cargo doc [OPTIONS]。这个命令需要在我们的 Package 目录下执行,Cargo 将自动查找所有源代码文件(包括我们依赖的 Crate 的源码文件)。

  1. 使用 cargo doc --verbose 可以看到调用 rustdoc 的详细参数

  2. cargo doc 目前不支持独立的 Markdown 文件

  3. cargo doc 命令不止处理我们自己的源码文件,还会处理依赖的 Crate 的源码文件,进而形成一整套的文档。运行 cargo doc --open 命令来构建所有本地依赖项提供的文档,并在浏览器中打开,这样就方便了我们查找需要的接口

源码

rustdoc 稍微有点特殊,它是作为 Rust 的编译工具链中的一个标准组件来发布的。因此,它没有一个单独的源码仓库,而是在 Rust 编译工具链的仓库 https://github.com/rust-lang/rust 中的一个部分

文档

  • 官方文档 The rustdoc book

  • 文档的源码位于 Rust 源码的 src\doc\rustdoc 目录下,可以使用命令 x.py doc src/doc/rustdoc 来构建,使用的是 mdBook 文档系统,详细介绍参见后文的 mdBook 章节的介绍

文档注释

rustdoc 会提取源码文件中特定格式的注释(官方称为文档注释)来生成文档,rustdoc 规定了用于生成的文档的注释必须以 /// 或者 //! 开头,并且注释的内容主要使用 Markdown 语法来进行编写。

  1. /// 用于放在函数、结构体、变量等代码前面以对代码进行注释,称为 outer documentation,示例如下:
  2. //! 用于放到一个源码文件的开头给一个源码文件注释,称为 inner documentation。示例如下:
    1. 当它位于 root crate 文件时,那么它就是文档主页的注释
    2. 其最后面需要加一个空行
  3. 可以在自己的 src/lib.rsmain.rs 的开头添加 #![warn(missing_docs)],而如果是一个要共享的库则可添加 #![deny(missing_docs)]
  4. 使用 #[doc(hidden)] 可以屏蔽将之后的内容提取到文档中

参考

  1. https://aws.github.io/aws-lc-rs/requirements/windows.html
  2. https://blog.csdn.net/fj_Author/article/details/132594546
  3. https://www.andy-pearce.com/blog/posts/2023/May/uncovering-rust-build-and-packaging/
  4. https://aws.github.io/aws-lc-rs/requirements/index.html
相关推荐
MOON404☾8 小时前
Rust 与 传统语言:现代系统编程的深度对比
开发语言·后端·python·rust
一只小松许️21 小时前
深入理解:Rust 的内存模型
java·开发语言·rust
修仙的人1 天前
Rust + WebAssembly 实战!别再听说,学会使用!
前端·rust
alwaysrun1 天前
Rust中的特征Trait
rust·trait·impl trait·dyn trait·派生特征
为java加瓦1 天前
Rust 的类型自动解引用:隐藏在人体工学设计中的魔法
java·服务器·rust
leiteorz1 天前
第三章 Ownership与结构体、枚举
rust
alwaysrun2 天前
Rust中所有权和作用域及生命周期
rust·生命周期·作用域·所有权·引用与借用
FleetingLore2 天前
Rust | str 常用方法
rust
一只小松许️3 天前
深入理解 Rust 的内存模型:变量、值与指针
java·开发语言·rust