外部工具

Cargo 的目标之一是与第三方工具(例如 IDE 和其他构建系统)轻松集成。为了使集成更容易,Cargo 提供了以下几种功能:

  • 一个 cargo metadata 命令,它以 JSON 格式输出包结构和依赖信息,

  • 一个 --message-format 标志,它输出关于特定构建的信息,以及

  • 支持自定义子命令。

包结构信息

你可以使用 cargo metadata 命令获取包结构和依赖信息。有关输出格式的详细信息,请参阅 cargo metadata 文档。

该格式是稳定的并且带有版本。调用 cargo metadata 时,应显式传递 --format-version 标志,以避免未来的不兼容性风险。

如果你正在使用 Rust,可以使用 cargo_metadata crate 来解析输出。

JSON 消息

传递 --message-format=json 时,Cargo 会在构建过程中输出以下信息:

  • 编译器错误和警告,

  • 生成的 artifacts,

  • 构建脚本的结果(例如,原生依赖项)。

输出会以每行一个 JSON 对象 的格式发送到 stdout。reason 字段区分不同类型的消息。package_id 字段是引用包的唯一标识符,也是许多命令的 --package 参数。语法规范可以在 包 ID 规范 章找到。

注意: --message-format=json 只控制 Cargo 和 Rustc 的输出。它无法控制其他工具的输出,例如 cargo run --message-format=json,或 procedural macros 的任意输出。在这种情况下,一个可能的解决方法是只有当一行以 { 开头时,才将其解释为 JSON。

--message-format 选项还可以接受额外的格式化值,这些值会改变 JSON 消息的计算和渲染方式。有关详细信息,请参阅 build command 文档 中关于 --message-format 选项的描述。

如果你正在使用 Rust,可以使用 cargo_metadata crate 来解析这些消息。

MSRV: 要使 package_id 成为包 ID 规范,需要 1.77 版本。在此之前,它是非透明的。

编译器消息

“compiler-message” 消息包含来自编译器的输出,例如警告和错误。有关 rustc 消息格式的详细信息,请参阅 rustc JSON 章,该格式嵌入在以下结构中:

{
    /* The "reason" indicates the kind of message. */
    "reason": "compiler-message",
    /* The Package ID, a unique identifier for referring to the package. */
    "package_id": "file:///path/to/my-package#0.1.0",
    /* Absolute path to the package manifest. */
    "manifest_path": "/path/to/my-package/Cargo.toml",
    /* The Cargo target (lib, bin, example, etc.) that generated the message. */
    "target": {
        /* Array of target kinds.
           - lib targets list the `crate-type` values from the
             manifest such as "lib", "rlib", "dylib",
             "proc-macro", etc. (default ["lib"])
           - binary is ["bin"]
           - example is ["example"]
           - integration test is ["test"]
           - benchmark is ["bench"]
           - build script is ["custom-build"]
        */
        "kind": [
            "lib"
        ],
        /* Array of crate types.
           - lib and example libraries list the `crate-type` values
             from the manifest such as "lib", "rlib", "dylib",
             "proc-macro", etc. (default ["lib"])
           - all other target kinds are ["bin"]
        */
        "crate_types": [
            "lib"
        ],
        /* The name of the target.
           For lib targets, dashes will be replaced with underscores.
        */
        "name": "my_package",
        /* Absolute path to the root source file of the target. */
        "src_path": "/path/to/my-package/src/lib.rs",
        /* The Rust edition of the target.
           Defaults to the package edition.
        */
        "edition": "2018",
        /* Array of required features.
           This property is not included if no required features are set.
        */
        "required-features": ["feat1"],
        /* Whether the target should be documented by `cargo doc`. */
        "doc": true,
        /* Whether or not this target has doc tests enabled, and
           the target is compatible with doc testing.
        */
        "doctest": true
        /* Whether or not this target should be built and run with `--test`
        */
        "test": true
    },
    /* The message emitted by the compiler.

    See https://doc.rust-lang.net.cn/rustc/json.html for details.
    */
    "message": {
        /* ... */
    }
}

Artifact 消息

对于每个编译步骤,都会发出一个“compiler-artifact”消息,其结构如下:

{
    /* The "reason" indicates the kind of message. */
    "reason": "compiler-artifact",
    /* The Package ID, a unique identifier for referring to the package. */
    "package_id": "file:///path/to/my-package#0.1.0",
    /* Absolute path to the package manifest. */
    "manifest_path": "/path/to/my-package/Cargo.toml",
    /* The Cargo target (lib, bin, example, etc.) that generated the artifacts.
       See the definition above for `compiler-message` for details.
    */
    "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
    },
    /* The profile indicates which compiler settings were used. */
    "profile": {
        /* The optimization level. */
        "opt_level": "0",
        /* The debug level, an integer of 0, 1, or 2, or a string
           "line-directives-only" or "line-tables-only". If `null`, it implies
           rustc's default of 0.
        */
        "debuginfo": 2,
        /* Whether or not debug assertions are enabled. */
        "debug_assertions": true,
        /* Whether or not overflow checks are enabled. */
        "overflow_checks": true,
        /* Whether or not the `--test` flag is used. */
        "test": false
    },
    /* Array of features enabled. */
    "features": ["feat1", "feat2"],
    /* Array of files generated by this step. */
    "filenames": [
        "/path/to/my-package/target/debug/libmy_package.rlib",
        "/path/to/my-package/target/debug/deps/libmy_package-be9f3faac0a26ef0.rmeta"
    ],
    /* A string of the path to the executable that was created, or null if
       this step did not generate an executable.
    */
    "executable": null,
    /* Whether or not this step was actually executed.
       When `true`, this means that the pre-existing artifacts were
       up-to-date, and `rustc` was not executed. When `false`, this means that
       `rustc` was run to generate the artifacts.
    */
    "fresh": true
}

