Rust 学习笔记 - 项目发布

1,022 阅读7分钟

cargo 发布配置

release profile

release profile 发布配置,是预定义的,可自定义,可使用不同的配置,对代码编译拥有更多的控制。

每个 profile 的配置都独立于其它的 profile

cargo 主要的两个 profile

  • dev profile 适用于开发,cargo build
  • release profile 适用于发布,``cargo build --release`

例子:

随便找一个项目,执行 cargo build 控制台会打印 Finished dev [unoptimized + debuginfo] target(s) in 0.04s。执行 cargo build --release 控制台会打印 Finished release [optimized] target(s) in 1.74s

自定义 profile

针对每个 profile,cargo 都提供了 xxx profile 的配置:可以在 Cargo.toml 里添加 [profile.xxxx] 区域,在里面覆盖默认配置的子集。

例子:

[package]
name = "minigrep"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3

opt-level 是指的优化程度,范围 0~3,dev 下是不进行优化的,release 的时候会进行优化。

对于每个配置的默认值和完整选项可以参考官方文档

发布 crate

crate 官方网站,我们可以通过发布包来共享代码,主要托管开源代码。

发布之前我们先里了解几个与发布相关的比较重要的内容。

文档注释

用于生成 HTML 文档,显示公共 API 的文档注释,使用 /// 标注,支持 Markdown,放置在被说明条目之前。

/// Adds one to the number given
/// 
/// # Examples
/// 
/// ```rust
/// let arg = 5;
/// let answer = minigrep::add_one(arg);
/// 
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

可以使用 cargo doc 命令生成文档,使用 cargo doc --open 生成并使用浏览器打开文档。生成的文档放在 target/doc 路径下。

上面的 Examples 是一个常用章节,表示示例。其它常用章节:

  • Panics:函数可以发生 panic 的场景
  • Error:如果函数返回 Result,描述可能的错误种类,以及可导致错误的条件
  • Safety:如果函数处于 unsafe 调用,就应该解释该函数 unsafe 调用的原因,以及调用者确保的使用前提

文档注释作为测试

使用 cargo test 会对文档注释中的例子进行测试。

为包含注释的项添加文档注释

使用 //! 符号。这类注释通常用来描述 crate 和模块,比如 crate root(按惯例 src/lib.rs)

或者是在一个模块内,将 crate 或模块作为一个整体进行记录。

//! # Minigrep
//! 
//! `some description`
// 省略后面的代码...

pub use

使用 pub use 导出方便使用的公共 API。

可能遇到的问题:crate 的程序结构在开发时对于开发者很合理,但对于它的使用者不够方便。开发者通常会把程序结构分为很多层,使用者想找到这种深层结构中的某个类型很费劲。

例如:my_crate::some_module::another_module::UsefulType

解决办法:使用 pub use,这样不需要重新组织内部代码结构,又可以重新导出,创建一个与内部私有结构不同的对外公共结构。

创建并设置 Crates.io 账号

发布 crate 前,需要在 crates.io 创建账号并获得 API token

进入 crate 官网之后首先需要进行登录,可以使用 github 账号登录,然后进入账号设置,取得 API token,然后使用 cargo login [API token] 进行登录。这个命令会通知 cargo 将你的 API token 存储在本地 ~/.cargo/credentialsAPI token 不要泄露给他人,如果泄露可以去官网进行撤销。另外,还需要设置一个有效的邮箱地址。

crate 发布

在发布 crate 之前,需要在 Cargo.toml[package] 区域为 crate 添加一些元数据:

  • name:crate 需要唯一的名称
  • description:一两句话即可,会出现在 crate 的搜索结果中
  • license:需要提供许可证标识值,例如:MITApache-2.0 等。 可以到 spdx.org/licenses/ 查找。多个 license 之间用 OR 隔开
  • version:版本
  • author:作者

最后使用 cargo publish 命令进行发布。

crate 一旦发布,就是永久性的,该版本无法覆盖,代码无法删除,其目的就是保证依赖于该版本的项目可以正常工作。

对于发布已存在的 crate 的新版本,我们只需要修改 Cargo.toml 里面的 version 值,再重新发布即可。可以参考 semver.org/ 来语义版本。

撤回版本

使用 yank 命令可以从 crates.io 撤回版本。

撤回版本不等于删除版本(也不能删除),但可以防止其它项目把它当做新的依赖,已经存在项目可以继续将其作为依赖并可以下载。所有已经产生 Cargo.lock 的项目都不会中断,但是任何将来生成的 Cargo.lock 文件都不会使用被 yank 的版本。

