Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8f32c4e
Fix Rust build example configuration handling
yanhu7150-tech Jul 7, 2026
f89e400
Add Rust build CI attach config
yanhu7150-tech Jul 9, 2026
a05282c
Fix Rust attach build failures
yanhu7150-tech Jul 9, 2026
5d38245
Fix Rust build failure propagation
yanhu7150-tech Jul 10, 2026
fb9252b
Fix Rust link flags for GCC and ArmClang
yanhu7150-tech Jul 10, 2026
75310a8
Fix Rust component anchor configuration
yanhu7150-tech Jul 10, 2026
fc87fc7
Fix ArmClang Rust core archive linking
yanhu7150-tech Jul 10, 2026
269212f
Fail closed on component feature parsing
yanhu7150-tech Jul 10, 2026
53a4e0c
Gate Rust optional APIs by configuration
yanhu7150-tech Jul 13, 2026
49869b5
Refine Rust fs feature condition
yanhu7150-tech Jul 13, 2026
076f21c
Make Rust core feature mapping declarative
yanhu7150-tech Jul 14, 2026
1d59a52
Merge origin/master into bugfix-rust-build-target-artifact
yanhu7150-tech Jul 16, 2026
6f86c79
Fix Rust core cleanup when disabled
yanhu7150-tech Jul 18, 2026
537b128
Merge remote-tracking branch 'origin/master' into bugfix-rust-build-t…
yanhu7150-tech Jul 22, 2026
33479c1
Fix top-level Rust cleanup when disabled
yanhu7150-tech Jul 22, 2026
cf128c8
Fix Rust example cleanup and remove M85 mapping
yanhu7150-tech Jul 23, 2026
425b779
Add Rust application CI and narrow module scope
yanhu7150-tech Jul 23, 2026
7a097ec
Fix Rust queue receive result handling
yanhu7150-tech Jul 28, 2026
795f63f
[rust][examples] Fix module example build configuration
yanhu7150-tech Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,25 @@ component.cherryusb_cdc:
- CONFIG_RT_CHERRYUSB_DEVICE_DWC2_ST=y
- CONFIG_RT_CHERRYUSB_DEVICE_CDC_ACM=y
- CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM=y
# ------ rust CI ------
Comment thread
yanhu7150-tech marked this conversation as resolved.
rust:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

新增的 Rust CI 目前也只覆盖 F407 GCC 下的 core/component,没有覆盖:

  • application 和 module;

另外作者是否有实际在硬件板卡上将RUST成功运行过?

@yanhu7150-tech yanhu7150-tech Jul 23, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

感谢提醒,已补充 application 的显式 CI 配置:
CONFIG_RT_RUST_BUILD_APPLICATIONS=y
我按照当前 F407 Rust CI 的环境,在 STM32F407 RT-Spark BSP 上使用 GCC 和 Rust Nightly 进行了等价构建验证,结果如下:

  • Rust core 构建并链接成功;
  • 5 个 application 示例构建成功:thread、queue、mutex、param、semaphore;
  • component registry 构建并链接成功;
  • 最终 rt-thread.elf、rtthread.bin 和 rtthread.hex 均成功生成;
  • BUILD_RC=0;
  • Cargo 和 SCons 错误数均为 0。
    module 方面,F407 当前未启用 RT_USING_MODULE,也没有合适的动态模块构建和加载验证环境。因此我没有强行在 F407 CI 中开启 module,并已撤回本 PR 中未经验证的 module 构建链扩展,只保留关闭选项后仍可执行 clean 的最小修复,该 clean 路径已完成动态验证。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

关于真实硬件:目前没有在真实 STM32F407 板卡上烧录并运行 Rust。当前完成的是 F407 GCC + Rust Nightly 的完整固件构建验证;此前的运行验证是在 QEMU A9 单核和 SMP 环境中完成的。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

目前新的 workflows 显示 awaiting approval。@Rbb666 @foxg1ove1 麻烦您们有空时批准一下 CI workflows,谢谢您们啦~

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

关于真实硬件:目前没有在真实 STM32F407 板卡上烧录并运行 Rust。当前完成的是 F407 GCC + Rust Nightly 的完整固件构建验证;此前的运行验证是在 QEMU A9 单核和 SMP 环境中完成的。

这个可以给出测试截图吗?然后最好建议找块实际的开发板,测试下这块是否可以真实跑通哈