构建脚本输出

“build-script-executed” 消息包含构建脚本的解析输出。请注意,即使构建脚本没有运行,也会发出此消息;它将显示先前缓存的值。有关构建脚本输出的更多详细信息,可在 构建脚本章 中找到。

{
    /* The "reason" indicates the kind of message. */
    "reason": "build-script-executed",
    /* The Package ID, a unique identifier for referring to the package. */
    "package_id": "file:///path/to/my-package#0.1.0",
    /* Array of libraries to link, as indicated by the `cargo::rustc-link-lib`
       instruction. Note that this may include a "KIND=" prefix in the string
       where KIND is the library kind.
    */
    "linked_libs": ["foo", "static=bar"],
    /* Array of paths to include in the library search path, as indicated by
       the `cargo::rustc-link-search` instruction. Note that this may include a
       "KIND=" prefix in the string where KIND is the library kind.
    */
    "linked_paths": ["/some/path", "native=/another/path"],
    /* Array of cfg values to enable, as indicated by the `cargo::rustc-cfg`
       instruction.
    */
    "cfgs": ["cfg1", "cfg2=\"string\""],
    /* Array of [KEY, VALUE] arrays of environment variables to set, as
       indicated by the `cargo::rustc-env` instruction.
    */
    "env": [
        ["SOME_KEY", "some value"],
        ["ANOTHER_KEY", "another value"]
    ],
    /* An absolute path which is used as a value of `OUT_DIR` environmental
       variable when compiling current package.
    */
    "out_dir": "/some/path/in/target/dir"
}

构建完成

“build-finished” 消息在构建结束时发出。

{
    /* The "reason" indicates the kind of message. */
    "reason": "build-finished",
    /* Whether or not the build finished successfully. */
    "success": true,
}

此消息有助于工具了解何时停止读取 JSON 消息。cargo testcargo run 等命令在构建完成后可能会产生额外的输出。此消息让工具知道 Cargo 不会再产生额外的 JSON 消息,但之后可能会生成其他输出(例如,由 cargo run 执行的程序生成的输出)。

注意:目前只在 nightly 版本中实验性地支持测试的 JSON 输出,因此如果启用该功能,在“build-finished”消息之后可能会开始出现额外的测试特定 JSON 消息。

自定义子命令

Cargo 被设计为可以通过新的子命令进行扩展,而无需修改 Cargo 本身。这是通过将形如 cargo (?<command>[^ ]+) 的 Cargo 调用转换为对外部工具 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 接口)。