11-外部工具
与 IDE、自定义工具集成 Cargo
译文 · 基于 The Cargo Book
原文链接: https://doc.rust-lang.org/cargo/reference/external-tools.html
Cargo 的目标之一是与第三方工具(如 IDE 与其他构建系统)简单集成。为便于集成,Cargo 提供若干设施:
你可以使用 cargo metadata 命令获取包结构与依赖信息。输出格式细节见 cargo metadata 文档。
该格式是稳定且带版本的。调用 cargo metadata 时应显式传递 --format-version 标志,以避免前向不兼容风险。
若你使用 Rust,可用 cargo_metadata crate 解析输出。
JSON 消息
传入 --message-format=json 时,Cargo 会在构建期间输出以下信息:
编译器错误与警告,
生成的产物,
构建脚本的结果(例如原生依赖)。
输出以每行一个 JSON 对象的格式发往 stdout。reason 字段区分不同类型的消息。
package_id 字段是引用该包的唯一标识符,也可用作许多命令的 --package 参数。语法文法见章节 包 ID 规范。
注意: --message-format=json 仅控制 Cargo 与 Rustc 的输出。
无法控制其他工具的输出,
例如 cargo run --message-format=json,
或过程宏的任意输出。
这些情况下一种可行的变通办法是:仅当一行以 { 开头时才将其解释为 JSON。
--message-format 选项还可接受额外的格式化值,以改变 JSON 消息的计算与呈现方式。更多细节见 build 命令文档 中对 --message-format 选项的说明。
若你使用 Rust,可用 cargo_metadata crate 解析这些消息。
MSRV: package_id 成为包 ID 规范需要 1.77。在此之前,它是不透明的。
编译器消息
「compiler-message」消息包含来自编译器的输出,如警告与错误。rustc 的消息格式细节见 rustc JSON 章节,其嵌入在以下结构中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| {
/* "reason" 表示消息种类。 */
"reason": "compiler-message",
/* 包 ID,用于引用该包的唯一标识符。 */
"package_id": "file:///path/to/my-package#0.1.0",
/* 包清单的绝对路径。 */
"manifest_path": "/path/to/my-package/Cargo.toml",
/* 生成该消息的 Cargo 目标(lib、bin、example 等)。 */
"target": {
/* 目标种类数组。
- lib 目标列出清单中的 `crate-type` 值,
如 "lib"、"rlib"、"dylib"、
"proc-macro" 等。(默认 ["lib"])
- 二进制为 ["bin"]
- 示例为 ["example"]
- 集成测试为 ["test"]
- 基准测试为 ["bench"]
- 构建脚本为 ["custom-build"]
*/
"kind": [
"lib"
],
/* crate 类型数组。
- lib 与示例库列出清单中的 `crate-type` 值,
如 "lib"、"rlib"、"dylib"、
"proc-macro" 等。(默认 ["lib"])
- 所有其他目标种类为 ["bin"]
*/
"crate_types": [
"lib"
],
/* 目标名称。
对 lib 目标,短横线会替换为下划线。
*/
"name": "my_package",
/* 目标根源文件的绝对路径。 */
"src_path": "/path/to/my-package/src/lib.rs",
/* 目标的 Rust edition。
默认为包的 edition。
*/
"edition": "2018",
/* 所需特性的数组。
若未设置所需特性,则不包含此属性。
*/
"required-features": ["feat1"],
/* 目标是否应由 `cargo doc` 生成文档。 */
"doc": true,
/* 此目标是否启用了文档测试,以及
该目标是否与文档测试兼容。
*/
"doctest": true
/* 此目标是否应使用 `--test` 构建并运行
*/
"test": true
},
/* 编译器发出的消息。
细节见 https://doc.rust-lang.org/rustc/json.html。
*/
"message": {
/* ... */
}
}
|
产物消息
对每一个编译步骤,都会发出结构如下的「compiler-artifact」消息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| {
/* "reason" 表示消息种类。 */
"reason": "compiler-artifact",
/* 包 ID,用于引用该包的唯一标识符。 */
"package_id": "file:///path/to/my-package#0.1.0",
/* 包清单的绝对路径。 */
"manifest_path": "/path/to/my-package/Cargo.toml",
/* 生成产物的 Cargo 目标(lib、bin、example 等)。
细节见上方 `compiler-message` 的定义。
*/
"target": {
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "my_package",
"src_path": "/path/to/my-package/src/lib.rs",
"edition": "2018",
"doc": true,
"doctest": true,
"test": true
},
/* profile 表示使用了哪些编译器设置。 */
"profile": {
/* 优化级别。 */
"opt_level": "0",
/* 调试级别,整数 0、1 或 2,或字符串
"line-directives-only" 或 "line-tables-only"。若为 `null`,
则表示 rustc 的默认值 0。
*/
"debuginfo": 2,
/* 是否启用调试断言。 */
"debug_assertions": true,
/* 是否启用溢出检查。 */
"overflow_checks": true,
/* 是否使用了 `--test` 标志。 */
"test": false
},
/* 已启用特性的数组。 */
"features": ["feat1", "feat2"],
/* 此步骤生成的文件数组。 */
"filenames": [
"/path/to/my-package/target/debug/libmy_package.rlib",
"/path/to/my-package/target/debug/deps/libmy_package-be9f3faac0a26ef0.rmeta"
],
/* 所创建可执行文件路径的字符串,若此步骤未生成可执行文件则为 null。
*/
"executable": null,
/* 此步骤是否实际被执行。
为 `true` 表示已有产物是最新的,
未执行 `rustc`。为 `false` 表示运行了
`rustc` 来生成产物。
*/
"fresh": true
}
|
构建脚本输出
「build-script-executed」消息包含构建脚本的解析输出。注意即使未运行构建脚本也会发出此消息;它会显示先前缓存的值。构建脚本输出的更多细节见构建脚本章节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| {
/* "reason" 表示消息种类。 */
"reason": "build-script-executed",
/* 包 ID,用于引用该包的唯一标识符。 */
"package_id": "file:///path/to/my-package#0.1.0",
/* 要链接的库数组,由 `cargo::rustc-link-lib`
指令指示。注意字符串中可能包含 "KIND=" 前缀,
其中 KIND 是库种类。
*/
"linked_libs": ["foo", "static=bar"],
/* 要加入库搜索路径的路径数组,由
`cargo::rustc-link-search` 指令指示。注意字符串中可能包含
"KIND=" 前缀,其中 KIND 是库种类。
*/
"linked_paths": ["/some/path", "native=/another/path"],
/* 要启用的 cfg 值数组,由 `cargo::rustc-cfg`
指令指示。
*/
"cfgs": ["cfg1", "cfg2=\"string\""],
/* 要设置的环境变量 [KEY, VALUE] 数组的数组,由
`cargo::rustc-env` 指令指示。
*/
"env": [
["SOME_KEY", "some value"],
["ANOTHER_KEY", "another value"]
],
/* 编译当前包时用作 `OUT_DIR` 环境变量值的绝对路径。
*/
"out_dir": "/some/path/in/target/dir"
}
|
构建完成
「build-finished」消息在构建结束时发出。
1
2
3
4
5
6
| {
/* "reason" 表示消息种类。 */
"reason": "build-finished",
/* 构建是否成功完成。 */
"success": true,
}
|
此消息有助于工具知道何时停止读取 JSON 消息。cargo test 或 cargo run 等命令可能在构建完成后产生额外输出。此消息让工具知道 Cargo 不会再产生额外的 JSON 消息,但之后仍可能有额外输出(例如 cargo run 所执行程序生成的输出)。
注意:测试的 JSON 输出有实验性的仅 nightly 支持,
若启用,在「build-finished」消息之后可能开始出现额外的测试专用 JSON 消息。
自定义子命令
Cargo 设计为可扩展新子命令,而无需修改 Cargo 本身。这是通过将形如 cargo (?<command>[^ ]+) 的调用转换为外部工具 cargo-${command} 的调用来实现的。外部工具必须存在于用户的某个 $PATH 目录中。
注意:Cargo 默认优先使用 $CARGO_HOME/bin 中的外部工具,
而非 $PATH。用户可通过将 $CARGO_HOME/bin
加入 $PATH 来覆盖此优先级。
当 Cargo 调用自定义子命令时,传给子命令的第一个参数照常是自定义子命令的文件名。第二个参数是子命令名称本身。例如,调用 cargo-${command} 时第二个参数为 ${command}。命令行上的任何额外参数会原样转发。
Cargo 也可通过 cargo help ${command} 显示自定义子命令的帮助输出。Cargo 假定若子命令的第三个参数为 --help,它会打印帮助消息。因此,cargo help ${command} 会调用 cargo-${command} ${command} --help。
自定义子命令可使用 CARGO 环境变量回调 Cargo。或者,它可以链接 cargo crate 作为库,但这种方法有缺点:
- 作为库的 Cargo 不稳定:API 可能在无弃用的情况下变更
- 所链接的 Cargo 库版本可能与 Cargo 二进制文件不同
相反,鼓励使用 CLI 接口来驱动 Cargo。可用 cargo metadata 命令获取关于当前项目的信息(cargo_metadata crate 为此命令提供 Rust 接口)。