@yanhu7150-tech yanhu7150-tech Jul 28, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

5b9fb030a2bb1b60e73f5b372c684472 1c0b5900d5726d4f9871e3fcdd27b71e b90a59b289e6fb95152eac76e7e7efc0 感谢您的建议,以上是我补充的PR 当前提交的测试结果和截图。 我在 stm32f407-rt-spark BSP 上使用 GCC 和 Nightly Rust 进行了完整交叉构建,Rust target 为 thumbv7em-none-eabihf。本次构建中: Rust core 构建并链接成功; thread、queue、mutex、param、semaphore 这 5 个 Rust application 示例均构建成功,并正常参与最终链接; Rust log component 构建成功,component registry library 正常参与链接; 最终成功生成 rt-thread.elf、rtthread.hex 和 rtthread.bin; SCons 正常执行至 scons: done building targets.。 构建时使用的主要命令为: RTT_EXEC_PATH=/usr/bin RUSTUP_TOOLCHAIN=nightly scons --strict -j1 我也补充验证了 clean 路径:先关闭全部 Rust 相关配置,再执行 scons -c。日志显示 build/rust、build/example_usrapp、build/example_component 和 build/rust_modules 均在配置关闭状态下成功注册并被清理,clean 过程中没有重新触发 Rust 构建。截图中的 rust_modules 使用的是专门的 cleanup sentinel,仅用于验证 clean guard,不代表完成了 module 运行验证。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

我可能表达有误,这块不仅是编译的截图,而是qemu运行RUST后的截图哈

@yanhu7150-tech yanhu7150-tech Jul 28, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

感谢您的指正,之前是我对您的意思理解不够准确。
按照您的建议,我随后在 qemu-vexpress-a9 BSP 下补充了 QEMU 实际运行验证。在执行 rust_queue_demo 时,进一步发现了消息队列接收返回值的处理问题:

  • RT-Thread C API rt_mq_recv() 返回的是 rt_ssize_t
  • 返回值大于等于 0 时表示接收成功,具体数值是接收到的数据长度;
  • 返回值小于 0 时才表示错误;
  • 原 Rust binding 将它声明为 rt_err_t,queue 封装又按照普通错误码语义处理返回值,因此正常的正数长度可能被错误识别为失败。
    完成了以下修复:
  1. 在 Rust binding 中补充与 RT-Thread ABI 一致的 rt_ssize_t 类型;
  2. rt_mq_recv() 的返回类型修正为 rt_ssize_t
  3. queue receive 路径改为:返回值大于等于 0 时成功,负值时才转换为错误;
  4. 没有修改全局 From<i32> 错误转换逻辑,避免影响其他 RT-Thread API。
    修复后重新进行了 QEMU 运行验证:
  • Rust 组件初始化成功;
  • rust_queue_demo 能够持续输出 waiting!recv msg
  • 消息队列接收路径能够连续正常运行;
  • rust_param_demo alpha beta 123 能够正确接收并输出全部参数,执行后正常返回 MSH。
    下面附上最新提交对应的 QEMU 实际运行截图。

@yanhu7150-tech yanhu7150-tech Jul 28, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

61fa2685-cdec-4469-820d-e9de5478ff33 image 第一张图执行 rust_param_demo alpha beta 123,后alpha、beta、123 被正确输出,随后命令正常返回 msh />。第二张图可以看到 RT-Thread 已在 QEMU 中启动,Rust component 初始化成功,并且 rust_queue_demo 已在 MSH 中实际执行,持续输出 waiting! / recv msg,消息队列接收路径能够连续正常工作。

<<: *scons
pre_build: |
python3 -m pip install --user toml
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly --profile minimal
sudo ln -sf "$HOME/.cargo/bin/cargo" /usr/local/bin/cargo
sudo ln -sf "$HOME/.cargo/bin/rustc" /usr/local/bin/rustc
sudo ln -sf "$HOME/.cargo/bin/rustup" /usr/local/bin/rustup
rustup target add thumbv7em-none-eabihf
rustc --version
cargo --version
kconfig:
- CONFIG_RT_USING_RUST=y
- CONFIG_RT_RUST_CORE=y
- CONFIG_RT_USING_RUST_EXAMPLES=y
- CONFIG_RT_RUST_BUILD_APPLICATIONS=y
- CONFIG_RT_RUST_BUILD_COMPONENTS=y
- CONFIG_RUST_LOG_COMPONENT=y

