diff --git a/CMakeLists.txt b/CMakeLists.txt index e36d9f68f6c..225c354d16c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ if(CONFIG_APPS_DIR) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) include(nuttx_add_luamod) include(nuttx_add_wamrmod) + include(nuttx_add_rust) nuttx_add_library(apps) if(NOT EXISTS {NUTTX_APPS_BINDIR}/dummy.c) file(TOUCH ${NUTTX_APPS_BINDIR}/dummy.c) diff --git a/cmake/nuttx_add_rust.cmake b/cmake/nuttx_add_rust.cmake new file mode 100644 index 00000000000..c071f2b2cd8 --- /dev/null +++ b/cmake/nuttx_add_rust.cmake @@ -0,0 +1,166 @@ +# ############################################################################## +# 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 +# +# 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 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") + else() + set(RUST_PROFILE "debug") + endif() + + # Get the Rust target triple + message(STATUS "LLVM_ARCHTYPE: ${LLVM_ARCHTYPE}") + 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 + cargo build --${RUST_PROFILE} -Zbuild-std=std,panic_abort --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/hello_rust_cargo/CMakeLists.txt b/examples/hello_rust_cargo/CMakeLists.txt new file mode 100644 index 00000000000..ee4b8353d0c --- /dev/null +++ b/examples/hello_rust_cargo/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# apps/examples/hello_rust_cargo/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}/hello) + + 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/hello_rust_cargo/Kconfig b/examples/hello_rust_cargo/Kconfig new file mode 100644 index 00000000000..47954ed0f41 --- /dev/null +++ b/examples/hello_rust_cargo/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/hello_rust_cargo/Make.defs b/examples/hello_rust_cargo/Make.defs new file mode 100644 index 00000000000..c4146519eda --- /dev/null +++ b/examples/hello_rust_cargo/Make.defs @@ -0,0 +1,26 @@ +############################################################################ +# apps/examples/hello_rust_cargo/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/hello_rust_cargo +EXTRA_LIBS += $(call RUST_GET_BINDIR,hello,$(APPDIR)/examples/hello_rust_cargo) +endif diff --git a/examples/hello_rust_cargo/Makefile b/examples/hello_rust_cargo/Makefile new file mode 100644 index 00000000000..a9df2afc83c --- /dev/null +++ b/examples/hello_rust_cargo/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/examples/hello_rust/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/hello_rust_cargo) + +clean:: + $(call RUST_CARGO_CLEAN,hello,$(APPDIR)/examples/hello_rust_cargo) + +include $(APPDIR)/Application.mk diff --git a/examples/hello_rust_cargo/hello/.gitignore b/examples/hello_rust_cargo/hello/.gitignore new file mode 100644 index 00000000000..eb5a316cbd1 --- /dev/null +++ b/examples/hello_rust_cargo/hello/.gitignore @@ -0,0 +1 @@ +target diff --git a/examples/hello_rust_cargo/hello/Cargo.toml b/examples/hello_rust_cargo/hello/Cargo.toml new file mode 100644 index 00000000000..d6cba821792 --- /dev/null +++ b/examples/hello_rust_cargo/hello/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "hello" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[profile.dev] +panic = "abort" + +# Special hanlding for the panic! macro, can be removed once +# the libstd port for NuttX is complete. +[profile.release] +panic = "abort" +lto = true + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +tokio = { version = "1", features = ["rt"] } diff --git a/examples/hello_rust_cargo/hello/src/lib.rs b/examples/hello_rust_cargo/hello/src/lib.rs new file mode 100644 index 00000000000..f50cf5bc5c3 --- /dev/null +++ b/examples/hello_rust_cargo/hello/src/lib.rs @@ -0,0 +1,56 @@ +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); + + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + println!("Hello world from tokio!"); + }); + + loop { + // Do nothing + } +} diff --git a/tools/Rust.mk b/tools/Rust.mk new file mode 100644 index 00000000000..f62d51e7bdc --- /dev/null +++ b/tools/Rust.mk @@ -0,0 +1,125 @@ +############################################################################ +# 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: +# - 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 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 + cargo build --release -Zbuild-std=std,panic_abort \ + --manifest-path $(2)/$(1)/Cargo.toml \ + --target $(call RUST_TARGET_TRIPLE) +endef +else +define RUST_CARGO_BUILD + @echo "Building Rust code with cargo..." + 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