Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Beginning of Rust support in Zephyr #75904

Merged
merged 18 commits into from
Aug 27, 2024
Merged

Conversation

d3zd3z
Copy link
Collaborator

@d3zd3z d3zd3z commented Jul 15, 2024

This package implements the bare minimum support needed to build a simple Zephyr application written in Rust. There are no bindings or Zephyr API's provided, just the build infrastructure. The is described in #75900.

lib/rust/Kconfig Outdated Show resolved Hide resolved
lib/rust/zephyr-build/src/lib.rs Show resolved Hide resolved
lib/rust/zephyr-build/src/lib.rs Show resolved Hide resolved
@alwa-nordic
Copy link
Collaborator

Thank you for making this PR. It's great to get the ball rolling! Let's make Rust in Zephyr great!

But, I think this PR tries to do a bit too much too quickly. I think the most important first step is to ensure building and linking is done correctly. I think would be best to focus on this and de-scope other parts of this PR for later. Please read on below where I try to explain.

LLVM triplets

This PR is using LLVM for compiling Rust, which I think is a good idea. But it does mean we need to think about ABI compatibility with the Zephyr toolchain.

Linking with LLVM objects is not a Rust-specific problem, and we should solve this first, possibly in a language-agnostic way. We should start with a solid foundation on how to correctly build LLVM objects for linking with the Zephyr ABIs.

I think the as first thing on the roadmap to Rust, Zephyr should have an utility that selects a foolproof LLVM target specification. And we must have tests that can verify that it works at runtime, e.g. by calling various types of functions and checking for data corruption.

The reason this may be a concern is that Zephyr defines its own ABI, which may or may not be equivalent to any of the well-known LLVM triplets. For example, in Zephyr int is always 32 bits, even on 64-bit platforms. Without a test suite, it's too easy to mess up here.

The PR uses the well-known thumbv8m.main-none-eabi and others. We need tests to confirm that we are not making a mistake here.

For prior art, see tylerwhall/zephyr-rust target specifications, which defines new LLVM triplets for Zephyr OS. This project also Apache-licensed.

Kconfig in Rust

I feel a framework for exposing Kconfig values in Rust should be done at a later stage, since there are many ways to do this and it has attracted comments on how to map the Kconfig values onto Rust types. I think it's better to defer this work, so that the initial Rust support is not delayed by this.

Rust already has core::env! for accessing environment variables at build time. I believe this is a good-enough solution for passing in Kconfig variables for now. It's simple enough to copy from Kconfig variables to environment variables in CMakeLists.txt.

I also worry there are good reasons for why core::env! is not like this PR, generating a module with symbols for all environment variables. Of course, it's fine to experiment.

Safety in samples

The sample declares printk as a two-argument function. But printk is actually a variadic C function. This not the same type so it is probably unsound.

Safety is integral to Rust culture. I think that all Rust samples code should be #![forbid(unsafe_code)] to reflect that. That means FFI calls in samples must be trough safe wrappers.

@d3zd3z
Copy link
Collaborator Author

d3zd3z commented Jul 19, 2024

But, I think this PR tries to do a bit too much too quickly.

So, I think we have different philosophies about what we need to do to get rust support into Zephyr. Yes, there is stuff that we need to do "right", but if we are too careful, we'll never get anything at all. My goal here is just to go through the build stuff, and I have a lot of other stuff in process that needs to builds to be able to happen. I guess I'd rather have it work well in a limited situation than solve everything before anything can go in.

triplets...

So, getting this right is important, but this is also difficult to come up with tests for. One possibility is to just be more restrictive about it, and pick configurations that we know work.

But, I don't think the calling is as difficult of an issue as you are imagining. Generally, getting the triple wrong is more likely to result in either code that doesn't run at all (using the wrong CPU), or fails at link time (usually from hard float mismatch).

