@ -1,17 +1,17 @@
## 将 crate 发布到 Crates.io
> [ch14-02-publishing-to-crates-io.md ](https://github.com/rust-lang/book/blob/main/src/ch14-02-publishing-to-crates-io.md ) < br >
> commit 3f2a6ef48943ade3e9c0eb23d69e2b8b41f057f1
<!-- https://github.com/rust - lang/book/blob/main/src/ch14 - 02 - publishing - to - crates - io.md -- >
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
我们曾经在项目中使用 [crates.io ](https://crates.io )<!-- ignore --> 上的包作为依赖,不过你也可以通过发布自己的包来向他人分享代码。[crates.io](https://crates.io)<!-- ignore --> 用来分发包的源代码,所以 它主要托管开源代码。
我们曾经在项目中使用 [crates.io ](https://crates.io )<!-- ignore --> 上的包作为依赖,不过你也可以通过发布自己的包来向他人分享代码。[crates.io](https://crates.io)<!-- ignore --> 上的 crate 注册表会分发你包的源代码,因此 它主要托管开源代码。
Rust 和 Cargo 有一些帮助他人更方便地找到和使用你发布的包的功能。我们将介绍一些这样的功能,接着讲到如何发布一个包。
### 编写有用的文档注释
准确的包文档有助于其他用户理解如何以及何时使用它们,所以花一些时间编写文档是值得的。第三章中我们讨论了如何使用双斜杠 `//` 注释 Rust 代码。Rust 也有特定的用于文档的注释类型,通常被称为 ** 文档注释**( _documentation comments_ ),它们会生成 HTML 文档。这些 HTML 展示公有 API 文档注释的内容,它们意在让对库感兴趣的程序员理解如何 ** 使用** 这个 crate, 而不是它是如何被 ** 实现** 的。
准确的包文档有助于其他用户理解如何以及何时使用它们,所以花一些时间编写文档是值得的。第三章中我们讨论了如何使用双斜杠 `//` 注释 Rust 代码。Rust 也有特定的用于文档的注释类型,通常被称为**文档注释**( *documentation comments* ),它们会生成 HTML 文档。这些 HTML 展示公有 API 文档注释的内容,它们意在让对库感兴趣的程序员理解如何**使用**这个 crate, 而不是它是如何被**实现**的。
文档注释使用三斜杠 `///` 而不是双斜杠以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。示例 14-1 展示了一个 `my_crate` crate 中 `add_one` 函数的文档注释,
文档注释使用三斜杠 `///` 而不是双斜杠以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。示例 14-1 展示了一个 `my_crate` crate 中 `add_one` 函数的文档注释。
< span class = "filename" > 文件名: src/lib.rs< / span >
@ -21,11 +21,11 @@ Rust 和 Cargo 有一些帮助他人更方便地找到和使用你发布的包
< span class = "caption" > 示例 14-1: 一个函数的文档注释< / span >
这里,我们提供了一个 `add_one` 函数工作的描述,接着开始了一个标题为 `Examples` 的部分,和展示如何使用 `add_one` 函数的代码。可以运行 `cargo doc` 来生成这个文档注释的 HTML 文档。这个命令运行由 Rust 分发的工具 `rustdoc` 并将生成的 HTML 文档放入 _target/doc_ 目录。
这里,我们提供了一个 `add_one` 函数工作的描述,接着开始了一个标题为 `Examples` 的部分,和展示如何使用 `add_one` 函数的代码。可以运行 `cargo doc` 来生成这个文档注释的 HTML 文档。这个命令运行由 Rust 分发的工具 `rustdoc` 并将生成的 HTML 文档放入 *target/doc* 目录。
为了方便起见,运行 `cargo doc --open` 会构建当前 crate 文档(同时还有所有 crate 依赖的文档)的 HTML 并在浏览器中打开。导航到 `add_one` 函数将会发现文档注释的文本是如何渲染的,如图 14-1 所示:
< img alt = " `my_crate` 的 `add_one` 函数所渲染的文档注释 HTML " src = "img/trpl14-01.png" class = "center" />
< img alt = " Rendered HTML documentation for the `add_one` function of `my_crate` " src = "img/trpl14-01.png" class = "center" />
< span class = "caption" > 图 14-1: `add_one` 函数的文档注释 HTML< / span >
@ -68,25 +68,25 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
< span class = "caption" > 示例 14-2: `my_crate` crate 整体的文档< / span >
注意 `//!` 的最后一行之后没有任何代码。因为它们以 `//!` 开头而不是 `///` ,这是属于包含此注释的项而不是注释之后项的文档。在这个情况下时 _src/lib.rs_ 文件,也就是 crate 根文件。这些注释描述了整个 crate。
注意 `//!` 的最后一行之后没有任何代码。因为它们以 `//!` 开头而不是 `///` ,这是属于包含此注释的项而不是注释之后项的文档。在这个情况下是 *src/lib.rs* 文件,也就是 crate 根文件。这些注释描述了整个 crate。
如果运行 `cargo doc --open` ,将会发现这些注释显示在 `my_crate` 文档的首页,位于 crate 中公有项列表之上,如图 14-2 所示:
< img alt = " crate 整体注释所渲染的 HTML 文档 " src = "img/trpl14-02.png" class = "center" / >
< img alt = " Rendered HTML documentation with a comment for the crate as a whole " src = "img/trpl14-02.png" class = "center" / >
< span class = "caption" > 图 14-2: 包含 `my_crate` 整体描述的注释所渲染的文档</ span >
位于项之中的文档注释对于描述 crate 和模块特别有用。使用它们描述其容器整体的目的来帮助 crate 用户理解你的代码组织。
### 使用 `pub use` 导出合适 的公有 API
### 使用 `pub use` 导出便捷 的公有 API
公有 API 的结构是你发布 crate 时主要需要考虑的。crate 用户没有你那么熟悉其结构,并且如果模块层级过大他们可能会难以找到所需的部分。
第七章介绍了如何使用 ` mod` 关键字来将代码组织进模块中,如何使用 ` pub` 关键字将项变为公有,和如何使用 `use` 关键字将项引入作用域。然而你开发时候使用的文件架构可能并不方便用户。你的结构可能是一个包含多个层级的分层结构,不过这对于用户来说并不方便。这是因为想要使用被定义在很深层级中的类型的人可能很难发现这些类型的存在。他们也可能会厌烦要使用 `use my_crate::some_module::another_module::UsefulType;` 而不是 `use my_crate::UsefulType;` 来使用类型。
第七章介绍了如何使用 ` pub` 关键字将项变为公有,和如何使用 `use` 关键字将项引入作用域。然而你开发时候使用的文件架构可能并不方便用户使用 。你的结构可能是一个包含多个层级的分层结构,不过这对于用户来说并不方便。这是因为想要使用被定义在很深层级中的类型的人可能很难发现这些类型的存在。他们也可能会厌烦要使用 `use my_crate::some_module::another_module::UsefulType;` 而不是 `use my_crate::UsefulType;` 来使用类型。
好消息是,即使文件结构对于用户来说 ** 不是** 很方便,你也无需重新安排内部组织:你可以选择使用 `pub use` 重导出( re-export) 项来使公有结构不同于私有结构。重导出获取位于一个位置的公有项并将其公开到另一个位置, 好像它就定义在这个新位置一样。
好消息是,即使文件结构对于用户来说**不是**很方便,你也无需重新安排内部组织:你可以选择使用 `pub use` 重导出( re-export) 项来使公有结构不同于私有结构。重导出获取位于一个位置的公有项并将其公开到另一个位置, 好像它就定义在这个新位置一样。
例如,假设我们创建了一个描述美术信息 的库 `art` 。这个库中包含了一个有两个枚举 `PrimaryColor` 和 `SecondaryColor` 的模块 `kinds` ,以及一个包含函数 `mix` 的模块 `utils` ,如示例 14-3 所示:
例如,假设我们创建了一个描述艺术概念 的库 `art` 。这个库中包含了一个有两个枚举 `PrimaryColor` 和 `SecondaryColor` 的模块 `kinds` ,以及一个包含函数 `mix` 的模块 `utils` ,如示例 14-3 所示:
< span class = "filename" > 文件名: src/lib.rs< / span >
@ -98,7 +98,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
`cargo doc` 所生成的 crate 文档首页如图 14-3 所示:
< img alt = " 包含 `kinds` 和 `utils` 模块的 `art` " src = "img/trpl14-03.png" class = "center" />
< img alt = " Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules " src = "img/trpl14-03.png" class = "center" />
< span class = "caption" > 图 14-3: 包含 `kinds` 和 `utils` 模块的库 `art` 的文档首页</ span >
@ -126,13 +126,13 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
< span class = "caption" > 示例 14-5: 增加 `pub use` 语句重导出项</ span >
现在此 crate 由 `cargo doc` 生成的 API 文档会在首页列出重导出的项以及其链接,如图 14-4 所示,这使得 `PrimaryColor` 和 `SecondaryColor` 类型和 `mix` 函数更易于查找。
现在此 crate 由 `cargo doc` 生成的 API 文档会在首页列出重导出的项以及其链接( re-exports) ,如图 14-4 所示,这使得 `PrimaryColor` 和 `SecondaryColor` 类型和 `mix` 函数更易于查找。
< img alt = "Rendered documentation for the `art` crate with the re-exports on the front page" src = "img/trpl14-04.png" class = "center" />
< span class = "caption" > 图 14-10: `art` 文档的首页,这里列出了重导出的项< / span >
`art` crate 的用户仍然可以看见和选择使用示例 14-4 中的内部结构 ,或者可以使用示例 14-5 中更为方便的结构,如示例 14-6 所示:
`art` crate 的用户仍然可以看到并使用示例 14-3 中的内部结构,如示例 14-4 所示 ,或者可以使用示例 14-5 中更为方便的结构,如示例 14-6 所示:
< span class = "filename" > 文件名: src/main.rs< / span >
@ -148,19 +148,20 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
### 创建 Crates.io 账号
在你可以发布任何 crate 之前,需要在 [crates.io ](https://crates.io )<!-- ignore --> 上注册账号并获取一个 API token。为此, 访问位于 [crates.io ](https://crates.io )<!-- ignore --> 的首页并使用 GitHub 账号登录。(目前 GitHub 账号是必须的,不过将来该网站可能会支持其他创建账号的方法)一旦登录之后,查看位于 [https://crates.io/me/ ](https://crates.io/me/ )<!-- ignore --> 的账户设置页面并获取 API token。接着使用该 API token 运行 `cargo login` 命令,像这样 :
在你可以发布任何 crate 之前,需要在 [crates.io ](https://crates.io )<!-- ignore --> 上注册账号并获取一个 API token。为此, 访问位于 [crates.io ](https://crates.io )<!-- ignore --> 的首页并使用 GitHub 账号登录。(目前 GitHub 账号是必须的,不过将来该网站可能会支持其他创建账号的方法)一旦登录之后,查看位于 [https://crates.io/me/ ](https://crates.io/me/ )<!-- ignore --> 的账户设置页面并获取 API token。然后运行 `cargo login` 命令,并在提示时粘贴该 token, 操作如下所示 :
```console
$ cargo login abcdefghijklmnopqrstuvwxyz012345
$ cargo login
abcdefghijklmnopqrstuvwxyz012345
```
这个命令会通知 Cargo 你的 API token 并将其储存在本地的 _~/.cargo/credentials_ 文件中。注意这个 token 是一个 ** 秘密**( **secret**)且不应该与其他人共享。如果因为任何原因与他人共享了这个信息,应该立即到 [crates.io ](https://crates.io )<!-- ignore --> 撤销并重新生成一个 token。
这个命令会通知 Cargo 你的 API token 并将其储存在本地的 *~/.cargo/credentials* 文件中。注意这个 token 是一个** 秘密**( **secret**)且不应该与其他人共享。如果因为任何原因与他人共享了这个信息,应该立即到 [crates.io ](https://crates.io )<!-- ignore --> 撤销并重新生成一个 token。
### 向新 crate 添加元信息
### 向新 crate 添加元数据
比如说你已经有一个希望发布的 crate。在发布之前, 你需要在 crate 的 _Cargo.toml_ 文件的 `[package]` 部分增加一些本 crate 的元信息 ( metadata) 。
比如说你已经有一个希望发布的 crate。在发布之前, 你需要在 crate 的 *Cargo.toml* 文件的 `[package]` 部分增加一些本 crate 的元数据 ( metadata) 。
首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 [crates.io ](https://crates.io )<!-- ignore --> 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 _Cargo.toml_ 中 `[package]` 里的名称 为你希望用于发布的名称,像这样:
首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 [crates.io ](https://crates.io )<!-- ignore --> 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 _Cargo.toml_ 中 `[package]` 里的`name` 字段 为你希望用于发布的名称,像这样:
< span class = "filename" > 文件名: Cargo.toml< / span >
@ -180,10 +181,10 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for
error: failed to publish to registry at https://crates.io
Caused by:
the remote server responded with an error: missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata
the remote server responded with an error (status 400 Bad Request) : missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for more information on configuring these fields
```
这个错误是因为我们缺少一些关键信息:关于该 crate 用途的描述和用户可能在何种条款下使用该 crate 的 license。在 _Cargo.toml_ 中添加通常是一两句话的描述,因为它将在搜索结果中和你的 crate 一起显示。对于 `license` 字段,你需要一个 **license 标识符值** ( _license identifier value_ )。[Linux 基金会的 Software Package Data Exchange (SPDX)][spdx] 列出了可以使用的标识符。例如,为了指定 crate 使用 MIT License, 增加 `MIT` 标识符:
这个错误是因为我们缺少一些关键信息:关于该 crate 用途的描述和用户可能在何种条款下使用该 crate 的 license。在 _Cargo.toml_ 中添加通常是一两句话的描述,因为它将在搜索结果中和你的 crate 一起显示。对于 `license` 字段,你需要一个 **license 标识符值** ( *license identifier value* )。[Linux 基金会的 Software Package Data Exchange (SPDX)][spdx] 列出了可以使用的标识符。例如,为了指定 crate 使用 MIT License, 增加 `MIT` 标识符:
< span class = "filename" > 文件名: Cargo.toml< / span >
@ -197,7 +198,7 @@ license = "MIT"
关于项目所适用的 license 指导超出了本书的范畴。很多 Rust 社区成员选择与 Rust 自身相同的 license, 这是一个双许可的 `MIT OR Apache-2.0` 。这个实践展示了也可以通过 `OR` 分隔为项目指定多个 license 标识符。
那么,有了唯一的名称、版本号、由 `cargo new` 新建项目时增加的作者信息、描述和所选择的 license, 已经准备好发布的项目的 _Cargo.toml_ 文件可能看起来像这样:
那么,有了唯一的名称、版本号、由 `cargo new` 新建项目时增加的作者信息、描述和所选择的 license, 已经准备好发布的项目的 *Cargo.toml* 文件可能看起来像这样:
< span class = "filename" > 文件名: Cargo.toml< / span >
@ -205,20 +206,20 @@ license = "MIT"
[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021 "
edition = "2024 "
description = "A fun game where you guess what number the computer has chosen."
license = "MIT OR Apache-2.0"
[dependencies]
```
[Cargo 的文档 ](http://doc.rust-lang.org/cargo/ ) 描述了其他可以指定的元信息 ,它们可以帮助你的 crate 更容易被发现和使用!
[Cargo 的文档 ](http://doc.rust-lang.org/cargo/ ) 描述了其他可以指定的元数据 ,它们可以帮助你的 crate 更容易被发现和使用!
### 发布到 Crates.io
现在我们创建了一个账号,保存了 API token, 为 crate 选择了一个名字,并指定了所需的元数据,你已经准备好发布了!发布 crate 会上传特定版本的 crate 到 [crates.io ](https://crates.io )<!-- ignore --> 以供他人使用。
发布 crate 时请多加小心,因为发布是 ** 永久性的**( _permanent_) 。对应版本不可能被覆盖, 其代码也不可能被删除。[crates.io](https://crates.io)<!-- ignore --> 的一个主要目标是作为一个存储代码的永久文档服务器,这样所有依赖 [crates.io ](https://crates.io )<!-- ignore --> 中的 crate 的项目都能一直正常工作。而允许删除版本没办法达成这个目标。然而,可以被发布的版本号却没有限制。
发布 crate 时请多加小心,因为发布是**永久性的**( _permanent_) 。对应版本不可能被覆盖, 其代码也不可能被删除。[crates.io](https://crates.io)<!-- ignore --> 的一个主要目标是作为一个存储代码的永久文档服务器,这样所有依赖 [crates.io ](https://crates.io )<!-- ignore --> 中的 crate 的项目都能一直正常工作。而允许删除版本没办法达成这个目标。然而,可以被发布的版本号却没有限制。
再次运行 `cargo publish` 命令。这次它应该会成功:
@ -229,21 +230,21 @@ $ cargo publish
Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
Finished ` dev` profile [unoptimized + debuginfo] target(s) in 0.19s
Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
```
恭喜!你现在向 Rust 社区分享了代码,而且任何人都可以轻松的将你的 crate 加入他们项目的依赖。
### 发布现存 crate 的新版本
### 发布现有 crate 的新版本
当你修改了 crate 并准备好发布新版本时,改变 _Cargo.toml_ 中 `version` 所指定的值。请使用 [语义化版本规则][semver] 来根据修改的类型决定下一个版本号。接着运行 `cargo publish` 来上传新版本。
当你修改了 crate 并准备好发布新版本时,改变 *Cargo.toml* 中 `version` 所指定的值。请使用[语义化版本规则][semver]来根据修改的类型决定下一个版本号。接着运行 `cargo publish` 来上传新版本。
### 使用 `cargo yank` 从 Crates.io 弃用 版本
### 使用 `cargo yank` 从 Crates.io 撤回 版本
虽然你不能删除之前版本的 crate, 但是可以阻止任何将来的项目将它们加入到依赖中。这在某个版本因为这样或那样的原因被破坏的情况很有用。对于这种情况, Cargo 支持 ** 撤回**( _yanking_ )某个版本。
虽然你不能删除 crate 的历史版本 , 但是可以阻止任何将来的项目将它们加入到依赖中。这在某个版本因为这样或那样的原因被破坏的情况很有用。对于这种情况, Cargo 支持**撤回**( *yanking* )某个版本。
撤回某个版本会阻止新项目依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 _Cargo.lock_ 的项目的依赖不会被破坏,同时任何新生成的 _Cargo.lock_ 将不能使用被撤回的版本。
** 撤回** 某个版本会阻止新项目依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 *Cargo.lock* 的项目的依赖不会被破坏,同时任何新生成的 *Cargo.lock* 将不能使用被撤回的版本。
为了撤回一个版本的 crate, 在之前发布 crate 的目录运行 `cargo yank` 并指定希望撤回的版本。例如,如果我们发布了一个名为 `guessing_game` 的 crate 的 1.0.1 版本并希望撤回它,在 `guessing_game` 项目目录运行:
@ -261,7 +262,7 @@ $ cargo yank --vers 1.0.1 --undo
Unyank guessing_game@1.0.1
```
撤回 ** 并没有** 删除任何代码。举例来说,撤回功能并不能删除不小心上传的秘密信息。如果出现了这种情况,请立即重新设置这些秘密信息。
撤回**并没有**删除任何代码。举例来说,撤回功能并不能删除不小心上传的秘密信息。如果出现了这种情况,请立即重新设置这些秘密信息。
[spdx]: http://spdx.org/licenses/
[semver]: http://semver.org/