Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 1 | # //tools/rust |
| 2 | |
| 3 | This directory contains scripts for building, packaging, and distributing the |
| 4 | Rust toolchain (the Rust compiler, and also C++/Rust FFI tools like |
| 5 | [Crubit](https://github.com/google/crubit)). |
| 6 | |
Lukasz Anforowicz | 2576d8dff | 2022-07-22 16:23:18 | [diff] [blame] | 7 | [TOC] |
| 8 | |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 9 | |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 10 | ## Background |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 11 | |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 12 | Like with Clang, Chromium uses bleeding edge Rust tooling. We track the upstream |
| 13 | projects' latest development as closely as possible. However, Chromium cannot |
| 14 | use official Rust builds for various reasons which require us to match the Rust |
danakj | f3d99d15 | 2022-11-28 21:24:51 | [diff] [blame] | 15 | LLVM backend version with the Clang we use. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 16 | |
| 17 | It would not be reasonable to build the tooling for every Chromium build, so we |
| 18 | build it centrally (with the scripts here) and distribute it for all to use |
| 19 | (also fetched with the scripts here). |
| 20 | |
Arthur Eubanks | be10ddc6 | 2024-05-13 21:09:26 | [diff] [blame] | 21 | Similar to the Clang package which exists as a tarball that is unpacked into |
| 22 | `third_party/llvm-build`, the Rust package exists as a tarball that is unpacked |
| 23 | into `third_party/rust-toolchain`. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 24 | |
| 25 | ## Rust build overview |
| 26 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 27 | Each Rust package is built from an Rust git, usually from HEAD directly, along |
| 28 | with the current Clang/LLVM revision in use in Chromium. Hence a new Rust |
| 29 | package must be built whenever either Rust or Clang is updated. When building |
| 30 | Rust we also build additional tools such as clippy and rustfmt, and interop |
| 31 | tools including bindgen and crubit. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 32 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 33 | The Rust build also includes building LLVM for rustc to use, and Clang for |
| 34 | bindgen and crubit to use. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 35 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 36 | The `*_upload_clang` and `*_upload_rust` trybots are used to build Clang and |
| 37 | Rust respectively from the revisions specified in the Chromium source tree. |
| 38 | These are uploaded to a storage bucket when the build succeeds. After being |
| 39 | copied from staging to production by a developer (see |
| 40 | [cs/copy_staging_to_prod_and_goma.sh]( |
| 41 | http://cs/copy_staging_to_prod_and_goma.sh)), they can then be fetched by |
| 42 | `gclient sync`. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 43 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 44 | The `update_rust.py` script is used by `gclient sync` to fetch the Rust |
| 45 | toolchain for the revisions specified in the script. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 46 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 47 | ## Rolling Rust |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 48 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 49 | Follow the directions in [//docs/updating_clang.md]( |
| 50 | ../../docs/updating_clang.md) to roll Clang and Rust together. To just |
| 51 | roll Rust on its own, use the `--skip-clang` argument when running |
| 52 | `upload_revision.py`. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 53 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 54 | The upload_revision.py script will update the revision of Rust to be |
| 55 | built and used in `update_rust.py` and will start the trybots that |
| 56 | will build the Rust toolchain. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 57 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 58 | After the build has succeeded and the new toolchain has been copied to |
| 59 | production, the CQ will run trybots to verify that our code still builds |
| 60 | and tests pass with the new Rust toolchain. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 61 | |
Arthur Eubanks | be10ddc6 | 2024-05-13 21:09:26 | [diff] [blame] | 62 | ### An overview of what is updated in a Rust roll |
| 63 | |
| 64 | During Rust packaging, the upstream Rust sources are checked out into |
| 65 | `third_party/rust-src`. |
| 66 | |
| 67 | During a Rust roll, a couple of things get updated. The most obvious one is |
| 68 | various toolchain binaries like `rustc` that live in |
| 69 | `third_party/rust-toolchain/bin`. These are the direct outputs of a Rust |
| 70 | toolchain build. |
| 71 | |
| 72 | We also update the Rust standard library. We actually provide two copies of the |
| 73 | standard library in Chromium: a prebuilt version only for use in host tools |
| 74 | (e.g. build scripts, proc macros), and a version built from source as part of |
| 75 | the normal Chromium build for use in target artifacts. These are the same |
| 76 | version of the standard library that the Rust toolchain revision provides. |
| 77 | |
| 78 | The reason we have a prebuilt version of the standard library for use in host |
| 79 | tools is that they are often loaded into `rustc` as a module, so to be safe we |
| 80 | use the same prebuilts that the toolchain linked against. These are copied from |
| 81 | the Rust toolchain build to |
| 82 | `third_party/rust-toolchain/lib/rustlib/$PLATFORM/lib/*.rlib`. We use these |
| 83 | when the gn arg `rust_prebuilt_stdlib` is true, which is manually set to true |
| 84 | for gn host toolchains. |
| 85 | |
| 86 | The sources of the standard library we build from source for target artifacts |
| 87 | live in `third_party/rust-toolchain/lib/rustlib/src/rust`. These are copied |
| 88 | from `third_party/rust-src`. Since Chromium uses gn as its build system, we |
| 89 | need some way to translate build files from Rust's build system, cargo, to gn |
| 90 | rules. This is the responsibility of `gnrt`, which is a Chromium-specific tool |
| 91 | that lives in [`tools/crates/gnrt`](https://crsrc.org/c/tools/crates/gnrt/), |
Lukasz Anforowicz | 7c78315a | 2025-03-12 15:35:39 | [diff] [blame] | 92 | written in Rust. `gnrt gen` takes a cargo workspace, runs `cargo metadata` |
| 93 | (or, more accurately `cargo guppy`) on |
Arthur Eubanks | be10ddc6 | 2024-05-13 21:09:26 | [diff] [blame] | 94 | it to get information about sources and dependencies, and outputs gn rules |
| 95 | corresponding to the cargo build rules. Rust has a |
| 96 | [`sysroot`](https://github.com/rust-lang/rust/tree/master/library/sysroot) |
| 97 | crate roughly corresponding to a top level cargo workspace we want for the |
| 98 | standard library. However, we want a couple of customizations without having to |
| 99 | patch the Rust sources, so we have another crate |
| 100 | [`fake_root`](https://crsrc.org/c/build/rust/std/fake_root/) above that depends |
| 101 | on `sysroot`. |
| 102 | [`tools/rust/gnrt_stdlib.py`](https://crsrc.org/c/tools/rust/gnrt_stdlib.py) |
| 103 | fetches and invokes the pinned `cargo` (see [`rustc` bootstrapping |
| 104 | explanation](https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html), |
| 105 | "pinned" is the "stage0" toolchain) to build and run `gnrt` with `fake_root` as |
| 106 | the base workspace, generating an updated |
| 107 | [`build/rust/std/rules/BUILD.gn`](https://crsrc.org/c/build/rust/std/rules/BUILD.gn) |
| 108 | that has gn rules for the new standard library sources. For convenience when |
| 109 | rolling Rust, this is one big `BUILD.gn` file as opposed to multiple files per |
| 110 | crate. Note that because we do not ship cargo build files in |
| 111 | `third_party/rust-toolchain`, we must run `gnrt` against `third_party/rust-src` |
| 112 | instead of `third_party/rust-toolchain`. But end users do not have |
| 113 | `third_party/rust-src` checked out, so we must rewrite the |
| 114 | `third_party/rust-src` paths to the copies of the sources in |
| 115 | `third_party/rust-toolchain/lib/rustlib/src/rust`, which is checked out by end |
| 116 | users as part of the Rust toolchain. |
| 117 | |
| 118 | As an aside, `gnrt` is also used to generate gn build rules for |
| 119 | non-standard-library Rust packages in `third_party/rust` used in Chromium's |
| 120 | build. It uses |
| 121 | [`third_party/rust/chromium_crates_io`](https://crsrc.org/c/third_party/rust/chromium_crates_io) |
| 122 | as the base workspace and vendors sources into |
| 123 | `third_party/rust/chromium_crates_io/vendor`. The `--for-std` argument to `gnrt |
| 124 | gen` does different things for creating gn rules for the standard library |
| 125 | versus for various non-standard-library packages, such as producing a single |
| 126 | BUILD.gn file. |
| 127 | |
danakj | eed0214 | 2023-07-19 14:59:28 | [diff] [blame] | 128 | ### Possible failure: Missing sources or inputs |
| 129 | |
| 130 | A build error when building the stdlib in Chromium may look like: |
| 131 | ``` |
| 132 | FAILED: local_rustc_sysroot/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd.rlib |
| 133 | ...build command... |
| 134 | ERROR: file not in GN sources: ../../third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/../../portable-simd/crates/std_float/src/lib.rs |
| 135 | ``` |
| 136 | |
| 137 | Or: |
| 138 | ``` |
| 139 | FAILED: local_rustc_sysroot/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd.rlib |
| 140 | ...build command... |
| 141 | ERROR: file not in GN inputs: ../../third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/../../stdarch/crates/core_arch/src/core_arch_docs.md |
| 142 | ``` |
| 143 | |
| 144 | When building the stdlib in Chromium, the GN rules must have every rust source |
| 145 | or other input file that makes up the crate listed in the `sources` and |
| 146 | `inputs` GN variables. gnrt will walk the directory tree from the root of the |
| 147 | crate and put every relevant file into the set. But sometimes a crate includes |
| 148 | modules from paths outside the crate root's directory tree, with a path |
| 149 | directive such as |
| 150 | ```rs |
| 151 | #[path = "../../stuff.rs"] |
| 152 | mod stuff; |
| 153 | ``` |
| 154 | or will `include!()` a file from another path, which is common for `.md` files: |
| 155 | ```rs |
| 156 | include!("../../other_place.md") |
| 157 | ``` |
| 158 | |
| 159 | The first error is saying the source file `std_float/src/lib.rs` did not |
| 160 | appear in the `sources` variable. The `../../` part of the path shows that |
| 161 | this is outside the crate root's directory tree. The second error is saying |
| 162 | that `core_arch/src/core_arch_docs.md` did not appear in the `inputs` variable. |
| 163 | |
| 164 | To fix the error: |
| 165 | * Determine the path that is missing, relative to the crate root. In the above |
| 166 | example this is `../../portable-simd/crates/std_float/src`. We could also use |
| 167 | `../../portable-simd` or anything in between, though that would add a lot |
| 168 | more sources to the GN rules than is necessary in this case. It's best to |
| 169 | point to the directory of the module root (where the `lib.rs` or `mod.rs` |
| 170 | is located). |
Alan Zhao | de04e7f | 2024-01-31 22:54:42 | [diff] [blame] | 171 | * Download the roll CL (on Gerrit, click on the 3 dots in the upper right |
| 172 | corner and click on "Download patch"). |
danakj | eed0214 | 2023-07-19 14:59:28 | [diff] [blame] | 173 | * Find the failing build target crate's rules in |
| 174 | `//build/rust/std/gnrt_config.toml`. The failing crate in the above example |
| 175 | is `libstd.rlib`, so we want the `[crate.std]` section of the config file. |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 176 | * Determine if the target being built is a library or a build script. Build |
| 177 | script targets end with the suffix `_build_script`. For example: |
| 178 | ``` |
| 179 | [13627/84339] RUST(BIN) clang_x64_for_rust_host_build_tools/compiler_builtins_compiler_builtins_vunknown_build_script |
Lukasz Anforowicz | e4eded4 | 2025-08-04 15:58:24 | [diff] [blame] | 180 | python3 ../../build/rust/gni_impl/rustc_wrapper.py --rustc=../../third_party/rust-toolchain/bin/rustc --depfi...(too long) |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 181 | ERROR: file not in GN sources: ../../third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.123/configure.rs |
| 182 | ``` |
danakj | eed0214 | 2023-07-19 14:59:28 | [diff] [blame] | 183 | * Determine if the missing file should go in `sources` or `inputs`. |
| 184 | * For `sources`, add the path to a `extra_src_roots` list in the crate's |
| 185 | rules. For the above example, we could add |
| 186 | `extra_src_roots = ['../../portable-simd/crates/std_float/src']`. |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 187 | * Or if it was a build script target, then |
| 188 | `extra_build_script_src_roots = ['../../portable-simd/crates/std_float/src']`. |
danakj | eed0214 | 2023-07-19 14:59:28 | [diff] [blame] | 189 | * For `inputs`, add the path to a `extra_input_roots` list in the crate's |
| 190 | rules. For the above example, we could add |
| 191 | `extra_input_roots = ['../../stdarch/crates/core_arch/src']`. |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 192 | * Or if it was a build script target, then |
| 193 | `extra_build_script_input_roots = ['../../stdarch/crates/core_arch/src']`. |
Alan Zhao | de04e7f | 2024-01-31 22:54:42 | [diff] [blame] | 194 | * With the roll CL checked out, run `gclient sync`. |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 195 | |
Alan Zhao | de04e7f | 2024-01-31 22:54:42 | [diff] [blame] | 196 | *** note |
| 197 | NOTE: `gclient sync` will download the version of the rust toolchain from the |
| 198 | roll CL. In order for this to work, the upload_rust bots should've completed and |
| 199 | `copy_staging_to_prod_and_goma.sh should've been run. |
| 200 | *** |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 201 | |
danakj | eed0214 | 2023-07-19 14:59:28 | [diff] [blame] | 202 | * Run `tools/rust/gnrt_stdlib.py` to use gnrt to rebuild the stdlib GN rules |
| 203 | using the updated config. |
| 204 | |
danakj | 324f54655 | 2024-09-18 16:31:11 | [diff] [blame] | 205 | *** note |
| 206 | NOTE: All gnrt_config options are found in |
| 207 | [//tools/crates/gnrt/lib/config.rs](https://source.chromium.org/chromium/chromium/src/+/main:tools/crates/gnrt/lib/config.rs). |
| 208 | The `CrateConfig` type has the various per-crate config options. |
| 209 | *** |
| 210 | |
danakj | bb4d0c77 | 2023-10-13 13:22:28 | [diff] [blame] | 211 | ### Generating `BUILD.gn` files for stdlib crates |
| 212 | |
| 213 | If the build structure changes in any way during a roll, the GN files need |
| 214 | to be regenerated. |
| 215 | |
| 216 | #### Simple way: |
| 217 | |
| 218 | Run `tools/rust/gnrt_stdlib.py`. |
| 219 | |
| 220 | #### Longer way |
| 221 | |
| 222 | This requires Rust to be installed and available in your system, typically |
| 223 | through [https://rustup.rs](https://rustup.rs). |
| 224 | |
| 225 | To generate `BUILD.gn` files for the crates with the `gnrt` tool: |
| 226 | 1. Change directory to the root `src/` dir of Chromium. |
| 227 | 1. Build `gnrt` to run on host machine: `cargo build --release --manifest-path |
| 228 | tools/crates/gnrt/Cargo.toml --target-dir out/gnrt`. |
| 229 | 1. Ensure you have a checkout of the Rust source tree in `third_party/rust-src` |
| 230 | which can be done with `tools/rust/build_rust.py --sync-for-gnrt`. |
| 231 | 1. Run `gnrt` with the `gen` action: |
| 232 | `out/gnrt/release/gnrt gen --for-std third_party/rust-src`. |
| 233 | |
| 234 | This will generate the `//build/rust/std/rules/BUILD.gn` file, with the changes |
| 235 | visible in `git status` and can be added with `git add`. |
| 236 | |
| 237 | ## Local development |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 238 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 239 | To build the Rust toolchain locally, run `//tools/rust/build_rust.py`. It |
| 240 | has additional flags to skip steps if you're making local changes and want |
| 241 | to retry a build. The script will produce its outputs in |
| 242 | `//third_party/rust-toolchain/`, which is the same place that `gclient sync` |
| 243 | places them. |
Collin Baker | 00a2153 | 2022-11-16 21:44:24 | [diff] [blame] | 244 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 245 | Building the `rust_build_tests` GN target is a good way to quickly verify the |
| 246 | toolchain is working. |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 247 | |
| 248 | ## Rolling Crubit tools |
| 249 | |
| 250 | Steps to roll the Crubit tools (e.g. `rs_bindings_from_cc` tool) |
| 251 | to a new version: |
| 252 | |
Lukasz Anforowicz | 2576d8dff | 2022-07-22 16:23:18 | [diff] [blame] | 253 | - Locally, update `CRUBIT_REVISION` in `update_rust.py`. |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 254 | (Update `CRUBIT_SUB_REVISION` when the build or packaging is changed, but |
| 255 | the upstream Rust revision we build from is not changed.) |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 256 | |
| 257 | - Locally, update `crubit_revision` in `//DEPS`, so that it matches |
| 258 | the revision from the previous bullet item. |
| 259 | |
| 260 | - Run manual tests locally (see the "Building and testing the tools locally" |
| 261 | section below). |
Alison Gale | 4d9c231 | 2024-04-26 19:15:24 | [diff] [blame] | 262 | TODO(crbug.com/40226863): These manual steps should |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 263 | be made obsolete once Rust-specific tryjobs cover Crubit |
| 264 | tests. |
| 265 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 266 | ## Building and testing Crubit locally |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 267 | |
| 268 | ### Prerequisites |
| 269 | |
Lukasz Anforowicz | 2576d8dff | 2022-07-22 16:23:18 | [diff] [blame] | 270 | #### Bazel |
| 271 | |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 272 | `build_crubit.py` depends on Bazel. |
Lukasz Anforowicz | 2576d8dff | 2022-07-22 16:23:18 | [diff] [blame] | 273 | |
| 274 | To get Bazel, ensure that you have `checkout_bazel` set in your `.gclient` file |
| 275 | and then rerun `gclient sync`: |
| 276 | |
| 277 | ```sh |
| 278 | $ cat ../.gclient |
| 279 | solutions = [ |
| 280 | { |
| 281 | "name": "src", |
| 282 | "url": "https://chromium.googlesource.com/chromium/src.git", |
| 283 | ... |
| 284 | "custom_vars": { |
| 285 | "checkout_bazel": True, |
Adrian Taylor | 8a824c2 | 2023-03-01 07:04:14 | [diff] [blame] | 286 | "checkout_crubit": True, |
Lukasz Anforowicz | 2576d8dff | 2022-07-22 16:23:18 | [diff] [blame] | 287 | }, |
| 288 | }, |
| 289 | ] |
| 290 | ``` |
| 291 | |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 292 | ### Building |
| 293 | |
danakj | 358a7f4a | 2023-04-13 22:06:01 | [diff] [blame] | 294 | Just run `tools/rust/build_crubit.py`. So far `build_crubit.py` has only been |
| 295 | tested on Linux hosts. |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 296 | |
| 297 | ### Deploying |
| 298 | |
Lukasz Anforowicz | 401dc9c | 2022-10-20 18:45:00 | [diff] [blame] | 299 | `build_crubit.py` will copy files into the directory specified in the |
| 300 | (optional) `--install-to` cmdline parameter - for example: |
| 301 | |
| 302 | ``` |
| 303 | $ tools/rust/build_crubit.py --install-to=third_party/rust-toolchain/bin/ |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 304 | ``` |
| 305 | |
| 306 | ### Testing |
| 307 | |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 308 | Crubit tests are under `//build/rust/tests/test_rs_bindings_from_cc`. Until |
| 309 | Crubit is built on the bots, the tests are commented out in |
| 310 | `//build/rust/tests/BUILD.gn`, but they should still be built and run before |
Alison Gale | 4d9c231 | 2024-04-26 19:15:24 | [diff] [blame] | 311 | rolling Crubit. TODO(crbug.com/40226863): Rephrase this paragraph |
Lukasz Anforowicz | c105ecee9 | 2022-07-19 23:03:03 | [diff] [blame] | 312 | after Crubit is built and tested on the bots. |