devices.soft_i2c:
<<: *scons
Expand Down
2 changes: 1 addition & 1 deletion components/rust/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def _has(sym: str) -> bool:
except Exception:
return bool(GetDepend(sym))

if not _has('RT_USING_RUST'):
if not _has('RT_USING_RUST') and not GetOption('clean'):
Return('objs')

cwd = GetCurrentDir()
Expand Down
11 changes: 11 additions & 0 deletions components/rust/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ crate-type = ["rlib", "staticlib"]
[features]
default = []
smp = []
fs = []
libdl = []

[package.metadata.rt-thread.features.smp]
all = ["RT_USING_SMP"]

[package.metadata.rt-thread.features.fs]
all = ["RT_USING_DFS", "DFS_USING_POSIX"]

[package.metadata.rt-thread.features.libdl]
all = ["RT_USING_MODULE"]
Comment thread
yanhu7150-tech marked this conversation as resolved.

[profile.dev]
panic = "abort"
Expand Down
86 changes: 66 additions & 20 deletions components/rust/core/SConscript
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
import sys
from building import *
from SCons.Subst import quote_spaces

cwd = GetCurrentDir()

Expand All @@ -11,6 +13,7 @@ from build_support import (
verify_rust_toolchain,
ensure_rust_target_installed,
cargo_build_staticlib,
get_staticlib_link_name,
clean_rust_build,
)
def _has(sym: str) -> bool:
Expand All @@ -20,10 +23,28 @@ def _has(sym: str) -> bool:
return bool(GetDepend(sym))


# Source files – MSH command glue
src = ['rust_cmd.c']
group = []
if not _has('RT_RUST_CORE') and not GetOption('clean'):
Return('group')


def get_staticlib_link_name_from_artifact(lib_path):
"""Derive the link name from the actual staticlib artifact file name."""
artifact_name = os.path.basename(os.fspath(lib_path))
if artifact_name.startswith("lib") and artifact_name.endswith(".a"):
return artifact_name[3:-2]
return None


# Source files – MSH command glue.
# rust_cmd.c references rust_init(), which is only provided by the Rust core
# static library. It must only enter the build when that library is actually
# produced; otherwise the C link stage fails with 'undefined reference to
# rust_init' instead of failing/skipping cleanly at the Rust build step.
LIBS = []
LIBPATH = []
LINKFLAGS = ""
include_rust_cmd = False

if GetOption('clean'):
# Register Rust artifacts for cleaning
Expand All @@ -33,39 +54,64 @@ if GetOption('clean'):
Clean('.', rust_build_dir)
else:
print('No rust build artifacts to clean')
# Keep rust_cmd.c in the group during clean so its object is cleaned too.
include_rust_cmd = True
else:
if verify_rust_toolchain():
import rtconfig
rust_build_dir = clean_rust_build(Dir('#').abspath)

target = detect_rust_target(_has, rtconfig)
if not target:
print('Error: Unable to detect Rust target; please check configuration')
sys.exit(1)
else:
print(f'Detected Rust target: {target}')

# Optional hint if target missing
ensure_rust_target_installed(target)

# Build mode and features
debug = bool(_has('RUST_DEBUG_BUILD'))
features = collect_features(_has)
if not ensure_rust_target_installed(target):
print('Error: Rust target is not installed; Rust library build failed')
sys.exit(1)
else:
# Build mode and features
debug = bool(_has('RUST_DEBUG_BUILD'))
features = collect_features(_has)

rustflags = make_rustflags(rtconfig, target)
rust_lib = cargo_build_staticlib(
rust_dir=cwd, target=target, features=features, debug=debug, rustflags=rustflags
)
rustflags = make_rustflags(rtconfig, target)
rust_lib = cargo_build_staticlib(
rust_dir=cwd, target=target, features=features, debug=debug, rustflags=rustflags, build_root=rust_build_dir
)

