From 39e8b76d86b12ce0fb042221e674cbcf1a4a90e6 Mon Sep 17 00:00:00 2001 From: Junkang Yuan Date: Fri, 11 Jul 2025 19:46:36 +0800 Subject: [PATCH] Migrate rust related PR from apache/nuttx-app --- cmake/nuttx_add_rust.cmake | 175 ++++++++++++++++++ examples/rust/.gitignore | 1 + examples/rust/CMakeLists.txt | 24 +++ examples/rust/Make.defs | 23 +++ examples/rust/Makefile | 25 +++ .../{hello_rust => rust/baremetal}/Kconfig | 3 +- .../{hello_rust => rust/baremetal}/Make.defs | 6 +- .../{hello_rust => rust/baremetal}/Makefile | 4 +- .../baremetal}/hello_rust_main.rs | 4 +- examples/rust/hello/.gitignore | 1 + examples/rust/hello/CMakeLists.txt | 31 ++++ examples/rust/hello/Cargo.toml | 23 +++ examples/rust/hello/Kconfig | 29 +++ examples/rust/hello/Make.defs | 26 +++ examples/rust/hello/Makefile | 36 ++++ examples/rust/hello/src/lib.rs | 81 ++++++++ examples/rust/slint/.gitignore | 1 + examples/rust/slint/CMakeLists.txt | 31 ++++ examples/rust/slint/Cargo.toml | 24 +++ examples/rust/slint/Kconfig | 29 +++ examples/rust/slint/Make.defs | 26 +++ examples/rust/slint/Makefile | 34 ++++ examples/rust/slint/build.rs | 28 +++ examples/rust/slint/src/lib.rs | 171 +++++++++++++++++ examples/rust/slint/ui/app.slint | 25 +++ tools/Rust.mk | 136 ++++++++++++++ 26 files changed, 992 insertions(+), 5 deletions(-) create mode 100644 cmake/nuttx_add_rust.cmake create mode 100644 examples/rust/.gitignore create mode 100644 examples/rust/CMakeLists.txt create mode 100644 examples/rust/Make.defs create mode 100644 examples/rust/Makefile rename examples/{hello_rust => rust/baremetal}/Kconfig (84%) rename examples/{hello_rust => rust/baremetal}/Make.defs (88%) rename examples/{hello_rust => rust/baremetal}/Makefile (94%) rename examples/{hello_rust => rust/baremetal}/hello_rust_main.rs (96%) create mode 100644 examples/rust/hello/.gitignore create mode 100644 examples/rust/hello/CMakeLists.txt create mode 100644 examples/rust/hello/Cargo.toml create mode 100644 examples/rust/hello/Kconfig create mode 100644 examples/rust/hello/Make.defs create mode 100644 examples/rust/hello/Makefile create mode 100644 examples/rust/hello/src/lib.rs create mode 100644 examples/rust/slint/.gitignore create mode 100644 examples/rust/slint/CMakeLists.txt create mode 100644 examples/rust/slint/Cargo.toml create mode 100644 examples/rust/slint/Kconfig create mode 100644 examples/rust/slint/Make.defs create mode 100644 examples/rust/slint/Makefile create mode 100644 examples/rust/slint/build.rs create mode 100644 examples/rust/slint/src/lib.rs create mode 100644 examples/rust/slint/ui/app.slint create mode 100644 tools/Rust.mk diff --git a/cmake/nuttx_add_rust.cmake b/cmake/nuttx_add_rust.cmake new file mode 100644 index 000000000..97ecdcec7 --- /dev/null +++ b/cmake/nuttx_add_rust.cmake @@ -0,0 +1,175 @@ +# ############################################################################## +# cmake/nuttx_add_rust.cmake +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +include(nuttx_parse_function_args) + +# ~~~ +# Convert architecture type to Rust NuttX target +# +# Supported architectures: +# - armv7a: armv7a-nuttx-eabi, armv7a-nuttx-eabihf +# - thumbv6m: thumbv6m-nuttx-eabi +# - thumbv7a: thumbv7a-nuttx-eabi, thumbv7a-nuttx-eabihf +# - thumbv7m: thumbv7m-nuttx-eabi +# - thumbv7em: thumbv7em-nuttx-eabihf +# - thumbv8m.main: thumbv8m.main-nuttx-eabi, thumbv8m.main-nuttx-eabihf +# - thumbv8m.base: thumbv8m.base-nuttx-eabi, thumbv8m.base-nuttx-eabihf +# - riscv32: riscv32imc/imac/imafc-unknown-nuttx-elf +# - riscv64: riscv64imac/imafdc-unknown-nuttx-elf +# - x86: i686-unknown-nuttx +# - x86_64: x86_64-unknown-nuttx +# +# Inputs: +# ARCHTYPE - Architecture type (e.g. thumbv7m, riscv32) +# ABITYPE - ABI type (e.g. eabi, eabihf) +# CPUTYPE - CPU type (e.g. cortex-m4, sifive-e20) +# +# Output: +# OUTPUT - Rust target triple (e.g. riscv32imac-unknown-nuttx-elf, +# thumbv7m-nuttx-eabi, thumbv7em-nuttx-eabihf) +# ~~~ + +function(nuttx_rust_target_triple ARCHTYPE ABITYPE CPUTYPE OUTPUT) + if(ARCHTYPE STREQUAL "x86_64") + set(TARGET_TRIPLE "x86_64-unknown-nuttx") + elseif(ARCHTYPE STREQUAL "x86") + set(TARGET_TRIPLE "i686-unknown-nuttx") + elseif(ARCHTYPE MATCHES "thumb") + if(ARCHTYPE MATCHES "thumbv8m") + # Extract just the base architecture type (thumbv8m.main or thumbv8m.base) + if(ARCHTYPE MATCHES "thumbv8m.main") + set(ARCH_BASE "thumbv8m.main") + elseif(ARCHTYPE MATCHES "thumbv8m.base") + set(ARCH_BASE "thumbv8m.base") + else() + # Otherwise determine if we should use thumbv8m.main or thumbv8m.base + # based on CPU type + if(CPUTYPE MATCHES "cortex-m23") + set(ARCH_BASE "thumbv8m.base") + else() + set(ARCH_BASE "thumbv8m.main") + endif() + endif() + set(TARGET_TRIPLE "${ARCH_BASE}-nuttx-${ABITYPE}") + else() + set(TARGET_TRIPLE "${ARCHTYPE}-nuttx-${ABITYPE}") + endif() + elseif(ARCHTYPE STREQUAL "riscv32") + if(CPUTYPE STREQUAL "sifive-e20") + set(TARGET_TRIPLE "riscv32imc-unknown-nuttx-elf") + elseif(CPUTYPE STREQUAL "sifive-e31") + set(TARGET_TRIPLE "riscv32imac-unknown-nuttx-elf") + elseif(CPUTYPE STREQUAL "sifive-e76") + set(TARGET_TRIPLE "riscv32imafc-unknown-nuttx-elf") + else() + set(TARGET_TRIPLE "riscv32imc-unknown-nuttx-elf") + endif() + elseif(ARCHTYPE STREQUAL "riscv64") + if(CPUTYPE STREQUAL "sifive-s51") + set(TARGET_TRIPLE "riscv64imac-unknown-nuttx-elf") + elseif(CPUTYPE STREQUAL "sifive-u54") + set(TARGET_TRIPLE "riscv64imafdc-unknown-nuttx-elf") + else() + set(TARGET_TRIPLE "riscv64imac-unknown-nuttx-elf") + endif() + endif() + set(${OUTPUT} + ${TARGET_TRIPLE} + PARENT_SCOPE) +endfunction() + +# ~~~ +# nuttx_add_rust +# +# Description: +# Build a Rust crate and add it as a static library to the NuttX build system +# +# Example: +# nuttx_add_rust( +# CRATE_NAME +# hello +# CRATE_PATH +# ${CMAKE_CURRENT_SOURCE_DIR}/hello +# ) +# ~~~ + +function(nuttx_add_rust) + + # parse arguments into variables + nuttx_parse_function_args( + FUNC + nuttx_add_rust + ONE_VALUE + CRATE_NAME + CRATE_PATH + REQUIRED + CRATE_NAME + CRATE_PATH + ARGN + ${ARGN}) + + # Determine build profile based on CONFIG_DEBUG_FULLOPT + if(CONFIG_DEBUG_FULLOPT) + set(RUST_PROFILE "release") + set(RUST_DEBUG_FLAGS "-Zbuild-std-features=panic_immediate_abort") + else() + set(RUST_PROFILE "debug") + set(RUST_DEBUG_FLAGS "") + endif() + + # Get the Rust target triple + nuttx_rust_target_triple(${LLVM_ARCHTYPE} ${LLVM_ABITYPE} ${LLVM_CPUTYPE} + RUST_TARGET) + + # Set up build directory in current binary dir + set(RUST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CRATE_NAME}) + set(RUST_LIB_PATH + ${RUST_BUILD_DIR}/${RUST_TARGET}/${RUST_PROFILE}/lib${CRATE_NAME}.a) + + # Create build directory + file(MAKE_DIRECTORY ${RUST_BUILD_DIR}) + + # Add a custom command to build the Rust crate + add_custom_command( + OUTPUT ${RUST_LIB_PATH} + COMMAND + ${CMAKE_COMMAND} -E env + NUTTX_INCLUDE_DIR=${PROJECT_SOURCE_DIR}/include:${CMAKE_BINARY_DIR}/include:${CMAKE_BINARY_DIR}/include/arch + cargo build --${RUST_PROFILE} -Zbuild-std=std,panic_abort + ${RUST_DEBUG_FLAGS} --manifest-path ${CRATE_PATH}/Cargo.toml --target + ${RUST_TARGET} --target-dir ${RUST_BUILD_DIR} + COMMENT "Building Rust crate ${CRATE_NAME}" + VERBATIM) + + # Add a custom target that depends on the built library + add_custom_target(${CRATE_NAME}_build ALL DEPENDS ${RUST_LIB_PATH}) + + # Add imported library target + add_library(${CRATE_NAME} STATIC IMPORTED GLOBAL) + set_target_properties(${CRATE_NAME} PROPERTIES IMPORTED_LOCATION + ${RUST_LIB_PATH}) + + # Add the Rust library to NuttX build + nuttx_add_extra_library(${RUST_LIB_PATH}) + + # Ensure the Rust library is built before linking + add_dependencies(${CRATE_NAME} ${CRATE_NAME}_build) + +endfunction() diff --git a/examples/rust/.gitignore b/examples/rust/.gitignore new file mode 100644 index 000000000..9e1d2593e --- /dev/null +++ b/examples/rust/.gitignore @@ -0,0 +1 @@ +/Kconfig diff --git a/examples/rust/CMakeLists.txt b/examples/rust/CMakeLists.txt new file mode 100644 index 000000000..a4fe835a8 --- /dev/null +++ b/examples/rust/CMakeLists.txt @@ -0,0 +1,24 @@ +# ############################################################################## +# apps/examples/rust/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +nuttx_add_subdirectory() +nuttx_generate_kconfig(MENUDESC "Rust Examples") diff --git a/examples/rust/Make.defs b/examples/rust/Make.defs new file mode 100644 index 000000000..e7fdfe51d --- /dev/null +++ b/examples/rust/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/rust/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(wildcard $(APPDIR)/examples/rust/*/Make.defs) diff --git a/examples/rust/Makefile b/examples/rust/Makefile new file mode 100644 index 000000000..5d75f316e --- /dev/null +++ b/examples/rust/Makefile @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/rust/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +MENUDESC = "Rust Examples" + +include $(APPDIR)/Directory.mk diff --git a/examples/hello_rust/Kconfig b/examples/rust/baremetal/Kconfig similarity index 84% rename from examples/hello_rust/Kconfig rename to examples/rust/baremetal/Kconfig index 92e3b1f34..a1b391bee 100644 --- a/examples/hello_rust/Kconfig +++ b/examples/rust/baremetal/Kconfig @@ -7,7 +7,8 @@ config EXAMPLES_HELLO_RUST tristate "\"Hello, Rust!\" example" default n ---help--- - Enable the \"Hello, Rust!\" example + Enable the \"Hello, Rust!\" example to show how to build + and run a baremetal Rust application with rustc. if EXAMPLES_HELLO_RUST diff --git a/examples/hello_rust/Make.defs b/examples/rust/baremetal/Make.defs similarity index 88% rename from examples/hello_rust/Make.defs rename to examples/rust/baremetal/Make.defs index 245662e68..9ea1b9f9f 100644 --- a/examples/hello_rust/Make.defs +++ b/examples/rust/baremetal/Make.defs @@ -1,5 +1,7 @@ ############################################################################ -# apps/examples/hello_rust/Make.defs +# apps/examples/rust/baremetal/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with @@ -19,5 +21,5 @@ ############################################################################ ifneq ($(CONFIG_EXAMPLES_HELLO_RUST),) -CONFIGURED_APPS += $(APPDIR)/examples/hello_rust +CONFIGURED_APPS += $(APPDIR)/examples/rust/baremetal endif diff --git a/examples/hello_rust/Makefile b/examples/rust/baremetal/Makefile similarity index 94% rename from examples/hello_rust/Makefile rename to examples/rust/baremetal/Makefile index 8415ff402..55e98ce7b 100644 --- a/examples/hello_rust/Makefile +++ b/examples/rust/baremetal/Makefile @@ -1,5 +1,7 @@ ############################################################################ -# apps/examples/hello_rust/Makefile +# apps/examples/rust/baremetal/Makefile +# +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/examples/hello_rust/hello_rust_main.rs b/examples/rust/baremetal/hello_rust_main.rs similarity index 96% rename from examples/hello_rust/hello_rust_main.rs rename to examples/rust/baremetal/hello_rust_main.rs index 3a276e94d..3d7a3eceb 100644 --- a/examples/hello_rust/hello_rust_main.rs +++ b/examples/rust/baremetal/hello_rust_main.rs @@ -1,5 +1,7 @@ /**************************************************************************** - * apps/examples/hello_rust/hello_rust_main.rs + * apps/examples/rust/baremetal/hello_rust_main.rs + * + * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with diff --git a/examples/rust/hello/.gitignore b/examples/rust/hello/.gitignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/examples/rust/hello/.gitignore @@ -0,0 +1 @@ +target diff --git a/examples/rust/hello/CMakeLists.txt b/examples/rust/hello/CMakeLists.txt new file mode 100644 index 000000000..c0daea419 --- /dev/null +++ b/examples/rust/hello/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# apps/examples/rust/hello/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_HELLO_RUST_CARGO) + + # Build the Rust crate using nuttx_add_rust + nuttx_add_rust(CRATE_NAME hello CRATE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + + nuttx_add_application( + NAME ${CONFIG_EXAMPLES_HELLO_RUST_CARGO_PROGNAME} STACKSIZE + ${CONFIG_EXAMPLES_HELLO_STACKSIZE} PRIORITY + ${CONFIG_EXAMPLES_HELLO_PRIORITY}) + +endif() # CONFIG_EXAMPLES_HELLO_RUST_CARGO diff --git a/examples/rust/hello/Cargo.toml b/examples/rust/hello/Cargo.toml new file mode 100644 index 000000000..f4c3c930f --- /dev/null +++ b/examples/rust/hello/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "hello" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" +opt-level = 'z' + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 'z' + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +tokio = { version = "1", features = ["time", "macros", "rt"] } diff --git a/examples/rust/hello/Kconfig b/examples/rust/hello/Kconfig new file mode 100644 index 000000000..47954ed0f --- /dev/null +++ b/examples/rust/hello/Kconfig @@ -0,0 +1,29 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_HELLO_RUST_CARGO + tristate "\"Hello, Rust!\" example with Cargo" + default n + ---help--- + Enable the \"Hello, Rust!\" example using Cargo to build. + +if EXAMPLES_HELLO_RUST_CARGO + +config EXAMPLES_HELLO_RUST_CARGO_PROGNAME + string "Program name" + default "hello_rust_cargo" + ---help--- + This is the name of the program that will be used when the + program is installed. + +config EXAMPLES_HELLO_RUST_CARGO_PRIORITY + int "Hello Rust task priority" + default 100 + +config EXAMPLES_HELLO_RUST_CARGO_STACKSIZE + int "Hello Rust stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/rust/hello/Make.defs b/examples/rust/hello/Make.defs new file mode 100644 index 000000000..829b1c507 --- /dev/null +++ b/examples/rust/hello/Make.defs @@ -0,0 +1,26 @@ +############################################################################ +# apps/examples/rust/hello/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/tools/Rust.mk + +ifneq ($(CONFIG_EXAMPLES_HELLO_RUST_CARGO),) +CONFIGURED_APPS += $(APPDIR)/examples/rust/hello +EXTRA_LIBS += $(call RUST_GET_BINDIR,hello,$(APPDIR)/examples/rust) +endif diff --git a/examples/rust/hello/Makefile b/examples/rust/hello/Makefile new file mode 100644 index 000000000..2c638eb3c --- /dev/null +++ b/examples/rust/hello/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/examples/rust/hello/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Hello, Rust! built-in application info + +PROGNAME = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO) + +context:: + $(call RUST_CARGO_BUILD,hello,$(APPDIR)/examples/rust) + +clean:: + $(call RUST_CARGO_CLEAN,hello,$(APPDIR)/examples/rust) + +include $(APPDIR)/Application.mk diff --git a/examples/rust/hello/src/lib.rs b/examples/rust/hello/src/lib.rs new file mode 100644 index 000000000..b00c78d6e --- /dev/null +++ b/examples/rust/hello/src/lib.rs @@ -0,0 +1,81 @@ +extern crate serde; +extern crate serde_json; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +struct Person { + name: String, + age: u8, +} + +// Function hello_rust_cargo without manglng +#[no_mangle] +pub extern "C" fn hello_rust_cargo_main() { + // Print hello world to stdout + + let john = Person { + name: "John".to_string(), + age: 30, + }; + + let json_str = serde_json::to_string(&john).unwrap(); + println!("{}", json_str); + + let jane = Person { + name: "Jane".to_string(), + age: 25, + }; + + let json_str_jane = serde_json::to_string(&jane).unwrap(); + println!("{}", json_str_jane); + + let json_data = r#" + { + "name": "Alice", + "age": 28 + }"#; + + let alice: Person = serde_json::from_str(json_data).unwrap(); + println!("Deserialized: {} is {} years old", alice.name, alice.age); + + let pretty_json_str = serde_json::to_string_pretty(&alice).unwrap(); + println!("Pretty JSON:\n{}", pretty_json_str); + + std::thread::spawn(|| loop { + println!("Hello world from thread! {:?}", std::thread::current().id()); + std::thread::sleep(std::time::Duration::from_secs(3)) + }); + + tokio::runtime::Builder::new_current_thread() + // .enable_all() + .enable_time() + .build() + .unwrap() + .block_on(async { + tokio::join!( + async { + loop { + println!( + "Hello world from tokio 1! {:?}", + std::thread::current().id() + ); + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + } + }, + async { + loop { + println!( + "Hello world from tokio 2! {:?}", + std::thread::current().id() + ); + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + } + } + ); + }); + + loop { + // Do nothing + } +} diff --git a/examples/rust/slint/.gitignore b/examples/rust/slint/.gitignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/examples/rust/slint/.gitignore @@ -0,0 +1 @@ +target diff --git a/examples/rust/slint/CMakeLists.txt b/examples/rust/slint/CMakeLists.txt new file mode 100644 index 000000000..6ed088aea --- /dev/null +++ b/examples/rust/slint/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# apps/examples/rust/slint/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_RUST_SLINT) + + # Build the Rust crate using nuttx_add_rust + nuttx_add_rust(CRATE_NAME slint CRATE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + + nuttx_add_application( + NAME ${CONFIG_EXAMPLES_RUST_SLINT_PROGNAME} STACKSIZE + ${CONFIG_EXAMPLES_RUST_SLINT_STACKSIZE} PRIORITY + ${CONFIG_EXAMPLES_RUST_SLINT_PRIORITY}) + +endif() # CONFIG_EXAMPLES_RUST_SLINT diff --git a/examples/rust/slint/Cargo.toml b/examples/rust/slint/Cargo.toml new file mode 100644 index 000000000..af585ceb1 --- /dev/null +++ b/examples/rust/slint/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "slint" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 'z' + +[dependencies] +libc = "0.2" +slint = { version = "1.9", default-features = false, features = ["compat-1-2", "renderer-software", "libm", "unsafe-single-threaded"] } +nuttx = { git = "https://github.com/no1wudi/nuttx-rs.git", branch = "main" } + +[build-dependencies] +slint-build = { version = "1.9" } diff --git a/examples/rust/slint/Kconfig b/examples/rust/slint/Kconfig new file mode 100644 index 000000000..babc19d04 --- /dev/null +++ b/examples/rust/slint/Kconfig @@ -0,0 +1,29 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_RUST_SLINT + tristate "Slint Basic Example" + default n + ---help--- + Enable the basic Slint example. + +if EXAMPLES_RUST_SLINT + +config EXAMPLES_RUST_SLINT_PROGNAME + string "Program name" + default "slint" + ---help--- + This is the name of the program that will be used when the + program is installed. + +config EXAMPLES_RUST_SLINT_PRIORITY + int "Task priority" + default 100 + +config EXAMPLES_RUST_SLINT_STACKSIZE + int "Stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/rust/slint/Make.defs b/examples/rust/slint/Make.defs new file mode 100644 index 000000000..9259913fc --- /dev/null +++ b/examples/rust/slint/Make.defs @@ -0,0 +1,26 @@ +############################################################################ +# apps/examples/rust/slint/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/tools/Rust.mk + +ifneq ($(CONFIG_EXAMPLES_RUST_SLINT),) +CONFIGURED_APPS += $(APPDIR)/examples/rust/slint +EXTRA_LIBS += $(call RUST_GET_BINDIR,slint,$(APPDIR)/examples/rust) +endif diff --git a/examples/rust/slint/Makefile b/examples/rust/slint/Makefile new file mode 100644 index 000000000..34b444e9d --- /dev/null +++ b/examples/rust/slint/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/examples/rust/slint/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_RUST_SLINT_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_RUST_SLINT_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_RUST_SLINT_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_RUST_SLINT) + +context:: + $(call RUST_CARGO_BUILD,slint,$(APPDIR)/examples/rust) + +clean:: + $(call RUST_CARGO_CLEAN,slint,$(APPDIR)/examples/rust) + +include $(APPDIR)/Application.mk diff --git a/examples/rust/slint/build.rs b/examples/rust/slint/build.rs new file mode 100644 index 000000000..6142de92a --- /dev/null +++ b/examples/rust/slint/build.rs @@ -0,0 +1,28 @@ +// ############################################################################## +// examples/rust/slint/build.rs +// +// Licensed to the Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. The ASF licenses this +// file to you under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// +// ############################################################################## + +fn main() { + slint_build::compile_with_config( + "ui/app.slint", + slint_build::CompilerConfiguration::new() + .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer), + ) + .unwrap(); +} diff --git a/examples/rust/slint/src/lib.rs b/examples/rust/slint/src/lib.rs new file mode 100644 index 000000000..7f81ce29c --- /dev/null +++ b/examples/rust/slint/src/lib.rs @@ -0,0 +1,171 @@ +// ############################################################################## +// examples/rust/slint/src/lib.rs +// +// Licensed to the Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with this work for +// additional information regarding copyright ownership. The ASF licenses this +// file to you under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// +// ############################################################################## + +use std::rc::Rc; +use std::{cell::RefCell, ffi::CStr}; + +use slint::platform::software_renderer::{LineBufferProvider, Rgb565Pixel}; + +use nuttx::input::touchscreen::*; +use nuttx::video::fb::*; +use slint::platform::WindowEvent; + +slint::include_modules!(); + +struct NuttXPlatform { + window: Rc, +} + +impl slint::platform::Platform for NuttXPlatform { + fn create_window_adapter( + &self, + ) -> Result, slint::PlatformError> { + Ok(self.window.clone()) + } + fn duration_since_start(&self) -> std::time::Duration { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + } +} + +fn create_slint_app() -> AppWindow { + AppWindow::new().expect("Failed to load UI") +} + +#[derive(Debug, Clone)] +struct NuttXRenderer { + frame_buffer: *mut u8, + line_buffer: Rc>>, + stride: usize, + lines: usize, +} + +impl LineBufferProvider for NuttXRenderer { + type TargetPixel = Rgb565Pixel; + fn process_line( + &mut self, + line: usize, + range: std::ops::Range, + render_fn: impl FnOnce(&mut [Self::TargetPixel]), + ) { + let fb_size = self.stride * self.lines; + let frame = unsafe { + std::slice::from_raw_parts_mut(self.frame_buffer as *mut Rgb565Pixel, fb_size) + }; + let mut buffer = self.line_buffer.borrow_mut(); + render_fn(&mut buffer[range.clone()]); + let offset = line * self.stride; + frame[offset..offset + range.end - range.start].copy_from_slice(&buffer[range]); + } +} + +#[no_mangle] +pub extern "C" fn slint_main() { + // Open the framebuffer device and get its information + let fbdev = match FrameBuffer::new(CStr::from_bytes_with_nul(b"/dev/fb0\0").unwrap()) { + Ok(fb) => fb, + Err(_) => { + println!("Failed to open framebuffer device"); + return; + } + }; + + let planeinfo = fbdev.get_plane_info().unwrap(); + let videoinfo = fbdev.get_video_info().unwrap(); + + println!("{:?}", planeinfo); + println!("{:?}", videoinfo); + + if videoinfo.fmt != FB_FMT_RGB16_565 as u8 { + println!("Unsupported pixel format, only RGB565 is supported for now"); + return; + } + + // Open the touchscreen device + let mut tsdev = match TouchScreen::open(CStr::from_bytes_with_nul(b"/dev/input0\0").unwrap()) { + Ok(ts) => ts, + Err(_) => { + println!("Failed to open touchscreen device"); + return; + } + }; + + // Setup the Slint backend + let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(Default::default()); + window.set_size(slint::PhysicalSize::new( + videoinfo.xres.into(), + videoinfo.yres.into(), + )); + + // Set the platform + slint::platform::set_platform(Box::new(NuttXPlatform { + window: window.clone(), + })) + .unwrap(); + + // Configure the UI + let _ui = create_slint_app(); + + // Create the renderer for NuttX + let nxrender = NuttXRenderer { + frame_buffer: planeinfo.fbmem as *mut u8, + line_buffer: Rc::new(RefCell::new(vec![ + Rgb565Pixel::default(); + videoinfo.xres as usize + ])), + stride: videoinfo.xres.into(), + lines: videoinfo.yres.into(), + }; + + let button = slint::platform::PointerEventButton::Left; + let mut last_touch_down = false; + let mut last_position = Default::default(); + + loop { + slint::platform::update_timers_and_animations(); + window.draw_if_needed(|renderer| { + renderer.render_by_line(nxrender.clone()); + }); + + if let Ok(sample) = tsdev.read_sample() { + if sample.npoints > 0 { + let point = sample.point[0]; + if point.is_pos_valid() { + let position = slint::PhysicalPosition::new(point.x.into(), point.y.into()) + .to_logical(window.scale_factor()); + last_position = position; + if point.is_touch_down() || point.is_touch_move() { + last_touch_down = true; + window.dispatch_event(WindowEvent::PointerPressed { position, button }); + } + } + } + } else { + if last_touch_down { + window.dispatch_event(WindowEvent::PointerReleased { + position: last_position, + button, + }); + last_touch_down = false; + } + } + } +} diff --git a/examples/rust/slint/ui/app.slint b/examples/rust/slint/ui/app.slint new file mode 100644 index 000000000..29c14af06 --- /dev/null +++ b/examples/rust/slint/ui/app.slint @@ -0,0 +1,25 @@ +import { Button, VerticalBox, HorizontalBox , AboutSlint } from "std-widgets.slint"; + +export component AppWindow inherits Window { + property counter: 0; + + timer := Timer { + interval: 1s; + running: true; + triggered() => { + counter += 1; + } + } + + VerticalBox { + Text { + horizontal-alignment: center; + vertical-alignment: center; + text: "Times since start on NuttX: \n\{root.counter}"; + font-size: 24px; + } + + AboutSlint { } + + } +} diff --git a/tools/Rust.mk b/tools/Rust.mk new file mode 100644 index 000000000..9a77ae57b --- /dev/null +++ b/tools/Rust.mk @@ -0,0 +1,136 @@ +############################################################################ +# apps/tools/Rust.mk +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +# Generate Rust target triple based on LLVM architecture configuration +# +# Uses the following LLVM variables directly: +# - LLVM_ARCHTYPE: Architecture type (e.g. thumbv7m, riscv32) +# - LLVM_ABITYPE: ABI type (e.g. eabi, eabihf) +# - LLVM_CPUTYPE: CPU type (e.g. cortex-m23, sifive-e20) +# +# Supported architectures and their target triples: +# - x86: i686-unknown-nuttx +# - x86_64: x86_64-unknown-nuttx +# - armv7a: armv7a-nuttx-eabi, armv7a-nuttx-eabihf +# - thumbv6m: thumbv6m-nuttx-eabi +# - thumbv7a: thumbv7a-nuttx-eabi, thumbv7a-nuttx-eabihf +# - thumbv7m: thumbv7m-nuttx-eabi +# - thumbv7em: thumbv7em-nuttx-eabihf +# - thumbv8m.main: thumbv8m.main-nuttx-eabi, thumbv8m.main-nuttx-eabihf +# - thumbv8m.base: thumbv8m.base-nuttx-eabi, thumbv8m.base-nuttx-eabihf +# - riscv32: riscv32imc/imac/imafc-unknown-nuttx-elf +# - riscv64: riscv64imac/imafdc-unknown-nuttx-elf +# +# Usage: $(call RUST_TARGET_TRIPLE) +# +# Output: +# Rust target triple (e.g. riscv32imac-unknown-nuttx-elf, +# thumbv7m-nuttx-eabi, thumbv7em-nuttx-eabihf) + +define RUST_TARGET_TRIPLE +$(or \ + $(and $(filter x86_64,$(LLVM_ARCHTYPE)), \ + x86_64-unknown-nuttx \ + ), \ + $(and $(filter x86,$(LLVM_ARCHTYPE)), \ + i686-unknown-nuttx \ + ), \ + $(and $(filter thumb%,$(LLVM_ARCHTYPE)), \ + $(if $(filter thumbv8m%,$(LLVM_ARCHTYPE)), \ + $(if $(filter cortex-m23,$(LLVM_CPUTYPE)),thumbv8m.base,thumbv8m.main)-nuttx-$(LLVM_ABITYPE), \ + $(LLVM_ARCHTYPE)-nuttx-$(LLVM_ABITYPE) \ + ) \ + ), \ + $(and $(filter riscv32,$(LLVM_ARCHTYPE)), \ + riscv32$(or \ + $(and $(filter sifive-e20,$(LLVM_CPUTYPE)),imc), \ + $(and $(filter sifive-e31,$(LLVM_CPUTYPE)),imac), \ + $(and $(filter sifive-e76,$(LLVM_CPUTYPE)),imafc), \ + imc \ + )-unknown-nuttx-elf \ + ), \ + $(and $(filter riscv64,$(LLVM_ARCHTYPE)), \ + riscv64$(or \ + $(and $(filter sifive-s51,$(LLVM_CPUTYPE)),imac), \ + $(and $(filter sifive-u54,$(LLVM_CPUTYPE)),imafdc), \ + imac \ + )-unknown-nuttx-elf \ + ) \ +) +endef + +# Build Rust project using cargo +# +# Usage: $(call RUST_CARGO_BUILD,cratename,prefix) +# +# Inputs: +# cratename - Name of the Rust crate (e.g. hello) +# prefix - Path prefix to the crate (e.g. path/to/project) +# +# Output: +# None, builds the Rust project + +ifeq ($(CONFIG_DEBUG_FULLOPT),y) +define RUST_CARGO_BUILD + NUTTX_INCLUDE_DIR=$(TOPDIR)/include:$(TOPDIR)/include/arch \ + cargo build --release -Zbuild-std=std,panic_abort \ + -Zbuild-std-features=panic_immediate_abort \ + --manifest-path $(2)/$(1)/Cargo.toml \ + --target $(call RUST_TARGET_TRIPLE) +endef +else +define RUST_CARGO_BUILD + @echo "Building Rust code with cargo..." + NUTTX_INCLUDE_DIR=$(TOPDIR)/include:$(TOPDIR)/include/arch \ + cargo build -Zbuild-std=std,panic_abort \ + --manifest-path $(2)/$(1)/Cargo.toml \ + --target $(call RUST_TARGET_TRIPLE) +endef +endif + +# Clean Rust project using cargo +# +# Usage: $(call RUST_CARGO_CLEAN,cratename,prefix) +# +# Inputs: +# cratename - Name of the Rust crate (e.g. hello) +# prefix - Path prefix to the crate (e.g. path/to/project) +# +# Output: +# None, cleans the Rust project + +define RUST_CARGO_CLEAN + cargo clean --manifest-path $(2)/$(1)/Cargo.toml +endef + +# Get Rust binary path for given crate and path prefix +# +# Usage: $(call RUST_GET_BINDIR,cratename,prefix) +# +# Inputs: +# cratename - Name of the Rust crate (e.g. hello) +# prefix - Path prefix to the crate (e.g. path/to/project) +# +# Output: +# Path to the Rust binary (e.g. path/to/project/target/riscv32imac-unknown-nuttx-elf/release/libhello.a) + +define RUST_GET_BINDIR +$(2)/$(1)/target/$(strip $(call RUST_TARGET_TRIPLE))/$(if $(CONFIG_DEBUG_FULLOPT),release,debug)/lib$(1).a +endef