Rust on Zephyr: cmake and code organization #75900
Labels
Architecture Review
Discussion in the Architecture WG required
RFC
Request For Comments: want input from the community
Introduction
RFC #65837 describes the work necessary to support applications written in Rust into Zephyr. This RFC attempts to discuss how that implementation will be done, practically, within the Zephyr source tree. This will cover where files will be placed into the source tree, and how they will integrate with the various toolchains.
As a bit of a background, this RFC assumes the reader is familiar with the build tools used within Zephyr, including CMake, Kconfig and devicetree. The primary build and dependency tool used for Zephyr is Cargo. Although Cargo is primarily concerned with building standalone applications, there are a few reasons that it is still a useful tool for writing Rust applications within Zephyr:
Before diving further in, it is important to defined a few terms:
.rs
that corresponds to a module.The rust toolchain has the following components:
rustup
: This is the most common tool developers use to install rust toolchains. It is especially helpful for embedded development as it supports not only installing the basic toolchain, but builds of the core and alloc libraries for many of the platforms supported by Rust.cargo
: The build and dependency management system of Rust. The build is described by aCargo.toml
file at the top of a crate. TheCargo.toml
file describes the version, name information, license, and attributes of the crate. There is a feature mechanism that can be used for conditional compilation. TheCargo.toml
file also lists the direct dependencies of the crate (these can be conditionalized by features). For example, it is common, in embedded Rust development, to have a crate that implements some functionality that will have astd
(ornostd
) feature. Whenstd
is available, additional debugging and testing will be available. Often these crates can be developed as ordinary userspace code on the development machine, and then made to work in the embedded system with relatively little effort.rustc
: This is the main compiler. Although it can be invoked, like gcc, on individual components of source code, it is usually left tocargo
to do this.rust-fmt
: This is a tool for automatic source code reformatting. It is common for developers to configured the IDE to run this tool before saving or before committing.rust-analyzer
: This is a language server for Rust that allows editors and IDEs to have source code analysis features available. Allowing this to work on Zephyr applications is an important constraint to the implementation proposed below.The rust compile provides the following libraries:
libgcc
, but it does provide a public API, which provides intrinsics and support that doesn't depend on any functionality outside of Rust.Vec
, dynamically allocated arrays,String
, and various dictionary and set types.std
at this time, but make similar functionality, when it makes sense, available in other crates that we provide.Problem description
We would like developers to be able to write Rust applications, on top of Zephyr, that feel comfortable for development, both from the perspective of a Rust developer, and from the perspective of a Zephyr developer. This RFC does not address the details of how Rust code will interface with Zephyr, but with how the rust code will be organized, and how it will be built.
A Rust application will be similar to how a C application is produced for Zephyr. At the top-level, there will be a
CMakeLists.txt
file that brings in the Zephyr cmake package, and describes that this is an application. The mechanisms for config and dts overrides will be identical to that of a C program. The main difference is that instead of needing to specify a list of C source files, the cmake file will have a single line:after the Zephyr package. The name is sufficiently detailed to allow for other uses of Rust within Zephyr, but captures the initial implementation of writing a main application using Cargo.
Beyond this, the application will have a fairly ordinary
Cargo.toml
file. This file should specify that this crate is to be built as a static library, but otherwise, only needs to list information relevant to this particular application (features and dependencies). This application can be built with the samewest build
tools for other programs. In addition, the build will make available a template file that can be copied or symlinked to.cargo/config.toml
in the source tree that will provide enough information to cargo so that tools such asrust-analyzer
will work unchanged with the source code. With this configuration file, the analyzer will be seeing the program as it is when cross compiled to work with Zephyr, which will make all functionality available, including config options (conditional Rust code by kconfig option will be greyed out by an IDE for example).Proposed change
There are two locations within the tree that will need to be added to in order to support this type of Rust application:
lib/rust
: This directory will have support that is needed to implement Rust support. Details below.cmake/modules/rust.cmake
: Implements therust_cargo_application()
function, and additional functionality for the build to work.One outstanding question is whether Rust code necessary to build Zephyr applications in Rust (support libraries) should be included directly in the Zephyr tree (perhaps under
lib/rust
), or whether there should be a module to support this code. It is also possible to have a hybrid approach, such as if we need to mirror a crate to implement functionality, it could be placed in a module, and the core support would be within the Zephyr tree.Detailed RFC
The bulk of the cmake work for this will be contained within
cmake/modules/rust.cmake
. This provides a functionrust_cargo_application()
that can be used by an application to build a Zephyr application where the main program is in Rust. This does a few things to make this work:build/rust
when the build is inbuild
).Cargo.toml
will be resolved via conventional mechanisms.build/rust/sample-cargo-config.toml
which can be copied or symlinked to.cargo/config.toml
to allow IDE and other tools to work without modification.The above change is sufficient to build a simple "hello world" Rust application that does a handful of manual tasks, and uses hand-crafted bindings to C functions within Zephyr.
Beyond this, there will be one more crates (either under
lib/rust/...
, or in one or more modules) that implement modules to make development of programs in Rust on Zephyr more usable:zephyr-sys
will likely be where bindings take place.Proposed change (Detailed)
cmake/modules/rust.cmake
, and add a line tocmake/modules/zephyr_default.cmake
to have it included.zephyr
crate underlib/rust/zephyr
to implement basic functionality.This is a framework for future work.
Dependencies
As this describes the initial work for Rust on Zephyr, the primary dependencies will be on the user having a functioning install of the Rust toolchain. This can generally be managed by rustup. It is probably desirable to extend Twister so that it can decide whether or not to run rust tests based on whether the toolchain is present.
Concerns and Unresolved Questions
libgcc
directly into the generated library file. However, the implementations are not sufficiently fine grained to prevent conflicts with libgcc. There is a workaround to simply tell the linker to ignore these duplicate symbols, but there should be an effort to help fix these within upstream.Alternatives
There has been previous work for Rust on Zephyr that added
std
support. The result was complex to build, as it required the user to rebuild the rust toolchain, and worked with a very specific version of Zephyr. This proposal tries to loosen the coupling between Rust versions and Zephyr versions, as well as realizing thatstd
isn't a very good initial goal.The text was updated successfully, but these errors were encountered: