Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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,谢谢您们啦~

<<: *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::*;
6 changes: 3 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,7 @@ pub(crate) fn queue_receive_wait(
msg_size: u64,
tick: i32,
) -> RttCResult {
unsafe { rt_mq_recv(handle, msg, msg_size, tick).into() }
unsafe { rt_mq_recv(handle, msg, msg_size as rt_size_t, tick).into() }
}

#[inline]
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" }
25 changes: 14 additions & 11 deletions components/rust/examples/component/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cwd = GetCurrentDir()

# Import component build module and build support
sys.path.append(os.path.join(cwd, '../../tools'))
from build_component import build_example_component
from build_component import ComponentBuildError, build_example_component
from build_support import clean_rust_build

# Load feature configurations
Expand All @@ -25,12 +25,11 @@ def _has(sym: str) -> bool:
return bool(GetDepend(sym))


# Early return if Rust or log component is not enabled
if not _has('RT_USING_RUST'):
if not _has('RT_USING_RUST') and not GetOption('clean'):
group = []
Return('group')

if not _has('RUST_LOG_COMPONENT'):
if not (_has('RT_RUST_BUILD_COMPONENTS') or _has('RT_RUST_BUILD_ALL_EXAMPLES')) and not GetOption('clean'):
group = []
Return('group')

Expand All @@ -51,12 +50,16 @@ if GetOption('clean'):
else:
# Build the component using the extracted build module
import rtconfig
LIBS, LIBPATH, LINKFLAGS = build_example_component(
cwd=cwd,
has_func=_has,
rtconfig=rtconfig,
build_root=os.path.join(Dir('#').abspath, "build", "example_component")
)
try:
LIBS, LIBPATH, LINKFLAGS = build_example_component(
cwd=cwd,
has_func=_has,
rtconfig=rtconfig,
build_root=os.path.join(Dir('#').abspath, "build", "example_component")
)
except ComponentBuildError as e:
print(f'Error: {e}')
sys.exit(1)

# Define component group for SCons
group = DefineGroup(
Expand All @@ -68,4 +71,4 @@ group = DefineGroup(
LINKFLAGS=LINKFLAGS
)

Return('group')
Return('group')
Loading
Loading