| # Creating a New Rust Crate in Sunstone |
| |
| 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). |
| |
| ## Overview |
| |
| 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: |
| |
| 1. We register the local crate in the central |
| `third_party/rust_crates/Cargo.toml`. |
| 2. The `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`. |
| 3. Other GN targets can then depend on our crate via |
| `//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._ |
| |
| --- |
| |
| ## Step-by-Step Integration Guide |
| |
| ### Step 1: Create the Crate Directory and Files |
| |
| Create a new directory for your crate under |
| `src/connectivity/bluetooth/sunstone/`: |
| |
| ```bash |
| cargo new <crate_name> --lib |
| ``` |
| |
| You should be able to observe the following files: |
| |
| #### 1. `Cargo.toml` |
| |
| ```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 |
| ``` |
| |
| #### 2. `src/lib.rs` |
| |
| Declare `no_std` compliance if this is a bare-metal/microcontroller target: |
| |
| ```rust |
| #![no_std] |
| |
| // Crate implementation... |
| ``` |
| |
| --- |
| |
| ### Step 2: Add to Sunstone Cargo Workspace |
| |
| 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`: |
| |
| ```toml |
| [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" |
| ``` |
| |
| --- |
| |
| ### Step 3: Register in the Central Third-Party Workspace |
| |
| To allow other crates (and Bazel) to depend on your new crate, you must register |
| it in `third_party/rust_crates/Cargo.toml`. |
| |
| #### 1. Add as a dependency |
| |
| Add your crate to the main `[dependencies]` section in |
| `third_party/rust_crates/Cargo.toml` in alphabetical order: |
| |
| ```toml |
| [dependencies] |
| # keep-sorted start |
| ... |
| sapphire-<crate-name> = "0.1.0" |
| # keep-sorted end |
| ``` |
| |
| #### 2. Patch the path |
| |
| Add the path redirection under the `[patch.crates-io]` section (in the |
| `### In-tree Crates` block): |
| |
| ```toml |
| [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)._ |
| |
| #### 3. Add GN Package Configuration |
| |
| Add a GN package configuration section (usually placed alphabetically among |
| other `[gn.package.*]` targets): |
| |
| ```toml |
| [gn.package.sapphire-<crate-name>."0.1.0"] |
| uses_fuchsia_license = true |
| ``` |
| |
| --- |
| |
| ### Step 4: Regenerate Build Files |
| |
| Run the Fuchsia utility to regenerate the Cargo lockfile, GN build targets, and |
| Bazel workspace configurations: |
| |
| ```bash |
| fx update-rustc-third-party |
| ``` |
| |
| This command will automatically generate: |
| |
| - `third_party/rust_crates/Cargo.lock` |
| - `third_party/rust_crates/BUILD.gn` |
| - `third_party/rust_crates/vendor/...` (Bazel build rules) |
| - `src/connectivity/bluetooth/sunstone/sapphire-<crate-name>/BUILD.bazel` |
| |
| --- |
| |
| ### Step 5: Configure GN Test Targets |
| |
| To compile and run host tests, you must declare a test target in |
| `src/connectivity/bluetooth/sunstone/BUILD.gn`. |
| |
| #### 1. Add `rustc_test` target |
| |
| Add your test definition (usually inside `if (is_host)` block): |
| |
| ```gn |
| 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 `sources` list. As your crate grows and you add new |
| > files/modules (e.g., `src/att.rs`), you must manually append them to the |
| > `sources` array in `BUILD.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. |
| |
| #### 2. Add to tests group |
| |
| Add the test target to the `group("tests")` target: |
| |
| ```gn |
| 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 |
| ] |
| } |
| ``` |
| |
| --- |
| |
| ## Verification |
| |
| To verify that your crate is correctly integrated and compiles, run the |
| following commands: |
| |
| 1. **Verify `no_std` Compilation**: Run the Sunstone zero-allocation check from |
| `src/connectivity/bluetooth/sunstone/`: |
| ```bash |
| ./check_no_std.sh |
| ``` |
| |
| 2. **Build the Tests**: |
| ```bash |
| fx build host_x64/sapphire_<crate_name_underscores>_test |
| ``` |
| |
| 3. **Run the Tests**: |
| ```bash |
| fx test sapphire_<crate_name_underscores>_test |
| ``` |
| |
| --- |
| |
| ## FAQ (Frequently Asked Questions) |
| |
| ### Q: When do I need to run `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). |
| - Any crate-level metadata that changes how dependencies are resolved. |
| |
| You **do not** need to run it when: |
| |
| - You are editing source code (`.rs` files) in your local crates. |
| - You are adding or editing unit tests. |
| - You are updating `BUILD.gn` files (unless you introduced a new third-party |
| dependency). |
| |
| --- |
| |
| ### Q: When do I need to run `fx build`? |
| |
| **A:** Run `fx build` (or target-specific builds like |
| `fx build host_x64/sapphire_<name>_test`) when: |
| |
| - You want to verify that your code changes compile successfully under the |
| Fuchsia build toolchain. |
| - You modified `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. |
| |
| --- |
| |
| ### Q: Do I need to run `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. |
| |
| --- |
| |
| ### Q: Do I have to run these commands every time I make a change? |
| |
| **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. |