Advanced development made easy

Rust in embedded Linux and Yocto

Having long since been mainstream, Rust is now also an integral part of embedded Linux systems. Thanks to its integration in Yocto since the Honister version, basic toolchains are directly available. But what should you do if you need a more recent version of Rust? This article offers effective strategies for integrating modern Rust applications into Yocto builds.

Rust is now mainstream for desktop and cloud applications. As such, components like compilers, build tools, etc. can be easily installed, and integration into the desired operating system is straightforward. Rust has also become part of the Linux kernel and thus allows the development of kernel modules in Rust.

But what about the use of Rust on embedded Linux platforms and distributions that are created using Yocto, for example?

How do I get Rust into my Yocto build?

It’s already there!

Rust and the necessary toolchain are probably already available in your Yocto build, because Rust has been part of openembedded-core since Yocto Honister. This means that no additional layers are needed. All dependencies and recipes are already available in poky/meta.

Rust versionYocto version (selection)
1.84.1Walnascar
1.75Scarthgap
1.59Kirkstone
1.54Honister

I would like to use a more recent version of Rust

If you need to use both an older Yocto version and a new Rust version, for example because you need the Rust 2024 Edition, special Yocto layers can be used.

LTS mixins

With the lts-rust-mixin layer, a current Rust version can be used. In this layer there are backports of current Rust versions for a few older Yocto versions. The layer can be added as usual to BBLAYERS in bblayers.conf, with the appropriate commit checked out.

Rust binary

The meta-rust-bin layer also pursues the goal of a backport from modern Rust to older Yocto versions. In contrast to the usual approach in Yocto of compiling all tools on the host, this layer provides binaries directly – with all the advantages and disadvantages of pre-compiled tools. The layer is added to bblayers.conf as described above. In recipes, you must make sure not to use inherit cargo, but inherit cargo_bin to clearly specify which variant you want to use.

Meta Rust

The historical layer meta-rust meta-rust need not (and should not) be used in a normal Yocto distribution, as the code is already contained in other layers.

Rust Programming Language Quiz

Ready for the Rust challenge?

Rust Programming Language Quiz

Put your knowledge to the test! Take our quiz for software developers and find out how much you know about Rust.

What do I have to do to integrate a Rust application into my system?

Integrating a Rust application into a system is not really any different from integrating a “C/C++” application. Let’s take a look at a potential scenario:

Project setup

The lts-rust-mixin layer must be added to bblayers.conf.

In local.conf, or better in a separate distro.conf, Rust is activated and the version specified. It is not imperative to specify the Rust version, but it is helpful as an error message appears if the lts-rust-mixin layer is configured incorrectly.

DISTRO_FEATURES:append = " rust"
RUST_VERSION = "1.85.1"
CARGO_VERSION = "1.85.1"

Yocto recipe

The recipe is independent of how Rust is activated for this system. Here is a minimal but complete recipe for compiling the application and installing it on the target system.

(1)
SUMMARY = "Simple Webserver"
DESCRIPTION = "A simple webserver in async rust"
HOMEPAGE = "https://github.com/<user>/simple_srv_rs"
LICENSE = "MIT"
LIC_FILES_CHKSUM = file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302

(2)
TAG = "0.1.6"
SRC_URI = "git://github.com/<user>/simple_srv_rs.git;branch=async_server;tag=v${TAG};protocol=https"
S = "${WORKDIR}/git"

(3)
TARGET_BIN_NAME= "webserver-rs" # Must match the project name in Cargo.toml

(4)
inherit cargo
do_fetch[network] = "1"
do_compile[network] = "1"
CARGO_BUILD_FLAGS:remove = "--frozen"
CARGO_DISABLE_BITBAKE_VENDORING = "1"