Matching the Zephyr types is important, but I wouldn't think of that as a compiler or toolchain, but a matching the ABI. It is also not addresed at all yet, as we aren't really calling anything.

Kconfig

I disagree about delaying it. I'm open for other ideas on how to do it, but getting access to them is foundational for most later work. Do you have specific concerns about how I'm doing it?

I'm not quite sure I understand how core::env() is useful. It can't be used for conditional compilation, which is one of the primary needs for getting to bool Kconfig values.

samples

As far as the samples, it isn't intended to stay that way, and in fact, my other PR has already removed it.

Basically, I'm concerned about spinning, while bike shedding things that really aren't that important. One of my goals is to make using Rust inside of Zephyr as nice (well, hopefully nicer) as writing C code. Coming next is making the DT available to Rust code.

Copy link
Collaborator

@kartben kartben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some documentation related feedback. I might have more when CI has a preview site up again
Thanks <3

cmake/modules/rust.cmake Outdated Show resolved Hide resolved

.. _rustup: https://rustup.rs/

.. code-block:: shell
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

samples/rust/.gitignore Outdated Show resolved Hide resolved
samples/rust/hello_rust/prj.conf Outdated Show resolved Hide resolved
samples/rust/hello_rust/prj.conf Outdated Show resolved Hide resolved
doc/develop/languages/rust/index.rst Show resolved Hide resolved
doc/develop/languages/rust/index.rst Outdated Show resolved Hide resolved
doc/develop/languages/rust/index.rst Outdated Show resolved Hide resolved
doc/develop/languages/rust/index.rst Outdated Show resolved Hide resolved
doc/develop/languages/rust/index.rst Outdated Show resolved Hide resolved
Add the `CONFIG_RUST` Kconfig.  This can be set to indicate that an
application wishes to use Rust support.

Adds `CONFIG_RUST_SUPPORTED` to indicate what platforms Rust is known to
work on.  This is set to the targets that are supported by subsequent
commits.

Signed-off-by: David Brown <david.brown@linaro.org>
Until further variants are needed, this provides a `main()` function
that simply calls into the `rust_main()` function.  This is adequate for
applications where main can be directly written in Rust.

The reason that this is written here, rather than just writing `main`
directly in Rust is that `kernel/main_weak.c` provides a weak default
implementation that will be brought in before the Rust application's
`main` is linked in.

Signed-off-by: David Brown <david.brown@linaro.org>
This is the initial Zephyr-specific crates that provide support for Rust
on Zephyr.  Currently, what they do is fairly minimal, but important.