if rust_lib:
LIBS = ['rt_rust']
LIBPATH = [os.path.dirname(rust_lib)]
print('Rust library linked successfully')
else:
print('Warning: Failed to build Rust library')
if rust_lib:
# Derive the link name from the actual artifact so it always
# matches the file that was built. Only fall back to
# re-parsing Cargo.toml when the artifact name does not
# follow the lib<name>.a convention.
link_lib_name = get_staticlib_link_name_from_artifact(rust_lib)
if not link_lib_name:
link_lib_name = get_staticlib_link_name(cwd)
LIBS = [link_lib_name]
LIBPATH = [os.path.dirname(rust_lib)]
if rtconfig.PLATFORM == 'armclang':
if not (os.path.isfile(rust_lib) and os.path.getsize(rust_lib) > 0):
print(f'Error: ArmClang Rust core link requires a non-empty archive, but got: {rust_lib}')
sys.exit(1)
LINKFLAGS = " " + quote_spaces(os.fspath(rust_lib))
LIBS = []
LIBPATH = []
include_rust_cmd = True
print('Rust library linked successfully')
else:
print('Error: Failed to build Rust library')
sys.exit(1)
else:
print('Warning: Rust toolchain not found')
print('Error: Rust toolchain not found')
print('Please install Rust from https://rustup.rs')
sys.exit(1)

# Define component group for SCons
group = DefineGroup('rust', src, depend=['RT_USING_RUST'], LIBS=LIBS, LIBPATH=LIBPATH)
# Only define the component group (with rust_cmd.c) when the Rust core static
# library was actually produced, so its rust_init() reference always resolves.
if include_rust_cmd:
group = DefineGroup('rust', ['rust_cmd.c'], depend=['RT_USING_RUST', 'RT_RUST_CORE'], LIBS=LIBS, LIBPATH=LIBPATH, LINKFLAGS=LINKFLAGS)

Return('group')
2 changes: 2 additions & 0 deletions components/rust/core/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod thread;
pub mod mutex;
pub mod sem;
pub mod queue;
#[cfg(feature = "libdl")]
pub mod libloading;


