Update the Rust documentation with state of the art perspective on Rust
Move to the top level docs, outside of the security subtree, for better visibility. R=adetaylor@chromium.org Bug: 1292073 Change-Id: I7eab0bb83b3a269febc80b2e2f4c6d047157c299 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4051881 Reviewed-by: Nico Weber <thakis@chromium.org> Reviewed-by: Adrian Taylor <adetaylor@chromium.org> Auto-Submit: danakj <danakj@chromium.org> Commit-Queue: Nico Weber <thakis@chromium.org> Cr-Commit-Position: refs/heads/main@{#1078295}
This commit is contained in:
@ -1,6 +0,0 @@
|
||||
# Rust toolchain
|
||||
|
||||
Chrome currently uses an experimental Rust toolchain built by the Android
|
||||
team, which supports only Linux and Android builds.
|
||||
|
||||
See [this document for information about how to use it](/docs/security/rust-toolchain.md).
|
112
docs/rust.md
Normal file
112
docs/rust.md
Normal file
@ -0,0 +1,112 @@
|
||||
# Rust in Chromium
|
||||
|
||||
[TOC]
|
||||
|
||||
# Why?
|
||||
|
||||
Parsing untrustworthy data is a major source of security bugs, and it's
|
||||
therefore against Chromium rules [to do it in the browser process](rule-of-2.md)
|
||||
unless you can use a memory-safe language.
|
||||
|
||||
Rust provides a cross-platform memory-safe language so that all platforms can
|
||||
handle untrustworthy data directly from a privileged process, without the
|
||||
performance overheads and complexity of a utility process.
|
||||
|
||||
# Guidelines
|
||||
|
||||
Rust in Chromium is not production-ready. It is guarded behind a GN flag which
|
||||
is off by default.
|
||||
|
||||
Rust is only used in //third_party/rust, for crates developed outside of the
|
||||
Chromium tree.
|
||||
|
||||
# Status
|
||||
|
||||
The Rust toolchain is still experimental and breaks frequently, but it is
|
||||
behind an off-by-default GN argument, so this does not affect our bots or
|
||||
developers.
|
||||
|
||||
We have a working Rust toolchain for Linux x64 and Android targets. We are
|
||||
working on support for other platforms.
|
||||
|
||||
For questions or help, reach out to `rust-dev@chromium.org` or `#rust` on the
|
||||
[Chromium Slack](https://www.chromium.org/developers/slack/).
|
||||
|
||||
# Building with Rust support
|
||||
|
||||
1. Add `enable_rust = true` in your `gn` arguments, via `gn args <outdir>`.
|
||||
1. Add `"use_rust": True` to your `.gclient` file in the `"custom vars"`
|
||||
section.
|
||||
|
||||
If you use VSCode, we have [additional advice below](#using-vscode).
|
||||
|
||||
# Using a third-party Rust library
|
||||
|
||||
## Importing a crate from crates.io
|
||||
|
||||
See [//tools/crates/README.md](../tools/crates/README.md) for instructions on
|
||||
how to import a third-party library and generate GN build rules for it.
|
||||
|
||||
## Third-party review
|
||||
|
||||
**Since the Rust toolchain is still experimental, there is no Rust in production
|
||||
and we're not ready to consider approving Rust libraries that aren't part of the
|
||||
experiment and stabilization of the Rust toolchain.**
|
||||
|
||||
All third-party crates need to go through third-party review. See
|
||||
[//docs/adding_to_third_party.md](adding_to_third_party.md) for instructions on
|
||||
how to have a library reviewed.
|
||||
|
||||
## Writing a wrapper for binding generation
|
||||
|
||||
Most Rust libraries will need a more C++-friendly API written on top of them in
|
||||
order to generate C++ bindings to them. Such wrapper libraries should be written
|
||||
in `//third_party/rust/<cratename>/<epoch>/wrapper`.
|
||||
|
||||
See
|
||||
[`//third_party/rust/serde_json_lenient/v0_1/wrapper/`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/serde_json_lenient/v0_1/wrapper/)
|
||||
for an example.
|
||||
|
||||
Rust libraries should use our
|
||||
[`rust_static_library`](https://source.chromium.org/chromium/chromium/src/+/main:build/rust/rust_static_library.gni)
|
||||
GN template, in place of built-in GN `rust_library`, in order to integrate
|
||||
properly into the build and get the correct compiler options applied to them.
|
||||
|
||||
We provide the [CXX](https://cxx.rs) tool for generating C++ bindings for the
|
||||
Rust library (or wrapper library). Add any Rust files with a CXX bridge macro to
|
||||
the `cxx_bindings` variable in the `rust_static_library` GN rule to have CXX
|
||||
generate C++ a header for that file.
|
||||
|
||||
# Building on non-Linux platforms
|
||||
|
||||
We only have a working Rust toolchain for Linux and Android at this time. To use
|
||||
Rust on other platforms, you will need to provide your own nightly Rust
|
||||
toolchain. You can then tell `gn` about it using these `gn` arguments:
|
||||
|
||||
```
|
||||
enable_rust=true
|
||||
rust_sysroot_absolute="/Users/you/.rustup/toolchains/<toolchain name>"
|
||||
rustc_version="<your rustc version>" # add output of rustc -V
|
||||
# added_rust_stdlib_libs=[]
|
||||
# removed_rust_stdlib_libs=[]
|
||||
```
|
||||
|
||||
The last two arguments are any Rust standard library .rlibs which have been
|
||||
added or removed between the version that's distributed for Linux/Android,
|
||||
and the version you're using. They should rarely be necessary; if you get errors
|
||||
about missing standard libraries then adjust `removed_rust_stdlib_libs`; if
|
||||
you get errors about undefined symbols then have a look in your equivalent
|
||||
of the `.rustup/toolchains/<toolchain name>/lib/rustlib/<target>/lib`
|
||||
directory and add any new libraries which are not listed in
|
||||
`//build/rust/std/BUILD.gn` to the `added_rust_stlib_libs` list.
|
||||
|
||||
# Using VSCode
|
||||
|
||||
1. Ensure you're using the `rust-analyzer` extension for VSCode, rather than
|
||||
earlier forms of Rust support.
|
||||
2. Run `gn` with this extra flag: `gn gen out/Release --export-rust-project`.
|
||||
3. `ln -s out/Release/rust-project.json rust-project.json`
|
||||
4. When you run VSCode, or any other IDE that uses
|
||||
[rust-analyzer](https://rust-analyzer.github.io/) it should detect the
|
||||
`rust-project.json` and use this to give you rich browsing, autocompletion,
|
||||
type annotations etc. for all the Rust within the Chromium codebase.
|
@ -1,310 +1,2 @@
|
||||
# Experimenting with Rust in Chromium
|
||||
|
||||
[TOC]
|
||||
|
||||
# Why?
|
||||
|
||||
Parsing untrustworthy data is a major source of security bugs, and it's
|
||||
therefore against Chromium rules [to do it in the browser process](rule-of-2.md)
|
||||
unless you can use a memory safe language.
|
||||
|
||||
For teams building browser process features which need to handle untrustworthy
|
||||
data, they usually have to do the parsing in a utility process which incurs a
|
||||
performance overhead and adds engineering complexity.
|
||||
|
||||
The Chrome security team is working to make a cross-platform memory safe
|
||||
language available to Chromium developers. This document describes how to use
|
||||
that language in Chromium. The language, at least for now, is Rust.
|
||||
|
||||
# Guidelines
|
||||
|
||||
Support for Rust in Chromium is experimental. We appreciate your help in these
|
||||
experiments, but please remember that Rust is not supported for production use
|
||||
cases.
|
||||
|
||||
So:
|
||||
|
||||
* any experiments must be reversible (you may have to write a C++ equivalent
|
||||
in order to ship)
|
||||
* Rust code must not affect production Chrome binaries nor be shipped to Chrome
|
||||
users (we provide `#if defined(...)` and other facilities to make this easy) -
|
||||
so if you put Rust code in Chrome, the sole purpose is to help experiment and
|
||||
provide data for evaluation of future memory safe language options
|
||||
* Rust is not yet available on all Chromium platforms (just Linux and Android
|
||||
for now)
|
||||
* Facilities and tooling in Rust are not as rich as other languages yet.
|
||||
|
||||
That said, if presence of Rust would make your feature easier, we are keen
|
||||
for you to join in our experiments. Here's how. Please also let us know
|
||||
your interest via `rust-dev@chromium.org`.
|
||||
|
||||
# Building with Rust support
|
||||
|
||||
Add `enable_rust = true` in your `gn` arguments. At the moment, this works
|
||||
only for Linux platforms (but [see below](#Building-on-non-Linux-platforms)
|
||||
for how to enable on other platforms).
|
||||
|
||||
Also add `"use_rust": True` to your `.gclient` file to enable fetching required
|
||||
tools and libraries:
|
||||
|
||||
```
|
||||
solutions = [
|
||||
{
|
||||
...
|
||||
"custom_vars": {
|
||||
...
|
||||
"use_rust": True,
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
See also [Using VSCode](#using-vscode).
|
||||
|
||||
# GN support
|
||||
|
||||
Assume you want to add some Rust code to an existing C++ `source_set`.
|
||||
Simply:
|
||||
|
||||
* `import("//build/rust/mixed_source_set.gni")`
|
||||
* Replace `source_set` with `mixed_source_set`
|
||||
* Add `rs_sources = [ "src/lib.rs" ]` (and likely `rs_cxx_bindings`, see below)
|
||||
* Add your Rust code in `src/lib.rs`
|
||||
* In your C++ code, make Rust calls based on the `#if defined(RUST_ENABLED)`.
|
||||
|
||||
In toolchains with Rust disabled, your `source_set` will continue to be a plain
|
||||
C++ source set and absolutely nothing will change.
|
||||
|
||||
In toolchains with Rust, `RUST_ENABLED` will be defined and then you can
|
||||
call into Rust code (again, see the section on C++/Rust interop bindings below).
|
||||
|
||||
## A note on source code naming
|
||||
|
||||
Within a mixed code source set, it's (currently) normal to have C/C++ code
|
||||
in its main directory, whilst Rust code goes into a subdirectory called `src`
|
||||
(and the main file is always called `lib.rs`.) This follows the practice of
|
||||
other teams, but if you don't like it, that's fine: feel free to store your
|
||||
`.rs` code alongside your `.cc` code, but specify also `rs_crate_root` in your
|
||||
`mixed_source_set`.
|
||||
|
||||
## I'm not using a `source_set`
|
||||
|
||||
There are equivalent templates for `mixed_component` and
|
||||
`mixed_executable`. But if you need to do something more sophisticated,
|
||||
you can create a new pure-Rust language target - see
|
||||
`//build/rust/rust_source_set.gni`. C++ targets can simply depend on
|
||||
this Rust target but with the suffix `_cpp_bindings` appended to the target
|
||||
name:
|
||||
|
||||
```
|
||||
deps = [ "//path/to/my_rust_target:my_rust_target_cpp_bindings" ]
|
||||
```
|
||||
|
||||
If your Rust code calls back into C++, this is more complex in order to
|
||||
avoid layering violations - look into `mutually_dependent_target` in
|
||||
that `.gni` file.
|
||||
|
||||
# Unit tests
|
||||
|
||||
Rust supports unit tests within the primary source code files (e.g. see
|
||||
[an example here](https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html)).
|
||||
This section describes how to build and run such unit tests.
|
||||
|
||||
## Automatically generated targets
|
||||
|
||||
GN templates that work with Rust sources will automatically generate a bonus
|
||||
`gn` target called
|
||||
`<your target name>_unittests` (for pure-Rust targets like `cargo_crate`,
|
||||
`executable`, or `rust_source_set`) or `<your target name>_rs_unittests` (for
|
||||
mixed C++/Rust targets like (`mixed_component`, `mixed_executable`, or
|
||||
`mixed_source_set`). This bonus target builds:
|
||||
- An `out/Default/<bonus target name>` executable containing Rust unit tests
|
||||
from your code
|
||||
- An `out/Default/bin/run_<bonus target name>` script that enables running
|
||||
the tests on Chromium bots.
|
||||
|
||||
## Explicitly defined groups of tests
|
||||
|
||||
To group multiple Rust unit test executables into a single test step,
|
||||
please use the `rust_unit_tests_group("my_test_group")` template:
|
||||
|
||||
```
|
||||
rust_unit_tests_group("my_group_of_rust_unit_tests") {
|
||||
deps = [
|
||||
"my_rust_source_set1",
|
||||
"my_rust_source_set2",
|
||||
"my_rust_executable",
|
||||
# ...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The example above will build all the `deps`. This will also generate a wrapper
|
||||
script that wraps all the Rust unit test executables from `deps` and their
|
||||
transitive dependencies. In the example above, the script will be generated at
|
||||
`out/Default/bin/run_my_group_of_rust_unit_tests`.
|
||||
|
||||
The generated script can be used for integration with Chromium bots, but can
|
||||
also be used as a convenience to manually/locally run all tests from the group.
|
||||
Run the script with the `--help` argument to see more details (e.g. how to
|
||||
filter which tests to run).
|
||||
|
||||
## Configuring running Rust unit tests on bots
|
||||
|
||||
To manually configure running Rust unit tests on bots, please follow the pattern
|
||||
from https://crrev.com/c/3322199:
|
||||
- Define a new isolate in `//testing/buildbot/gn_isolate_map.pyl`:
|
||||
- Set `label` to the fully qualified name of either
|
||||
the implicit bonus target (e.g. `..._rs_unittests`)
|
||||
or the explicit `rust_unit_tests_group` target.
|
||||
- Set `type` to `generated_script`
|
||||
- There are no requirements on the name of the new isolate,
|
||||
but typically it will have the same name as the target mentioned
|
||||
in the `label`.
|
||||
- Define a new test step, or extend an existing test step in
|
||||
`//testing/buildbot/test_suites.pyl` (adding an entry referring to
|
||||
the new isolate above). Note that the tests grouped under the test
|
||||
step need to have uniform kind (e.g. cannot mix GTest and Rust tests).
|
||||
- Ensure that `//testing/buildbot/waterfalls.pyl` asks to run the test step
|
||||
on specific bots. The test step needs to be listed under the
|
||||
`isolated_scripts` key (rather than under `gtest_tests` key).
|
||||
- Run `//testing/buildbot/generate_buildbot_json.py`.
|
||||
|
||||
Future work:
|
||||
- At present, there is no automatic integration of such unit tests into our
|
||||
existing test infrastructure, but this is something we're working on.
|
||||
- At present, the bot integration only supports reporting whether the tests
|
||||
passed or failed, and doesn't capture results or output of individual tests.
|
||||
- At present, there is no support for running native Rust unit tests on
|
||||
Android.
|
||||
|
||||
# Third party dependencies
|
||||
|
||||
Adding Rust third party dependencies follows the same protocols
|
||||
[as for C++ or other languages](../adding_to_third_party.md). But practically,
|
||||
Rust libraries are almost always distributed as cargo "crates" which have
|
||||
build scripts and metadata in `Cargo.toml` files.
|
||||
|
||||
The crate you need may already be listed in
|
||||
`//third_party/rust/third_party.toml` - if so, just depend upon it like this:
|
||||
|
||||
```
|
||||
deps = [ "//third_party/rust/cxx/v1:lib" ]
|
||||
```
|
||||
|
||||
(Only those crates explicitly listed in `//third_party/rust/third_party.toml`
|
||||
are visible to first-party code; other crates in `//third_party/rust` are
|
||||
transitive dependencies).
|
||||
|
||||
If you need to add new Rust third-party dependencies, there are scripts and gn
|
||||
templates to make it nearly automatic (except of course for review). Please
|
||||
reach out to `rust-dev@chromium.org` for advice.
|
||||
|
||||
# C++/Rust interop
|
||||
|
||||
There are multiple different solutions for Rust/C++ interop. In this phase of our
|
||||
experiments, we're supporting just one:
|
||||
[cxx, described in this excellent online book](https://cxx.rs).
|
||||
|
||||
To use this interop facility in Chromium:
|
||||
|
||||
* define your `#[cxx::bridge]` module in your `.rs` file
|
||||
* in your `mixed_source_set`, add `rs_cxx_bindings = [ "src/lib.rs" ]`
|
||||
* from your C++,
|
||||
|
||||
```
|
||||
#ifdef RUST_ENABLED
|
||||
#include "path/to/your/target/src/lib.rs.h`
|
||||
#endif
|
||||
```
|
||||
|
||||
You can now simply call functions and use types declared/defined in your CXX
|
||||
bridge. A typical usage might be to pass a `const std::string&` or
|
||||
`rust::Slice<const uint8_t>` from C++ into Rust and then return a struct with
|
||||
the parsed results.
|
||||
|
||||
If you need to call back into C++ from Rust, this is also supported -
|
||||
`include!` directives within an `extern "C++"` section should work:
|
||||
|
||||
```
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("path/to/my_cpp_header.h");
|
||||
fn some_function_defined_in_cpp();
|
||||
}
|
||||
}
|
||||
|
||||
// Rust code calls ffi::some_function_defined_in_cpp()
|
||||
```
|
||||
|
||||
Future work may expose existing C++ Chromium APIs to Rust with no need
|
||||
to declare the interface in a `#[cxx::bridge]` module.
|
||||
|
||||
## Dependencies between Rust targets
|
||||
|
||||
If your `rust_source_set` exposes Rust APIs for other Rust targets in Chromium,
|
||||
those targets should be able to depend directly on your `rust_source_set`
|
||||
target.
|
||||
|
||||
If you have a `mixed_source_set` or any other component which is intended for
|
||||
both Rust _and_ C++ consumers, please reach out to `rust-dev@chromium.org`
|
||||
with your use-case. (This _should_ be possible with the current gn rules but
|
||||
layering here is fragile so we'd rather discuss it.)
|
||||
|
||||
# Example
|
||||
|
||||
To see an example of all this, look at `//build/rust/tests/test_variable_source_set`.
|
||||
|
||||
# Tooling
|
||||
|
||||
## Known cases which don't work
|
||||
|
||||
* At the moment LTO doesn't work, so you can't use `is_official_build = true`.
|
||||
([Bug.](https://crbug.com/1229423))
|
||||
* Windows doesn't work just yet. ([Bug.](https://crbug.com/1268157))
|
||||
|
||||
## Building on non-Linux platforms
|
||||
|
||||
The Rust toolchain is provided only for Linux and Android. To use it on
|
||||
other platforms, you will need to provide your own nightly Rust toolchain.
|
||||
You can then tell `gn` about it using these `gn` arguments:
|
||||
|
||||
```
|
||||
enable_rust=true
|
||||
rust_sysroot_absolute="/Users/you/.rustup/toolchains/<toolchain name>"
|
||||
rustc_version="<your rustc version>" # add output of rustc -V
|
||||
# added_rust_stdlib_libs=[]
|
||||
# removed_rust_stdlib_libs=[]
|
||||
```
|
||||
|
||||
The last two arguments are any Rust standard library .rlibs which have been
|
||||
added or removed between the version that's distributed for Linux/Android,
|
||||
and the version you're using. They should rarely be necessary; if you get errors
|
||||
about missing standard libraries then adjust `removed_rust_stdlib_libs`; if
|
||||
you get errors about undefined symbols then have a look in your equivalent
|
||||
of the `.rustup/toolchains/<toolchain name>/lib/rustlib/<target>/lib`
|
||||
directory and add any new libraries which are not listed in
|
||||
`//build/rust/std/BUILD.gn` to the `added_rust_stlib_libs` list.
|
||||
|
||||
## Using VSCode
|
||||
|
||||
1. Ensure you're using the `rust-analyzer` extension for VSCode, rather than
|
||||
earlier forms of Rust support.
|
||||
2. Run `gn` with this extra flag: `gn gen out/Release --export-rust-project`.
|
||||
3. `ln -s out/Release/rust-project.json rust-project.json`
|
||||
4. When you run VSCode, or any other IDE that uses
|
||||
[rust-analyzer](https://rust-analyzer.github.io/) it should detect the
|
||||
`rust-project.json` and use this to give you rich browsing, autocompletion,
|
||||
type annotations etc. for all the Rust within the Chromium codebase.
|
||||
|
||||
## Source code format
|
||||
|
||||
- `git cl format` and `git cl presubmit` have been extended to automatically
|
||||
cover `.rs` files on Linux.
|
||||
(Currently whole files are re-formatted and/or checked; support for
|
||||
only looking at the modified lines is not yet implemented.)
|
||||
- Rust defaults to 100-columns-wide formatting (similarly to Java).
|
||||
This may necessitate tweaking settings in various tools - e.g. in Gerrit you
|
||||
might want to increase the default "Diff width" under
|
||||
https://chromium-review.googlesource.com/settings/
|
||||
Please see [//docs/rust.md](../docs/rust.md) for documentation on the Rust
|
||||
toolchain in Chromium.
|
||||
|
Reference in New Issue
Block a user