命令举例:cargo yank --vers 1.0.1

取消撤回:cargo yank --vers 1.0.1 --undo

Cargo 工作空间(Workspaces)

cargo 工作空间就是帮助管理多个相互关联且需要协同开发的 crate。

cargo 工作空间是一套共享同一个 Cargo.lock 和输出文件夹的包。

使用一个实例来演示工作空间的创建等操作,实例包含 1 个二进制 crate,2 个库 crate,二进制 crate 的 main 函数,依赖于其它 2 个库 crate。其中一个库 crate 提供 add_one 函数,另一个库 crate 提供 add_two 函数。

创建工作空间

Step1

创建一个新的目录 ADD,在这个目录下创建一个文件 Cargo.toml

Step2

配置工作空间并添加一个名为 adder 的成员,在 Cargo.toml 中添加:

[workspace]

members = [
    "adder"
]

然后进入 ADD 目录去创建 adder 项目(二进制 crate):

cargo new adder

编译:

cargo build

编译之后会生成 Cargo.locktarget 目录,target 目录会存放所有成员的产出物,而 adder 下就没有 target 目录了,因为如果各个 crate 都有自己的 target 目录,那么有相互依赖关系的包就会出现反复编译的情况。

Step3

再添加一个成员 add-one,这是一个库 crate:

[workspace]

members = [
    "adder",
    "add-one",
]

创建:

cargo new add-one --lib

添加代码:

// lib.rs
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Step4

配置 adderCargo.toml,使 adder 依赖于 add-one:

[dependencies]

add-one = { path = "../add-one" }

修改 adder 的代码:

// main.rs
use add_one;

fn main() {
    let num = 10;
    println!(
        "Hello, world! {} plus one is {}!",
        num,
        add_one::add_one(num)
    );
}

编译

cargo build

运行 adder

cargo run -p adder

配置依赖外部 crate

工作空间只有一个 Cargo.lock 文件,在工作空间的顶层目录,这样可以保证工作空间内所有 crate 使用的依赖的版本都相同,也可以使工作空间内所有 crate 相互兼容。

Step5

现在我们把 adderadd-one 都加入 rand 这个外部依赖,adder 加入 rand = "0.3.14"add-one 加入 rand = "0.3.15",然后编译。

可以看到 Cargo.lock 中,他们对 rand 的版本统一成了 0.3.23

[[package]]
name = "add-one"
version = "0.1.0"
dependencies = [
 "rand 0.3.23",
]

[[package]]
name = "adder"
version = "0.1.0"
dependencies = [
 "add-one",
 "rand 0.3.23",
]

Step6

再添加一个成员 add-two,这是一个库 crate:

[workspace]

members = [
    "adder",
    "add-one",
    "add-two",
]

创建:

cargo new add-two --lib

添加代码:

// lib.rs
use rand;

此时执行编译就会报错,因为我们没有将外部依赖 rand 添加到 add-two 的配置中去。

为工作空间添加测试

Step7

add-one 中添加测试代码:

// lib.rs
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(3, add_one(2));
    }
}

执行测试 cargo test,从结果可以看出,我们执行了工作空间下的所有测试。

如果想要执行某一个 crate 的测试可以使用:cargo test -p add-one

注意: 工作空间下的 crate,不能批量进行发布,必须手动切换到每个目录下,一个一个使用 cargo publish 进行发布。

安装二进制 crate

crates.io 安装二进制 crate,可以使用 cargo install xxx 命令。我们只能安装具有二进制目标(binary target)的 crate,这个二进制目标就是一个可执行程序,由拥有 src/main.rs 或其它被指定为二进制文件的 crate 生成。

通常 README 里面有关于 crate 的描述,比如:拥有哪些 library target;或者是否拥有 binary target

cargo install 安装的二进制存放在根目录的 bin 文件夹下,如果你用 rustup 安装的 Rust,没有任何自定义配置,那么二进制存放目录是 $HOME/.cargo/bin,要是安装的程序能够执行运行就要确保该目录在 PATH 环境变量中已配置。

使用自定义命令扩展 cargo

cargo 被设计成可以使用子命令来扩展。例如:如果 PATH 中的某个二进制是 cargo-something,你可以像子命令一样运行:cargo something

类似这样的命令可以通过 cargo --list 列出来。

这样的有点就是可以使用 cargo install 来安装扩展,像内置工具一样运行。