Rust on ChromeOS
This provides information on creating Rust projects for installation within ChromeOS and ChromeOS SDK. All commands and paths given are from within the SDK's chroot.
Usage
cros-rust.eclass is an eclass that supports first-party and third-party crate dependencies. Rust project's ebuild should inherit the eclass to support Rust building in ChromeOS build system.
All the Rust projects using cros-rust.eclass
will get dependent crates from
/build/${BOARD}/usr/lib/cros_rust_registry
instead of crates.io.
You'll learn how to publish first-party and third-party crates to
cros_rust_registry
in the following sections.
Third-party ebuild crates
To add a third-party crate, follow the
instructions in the rust_crates
repository. No
ebuild is needed for third-party crates.
First-party crates
You can create your Rust project in anywhere in the ChromeOS system and its
ebuild in a suitable place in chromiumos-overlay
with name
<category>/<crate_name>-9999.ebuild
. Here is an ebuild for
an example first-party crate:
# Copyright <copyright_year> The ChromiumOS Authors
# Distributed under the terms of the GNU General Public License v2
EAPI="7"
CROS_WORKON_INCREMENTAL_BUILD=1
CROS_WORKON_LOCALNAME="example"
CROS_WORKON_PROJECT="path/to/project/repository"
# We don't use CROS_WORKON_OUTOFTREE_BUILD here since project's Cargo.toml is
# using "provided by ebuild" macro which supported by cros-rust.
CROS_WORKON_SUBTREE="path/to/project/subtree"
inherit cros-workon cros-rust
DESCRIPTION="An example first party project"
HOMEPAGE="home_page_url"
LICENSE="BSD-Google"
SLOT="0/${PVR}"
KEYWORDS="~*"
IUSE="test"
DEPEND="
dev-rust/third-party-crates-src:=
"
# (crbug.com/1182669): build-time only deps need to be in RDEPEND so they are pulled in when
# installing binpkgs since the full source tree is required to use the crate.
RDEPEND="${DEPEND}"
# Only include this if you need to install binaries or multiple crates.
src_install() {
# 1. Publish this library for other first-party crates.
# This is needed if other crates depend on this crate.
cros-rust_src_install
# 2. Install the binary to image. This isn't needed for libraries.
dobin "$(cros-rust_get_build_dir)/example_bin"
}
Then follow the instructions in the
rust_crates
repository for adding a first-party crate.
WARNING: Please make sure your project could be built by both steps for engineering productivity:
From chroot with
emerge-${BOARD} CRATE-EBUILD-NAME
Tips: You can set
USE=-lto
to speed up build times when using emerge. This turns off link time optimization, which is useful for release builds but significantly increases build times and isn't really needed during development.From project root directory with
cargo build
We add two macros to resolve conflicts between these two build system. Check details from the following section.
Ebuild versioning
Ebuild versions should match the version in Cargo.toml
. To keep the versions
in sync, add a files/chromeos-version.sh
script like this:
#!/bin/sh
#
# Copyright <copyright_year> The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Assumes the first 'version =' line in the Cargo.toml is the version for the
# crate.
awk '/^version = / { print $3; exit }' "$1/<path/to/Cargo.toml>" | tr -d '"'
Replace <path/to/Cargo.toml>
with the path of Cargo.toml
relative
to the root of the repository it's in.
Cargo.toml macros
The cros-rust
eclass supports building crates in the ChromeOS build system,
but it breaks cargo build
in some situations. We add two macros which are
recognized by the eclass to keep both build systems working. The macros take
the form of special comments in Cargo.toml
.
-
# provided by ebuild
This macro introduces a replacement of:
[dependencies] data_model = { path = "../data_model" } # provided by ebuild
with:
[dependencies] data_model = { version = "*" }
Example usage: Add dependency to first-party crate
- Add the dependent crate to DEPEND section in ebuild.
- Use relative path for imported crates in
Cargo.toml
but with# provided by ebuild
macro:
[dependencies] data_model = { path = "../data_model" } # provided by ebuild
-
# ignored by ebuild
We will use this to discard parts of
[patch.crates-io]
which should be applied to local developer builds but not to ebuilds.This macro introduces a replacement of:
audio_streams = { path = "../../third_party/adhd/audio_streams" } # ignored by ebuild
with empty line while building with emerge.
Example usage: Add dependency to first-party crate for sub-crates from root crate.
- Add the dependent crate to DEPEND section in root crate's ebuild.
- Use relative path for imported crates in
[patch.crates-io]
section in root crate'sCargo.toml
but with# ignored by ebuild
macro:
[patch.crates-io] audio_streams = { path = "../../third_party/adhd/audio_streams" } # ignored by ebuild
Panic Signatures
The default crash handler is not able to generate useful signatures for crashes in Rust binaries. To get better crash signatures, you need to install a custom panic handler which integrates with ChromeOS's crash collector.
All you need to do is to call the following function at the top of main()
:
fn main() {
libchromeos::panic_handler::install_memfd_handler();
...
}
Ensure that your package & ebuild depend on libchromeos:
Cargo.toml
[dependencies]
libchromeos = { path = "../libchromeos-rs" } # provided by ebuild
...
Ebuild:
DEPEND="
${COMMON_DEPEND}
...
dev-rust/libchromeos:=
...
"
Workflows
There are a few different ways for building and testing first party Rust code.
cros_workon_make
Using cros_workon_make is the same for Rust as with typical platform2 packages
on ChromeOS. The downsides for using it with Rust on CrOS are it modifies the
Cargo.toml file and can be slower than running cargo directly. You will want
to locally commit any Cargo.toml changes before running cros_workon_make. If
you try to run cargo directly with the cros_workon_make
-modified Cargo.toml,
things will likely be broken, so be sure to checkout the unmodified copy before
calling cargo.
cargo
The primary downside of using cargo directly is it will fetch dependencies from crates.io though the internet. These often do not match with the versions used by ChromeOS, leading to undesired consequences.
One workaround is to have an up-to-date Cargo.lock file checked in for your project. Run the following outside the chroot in the crate root:
cros_sdk --working-dir=. -- \
cargo --config 'source.crates-io.directory="/usr/lib/cros_rust_registry/registry"' generate-lockfile
Also make sure to allow the Cargo.lock
in the .gitignore
of your crate.
Another is to use the same cargo config as cros_workon_make. There is a helper script that set this up for you:
BOARD=<board> ~/chromiumos/src/platform/dev/contrib/setup_cros_cargo_home
This can be applied outside the ChromeOS chroot by copying the
~/.cargo/config
from the chroot outside and updating the paths to be correct
for outside the chroot.
***note
Note the resulting ~/.cargo/config
depends on the cros_rust_registry for
the specified ${BOARD}. You may need to run build_packages
for the board or
emerge-${BOARD} dev-rust/<dependency>
to install or update dependencies.
Cross-compiling
This is taken care of in the ~/.cargo/config
if you preform the setup above.
The toolchain that is installed by default is targetable to the following triples:
Target Triple | Description |
---|---|
x86_64-pc-linux-gnu |
(default) Used exclusively for packages installed in the chroot |
armv7a-cros-linux-gnueabihf |
Used by 32-bit usermode ARM devices |
aarch64-cros-linux-gnu |
Used by 64-bit usermode ARM devices |
x86_64-cros-linux-gnu |
Used by x86_64 devices |
When building Rust projects for development, a non-default target can be selected as follows:
cargo build --target=<target_triple>
If a specific board is being targeted, that board's sysroot can be used for
compiling and linking purposes by setting the SYSROOT
environment variable as
follows:
export SYSROOT="/build/<board>"
If C files are getting compiled with a build script that uses the cc
or gcc
crates, you may also need to set the TARGET_CC
environment variable to point
at the appropriate C compiler.
export TARGET_CC="<target_triple>-clang"
If a C/C++ package is being pulled in via pkg-config
, the
PKG_CONFIG_ALLOW_CROSS
environment
variable should be exposed. Without this, you might see CrossCompilation
as part of an error message during build script execution.
export PKG_CONFIG_ALLOW_CROSS=1