Expand All @@ -24,4 +25,5 @@ pub use thread::*;
pub use mutex::*;
pub use sem::*;
pub use queue::*;
#[cfg(feature = "libdl")]
pub use libloading::*;
11 changes: 8 additions & 3 deletions components/rust/core/src/api/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub type APIRawQueue = rt_mq_t;
pub(crate) fn queue_create(name: &str, len: u64, message_size: u64) -> Option<APIRawQueue> {
let s = CString::new(name).unwrap();
let raw;
unsafe { raw = rt_mq_create(s.as_ptr(), message_size, len, 0) }
unsafe { raw = rt_mq_create(s.as_ptr(), message_size as rt_size_t, len as rt_size_t, 0) }
if raw == ptr::null_mut() {
None
} else {
Expand All @@ -35,7 +35,7 @@ pub(crate) fn queue_send_wait(
msg_size: u64,
tick: i32,
) -> RttCResult {
unsafe { rt_mq_send_wait(handle, msg, msg_size, tick).into() }
unsafe { rt_mq_send_wait(handle, msg, msg_size as rt_size_t, tick).into() }
}

#[inline]
Expand All @@ -45,7 +45,12 @@ pub(crate) fn queue_receive_wait(
msg_size: u64,
tick: i32,
) -> RttCResult {
unsafe { rt_mq_recv(handle, msg, msg_size, tick).into() }
let ret = unsafe { rt_mq_recv(handle, msg, msg_size as rt_size_t, tick) };
if ret >= 0 {
RttCResult::Ok
} else {
ret.into()
}
}

#[inline]
Expand Down
3 changes: 2 additions & 1 deletion components/rust/core/src/bindings/librt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub type rt_int32_t = c_int;
pub type rt_uint8_t = c_uchar;
pub type rt_tick_t = rt_uint32_t;
pub type rt_size_t = rt_ubase_t;
pub type rt_ssize_t = rt_base_t;

pub type rt_thread_t = *mut c_void;
pub type rt_sem_t = *mut c_void;
Expand Down Expand Up @@ -94,7 +95,7 @@ unsafe extern "C" {
pub fn rt_mq_create(name: *const c_char, msg_size: rt_size_t, max_msgs: rt_size_t, flag: rt_uint8_t) -> rt_mq_t;
pub fn rt_mq_send(mq: rt_mq_t, buffer: *const c_void, size: rt_size_t) -> rt_err_t;
pub fn rt_mq_send_wait(mq: rt_mq_t, buffer: *const c_void, size: rt_size_t, timeout: rt_int32_t) -> rt_err_t;
pub fn rt_mq_recv(mq: rt_mq_t, buffer: *mut c_void, size: rt_size_t, timeout: rt_int32_t) -> rt_base_t;
pub fn rt_mq_recv(mq: rt_mq_t, buffer: *mut c_void, size: rt_size_t, timeout: rt_int32_t) -> rt_ssize_t;
pub fn rt_mq_delete(mq: rt_mq_t) -> rt_err_t;
pub fn rt_mq_detach(mq: rt_mq_t) -> rt_err_t;
}
Expand Down
3 changes: 1 addition & 2 deletions components/rust/core/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ pub use librt::{

/* Memory management functions */
pub use librt::{
rt_malloc, rt_free, rt_realloc, rt_calloc, rt_malloc_align, rt_free_align,
rt_safe_malloc, rt_safe_free
rt_malloc, rt_free, rt_realloc, rt_calloc, rt_malloc_align, rt_free_align
};

/* Device management functions */
Expand Down
2 changes: 1 addition & 1 deletion components/rust/core/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl File {

pub fn seek(&self, offset: i64) -> RTResult<i64> {
let n = unsafe { libc::lseek(self.fd, offset as libc::off_t, libc::SEEK_SET) };
if n < 0 { Err(FileSeekErr) } else { Ok(n) }
if n < 0 { Err(FileSeekErr) } else { Ok(n.into()) }
}

pub fn flush(&self) -> RTResult<()> {
Expand Down
3 changes: 2 additions & 1 deletion components/rust/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ and device interfaces. Designed for embedded devices running RT-Thread.
*/

#![no_std]
#![feature(alloc_error_handler)]
#![feature(linkage)]
#![allow(dead_code)]

Expand All @@ -33,13 +32,15 @@ pub mod init;
pub mod allocator;
pub mod mutex;
pub mod out;
#[cfg(feature = "fs")]
pub mod fs;
pub mod panic;
pub mod param;
pub mod queue;
pub mod sem;
pub mod thread;
pub mod time;
#[cfg(feature = "libdl")]
pub mod libloader;

mod prelude;
Expand Down
22 changes: 13 additions & 9 deletions components/rust/examples/application/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cwd = GetCurrentDir()

# Import usrapp build module and build support
sys.path.append(os.path.join(cwd, '../../tools'))
from build_usrapp import build_example_usrapp
from build_usrapp import UserAppBuildError, build_example_usrapp
from build_support import clean_rust_build


Expand All @@ -31,7 +31,7 @@ def load_extended_feature_configs():

group = []

if not _has('RT_RUST_BUILD_APPLICATIONS'):
Comment thread
yanhu7150-tech marked this conversation as resolved.
if not (_has('RT_RUST_BUILD_APPLICATIONS') or _has('RT_RUST_BUILD_ALL_EXAMPLES')) and not GetOption('clean'):
Return('group')

# Load extended feature configurations
Expand All @@ -51,12 +51,16 @@ if GetOption('clean'):
print('No example_usrapp build artifacts to clean')
else:
import rtconfig
LIBS, LIBPATH, LINKFLAGS = build_example_usrapp(
cwd=cwd,
has_func=_has,
rtconfig=rtconfig,
build_root=os.path.join(Dir('#').abspath, "build", "example_usrapp")
)
try:
LIBS, LIBPATH, LINKFLAGS = build_example_usrapp(
cwd=cwd,
has_func=_has,
rtconfig=rtconfig,
build_root=os.path.join(Dir('#').abspath, "build", "example_usrapp")
)
except UserAppBuildError as e:
print(f'Error: {e}')
sys.exit(1)

group = DefineGroup(
'example_usrapp',
Expand All @@ -67,4 +71,4 @@ group = DefineGroup(
LINKFLAGS=LINKFLAGS
)

Return('group')
Return('group')
4 changes: 2 additions & 2 deletions components/rust/examples/application/fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ default = []
enable-log = ["em_component_log/enable-log"]

[dependencies]
rt-rust = { path = "../../../core" }
rt-rust = { path = "../../../core", features = ["fs"] }
rt_macros = { path = "../../../rt_macros" }
em_component_log = { path = "../../component/log"}
em_component_log = { path = "../../component/log"}
4 changes: 2 additions & 2 deletions components/rust/examples/application/loadlib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ name = "em_loadlib"
crate-type = ["staticlib"]

[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
rt-rust = { path = "../../../core", features = ["libdl"] }
rt_macros = { path = "../../../rt_macros" }
Loading
Loading