This document guides you through the process of creating a new Rust crate under the Sunstone project and integrating it into both the Cargo workspace and Fuchsia's build systems (GN and Bazel).
Sunstone is designed with a modular architecture consisting of multiple no_std helper crates (like sapphire-uuid, sapphire-peer-cache, and sapphire-gatt).
Fuchsia currently uses GN as its active build system (as Bazel integration is not yet fully ready for the platform).
To integrate our local in-tree crates into the GN build without writing manual BUILD.gn files in every crate directory:
third_party/rust_crates/Cargo.toml.fx update-rustc-third-party tool runs cargo-gnaw under the hood to automatically generate the production GN targets inside third_party/rust_crates/BUILD.gn.//third_party/rust_crates:sapphire-<crate-name>.Note: The update script also runs crate_universe to generate Bazel bindings (BUILD.bazel files) for future migration readiness, but GN remains the active compiler. Once Fuchsia fully transitions to Bazel, these integration steps will change slightly.
Create a new directory for your crate under src/connectivity/bluetooth/sunstone/:
cargo new <crate_name> --lib
You should be able to observe the following files:
Cargo.toml[package] name = "sapphire-<crate-name>" version = "0.1.0" edition = "2024" publish = false # add this to prevent accidental publication [dependencies] # Add crate dependencies here
src/lib.rsDeclare no_std compliance if this is a bare-metal/microcontroller target:
#![no_std] // Crate implementation...
Verify that the new crate is added to the members list (in alphabetical order) in the Sunstone root Cargo.toml (src/connectivity/bluetooth/sunstone/Cargo.toml). This should already be done when running cargo new --lib:
[workspace] members = [ # keep-sorted start "sapphire-<crate-name>", # Add your new crate here, "sapphire-gatt", "sapphire-peer-cache", "sapphire-uuid" # keep-sorted end ] resolver = "3"
To allow other crates (and Bazel) to depend on your new crate, you must register it in third_party/rust_crates/Cargo.toml.
Add your crate to the main [dependencies] section in third_party/rust_crates/Cargo.toml in alphabetical order:
[dependencies] # keep-sorted start ... sapphire-<crate-name> = "0.1.0" # keep-sorted end
Add the path redirection under the [patch.crates-io] section (in the ### In-tree Crates block):
[patch.crates-io] ... ### In-tree Crates: crates which are on crates.io but which we build from our in-tree copy # keep-sorted start ... sapphire-<crate-name> = { path = "intree/sunstone/sapphire-<crate-name>" } # keep-sorted end
(Note: intree/sunstone is a pre-existing symlink pointing to src/connectivity/bluetooth/sunstone, so you do not need to create new symlinks).
Add a GN package configuration section (usually placed alphabetically among other [gn.package.*] targets):
[gn.package.sapphire-<crate-name>."0.1.0"] uses_fuchsia_license = true
Run the Fuchsia utility to regenerate the Cargo lockfile, GN build targets, and Bazel workspace configurations:
fx update-rustc-third-party
This command will automatically generate:
third_party/rust_crates/Cargo.lockthird_party/rust_crates/BUILD.gnthird_party/rust_crates/vendor/... (Bazel build rules)src/connectivity/bluetooth/sunstone/sapphire-<crate-name>/BUILD.bazelTo compile and run host tests, you must declare a test target in src/connectivity/bluetooth/sunstone/BUILD.gn.
rustc_test targetAdd your test definition (usually inside if (is_host) block):
if (is_host) { ... rustc_test("sapphire_<crate_name_underscores>_test") { edition = "2024" source_root = "sapphire-<crate-name>/src/lib.rs" sources = [ "sapphire-<crate-name>/src/lib.rs" ] # add all of your files here deps = [ # Add regular and test dependencies here (e.g. "//third_party/rust_crates:proptest") ] } }
[!IMPORTANT] Fuchsia's GN build system requires all Rust source files to be explicitly listed in the
sourceslist. As your crate grows and you add new files/modules (e.g.,src/att.rs), you must manually append them to thesourcesarray inBUILD.gn. Otherwise, the GN build will fail or fail to track changes. So for any working commit on your crate, if there are any additional files, you must add them here.
Add the test target to the group("tests") target:
group("tests") { testonly = true deps = [ ":sapphire_gatt_test($host_toolchain)", ":sapphire_peer_cache_test($host_toolchain)", ":sapphire_uuid_test($host_toolchain)", ":sapphire_<crate_name_underscores>_test($host_toolchain)", # Add here ] }
To verify that your crate is correctly integrated and compiles, run the following commands:
Verify no_std Compilation: Run the Sunstone zero-allocation check from src/connectivity/bluetooth/sunstone/:
./check_no_std.sh
Build the Tests:
fx build host_x64/sapphire_<crate_name_underscores>_test
Run the Tests:
fx test sapphire_<crate_name_underscores>_test
fx update-rustc-third-party?A: You only need to run this command when you make changes to:
third_party/rust_crates/Cargo.toml (e.g., adding a new crate, modifying dependencies, or updating versions).You do not need to run it when:
.rs files) in your local crates.BUILD.gn files (unless you introduced a new third-party dependency).fx build?A: Run fx build (or target-specific builds like fx build host_x64/sapphire_<name>_test) when:
BUILD.gn files.Tip: For local Rust development, running cargo check in your crate directory (e.g., under sapphire-gatt/) is much faster for syntax and type checking. Use fx build as your final compilation check.
fx build before fx test?A: No. Running fx test sapphire_<name>_test will automatically trigger an incremental build for the test target before running the tests. You can directly run fx test during test iteration.
A:
fx update-rustc-third-party: No, only on dependency changes.fx build: No, you can rely on fx test to build and test in one step, or use cargo check for faster local iteration.fx test: Yes, run this whenever you want to verify that your code changes did not break any tests.