(5)
do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${B}/target/${RUST_TARGET_SYS}/release/${TARGET_BIN_NAME} ${D}${bindir}/${TARGET_BIN_NAME}
    # --- other install tasks ---
}
FILES:${PN} += "${bindir}/${TARGET_BIN_NAME}"
  1. Header with the necessary names and the licence with the appropriate hash.
  2. The desired git tag from the desired branch is used.
  3. Bitbake makes a poor attempt at the name of the application. The variable is used to install the application.
  4. Derive from the cargo class so that the subsequent commands and variables are known. The rustclass is not usually needed.
    Network access is required to download crates.
    CARGO_BUILD_FLAGS:remove = "—frozen"
    The flag —frozen means —locked and —offline together. If —frozen is removed from the recipe, cargo has access to the web to download crates. For the build in a CI, or for other reasons, you can leave the download to Bitbake. More on this later. Disabled vendoring means that all dependencies must be downloaded.
  5. The application is installed on the target system. RUST_TARGET_SYS is derived from the Rust target triple, i.e. something like armv7-unknown-linux-gnueabihf. A release build is created by default.
Icon for Rust Transition Service

Be future-proof

Rust Transition Service

Regulatory pressure to make software more secure, efficient and cost-effective is constantly growing. Rust relieves this pressure.

Dependencies managed by Yocto

You often want the crates to be managed by Yocto. For example, if all dependencies are to be cached in the CI/CD pipeline. For this to be possible, the dependencies must be specified in the recipe in the form:

SRC_URI += " \
    crate://crates.io/addr2line/0.24.2 \
"
SRC_URI[addr2line-0.24.2.sha256sum] = "dfb..1c1"

A non-trivial application already has several dozen dependencies. To ensure that the recipe remains readable and to enable automation, it is advisable to pack these dependencies into a <recipe>-crates.inc file.

There are different ways to list the dependencies together with their checksum in this file.

Here is the recipe from earlier, but following this approach:

SUMMARY = "Simple Webserver"  
DESCRIPTION = "A simple webserver in async rust"  
HOMEPAGE = "https://github.com/<user>/simple_srv_rs"  
LICENSE = "MIT"  
LIC_FILES_CHKSUM = file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302  
 
(1)
inherit cargo
inherit cargo-update-recipe-crates
(2)
require ${BPN}-crates.inc

TAG = "0.1.6"  
SRC_URI = "git://github.com/<user>/simple_srv_rs.git;branch=async_server;tag=v${TAG};protocol=https"  
S = "${WORKDIR}/git"  

TARGET_BIN_NAME = "webserver-rs"
(3)
# Keep network access during fetch so that the initial download is possible
do_fetch[network] = "1"
# Remove network access during build since we have all dependencies
do_compile[network] = "0"

do_install() {  
    install -d ${D}${bindir}  
    install -m 0755 ${B}/target/${RUST_TARGET_SYS}/release/${TARGET_BIN_NAME} ${D}${bindir}/${TARGET_BIN_NAME}  
    # --- other install tasks ---  
}  
FILES:${PN} += "${bindir}/${TARGET_BIN_NAME}"
  1. inherit cargo-update-recipe-crates is required to execute the bitbake -c update_crates<recipe> command..
  2. The file with the dependencies is used.
  3. Network access is no longer required during compilation. However, in order to receive the crates initially, network access is required for the fetch.
    CARGO_DISABLE_BITBAKE_VENDORING is set to false by default.

Conclusion

Rust is now on embedded Linux systems and the procedure described here can be used to integrate a simple but complete application with Yocto into a Linux system. The long dependency lists must be maintained, but the right tools do the heavy lifting for you.

The expert

Oliver With

Oliver With is an expert in embedded software. As a senior developer, he is convinced that the best way to solve complex problems is by teams working closely together. He combines creative approaches to finding solutions with high-quality development to create successful products. He is a Rust enthusiast, because for the first time Rust provides a language that combines security, performance, industry acceptance and ergonomics for developers.

Attention!

Sorry, so far we got only content in German for this section.