Cargo GNaw

Tooling to convert Cargo.toml files into native GN rules license cargo-gnaw


$ cargo install --path ~/cargo-gnaw


$ cargo gnaw --manifest-path ~/fuchsia/third_party/rust_crates/Cargo.toml -o ~/fuchsia/third_party/rust_crates/


  • --skip-root - Skip the root package in the Cargo.toml and treat it's dependencies as the top-level targets
  • --gn-bin - Path to GN binary for formatting the output

How it works

Cargo GNaw operates on vendored crates to convert them into GN rules. The resulting file is expected to be vendored with the crates and provides targets for the GN build system to reference.

All top-level crates are given an easy to use GN alias group that references the version exposed in the Cargo.toml. Direct dependencies of the root crate can be “lifted” to the top-level by skipping the default root crate.

Simple Example

name = "simple"
version = "1.0.25"
authors = ["Benjamin Brittain <>"]
edition = "2018"


converts to:

group("simple") { deps = [":simple-1-0-25"] } rust_library("simple-1-0-25") { crate_name = "simple" crate_root = "//tools/cargo-gnaw/src/tests/simple/src/" output_name = "simple-9ac42213326ac72d" deps = [] rustenv = [] rustflags = ["--cap-lints=allow", "--edition=2018", "-Cmetadata=9ac42213326ac72d", "-Cextra-filename=-9ac42213326ac72d"] }

Build Scripts

GNaw intentionally does not handle scripts at compilation time. Any evaluation of a script is done when the crate is vendored. The resulting configuration which is usually produced by the script is put into a section in the source Cargo.toml. Simple scripts (ones that only depend upon Rust's std library) evaluate and automatically provide the author with the expected configuration.

Packages vs. targets vs. crates

The difference between a “package”, “target”, and a “crate” is useful knowledge when working with GNaw:

  • A Cargo “target” corresponds to source files that can be compiled into a Rust “crate”. There are multiple target types, including library and binary.
  • A Cargo “package” contains one or more “targets”, and can contain at most one library target.

(Paraphrased from

Many Cargo packages only contain a single library target that produces a single library crate. Because of this (and because of the catchiness of the term) “crate” is often used imprecisely to refer to any of those three items depending on the context. However, when packages contain multiple targets, like one or more binaries alongside the library, it becomes important to distinguish between these terms.

GN configs

Some targets, including but not limited to those that use scripts, require additional configuration that can be specified in the Cargo.toml file. This configuration is consumed by GNaw, not Cargo, and used when generating GN targets.

Basic configuration

Basic configuration for a package's library target is specified as gn.package.<PackageName>.<ExactVersion>. This configuration will be applied to the GN library target unconditionally (for all platforms) and can include the following arrays:

  • configs - native GN config
  • deps - native GN dependency
  • env_vars - environment variables, usually used for pretending to be Cargo
  • rustflags - flags to pass through to rustc


rustflags = [ "--cfg=backtrace" ]

Platform-specific configuration

Configuration can also be applied to only specific platforms, for example only when building for Fuchsia or only when building for a specific host platform.

Platforms are specified in the Rust cfg format (e.g. cfg(unix)). The same four configuration fields (configs, deps, env_vars, rustflags) documented above are supported.


["1.2.3".platform."cfg(target_os = \"fuchsia\")"]
configs = [ "//some:fuchsia_specific_config" ]

Generating executables for binary targets

GNaw also supports generating GN executable targets for Cargo binary targets. GNaw must be told to generate such targets; none are generated by default.

Platform-independent or -dependent configuration can be specified as well, similar to above. The same four configuration fields (configs, deps, env_vars, rustflags) documented above are supported.

In the example below, my-gn-name will become both the name of the group target to depend upon and the executable's output_name, so usages can assume output_name == target_name (and must since get_target_outputs only works within the same file).


This example generates a group target named my-gn-name and an executable target that produces my-gn-name from the binary my-cargo-target target inside package my-cargo-package version 1.2.3. Also, when building for Unix this example applies an extra GN config.

output_name = "my-gn-name"
rustflags = [ "--cfg=feature" ]

configs = [ "//some:unix_specific_config" ]