Adding Rust Language Support for RT-Thread #10910
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled
Weekly CI Scheduler / Trigger and Monitor CIs (push) Has been cancelled
Weekly CI Scheduler / Create Discussion Report (push) Has been cancelled

This commit is contained in:
zhang san
2025-12-08 18:34:25 +08:00
committed by GitHub
parent cd1d47b87c
commit 69980f8b9d
88 changed files with 6734 additions and 4 deletions
+104
View File
@@ -0,0 +1,104 @@
menuconfig RT_USING_RUST_EXAMPLES
bool "Enable Rust Examples"
depends on RT_USING_RUST
default n
help
Enable Rust example applications, components, and modules.
if RT_USING_RUST_EXAMPLES
config RT_RUST_BUILD_ALL_EXAMPLES
bool "Build All Examples"
default n
help
Build all available Rust examples.
menu "Application Examples"
config RT_RUST_BUILD_APPLICATIONS
bool "Build Application Examples"
default y
depends on RT_USING_FINSH
help
Build Rust application examples.
if RT_RUST_BUILD_APPLICATIONS
config RT_RUST_EXAMPLE_FS
bool "File System Example"
default n
help
File system operations example.
config RT_RUST_EXAMPLE_LOADLIB
bool "Dynamic Library Loading Example"
default n
depends on RT_USING_MODULE
help
Dynamic library loading and usage example.
config RT_RUST_EXAMPLE_MUTEX
bool "Mutex Example"
default y
help
Mutex synchronization example.
config RT_RUST_EXAMPLE_PARAM
bool "Parameter Example"
default y
help
Basic parameter handling example.
config RT_RUST_EXAMPLE_QUEUE
bool "Queue Example"
default y
help
Message queue example.
config RT_RUST_EXAMPLE_SEMAPHORE
bool "Semaphore Example"
default y
help
Semaphore synchronization example.
config RT_RUST_EXAMPLE_THREAD
bool "Thread Example"
default y
help
Thread creation and management example.
endif
endmenu
menu "Component Examples"
config RT_RUST_BUILD_COMPONENTS
bool "Build Component Examples"
default y
help
Build Rust component examples.
if RT_RUST_BUILD_COMPONENTS
config RUST_LOG_COMPONENT
bool "Auto-initialize Rust log component"
default y
help
Automatically initialize Rust log component during RT-Thread startup.
endif
endmenu
menu "Module Examples"
config RT_RUST_BUILD_MODULES
bool "Build Module Examples"
default n
depends on RT_USING_MODULE
help
Build Rust dynamic module examples.
if RT_RUST_BUILD_MODULES
config RT_RUST_MODULE_SIMPLE_MODULE
bool "Simple Module"
default y
help
Basic dynamic module template.
endif
endmenu
endif
+22
View File
@@ -0,0 +1,22 @@
# RT-Thread building script for Rust examples
import os
from building import *
Import('rtconfig')
cwd = GetCurrentDir()
group = []
entries = os.listdir(cwd)
subdirs = ['application', 'component', 'modules']
for subdir in subdirs:
if subdir in entries:
result = SConscript(os.path.join(subdir, 'SConscript'))
if isinstance(result, (list, tuple)):
group.extend(result)
else:
group.append(result)
Return('group')
@@ -0,0 +1,70 @@
import os
import sys
from building import *
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_support import clean_rust_build
def _has(sym: str) -> bool:
try:
return bool(GetDepend([sym]))
except Exception:
return bool(GetDepend(sym))
def load_extended_feature_configs():
"""
Load extended feature configurations if available.
This allows users to add custom configuration mappings.
"""
try:
from feature_config_examples import setup_all_example_features
setup_all_example_features()
print("Extended feature configurations loaded successfully.")
except ImportError:
print("Using default feature configurations.")
group = []
if not _has('RT_RUST_BUILD_APPLICATIONS'):
Return('group')
# Load extended feature configurations
load_extended_feature_configs()
src = []
LIBS = []
LIBPATH = []
LINKFLAGS = ""
if GetOption('clean'):
app_build_dir = clean_rust_build(Dir('#').abspath, "example_usrapp")
if os.path.exists(app_build_dir):
print(f'Registering {app_build_dir} for cleanup')
Clean('.', app_build_dir)
else:
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")
)
group = DefineGroup(
'example_usrapp',
src,
depend=['RT_USING_RUST'],
LIBS=LIBS,
LIBPATH=LIBPATH,
LINKFLAGS=LINKFLAGS
)
Return('group')
@@ -0,0 +1,17 @@
[package]
name = "em_fs"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_fs"
crate-type = ["staticlib"]
[features]
default = []
enable-log = ["em_component_log/enable-log"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
em_component_log = { path = "../../component/log"}
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-20 foxglove Rust file operation test.
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
use rt_macros::msh_cmd_export;
use rt_rust::{fs, println};
use rt_rust::param::Param;
use em_component_log::{info, error};
#[msh_cmd_export(name = "rust_file_demo", desc = "Rust example app.")]
fn main(_param: Param) {
println!("[rust_file_test] start");
let mut file = match fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(false)
.truncate(true)
.open("test.txt")
{
Ok(f) => {
info!("open test.txt ok");
f
}
Err(e) => {
error!("open error: {:?}", e);
return;
}
};
if let Err(e) = file.write_all("Hello from FS wrapper!\n") {
error!("write_all error: {:?}", e);
return;
}
info!("write_all done");
if let Err(e) = file.flush() {
error!("flush error: {:?}", e);
} else {
info!("flush ok");
}
match file.read_to_string() {
Ok(s) => info!("read_back: {}", s),
Err(e) => error!("read_to_string error: {:?}", e),
}
if let Err(e) = file.set_len(5) {
error!("truncate error: {:?}", e);
} else {
info!("truncate to 5 ok");
}
if let Err(e) = file.close() {
error!("close error: {:?}", e);
} else {
info!("close ok");
}
info!("[rust_file_test] end");
}
@@ -0,0 +1,12 @@
[package]
name = "em_loadlib"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_loadlib"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove load library example
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
use rt_macros::msh_cmd_export;
use rt_rust::println;
use rt_rust::get_libfn;
use core::ffi::{c_int, c_char};
use rt_rust::param::Param;
#[msh_cmd_export(name = "rust_dl_demo", desc = "Rust dynamic library demo")]
fn main(_param: Param) {
println!("\n=== Dynamic Library Demo ===");
get_libfn!("/hello.mo", "main", my_hello, ());
my_hello();
get_libfn!("/libmylib.mo", "rust_mylib_add", my_add, c_int, a: c_int, b: c_int);
let s = my_add(15, 20);
println!("my_add(15, 20) = {}", s);
get_libfn!("/libmylib.mo", "rust_mylib_println", my_println, (), s: *const c_char);
my_println(b"rustlib: Hello World\0".as_ptr() as *const c_char);
}
@@ -0,0 +1,12 @@
[package]
name = "em_mutex"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_mutex"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove mutex test demo
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
use alloc::sync::Arc;
use core::time::Duration;
use rt_macros::msh_cmd_export;
use rt_rust::mutex::Mutex;
use rt_rust::param::Param;
use rt_rust::println;
use rt_rust::thread;
use rt_rust::time;
#[msh_cmd_export(name = "rust_mutex_demo", desc = "Rust example app.")]
fn main(_param: Param) {
let counter = Arc::new(Mutex::new(0).unwrap());
let run = move || loop {
time::sleep(Duration::new(2, 0));
{
let mut c = counter.lock().unwrap();
*c += 1;
println!("{}", *c);
}
};
let _ = thread::Thread::new()
.name("thread 1")
.stack_size(2048)
.start(run.clone());
time::sleep(Duration::new(1, 0));
let _ = thread::Thread::new()
.name("thread 2")
.stack_size(2048)
.start(run.clone());
}
@@ -0,0 +1,13 @@
[package]
name = "em_param"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_param"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove param test demo
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
use alloc::string::String;
use rt_macros::{rt_app_export, msh_cmd_export};
use rt_rust::param::Param;
use rt_rust::println;
// 演示新的模块化宏接口 - 使用 msh_cmd_export 导出参数测试命令
#[msh_cmd_export(name = "rust_param_demo", desc = "Rust parameter demo")]
fn main(param: Param) {
for i in param {
println!("{}", String::from_utf8_lossy(&*i))
}
}
@@ -0,0 +1,12 @@
[package]
name = "em_queue"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_queue"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove queue test demo
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
use alloc::string::String;
use alloc::sync::Arc;
use core::time::Duration;
use rt_macros::msh_cmd_export;
use rt_rust::queue::Queue;
use rt_rust::param::Param;
use rt_rust::println;
use rt_rust::thread;
use rt_rust::time;
#[msh_cmd_export(name = "rust_queue_demo", desc = "Rust example app.")]
fn main(_param: Param) {
let send = Arc::new(Queue::new(2).unwrap());
let recv = send.clone();
let _ = thread::Thread::new()
.name("thread 1")
.stack_size(1024)
.start(move || {
loop {
time::sleep(Duration::new(1, 0));
send.send(String::from("msg"), 0).unwrap();
}
});
time::sleep(Duration::new(1, 0));
let _ = thread::Thread::new()
.name("thread 2")
.stack_size(1024)
.start(move || {
loop {
println!("waiting!");
let a = recv.recv_wait_forever().unwrap();
println!("recv {}", a);
}
});
}
@@ -0,0 +1,12 @@
[package]
name = "em_sem"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_sem"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove semaphore test demo
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
use alloc::sync::Arc;
use core::time::Duration;
use rt_macros::msh_cmd_export;
use rt_rust::sem::Semaphore;
use rt_rust::param::Param;
use rt_rust::println;
use rt_rust::thread;
use rt_rust::time;
#[msh_cmd_export(name = "rust_sem_demo", desc = "Rust example app.")]
fn main(_param: Param) {
let send = Arc::new(Semaphore::new().unwrap());
let recv = send.clone();
let _ = thread::Thread::new()
.name("thread 1")
.stack_size(1024)
.start(move || {
loop {
time::sleep(Duration::new(1, 0));
send.release()
}
});
time::sleep(Duration::new(1, 0));
let _ = thread::Thread::new()
.name("thread 2")
.stack_size(1024)
.start(move || {
loop {
println!("waiting!");
recv.take_wait_forever().unwrap();
println!("recv a sem!")
}
});
}
@@ -0,0 +1,12 @@
[package]
name = "em_thread"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_thread"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove thread test demo
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
use core::time::Duration;
use rt_macros::msh_cmd_export;
use rt_rust::param::Param;
use rt_rust::println;
use rt_rust::thread;
use rt_rust::time;
#[msh_cmd_export(name = "rust_thread_demo", desc = "Rust example app.")]
fn main(_param: Param) {
let _ = thread::Thread::new()
.name("thread 1")
.stack_size(1024)
.start(move || {
loop {
println!("thread a will sleep 1s");
time::sleep(Duration::new(1, 0));
}
});
let _ = thread::Thread::new()
.name("thread 2")
.stack_size(1024)
.start(move || {
loop {
println!("thread b will sleep 3s");
time::sleep(Duration::new(3, 0));
}
});
}
@@ -0,0 +1,71 @@
import os
import sys
from building import *
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_support import clean_rust_build
# Load feature configurations
try:
from feature_config_component import setup_all_component_features
setup_all_component_features()
except ImportError:
print("Warning: Could not load component feature configurations")
def _has(sym: str) -> bool:
"""Helper function to check if a configuration symbol is enabled."""
try:
return bool(GetDepend([sym]))
except Exception:
return bool(GetDepend(sym))
# Early return if Rust or log component is not enabled
if not _has('RT_USING_RUST'):
group = []
Return('group')
if not _has('RUST_LOG_COMPONENT'):
group = []
Return('group')
# Source files component glue code if needed
src = []
LIBS = []
LIBPATH = []
LINKFLAGS = ""
# Handle clean operation
if GetOption('clean'):
comp_build_dir = clean_rust_build(Dir('#').abspath, "example_component")
if os.path.exists(comp_build_dir):
print(f'Registering {comp_build_dir} for cleanup')
Clean('.', comp_build_dir)
else:
print('No example_component build artifacts to 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")
)
# Define component group for SCons
group = DefineGroup(
'example_component_log',
src,
depend=['RT_USING_RUST'],
LIBS=LIBS,
LIBPATH=LIBPATH,
LINKFLAGS=LINKFLAGS
)
Return('group')
@@ -0,0 +1,19 @@
[package]
name = "em_component_registry"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_component_registry"
crate-type = ["staticlib"]
[dependencies]
rt-rust = { path = "../../../core" }
rt_macros = { path = "../../../rt_macros" }
# Optional dependencies - only included when features are enabled
em_component_log = { path = "../log", optional = true }
[features]
default = []
enable-log = ["em_component_log", "em_component_log/enable-log"]
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-25 foxglove Component registry for unified component registration
* 2025-10-29 foxglove Updated to demonstrate new modular macro interface
*/
#![no_std]
extern crate alloc;
/* Demonstrate the new modular macro interface */
use rt_macros::rt_component_export;
use rt_rust::param::{Param, ParamItem};
use rt_rust::println;
/* Re-export component functionality for other modules */
#[cfg(feature = "enable-log")]
pub use em_component_log::*;
#[cfg(feature = "enable-log")]
use em_component_log::logging::Level;
/** Unified component registration entrypoint */
#[cfg(feature = "enable-log")]
#[rt_component_export(name = "rust_component_registry")]
fn init_registry_component() {
println!("[logging component init] hello world");
log!(Level::Info, "hello world");
info!("hello world");
warn!("hello world");
error!("hello world");
trace!("hello world");
debug!("hello world");
}
/** Provide a no-op implementation when no component feature is enabled */
#[cfg(not(feature = "enable-log"))]
pub extern "C" fn component_init() {
/* Empty implementation to ensure the library still links */
}
@@ -0,0 +1,15 @@
[package]
name = "em_component_log"
version = "0.1.0"
edition = "2021"
[lib]
name = "em_component_log"
crate-type = ["rlib", "staticlib"]
[features]
default = []
enable-log = []
[dependencies]
rt-rust = { path = "../../../core" }
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-23 foxglove log component demo
*/
#![no_std]
extern crate alloc;
/* When the `enable-log` feature is enabled, expose logging module and helpers */
#[cfg(feature = "enable-log")]
pub mod logging;
#[cfg(feature = "enable-log")]
use rt_rust::println;
#[cfg(feature = "enable-log")]
use crate::logging::Level;
/* Import panic handler parameters from rt_rust library */
use rt_rust::param::{Param, ParamItem};
/* Provide a no-op implementation when the `enable-log` feature is disabled */
#[cfg(not(feature = "enable-log"))]
pub extern "C" fn component_init() {
/* Empty implementation to ensure the library still links */
}
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-20 foxglove micro rust log component
*/
/* Basic logging primitives and console output helpers */
use alloc::string::String;
use rt_rust::println;
#[repr(usize)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Level {
Error = 1,
Warn = 2,
Info = 3,
Debug = 4,
Trace = 5,
}
/* Top-level logging macro */
#[macro_export]
macro_rules! log {
($level:expr, $($arg:tt)*) => ({
$crate::logging::_log($level, format_args!($($arg)*));
});
}
/* Error-level logging */
#[macro_export]
macro_rules! error {
($($arg:tt)*) => ({
$crate::logging::_log($crate::logging::Level::Error, format_args!($($arg)*));
});
}
/* Warning-level logging */
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => ({
$crate::logging::_log($crate::logging::Level::Warn, format_args!($($arg)*));
});
}
/* Info-level logging */
#[macro_export]
macro_rules! info {
($($arg:tt)*) => ({
$crate::logging::_log($crate::logging::Level::Info, format_args!($($arg)*));
});
}
/* Debug-level logging */
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => ({
$crate::logging::_log($crate::logging::Level::Debug, format_args!($($arg)*));
});
}
/* Trace-level logging */
#[macro_export]
macro_rules! trace {
($($arg:tt)*) => ({
$crate::logging::_log($crate::logging::Level::Trace, format_args!($($arg)*));
});
}
/* Internal logging handler: formats message and prints with color */
pub fn _log(level: Level, args: core::fmt::Arguments) {
use core::fmt::Write;
use rt_rust::time;
let mut s = String::new();
write!(&mut s, "[{:?}][{:?}]", level, time::get_time()).unwrap();
write!(&mut s, "{}", args).unwrap();
match level {
Level::Error => {
println!("\x1b[1;31m{}\x1b[0m", s);
}
Level::Warn => {
println!("\x1b[1;33m{}\x1b[0m", s);
}
Level::Info => {
println!("\x1b[1;32m{}\x1b[0m", s);
}
Level::Debug => {
println!("\x1b[1;34m{}\x1b[0m", s);
}
Level::Trace => {
println!("\x1b[1;30m{}\x1b[0m", s);
}
}
}
+159
View File
@@ -0,0 +1,159 @@
import os
import sys
import subprocess
import toml
from building import *
cwd = GetCurrentDir()
RUSTC_FLAGS = {
"linker": "-C linker=ld.lld",
"panic": "-C panic=abort",
}
CARGO_CMD = {
"base": "cargo build",
"build_std": "-Z build-std=core,alloc,panic_abort",
"target_flag": "--target",
"target_arch": "%s",
"release_profile": "--release",
"debug_profile": "", # No additional flag for debug mode
}
tools_dir = os.path.join(cwd, '..', '..', 'tools')
sys.path.insert(0, tools_dir)
from build_support import detect_rust_target, ensure_rust_target_installed, clean_rust_build
def _has(sym: str) -> bool:
"""Helper function to check if a configuration symbol is enabled"""
try:
return bool(GetDepend([sym]))
except Exception:
return bool(GetDepend(sym))
def detect_target_for_dynamic_modules():
"""
Detect the appropriate Rust target for dynamic modules.
For dynamic modules, we need Linux targets instead of bare-metal targets.
"""
if detect_rust_target is not None:
try:
import rtconfig
bare_metal_target = detect_rust_target(_has, rtconfig)
if bare_metal_target:
if "riscv64" in bare_metal_target:
return "riscv64gc-unknown-linux-gnu"
elif "riscv32" in bare_metal_target:
return "riscv32gc-unknown-linux-gnu"
elif "aarch64" in bare_metal_target:
return "aarch64-unknown-linux-gnu"
elif "arm" in bare_metal_target or "thumb" in bare_metal_target:
return "armv7-unknown-linux-gnueabihf"
except Exception as e:
print(f"Error: Target detection failed: {e}")
raise RuntimeError(f"Failed to detect Rust target for dynamic modules: {e}")
print("Error: Unable to detect appropriate Rust target for dynamic modules")
raise RuntimeError("Target detection failed - no valid target found")
def build_rust_module(module_dir, build_root):
"""Build a Rust dynamic module with automatic target detection"""
cargo_toml_path = os.path.join(module_dir, 'Cargo.toml')
if not os.path.exists(cargo_toml_path):
return [], [], ""
with open(cargo_toml_path, 'r') as f:
cargo_config = toml.load(f)
module_name = cargo_config['package']['name']
# Detect target automatically based on the current configuration
target = detect_target_for_dynamic_modules()
print(f"Building Rust module '{module_name}' for target: {target}")
# Detect debug mode from rtconfig (same as main rust/SConscript)
debug = bool(_has('RUST_DEBUG_BUILD'))
build_mode = "debug" if debug else "release"
print(f"Building in {build_mode} mode")
# Use global RUSTFLAGS configuration for dynamic modules
rustflags = " ".join(RUSTC_FLAGS.values())
# Verify that the target is installed
if ensure_rust_target_installed is not None:
if not ensure_rust_target_installed(target):
print(f"Error: Rust target '{target}' is not installed")
print(f"Please install it with: rustup target add {target}")
return [], [], ""
else:
print(f"Warning: Cannot verify if target '{target}' is installed")
# Set up build environment
env = os.environ.copy()
env['RUSTFLAGS'] = rustflags
env['CARGO_TARGET_DIR'] = build_root
# Build the module with configurable parameters using dictionary configuration
build_cmd = [
CARGO_CMD["base"].split()[0], # 'cargo'
CARGO_CMD["base"].split()[1], # 'build'
CARGO_CMD["target_flag"], # '--target'
target, # actual target architecture
]
# Add profile flag based on debug mode
profile_flag = CARGO_CMD["debug_profile"] if debug else CARGO_CMD["release_profile"]
if profile_flag:
build_cmd.append(profile_flag)
# Add build-std flag if specified
if CARGO_CMD["build_std"]:
build_std_parts = CARGO_CMD["build_std"].split()
build_cmd.extend(build_std_parts)
try:
subprocess.run(build_cmd, cwd=module_dir, env=env, check=True, capture_output=True)
lib_dir = os.path.join(build_root, target, build_mode)
return [module_name], [lib_dir], ""
except subprocess.CalledProcessError:
return [], [], ""
# Check dependencies
if not _has('RT_RUST_BUILD_MODULES'):
Return([])
build_root = os.path.join(Dir('#').abspath, "build", "rust_modules")
# Handle clean operation
if GetOption('clean'):
if clean_rust_build is not None:
modules_build_dir = clean_rust_build(Dir('#').abspath, "rust_modules")
if os.path.exists(modules_build_dir):
print(f'Registering {modules_build_dir} for cleanup')
Clean('.', modules_build_dir)
else:
print('No rust_modules build artifacts to clean')
else:
print('Warning: clean_rust_build function not available')
else:
# Build all Rust modules in subdirectories
modules_built = []
for item in os.listdir(cwd):
item_path = os.path.join(cwd, item)
if os.path.isdir(item_path) and os.path.exists(os.path.join(item_path, 'Cargo.toml')):
result = build_rust_module(item_path, build_root)
if result[0]:
modules_built.extend(result[0])
if modules_built:
print(f"Successfully built {len(modules_built)} Rust dynamic module(s): {', '.join(modules_built)}")
group = DefineGroup(
'rust_modules',
[],
depend=['RT_RUST_BUILD_MODULES']
)
Return('group')
@@ -0,0 +1,37 @@
# Rust build artifacts (now in build/rust)
/target/
**/*.rs.bk
*.pdb
# Cargo lock file (optional - uncomment if you want to exclude it)
# Cargo.lock
# Build directories
/build/
*.o
*.a
*.so
*.dylib
*.dll
# IDE specific files
.idea/
.vscode/
*.swp
*.swo
*~
.DS_Store
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Backup files
*.bak
*.tmp
*.temp
# Log files
*.log
@@ -0,0 +1,12 @@
[workspace]
[package]
name = "examplelib"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
rt-rust = { path = "../../.." }
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author notes
* 2025-10-10 foxglove Basic library module template
*/
#![no_std]
/* Bring rt-rust's println! macro into scope */
use rt_rust::println;
use core::ffi::{c_char, CStr};
#[unsafe(no_mangle)]
pub extern "C" fn rust_mylib_println(s: *const c_char) {
if s.is_null() {
println!("");
} else {
let cs = unsafe {CStr::from_ptr(s)};
match cs.to_str() {
Ok(msg) => println!("{}", msg),
Err(_) => println!("[invalid UTF-8]"),
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn rust_mylib_add(a: usize, b: usize) -> usize {
a + b
}