`zephyr-build` is a build time crate (linked against the `zephyr`
crate's build.rs) that processes the current build's Kconfig settings,
and does three things with them:

  - Boolean Kconfig values given given to the Rust toolchain so that
    conditional compilation can be based off of them.
  - Numeric Kconfig settings end up as constants in `zephyr::kconfig`.
  - String valued Kconfig settings end up as constants in
    `zephyr::kconfig`.

None of these cause code or data to be generated or allocated (but note
that if there is a reference to a string, that string will be placed in
read-only memory).

The `zephyr` crate is built for the target, and intended to be
referenced by the application.  It provides minimal support needed for a
"bare" Rust build, notably a panic handler.  At this point, the panic
handler is not implemented as we need better support for calling into
Zephyr's C code, so it just stops in an infinite loop.

It also ensures that `CONFIG_RUST` is set as a sanity check, and
includes the generated kconfig.rs file with the kconfig definitions for
the current build.

Signed-off-by: David Brown <david.brown@linaro.org>
This provides the function `rust_cargo_application()` within Cmake to
give applications written in Rust a simple way to start.

This implements the basic functionality needed to build a rust
application on Arm, including target mapping, and invoking cargo during
the build.

Most of the functionality is about passing the appropriate flags to
`cargo`, which is used to perform the actual build of the rust code.
Cargo generates `librustapp.a` which is added to the link dependencies
for the application.

The cargo rule is written such that cargo will always be built (provided
`app` is being built), as there will be complex dependencies on the
cargo side.  Cargo will not modify the `librustapp.a` file if nothing
needs to be build, so this will generally be fast if there are no
changes to files that affect the Rust build.

Signed-off-by: David Brown <david.brown@linaro.org>
When `CONFIG_PRINTK` is enabled, provide macros `printk` and `printkln`
within the `zephyr` crate that applications can use to print.  This
currently sends messages, with a small amount of buffering, using
`k_str_out`.

Until C functions are made generally available from the Rust side,
we use a small wrapper function to call `k_str_out`, as it is
implemented as an inline syscall.

Signed-off-by: David Brown <david.brown@linaro.org>
Create the Rust equivalent of the hello_world application.  This shows
the use of printk from Rust as well as accessing a string value from
Kconfig.

Signed-off-by: David Brown <david.brown@linaro.org>
Indicate the new code is maintained.

Signed-off-by: David Brown <david.brown@linaro.org>
Add initial docs for Rust language support.  Explains the support
currently present, how to install the toolchain needed, and explains
what is in the current sample.

Signed-off-by: David Brown <david.brown@linaro.org>
We use the `#[cfg(...)]` directive in rust to pass Kconfig values through.
Because it is possible for the code to depend on a Kconfig value that isn't
present in a given build, there isn't a way for us to tell the Rust
toolchain about all possible config values.

For now, just disable this warning entirely.  This functionality could be
supported by the patch validation scripts, which seems like a better place
than trying to gather a list of all possible configs at build time.

Signed-off-by: David Brown <david.brown@linaro.org>
The Cortex-M7 is build the same as M4.  Catch it in the same rules.

Signed-off-by: David Brown <david.brown@linaro.org>
The rust tag associates rust tests and samples with code that adds rust
support to Zephyr.

Signed-off-by: David Brown <david.brown@linaro.org>
Add the 'rust' tag so that CI will invoke this test when rust support
files have changed.

Signed-off-by: David Brown <david.brown@linaro.org>
@d3zd3z d3zd3z force-pushed the r/setup branch 2 times, most recently from 5c062d2 to a56d8e4 Compare August 26, 2024 16:37
cfriedt
cfriedt previously approved these changes Aug 26, 2024
Copy link
Member

@cfriedt cfriedt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines 90 to 96
- name: Install Rust Toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# Add to the local environment to be able to use.
source "$HOME/.cargo/env"
# Install support for the targets that are supported.
for target in $`cat lib/rust/targets.txt`; do rustup target install $$target; done
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary if we use the Rust toolchain integrated in the CI Docker image?

@@ -0,0 +1,178 @@
name: Build Rust tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a separate workflow for building and running Rust applications? It would be better to integrate this into the existing twister workflow.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a mistake, I will remove this file entirely.

Apply the rules to ignore some cargo generated or used files to the
whole tree, not just the samples.

Signed-off-by: David Brown <david.brown@linaro.org>
Create a Readme for this sample, explaining how to build and what it
does.

Signed-off-by: David Brown <david.brown@linaro.org>
Rename the sample to just hello world.  As long as the actual sample
name is different, there won't be index conflicts in the documentation.

Signed-off-by: David Brown <david.brown@linaro.org>
Refill the text to 100 columns instead of 72 to match the rest of the
project.

Signed-off-by: David Brown <david.brown@linaro.org>
Fix various minor typos in the Rust documentation.

Signed-off-by: David Brown <david.brown@linaro.org>
Include the Sample Rust applications in the doc generation.

Signed-off-by: David Brown <david.brown@linaro.org>
Copy link
Collaborator

@kartben kartben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for docs/samples - thanks for this @d3zd3z!

@d3zd3z d3zd3z merged commit 4dd43ca into zephyrproject-rtos:collab-rust Aug 27, 2024
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Rust on Zephyr: cmake and code organization