Compare commits
39 Commits
v4.3.0
...
collab-rus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f67ff24ff0 | ||
|
|
646ee215c7 | ||
|
|
f7fed4da0f | ||
|
|
71619deaee | ||
|
|
389c78d573 | ||
|
|
60fde89b38 | ||
|
|
0332ce8554 | ||
|
|
a57ceb78ed | ||
|
|
c93d7a5ee6 | ||
|
|
a40ddef8d9 | ||
|
|
0756a9cfbb | ||
|
|
fc8d858ce8 | ||
|
|
d98eae8ca9 | ||
|
|
c440acd3a9 | ||
|
|
8dc77158ff | ||
|
|
acac09ec6d | ||
|
|
542d65fcd4 | ||
|
|
f806a3f9e5 | ||
|
|
4dd43cafbd | ||
|
|
be6c708901 | ||
|
|
efd15c89d1 | ||
|
|
63097dfac5 | ||
|
|
21a58bebed | ||
|
|
42b25ccef0 | ||
|
|
9daf9a6238 | ||
|
|
5249c82425 | ||
|
|
24a71d59b4 | ||
|
|
6c8b63cee6 | ||
|
|
15556acd18 | ||
|
|
6b436713f1 | ||
|
|
3f8ebadde7 | ||
|
|
68e2023837 | ||
|
|
1ed0aeab6b | ||
|
|
5230bb462e | ||
|
|
148e135137 | ||
|
|
3496844d37 | ||
|
|
5cbb204210 | ||
|
|
45c96c74e2 | ||
|
|
8c4b1635ba |
7
.github/workflows/twister.yaml
vendored
7
.github/workflows/twister.yaml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
runs-on:
|
||||
group: zephyr-runner-v2-linux-x64-4xlarge
|
||||
container:
|
||||
image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.26.13.20240601
|
||||
image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.26.14.20240823
|
||||
options: '--entrypoint /bin/bash'
|
||||
outputs:
|
||||
subset: ${{ steps.output-services.outputs.subset }}
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
needs: twister-build-prep
|
||||
if: needs.twister-build-prep.outputs.size != 0
|
||||
container:
|
||||
image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.26.13.20240601
|
||||
image: ghcr.io/zephyrproject-rtos/ci-repo-cache:v0.26.14.20240823
|
||||
options: '--entrypoint /bin/bash'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -188,6 +188,7 @@ jobs:
|
||||
git log --pretty=oneline | head -n 10
|
||||
fi
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
west init -l . || true
|
||||
west config manifest.group-filter -- +ci,+optional
|
||||
@@ -201,6 +202,8 @@ jobs:
|
||||
run: |
|
||||
cmake --version
|
||||
gcc --version
|
||||
cargo --version
|
||||
rustup target list --installed
|
||||
ls -la
|
||||
echo "github.ref: ${{ github.ref }}"
|
||||
echo "github.base_ref: ${{ github.base_ref }}"
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -59,6 +59,18 @@ venv
|
||||
.clangd
|
||||
new.info
|
||||
|
||||
# Cargo drops lock files in projects to capture resolved dependencies.
|
||||
# We don't want to record these.
|
||||
Cargo.lock
|
||||
|
||||
# Cargo encourages a .cargo/config.toml file to symlink to a generated
|
||||
# file. Don't save these.
|
||||
.cargo/
|
||||
|
||||
# Normal west builds will place the Rust target directory under the build directory. However,
|
||||
# sometimes IDEs and such will litter these target directories as well.
|
||||
target/
|
||||
|
||||
# CI output
|
||||
compliance.xml
|
||||
_error.types
|
||||
|
||||
@@ -3172,6 +3172,17 @@ Retention:
|
||||
labels:
|
||||
- "area: Retention"
|
||||
|
||||
Rust:
|
||||
status: maintained
|
||||
maintainers:
|
||||
- d3zd3z
|
||||
files:
|
||||
- cmake/modules/rust.cmake
|
||||
- lib/rust/
|
||||
- samples/rust/
|
||||
labels:
|
||||
- "area: Rust"
|
||||
|
||||
Samples:
|
||||
status: maintained
|
||||
maintainers:
|
||||
|
||||
185
cmake/modules/rust.cmake
Normal file
185
cmake/modules/rust.cmake
Normal file
@@ -0,0 +1,185 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Rust make support
|
||||
|
||||
# Zephyr targets are defined through Kconfig. We need to map these to
|
||||
# an appropriate llvm target triple. This sets `RUST_TARGET` in the
|
||||
# parent scope, or an error if the target is not yet supported by
|
||||
# Rust.
|
||||
function(_rust_map_target)
|
||||
# Map Zephyr targets to LLVM targets.
|
||||
if(CONFIG_CPU_CORTEX_M)
|
||||
if(CONFIG_CPU_CORTEX_M0 OR CONFIG_CPU_CORTEX_M0PLUS OR CONFIG_CPU_CORTEX_M1)
|
||||
set(RUST_TARGET "thumbv6m-none-eabi" PARENT_SCOPE)
|
||||
elseif(CONFIG_CPU_CORTEX_M3)
|
||||
set(RUST_TARGET "thumbv7m-none-eabi" PARENT_SCOPE)
|
||||
elseif(CONFIG_CPU_CORTEX_M4 OR CONFIG_CPU_CORTEX_M7)
|
||||
if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI)
|
||||
set(RUST_TARGET "thumbv7em-none-eabihf" PARENT_SCOPE)
|
||||
else()
|
||||
set(RUST_TARGET "thumbv7em-none-eabi" PARENT_SCOPE)
|
||||
endif()
|
||||
elseif(CONFIG_CPU_CORTEX_M23)
|
||||
set(RUST_TARGET "thumbv8m.base-none-eabi" PARENT_SCOPE)
|
||||
elseif(CONFIG_CPU_CORTEX_M33 OR CONFIG_CPU_CORTEX_M55)
|
||||
# Not a typo, Zephyr, uses ARMV7_M_ARMV8_M_FP to select the FP even on v8m.
|
||||
if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI)
|
||||
set(RUST_TARGET "thumbv8m.main-none-eabihf" PARENT_SCOPE)
|
||||
else()
|
||||
set(RUST_TARGET "thumbv8m.main-none-eabi" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
# Todo: The M55 is thumbv8.1m.main-none-eabi, which can be added when Rust
|
||||
# gain support for this target.
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown Cortex-M target.")
|
||||
endif()
|
||||
elseif(CONFIG_RISCV)
|
||||
if(CONFIG_RISCV_ISA_RV64I)
|
||||
# TODO: Should fail if the extensions don't match.
|
||||
set(RUST_TARGET "riscv64imac-unknown-none-elf" PARENT_SCOPE)
|
||||
elseif(CONFIG_RISCV_ISA_RV32I)
|
||||
# TODO: We have multiple choices, try to pick the best.
|
||||
set(RUST_TARGET "riscv32i-unknown-none-elf" PARENT_SCOPE)
|
||||
else()
|
||||
message(FATAL_ERROR "Rust: Unsupported riscv ISA")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Rust: Add support for other target")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(get_include_dirs target dirs)
|
||||
get_target_property(include_dirs ${target} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(include_dirs)
|
||||
set(${dirs} ${include_dirs} PARENT_SCOPE)
|
||||
else()
|
||||
set(${dirs} "" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(rust_cargo_application)
|
||||
# For now, hard-code the Zephyr crate directly here. Once we have
|
||||
# more than one crate, these should be added by the modules
|
||||
# themselves.
|
||||
set(LIB_RUST_CRATES zephyr zephyr-build)
|
||||
|
||||
get_include_dirs(zephyr_interface include_dirs)
|
||||
|
||||
get_property(include_defines TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS)
|
||||
message(STATUS "Includes: ${include_dirs}")
|
||||
message(STATUS "Defines: ${include_defines}")
|
||||
|
||||
_rust_map_target()
|
||||
message(STATUS "Building Rust llvm target ${RUST_TARGET}")
|
||||
|
||||
# TODO: Make sure RUSTFLAGS is not set.
|
||||
|
||||
# TODO: Let this be configurable, or based on Kconfig debug?
|
||||
set(RUST_BUILD_TYPE debug)
|
||||
set(BUILD_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}")
|
||||
|
||||
set(CARGO_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/rust/target")
|
||||
set(RUST_LIBRARY "${CARGO_TARGET_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}/librustapp.a")
|
||||
set(SAMPLE_CARGO_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/rust/sample-cargo-config.toml")
|
||||
|
||||
# The generated C binding wrappers. These are bindgen-generated wrappers for the inline functions
|
||||
# within Zephyr.
|
||||
set(WRAPPER_FILE "${CMAKE_CURRENT_BINARY_DIR}/rust/wrapper.c")
|
||||
|
||||
# To get cmake to always invoke Cargo requires a bit of a trick. We make the output of the
|
||||
# command a file that never gets created. This will cause cmake to always rerun cargo. We
|
||||
# add the actual library as a BYPRODUCTS list of this command, otherwise, the first time the
|
||||
# link will fail because it doesn't think it knows how to build the library. This will also
|
||||
# cause the relink when the cargo command actually does rebuild the rust code.
|
||||
set(DUMMY_FILE "${CMAKE_BINARY_DIR}/always-run-cargo.dummy")
|
||||
|
||||
# For each module in zephyr-rs, add entry both to the .cargo/config template and for the
|
||||
# command line, since either invocation will need to see these.
|
||||
set(command_paths)
|
||||
set(config_paths "")
|
||||
message(STATUS "Processing crates: ${ZEPHYR_RS_MODULES}")
|
||||
foreach(module IN LISTS LIB_RUST_CRATES)
|
||||
message(STATUS "module: ${module}")
|
||||
set(config_paths
|
||||
"${config_paths}\
|
||||
${module}.path = \"${ZEPHYR_BASE}/lib/rust/${module}\"
|
||||
")
|
||||
list(APPEND command_paths
|
||||
"--config"
|
||||
"patch.crates-io.${module}.path=\\\"${ZEPHYR_BASE}/lib/rust/${module}\\\""
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Write out a cargo config file that can be copied into `.cargo/config.toml` (or made a
|
||||
# symlink) in the source directory to allow various IDE tools and such to work. The build we
|
||||
# invoke will override these settings, in case they are out of date. Everything set here
|
||||
# should match the arguments given to the cargo build command below.
|
||||
file(WRITE ${SAMPLE_CARGO_CONFIG} "
|
||||
# This is a generated sample .cargo/config.toml file from the Zephyr build.
|
||||
# At the time of generation, this represented the settings needed to allow
|
||||
# a `cargo build` command to compile the rust code using the current Zephyr build.
|
||||
# If any settings in the Zephyr build change, this could become out of date.
|
||||
[build]
|
||||
target = \"${RUST_TARGET}\"
|
||||
target-dir = \"${CARGO_TARGET_DIR}\"
|
||||
|
||||
[env]
|
||||
BUILD_DIR = \"${CMAKE_CURRENT_BINARY_DIR}\"
|
||||
DOTCONFIG = \"${DOTCONFIG}\"
|
||||
ZEPHYR_DTS = \"${ZEPHYR_DTS}\"
|
||||
INCLUDE_DIRS = \"${include_dirs}\"
|
||||
INCLUDE_DEFINES = \"${include_defines}\"
|
||||
WRAPPER_FILE = \"${WRAPPER_FILE}\"
|
||||
|
||||
[patch.crates-io]
|
||||
${config_paths}
|
||||
")
|
||||
|
||||
# The library is built by invoking Cargo.
|
||||
add_custom_command(
|
||||
OUTPUT ${DUMMY_FILE}
|
||||
BYPRODUCTS ${RUST_LIBRARY} ${WRAPPER_FILE}
|
||||
COMMAND
|
||||
${CMAKE_EXECUTABLE}
|
||||
env BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
DOTCONFIG=${DOTCONFIG}
|
||||
ZEPHYR_DTS=${ZEPHYR_DTS}
|
||||
INCLUDE_DIRS="${include_dirs}"
|
||||
INCLUDE_DEFINES="${include_defines}"
|
||||
WRAPPER_FILE="${WRAPPER_FILE}"
|
||||
cargo build
|
||||
# TODO: release flag if release build
|
||||
# --release
|
||||
|
||||
# Override the features according to the shield given. For a general case,
|
||||
# this will need to come from a variable or argument.
|
||||
# TODO: This needs to be passed in.
|
||||
# --no-default-features
|
||||
# --features ${SHIELD_FEATURE}
|
||||
|
||||
# Set a replacement so that packages can just use `zephyr-sys` as a package
|
||||
# name to find it.
|
||||
${command_paths}
|
||||
--target ${RUST_TARGET}
|
||||
--target-dir ${CARGO_TARGET_DIR}
|
||||
COMMENT "Building Rust application"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Be sure we don't try building this until all of the generated headers have been generated.
|
||||
add_custom_target(librustapp ALL
|
||||
DEPENDS ${DUMMY_FILE}
|
||||
# The variables, defined at the top level, don't seem to be accessible here.
|
||||
syscall_list_h_target
|
||||
driver_validation_h_target
|
||||
kobj_types_h_target
|
||||
)
|
||||
|
||||
target_link_libraries(app PUBLIC -Wl,--allow-multiple-definition ${RUST_LIBRARY})
|
||||
add_dependencies(app librustapp)
|
||||
|
||||
# Presumably, Rust applications will have no C source files, but cmake will require them.
|
||||
# Add an empty file so that this will build. The main will come from the rust library.
|
||||
target_sources(app PRIVATE ${ZEPHYR_BASE}/lib/rust/main.c ${WRAPPER_FILE})
|
||||
endfunction()
|
||||
@@ -112,6 +112,7 @@ list(APPEND zephyr_cmake_modules kconfig)
|
||||
list(APPEND zephyr_cmake_modules arch_v2)
|
||||
list(APPEND zephyr_cmake_modules soc_v1)
|
||||
list(APPEND zephyr_cmake_modules soc_v2)
|
||||
list(APPEND zephyr_cmake_modules rust)
|
||||
|
||||
foreach(component ${SUB_COMPONENTS})
|
||||
if(NOT ${component} IN_LIST zephyr_cmake_modules)
|
||||
|
||||
@@ -8,3 +8,4 @@ Language Support
|
||||
|
||||
c/index.rst
|
||||
cpp/index.rst
|
||||
rust/index.rst
|
||||
|
||||
52
doc/develop/languages/rust/bindings.rst
Normal file
52
doc/develop/languages/rust/bindings.rst
Normal file
@@ -0,0 +1,52 @@
|
||||
.. _rust_bindings:
|
||||
|
||||
Bindings to Zephyr for Rust
|
||||
###########################
|
||||
|
||||
Zephyr is written in C, and its primary API is also made available to C. It is written as a
|
||||
mixture of types, function prototypes, inline functions, and macros.
|
||||
|
||||
Although Rust interfaces fairly easily with C at an ABI level, the compiler does not have any
|
||||
support for generating the code necessary for these bindings. In order to call C code, the types
|
||||
must be reproduced in Rust, function prototypes written in Rust, and often wrappers made for inline
|
||||
functions and macros (or this functionality replicated in Rust).
|
||||
|
||||
This is a tedious, and error-prone process, and the end result would quickly get out of date with
|
||||
the development of Zephyr.
|
||||
|
||||
The `bindgen`_ project seeks to address much of this issue by automatically generating bindings to C
|
||||
code for Rust. It makes use of ``libclang`` to parse and interpret a given set of headers, and
|
||||
generate the Rust code needed to call this code.
|
||||
|
||||
.. _bindgen: https://github.com/rust-lang/rust-bindgen
|
||||
|
||||
Because the Zephyr headers use numerous conditional compilation macros, the bindings needed will be
|
||||
very specialized to a given board, and even a given configuration. To do this, the file
|
||||
:file:`lib/rust/zephyr-sys/build.rs`, which ``cargo`` knows to run at build time, will
|
||||
generate the bindings for the particular build of Zephyr being used by the current Rust application.
|
||||
|
||||
These bindings will be made available in the ``zephyr-sys`` crate, as well as under ``zephyr::raw``.
|
||||
|
||||
Using the Bindings
|
||||
******************
|
||||
|
||||
In general, using direct bindings to C functions from Rust is a bit more difficult than calling them
|
||||
from C. Although all of these calls are considered "unsafe", and must be placed in an ``unsafe``
|
||||
block, the Rust language has stricter constraints on what is allowed, even by unsafe code. The
|
||||
intent of supporting the Rust Language on Zephyr project is to allow full use of Zephyr, without
|
||||
needing to resort to unsafe code, it is understandable that this implementation will be incomplete,
|
||||
and some applications may require resorting to, what is sometimes referred to as "The Dark Arts".
|
||||
The `Rustinomicon`_ is a good resource for those wishing to delve into this area.
|
||||
|
||||
.. _Rustinomicon: https://doc.rust-lang.org/nomicon/
|
||||
|
||||
In addition, the code in :file:`zephyr/lib/rust/zephyr` that provides the higher level abstractions
|
||||
will primarily be implemented using these auto-generated bindings.
|
||||
|
||||
Generation
|
||||
**********
|
||||
|
||||
The generation of the bindings is controlled by the ``build.rs`` program. This attempts to capture
|
||||
the symbols that are needed for successful bindings, but there may be things missing. Additional
|
||||
bindings can be included by adding their patterns to the list of patterns in ``build.rs``, and
|
||||
possibly adding additional ``#include`` directives to :file:`lib/rust/zephyr-sys/wrapper.h`.
|
||||
153
doc/develop/languages/rust/index.rst
Normal file
153
doc/develop/languages/rust/index.rst
Normal file
@@ -0,0 +1,153 @@
|
||||
.. _language_rust:
|
||||
|
||||
Rust Language Support
|
||||
#####################
|
||||
|
||||
Rust is a systems programming language focused on safety, speed, and concurrency. Designed to
|
||||
prevent common programming errors such as null pointer dereferencing and buffer overflows, Rust
|
||||
emphasizes memory safety without sacrificing performance. Its powerful type system and ownership
|
||||
model ensure thread-safe programming, making it an ideal choice for developing reliable and
|
||||
efficient low-level code. Rust's expressive syntax and modern features make it a robust alternative
|
||||
for developers working on embedded systems, operating systems, and other performance-critical
|
||||
applications.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
bindings.rst
|
||||
|
||||
Enabling Rust Support
|
||||
*********************
|
||||
|
||||
Currently, Zephyr supports applications written in Rust and C. The enable Rust support, you must
|
||||
select the :kconfig:option:`CONFIG_RUST` in the application configuration file.
|
||||
|
||||
The rust toolchain is separate from the rest of the Zephyr SDK. It is recommended to use the
|
||||
`rustup`_ tool to install the rust toolchain. In addition to the base compiler, you will need to
|
||||
install core libraries for the target(s) you wish to compile on. It is easiest to determine what
|
||||
needs to be installed by trying a build. The diagnostics from the rust compilation will indicate
|
||||
the rust command needed to install the appropriate target support:
|
||||
|
||||
.. _rustup: https://rustup.rs/
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ west build ...
|
||||
...
|
||||
error[E0463]: can't find crate for `core`
|
||||
|
|
||||
= note: the `thumbv7m-none-eabi` target may not be installed
|
||||
= help: consider downloading the target with `rustup target add thumbv7m-none-eabi`
|
||||
|
||||
In this case, the given ``rustup`` command will install the needed target support. The target
|
||||
needed will depend on both the board selected, as well as certain configuration choices (such as
|
||||
whether floating point is enabled).
|
||||
|
||||
Writing a Rust Application
|
||||
**************************
|
||||
|
||||
See :zephyr_file:`samples/rust` for examples of an application.
|
||||
|
||||
CMake files
|
||||
-----------
|
||||
|
||||
The application should contain a :file:`CMakeLists.txt`, similar to the following:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(hello_world)
|
||||
|
||||
rust_cargo_application()
|
||||
|
||||
Cargo files
|
||||
-----------
|
||||
|
||||
Rust applications are built with Cargo. The Zephyr build system will configure and invoke cargo in
|
||||
your application directory, with options set so that it can find the Zephyr support libraries, and
|
||||
that the output will be contained within the Zephyr build directory.
|
||||
|
||||
The :file:`Cargo.toml` will need to have a ``[lib]`` section that sets ``crate-type =
|
||||
["staticlib"]``, and will need to include ``zephyr = "0.1.0"`` as a dependency. You can use
|
||||
crates.io and the Crate ecosystem to include any other dependencies you need. Just make sure that
|
||||
you use crates that support building with no-std.
|
||||
|
||||
Application
|
||||
-----------
|
||||
|
||||
The application source itself should live in :file:`src/lib.rs`. A few things are needed. A minimal
|
||||
file would be:
|
||||
|
||||
.. code-block:: rust
|
||||
|
||||
#![no_std]
|
||||
|
||||
extern crate zephyr;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn rust_main() {
|
||||
}
|
||||
|
||||
The ``no_std`` declaration is needed to prevent the code from referencing the ``std`` library. The
|
||||
extern reference will cause the zephyr crate to be brought in, even if nothing from it is used.
|
||||
Practically, any meaningful Rust application on Zephyr will use something from this crate, and this
|
||||
line is not necessary. Lastly, the main declaration exports the main symbol so that it can be
|
||||
called by C code. The build ``rust_cargo_application()`` cmake function will include a small C file
|
||||
that will call into this from the C main function.
|
||||
|
||||
Zephyr Functionality
|
||||
********************
|
||||
|
||||
The bindings to Zephyr for Rust are under development, and are currently rather minimalistic.
|
||||
|
||||
Bool Kconfig settings
|
||||
---------------------
|
||||
|
||||
Boolean Kconfig settings can be used from within Rust code. Due to design constraints by the Rust
|
||||
language, settings that affect compilation must be determined before the build is made. In order to
|
||||
use this in your application, you will need to use the ``zephyr-build`` crate, provided, to make
|
||||
these symbols available.
|
||||
|
||||
To your ``Cargo.toml`` file, add the following:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[build-dependencies]
|
||||
zephyr-build = "0.1.0"
|
||||
|
||||
Then, you will need a ``build.rs`` file to call the support function. The following will work:
|
||||
|
||||
.. code-block:: rust
|
||||
|
||||
fn main() {
|
||||
zephyr_build::export_bool_kconfig();
|
||||
}
|
||||
|
||||
At this point, it will be possible to use the ``cfg`` directive in Rust on boolean Kconfig values.
|
||||
For example:
|
||||
|
||||
.. code-block:: rust
|
||||
|
||||
#[cfg(CONFIG_SCHED_DUMB)]
|
||||
one_declaration;
|
||||
|
||||
#[cfg(not(CONFIG_SCHED_DUMB))]
|
||||
other_declaration;
|
||||
|
||||
Other Kconfig settings
|
||||
----------------------
|
||||
|
||||
All bool, numeric and string Kconfig settings are accessible from the ``zephyr::kconfig`` module.
|
||||
For example:
|
||||
|
||||
.. code-block:: rust
|
||||
|
||||
let ceiling = zephyr::kconfig::CONFIG_PRIORITY_CEILING - 1;
|
||||
|
||||
Other functionality
|
||||
-------------------
|
||||
|
||||
Access to other functionality within zephyr is a work-in-progress, and this document will be updated
|
||||
as that is done.
|
||||
@@ -21,6 +21,8 @@ source "lib/posix/Kconfig"
|
||||
|
||||
source "lib/open-amp/Kconfig"
|
||||
|
||||
source "lib/rust/Kconfig"
|
||||
|
||||
source "lib/smf/Kconfig"
|
||||
|
||||
source "lib/acpi/Kconfig"
|
||||
|
||||
22
lib/rust/Kconfig
Normal file
22
lib/rust/Kconfig
Normal file
@@ -0,0 +1,22 @@
|
||||
# Rust configuration options
|
||||
#
|
||||
# Copyright (c) 2024 Linaro LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menu "Rust Language Support"
|
||||
|
||||
config RUST_SUPPORTED
|
||||
bool
|
||||
default y if (CPU_CORTEX_M || \
|
||||
(RISCV && !RISCV_ISA_RV32E && !RISCV_ISA_RV128I))
|
||||
help
|
||||
Selected for platforms that have support for Rust.
|
||||
|
||||
config RUST
|
||||
bool "Rust support for the application"
|
||||
depends on RUST_SUPPORTED
|
||||
select EXPERIMENTAL
|
||||
help
|
||||
This option enables the use of applications written in Rust.
|
||||
|
||||
endmenu
|
||||
19
lib/rust/main.c
Normal file
19
lib/rust/main.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Linaro LTD
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* This main is brought into the Rust application. */
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#ifdef CONFIG_RUST
|
||||
|
||||
extern void rust_main(void);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
rust_main();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
17
lib/rust/zephyr-build/Cargo.toml
Normal file
17
lib/rust/zephyr-build/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2024 Linaro LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
[package]
|
||||
name = "zephyr-build"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = """
|
||||
Build-time support for Rust-based applications that run on Zephyr.
|
||||
Provides utilities for accessing Kconfig and devicetree information.
|
||||
"""
|
||||
|
||||
# These are needed at build time.
|
||||
# Whether these need to be vendored is an open question. They are not
|
||||
# used by the core Zephyr tree, but are needed by zephyr applications.
|
||||
[dependencies]
|
||||
regex = "1.10.3"
|
||||
77
lib/rust/zephyr-build/src/lib.rs
Normal file
77
lib/rust/zephyr-build/src/lib.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Pre-build code for zephyr module.
|
||||
|
||||
// This module makes the values from the generated .config available as conditional compilation.
|
||||
// Note that this only applies to the zephyr module, and the user's application will not be able to
|
||||
// see these definitions. To make that work, this will need to be moved into a support crate which
|
||||
// can be invoked by the user's build.rs.
|
||||
|
||||
// This builds a program that is run on the compilation host before the code is compiled. It can
|
||||
// output configuration settings that affect the compilation.
|
||||
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
/// Export boolean Kconfig entries. This must happen in any crate that wishes to access the
|
||||
/// configuration settings.
|
||||
pub fn export_bool_kconfig() {
|
||||
let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper");
|
||||
|
||||
// Ensure the build script is rerun when the dotconfig changes.
|
||||
println!("cargo:rerun-if-env-changed=DOTCONFIG");
|
||||
println!("cargo-rerun-if-changed={}", dotconfig);
|
||||
|
||||
let config_y = Regex::new(r"^(CONFIG_.*)=y$").unwrap();
|
||||
|
||||
let file = File::open(&dotconfig).expect("Unable to open dotconfig");
|
||||
for line in BufReader::new(file).lines() {
|
||||
let line = line.expect("reading line from dotconfig");
|
||||
if let Some(caps) = config_y.captures(&line) {
|
||||
println!("cargo:rustc-cfg={}", &caps[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Capture bool, numeric and string kconfig values in a 'kconfig' module.
|
||||
/// This is a little simplistic, and will make the entries numeric if they look like numbers.
|
||||
/// Ideally, this would be built on the types of the values, but that will require more
|
||||
/// introspection.
|
||||
pub fn build_kconfig_mod() {
|
||||
let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper");
|
||||
let outdir = env::var("OUT_DIR").expect("OUT_DIR must be set");
|
||||
|
||||
// The assumption is that hex values are unsigned, and decimal are signed.
|
||||
let config_hex = Regex::new(r"^(CONFIG_.*)=(0x[0-9a-fA-F]+)$").unwrap();
|
||||
let config_int = Regex::new(r"^(CONFIG_.*)=(-?[1-9][0-9]*)$").unwrap();
|
||||
// It is unclear what quoting might be used in the .config.
|
||||
let config_str = Regex::new(r#"^(CONFIG_.*)=(".*")$"#).unwrap();
|
||||
let gen_path = Path::new(&outdir).join("kconfig.rs");
|
||||
|
||||
let mut f = File::create(&gen_path).unwrap();
|
||||
writeln!(&mut f, "pub mod kconfig {{").unwrap();
|
||||
|
||||
let file = File::open(&dotconfig).expect("Unable to open dotconfig");
|
||||
for line in BufReader::new(file).lines() {
|
||||
let line = line.expect("reading line from dotconfig");
|
||||
if let Some(caps) = config_hex.captures(&line) {
|
||||
writeln!(&mut f, " #[allow(dead_code)]").unwrap();
|
||||
writeln!(&mut f, " pub const {}: usize = {};",
|
||||
&caps[1], &caps[2]).unwrap();
|
||||
} else if let Some(caps) = config_int.captures(&line) {
|
||||
writeln!(&mut f, " #[allow(dead_code)]").unwrap();
|
||||
writeln!(&mut f, " pub const {}: isize = {};",
|
||||
&caps[1], &caps[2]).unwrap();
|
||||
} else if let Some(caps) = config_str.captures(&line) {
|
||||
writeln!(&mut f, " #[allow(dead_code)]").unwrap();
|
||||
writeln!(&mut f, " pub const {}: &'static str = {};",
|
||||
&caps[1], &caps[2]).unwrap();
|
||||
}
|
||||
}
|
||||
writeln!(&mut f, "}}").unwrap();
|
||||
}
|
||||
18
lib/rust/zephyr-sys/Cargo.toml
Normal file
18
lib/rust/zephyr-sys/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2024 Linaro LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
[package]
|
||||
name = "zephyr-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = """
|
||||
Zephyr low-level API bindings.
|
||||
"""
|
||||
|
||||
# These are needed at build time.
|
||||
# Whether these need to be vendored is an open question. They are not
|
||||
# used by the core Zephyr tree, but are needed by zephyr applications.
|
||||
[build-dependencies]
|
||||
anyhow = "1.0"
|
||||
bindgen = { version = "0.69.4", features = ["experimental"] }
|
||||
# zephyr-build = { version = "0.1.0", path = "../zephyr-build" }
|
||||
93
lib/rust/zephyr-sys/build.rs
Normal file
93
lib/rust/zephyr-sys/build.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Pre-build code for zephyr module.
|
||||
|
||||
// This module makes the values from the generated .config available as conditional compilation.
|
||||
// Note that this only applies to the zephyr module, and the user's application will not be able to
|
||||
// see these definitions. To make that work, this will need to be moved into a support crate which
|
||||
// can be invoked by the user's build.rs.
|
||||
|
||||
// This builds a program that is run on the compilation host before the code is compiled. It can
|
||||
// output configuration settings that affect the compilation.
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use bindgen::Builder;
|
||||
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Determine which version of Clang we linked with.
|
||||
let version = bindgen::clang_version();
|
||||
println!("Clang version: {:?}", version);
|
||||
|
||||
// Pass in the target used to build the native code.
|
||||
let target = env::var("TARGET")?;
|
||||
|
||||
// Rustc uses some complex target tuples for the riscv targets, whereas clang uses other
|
||||
// options. Fortunately, these variants shouldn't affect the structures generated, so just
|
||||
// turn this into a generic target.
|
||||
let target = if target.starts_with("riscv32") {
|
||||
"riscv32-unknown-none-elf".to_string()
|
||||
} else {
|
||||
target
|
||||
};
|
||||
|
||||
// Likewise, do the same with RISCV-64.
|
||||
let target = if target.starts_with("riscv64") {
|
||||
"riscv64-unknown-none-elf".to_string()
|
||||
} else {
|
||||
target
|
||||
};
|
||||
|
||||
let target_arg = format!("--target={}", target);
|
||||
|
||||
// println!("includes: {:?}", env::var("INCLUDE_DIRS"));
|
||||
// println!("defines: {:?}", env::var("INCLUDE_DEFINES"));
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let wrapper_path = PathBuf::from(env::var("WRAPPER_FILE").unwrap());
|
||||
|
||||
// Bindgen everything.
|
||||
let bindings = Builder::default()
|
||||
.header(Path::new("wrapper.h").canonicalize().unwrap().to_str().unwrap())
|
||||
.use_core()
|
||||
.clang_arg(&target_arg);
|
||||
let bindings = define_args(bindings, "-I", "INCLUDE_DIRS");
|
||||
let bindings = define_args(bindings, "-D", "INCLUDE_DEFINES");
|
||||
let bindings = bindings
|
||||
.wrap_static_fns(true)
|
||||
.wrap_static_fns_path(wrapper_path)
|
||||
// <inttypes.h> seems to come from somewhere mysterious in Zephyr. For us, just pull in the
|
||||
// one from the minimal libc.
|
||||
.clang_arg("-DRUST_BINDGEN")
|
||||
.clang_arg("-I../../../lib/libc/minimal/include")
|
||||
.derive_copy(false)
|
||||
.allowlist_function("k_.*")
|
||||
.allowlist_function("gpio_.*")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn define_args(bindings: Builder, prefix: &str, var_name: &str) -> Builder {
|
||||
let text = env::var(var_name).unwrap();
|
||||
let mut bindings = bindings;
|
||||
for entry in text.split(" ") {
|
||||
if entry.is_empty() {
|
||||
continue;
|
||||
}
|
||||
println!("Entry: {}{}", prefix, entry);
|
||||
let arg = format!("{}{}", prefix, entry);
|
||||
bindings = bindings.clang_arg(arg);
|
||||
}
|
||||
bindings
|
||||
}
|
||||
20
lib/rust/zephyr-sys/src/lib.rs
Normal file
20
lib/rust/zephyr-sys/src/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Zephyr application support for Rust
|
||||
//!
|
||||
//! This crates provides the core functionality for applications written in Rust that run on top of
|
||||
//! Zephyr.
|
||||
|
||||
#![no_std]
|
||||
|
||||
// Allow rust naming convention violations.
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
// Zephyr makes use of zero-sized structs, which Rustc considers invalid. Suppress this warning.
|
||||
// Note, however, that this suppresses any warnings in the bindings about improper C types.
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
34
lib/rust/zephyr-sys/wrapper.h
Normal file
34
lib/rust/zephyr-sys/wrapper.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Linaro LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is the seed point for bindgen. This determines every header that will be visited for
|
||||
* binding generation. It should include any Zephyr headers we need bindings for. The driver in
|
||||
* build.rs will also select which functions need generation, which will determine the types that
|
||||
* are output.
|
||||
*/
|
||||
|
||||
#ifdef RUST_BINDGEN
|
||||
/* errno is coming from somewhere in Zephyr's build. Add the symbol when running bindgen so that it
|
||||
* is defined here.
|
||||
*/
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
/* First, make sure we have all of our config settings. */
|
||||
#include <zephyr/autoconf.h>
|
||||
|
||||
/* Gcc defines __SOFT_FP__ when the target uses software floating point, and the CMSIS headers get
|
||||
* confused without this.
|
||||
*/
|
||||
#if defined(CONFIG_CPU_CORTEX_M)
|
||||
#if !defined(CONFIG_FP_HARDABI) && !defined(FORCE_FP_HARDABI) && !defined(__SOFTFP__)
|
||||
#define __SOFTFP__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
19
lib/rust/zephyr/Cargo.toml
Normal file
19
lib/rust/zephyr/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2024 Linaro LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
[package]
|
||||
name = "zephyr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = """
|
||||
Functionality for Rust-based applications that run on Zephyr.
|
||||
"""
|
||||
|
||||
[dependencies]
|
||||
zephyr-sys = { version = "0.1.0", path = "../zephyr-sys" }
|
||||
|
||||
# These are needed at build time.
|
||||
# Whether these need to be vendored is an open question. They are not
|
||||
# used by the core Zephyr tree, but are needed by zephyr applications.
|
||||
[build-dependencies]
|
||||
zephyr-build = { version = "0.1.0", path = "../zephyr-build" }
|
||||
17
lib/rust/zephyr/build.rs
Normal file
17
lib/rust/zephyr/build.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Pre-build code for zephyr module.
|
||||
|
||||
// This module makes the values from the generated .config available as conditional compilation.
|
||||
// Note that this only applies to the zephyr module, and the user's application will not be able to
|
||||
// see these definitions. To make that work, this will need to be moved into a support crate which
|
||||
// can be invoked by the user's build.rs.
|
||||
|
||||
// This builds a program that is run on the compilation host before the code is compiled. It can
|
||||
// output configuration settings that affect the compilation.
|
||||
|
||||
fn main() {
|
||||
zephyr_build::export_bool_kconfig();
|
||||
zephyr_build::build_kconfig_mod();
|
||||
}
|
||||
41
lib/rust/zephyr/src/lib.rs
Normal file
41
lib/rust/zephyr/src/lib.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Zephyr application support for Rust
|
||||
//!
|
||||
//! This crates provides the core functionality for applications written in Rust that run on top of
|
||||
//! Zephyr.
|
||||
|
||||
#![no_std]
|
||||
#![allow(unexpected_cfgs)]
|
||||
|
||||
// Bring in the generated kconfig module
|
||||
include!(concat!(env!("OUT_DIR"), "/kconfig.rs"));
|
||||
|
||||
// Ensure that Rust is enabled.
|
||||
#[cfg(not(CONFIG_RUST))]
|
||||
compile_error!("CONFIG_RUST must be set to build Rust in Zephyr");
|
||||
|
||||
// Printk is provided if it is configured into the build.
|
||||
#[cfg(CONFIG_PRINTK)]
|
||||
pub mod printk;
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
/// Override rust's panic. This simplistic initial version just hangs in a loop.
|
||||
#[panic_handler]
|
||||
fn panic(_ :&PanicInfo) -> ! {
|
||||
loop {
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-export of zephyr-sys as `zephyr::raw`.
|
||||
pub mod raw {
|
||||
pub use zephyr_sys::*;
|
||||
}
|
||||
|
||||
/// Provide symbols used by macros in a crate-local namespace.
|
||||
#[doc(hidden)]
|
||||
pub mod _export {
|
||||
pub use core::format_args;
|
||||
}
|
||||
113
lib/rust/zephyr/src/printk.rs
Normal file
113
lib/rust/zephyr/src/printk.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Printk implementation for Rust.
|
||||
//!
|
||||
//! This uses the `k_str_out` syscall, which is part of printk to output to the console.
|
||||
|
||||
use core::fmt::{
|
||||
Arguments,
|
||||
Result,
|
||||
Write,
|
||||
write,
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! printk {
|
||||
($($arg:tt)*) => {{
|
||||
$crate::printk::printk(format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! printkln {
|
||||
($($arg:tt)*) => {{
|
||||
$crate::printk::printkln(format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
// This could readily be optimized for the configuration where we don't have userspace, as well as
|
||||
// when we do, and are not running in userspace. This initial implementation will always use a
|
||||
// string buffer, as it doesn't depend on static symbols in print.c.
|
||||
//
|
||||
// A consequence is that the CONFIG_PRINTK_SYNC is enabled, the synchonization will only occur at
|
||||
// the granularity of these buffers rather than at the print message level.
|
||||
|
||||
/// The buffer size for syscall. This is a tradeoff between efficiency (large buffers need fewer
|
||||
/// syscalls) and needing more stack space.
|
||||
const BUF_SIZE: usize = 32;
|
||||
|
||||
struct Context {
|
||||
// How many characters are used in the buffer.
|
||||
count: usize,
|
||||
// Bytes written.
|
||||
buf: [u8; BUF_SIZE],
|
||||
}
|
||||
|
||||
fn utf8_byte_length(byte: u8) -> usize {
|
||||
if byte & 0b1000_0000 == 0 {
|
||||
// Single byte (0xxxxxxx)
|
||||
1
|
||||
} else if byte & 0b1110_0000 == 0b1100_0000 {
|
||||
// Two-byte sequence (110xxxxx)
|
||||
2
|
||||
} else if byte & 0b1111_0000 == 0b1110_0000 {
|
||||
// Three-byte sequence (1110xxxx)
|
||||
3
|
||||
} else if byte & 0b1111_1000 == 0b1111_0000 {
|
||||
// Four-byte sequence (11110xxx)
|
||||
4
|
||||
} else {
|
||||
// Continuation byte or invalid (10xxxxxx)
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
fn add_byte(&mut self, b: u8) {
|
||||
// Ensure we have room for an entire UTF-8 sequence.
|
||||
if self.count + utf8_byte_length(b) > self.buf.len() {
|
||||
self.flush();
|
||||
}
|
||||
|
||||
self.buf[self.count] = b;
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
if self.count > 0 {
|
||||
unsafe {
|
||||
zephyr_sys::k_str_out(self.buf.as_mut_ptr() as *mut i8, self.count);
|
||||
}
|
||||
self.count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Context {
|
||||
fn write_str(&mut self, s: &str) -> Result {
|
||||
for b in s.bytes() {
|
||||
self.add_byte(b);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printk(args: Arguments<'_>) {
|
||||
let mut context = Context {
|
||||
count: 0,
|
||||
buf: [0; BUF_SIZE],
|
||||
};
|
||||
write(&mut context, args).unwrap();
|
||||
context.flush();
|
||||
}
|
||||
|
||||
pub fn printkln(args: Arguments<'_>) {
|
||||
let mut context = Context {
|
||||
count: 0,
|
||||
buf: [0; BUF_SIZE],
|
||||
};
|
||||
write(&mut context, args).unwrap();
|
||||
context.add_byte(b'\n');
|
||||
context.flush();
|
||||
}
|
||||
@@ -24,6 +24,7 @@ Samples and Demos
|
||||
application_development/*
|
||||
shields/*
|
||||
cpp/*
|
||||
rust/*
|
||||
posix/*
|
||||
kernel/*
|
||||
tfm_integration/tfm_integration.rst
|
||||
|
||||
8
samples/rust/hello_world/CMakeLists.txt
Normal file
8
samples/rust/hello_world/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(hello_rust_world)
|
||||
|
||||
rust_cargo_application()
|
||||
16
samples/rust/hello_world/Cargo.toml
Normal file
16
samples/rust/hello_world/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2024 Linaro LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
[package]
|
||||
# This must be rustapp for now.
|
||||
name = "rustapp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "A sample hello world application in Rust"
|
||||
license = "Apache-2.0 or MIT"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
zephyr = "0.1.0"
|
||||
35
samples/rust/hello_world/README.rst
Normal file
35
samples/rust/hello_world/README.rst
Normal file
@@ -0,0 +1,35 @@
|
||||
.. _hello_rust_world:
|
||||
|
||||
Hello Rust World
|
||||
################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
A simple :ref:`Rust <language_rust>` sample that can be used with many
|
||||
supported boards and prints a Hello World message to the console.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
Once the proper rust toolchain has been installed. This configuration
|
||||
can be built and executed on QEMU as follows:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/rust/hello_world
|
||||
:host-os: unix
|
||||
:board: qemu_riscv32
|
||||
:goals: run
|
||||
:compact:
|
||||
|
||||
To build for another board, change "qemu_riscv32" above to that
|
||||
board's name.
|
||||
|
||||
Sample Output
|
||||
=============
|
||||
|
||||
.. code-block: console
|
||||
|
||||
Hello world from Rust on qemu_riscv32
|
||||
|
||||
Exit QEMU by pressing :kbd:`CTRL+C`
|
||||
4
samples/rust/hello_world/prj.conf
Normal file
4
samples/rust/hello_world/prj.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
# Copyright (c) 2024 Linaro LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CONFIG_RUST=y
|
||||
14
samples/rust/hello_world/sample.yaml
Normal file
14
samples/rust/hello_world/sample.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
sample:
|
||||
description: Hello world, but in Rust
|
||||
name: hello rust world
|
||||
common:
|
||||
harness: console
|
||||
harness_config:
|
||||
type: one_line
|
||||
regex:
|
||||
- "Hello world from Rust on (.*)"
|
||||
tags: rust
|
||||
filter: CONFIG_RUST_SUPPORTED
|
||||
tests:
|
||||
sample.rust.helloworld:
|
||||
tags: introduction
|
||||
16
samples/rust/hello_world/src/lib.rs
Normal file
16
samples/rust/hello_world/src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2024 Linaro LTD
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![no_std]
|
||||
|
||||
use zephyr::printkln;
|
||||
|
||||
// Reference the Zephyr crate so that the panic handler gets used. This is only needed if no
|
||||
// symbols from the crate are directly used.
|
||||
extern crate zephyr;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn rust_main() {
|
||||
printkln!("Hello world from Rust on {}",
|
||||
zephyr::kconfig::CONFIG_BOARD);
|
||||
}
|
||||
10
samples/rust/rust.rst
Normal file
10
samples/rust/rust.rst
Normal file
@@ -0,0 +1,10 @@
|
||||
.. _rust-samples:
|
||||
|
||||
Rust Samples
|
||||
############
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
**/*
|
||||
@@ -85,6 +85,11 @@ posix:
|
||||
files:
|
||||
- lib/posix/
|
||||
|
||||
rust:
|
||||
files:
|
||||
- lib/rust/
|
||||
- cmake/modules/rust.cmake
|
||||
|
||||
# cbprintf:
|
||||
# files:
|
||||
# - lib/os/cbprintf*
|
||||
|
||||
Reference in New Issue
Block a user