diff options
| author | zhang san <[email protected]> | 2025-12-08 18:34:25 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-08 18:34:25 +0800 |
| commit | 69980f8b9d29b38dbd4ddf6373b4b45f3a138409 (patch) | |
| tree | 21dd9a8ce80e3d30192e9ffa56ea39a199bae309 /components/rust/examples | |
| parent | cd1d47b87c5d47e131730e5506eb3ad77d357653 (diff) | |
Adding Rust Language Support for RT-Thread #10910
Diffstat (limited to 'components/rust/examples')
27 files changed, 1118 insertions, 0 deletions
diff --git a/components/rust/examples/Kconfig b/components/rust/examples/Kconfig new file mode 100644 index 0000000000..cd18690868 --- /dev/null +++ b/components/rust/examples/Kconfig @@ -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
\ No newline at end of file diff --git a/components/rust/examples/SConscript b/components/rust/examples/SConscript new file mode 100644 index 0000000000..1ac5417eff --- /dev/null +++ b/components/rust/examples/SConscript @@ -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')
\ No newline at end of file diff --git a/components/rust/examples/application/SConscript b/components/rust/examples/application/SConscript new file mode 100644 index 0000000000..c6c4de3db8 --- /dev/null +++ b/components/rust/examples/application/SConscript @@ -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')
\ No newline at end of file diff --git a/components/rust/examples/application/fs/Cargo.toml b/components/rust/examples/application/fs/Cargo.toml new file mode 100644 index 0000000000..2556b5f0a4 --- /dev/null +++ b/components/rust/examples/application/fs/Cargo.toml @@ -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"}
\ No newline at end of file diff --git a/components/rust/examples/application/fs/src/lib.rs b/components/rust/examples/application/fs/src/lib.rs new file mode 100644 index 0000000000..53cf8561d1 --- /dev/null +++ b/components/rust/examples/application/fs/src/lib.rs @@ -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"); +} diff --git a/components/rust/examples/application/loadlib/Cargo.toml b/components/rust/examples/application/loadlib/Cargo.toml new file mode 100644 index 0000000000..1e1065f91a --- /dev/null +++ b/components/rust/examples/application/loadlib/Cargo.toml @@ -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" }
\ No newline at end of file diff --git a/components/rust/examples/application/loadlib/src/lib.rs b/components/rust/examples/application/loadlib/src/lib.rs new file mode 100644 index 0000000000..d60175f5fa --- /dev/null +++ b/components/rust/examples/application/loadlib/src/lib.rs @@ -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); +} diff --git a/components/rust/examples/application/mutex/Cargo.toml b/components/rust/examples/application/mutex/Cargo.toml new file mode 100644 index 0000000000..188ad1ba1d --- /dev/null +++ b/components/rust/examples/application/mutex/Cargo.toml @@ -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" } diff --git a/components/rust/examples/application/mutex/src/lib.rs b/components/rust/examples/application/mutex/src/lib.rs new file mode 100644 index 0000000000..3160ab6b76 --- /dev/null +++ b/components/rust/examples/application/mutex/src/lib.rs @@ -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()); +} diff --git a/components/rust/examples/application/param/Cargo.toml b/components/rust/examples/application/param/Cargo.toml new file mode 100644 index 0000000000..aa3daae8c9 --- /dev/null +++ b/components/rust/examples/application/param/Cargo.toml @@ -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" } + diff --git a/components/rust/examples/application/param/src/lib.rs b/components/rust/examples/application/param/src/lib.rs new file mode 100644 index 0000000000..209daa14f1 --- /dev/null +++ b/components/rust/examples/application/param/src/lib.rs @@ -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)) + } +} diff --git a/components/rust/examples/application/queue/Cargo.toml b/components/rust/examples/application/queue/Cargo.toml new file mode 100644 index 0000000000..4ea390d183 --- /dev/null +++ b/components/rust/examples/application/queue/Cargo.toml @@ -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" } diff --git a/components/rust/examples/application/queue/src/lib.rs b/components/rust/examples/application/queue/src/lib.rs new file mode 100644 index 0000000000..fda7c7c0f3 --- /dev/null +++ b/components/rust/examples/application/queue/src/lib.rs @@ -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); + } + }); +} + diff --git a/components/rust/examples/application/semaphore/Cargo.toml b/components/rust/examples/application/semaphore/Cargo.toml new file mode 100644 index 0000000000..16aa670145 --- /dev/null +++ b/components/rust/examples/application/semaphore/Cargo.toml @@ -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" } diff --git a/components/rust/examples/application/semaphore/src/lib.rs b/components/rust/examples/application/semaphore/src/lib.rs new file mode 100644 index 0000000000..986acd1957 --- /dev/null +++ b/components/rust/examples/application/semaphore/src/lib.rs @@ -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!") + } + }); +} + diff --git a/components/rust/examples/application/thread/Cargo.toml b/components/rust/examples/application/thread/Cargo.toml new file mode 100644 index 0000000000..a85e275c77 --- /dev/null +++ b/components/rust/examples/application/thread/Cargo.toml @@ -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" } diff --git a/components/rust/examples/application/thread/src/lib.rs b/components/rust/examples/application/thread/src/lib.rs new file mode 100644 index 0000000000..7f2cdf04c3 --- /dev/null +++ b/components/rust/examples/application/thread/src/lib.rs @@ -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)); + } + }); +} diff --git a/components/rust/examples/component/SConscript b/components/rust/examples/component/SConscript new file mode 100644 index 0000000000..40d11c0446 --- /dev/null +++ b/components/rust/examples/component/SConscript @@ -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')
\ No newline at end of file diff --git a/components/rust/examples/component/component_registry/Cargo.toml b/components/rust/examples/component/component_registry/Cargo.toml new file mode 100644 index 0000000000..64ec88c2d4 --- /dev/null +++ b/components/rust/examples/component/component_registry/Cargo.toml @@ -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"]
\ No newline at end of file diff --git a/components/rust/examples/component/component_registry/src/lib.rs b/components/rust/examples/component/component_registry/src/lib.rs new file mode 100644 index 0000000000..beff2df698 --- /dev/null +++ b/components/rust/examples/component/component_registry/src/lib.rs @@ -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 */ +} diff --git a/components/rust/examples/component/log/Cargo.toml b/components/rust/examples/component/log/Cargo.toml new file mode 100644 index 0000000000..901772b771 --- /dev/null +++ b/components/rust/examples/component/log/Cargo.toml @@ -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" } diff --git a/components/rust/examples/component/log/src/lib.rs b/components/rust/examples/component/log/src/lib.rs new file mode 100644 index 0000000000..ff7e75c32f --- /dev/null +++ b/components/rust/examples/component/log/src/lib.rs @@ -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 */ +} diff --git a/components/rust/examples/component/log/src/logging.rs b/components/rust/examples/component/log/src/logging.rs new file mode 100644 index 0000000000..ccbf8ee1e2 --- /dev/null +++ b/components/rust/examples/component/log/src/logging.rs @@ -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); + } + } +} diff --git a/components/rust/examples/modules/SConscript b/components/rust/examples/modules/SConscript new file mode 100644 index 0000000000..98e57d1fd9 --- /dev/null +++ b/components/rust/examples/modules/SConscript @@ -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')
\ No newline at end of file diff --git a/components/rust/examples/modules/example_lib/.gitignore b/components/rust/examples/modules/example_lib/.gitignore new file mode 100644 index 0000000000..5f4fb2595d --- /dev/null +++ b/components/rust/examples/modules/example_lib/.gitignore @@ -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
\ No newline at end of file diff --git a/components/rust/examples/modules/example_lib/Cargo.toml b/components/rust/examples/modules/example_lib/Cargo.toml new file mode 100644 index 0000000000..e44aae2848 --- /dev/null +++ b/components/rust/examples/modules/example_lib/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] + +[package] +name = "examplelib" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +rt-rust = { path = "../../.." }
\ No newline at end of file diff --git a/components/rust/examples/modules/example_lib/src/lib.rs b/components/rust/examples/modules/example_lib/src/lib.rs new file mode 100644 index 0000000000..5f81c1553f --- /dev/null +++ b/components/rust/examples/modules/example_lib/src/lib.rs @@ -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 +}
\ No newline at end of file |
