# ##############################################################################
# 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)
# Check if required LLVM parameters are defined If any are missing, warn and
# skip the entire Rust logic
if(NOT DEFINED LLVM_ARCHTYPE
OR NOT DEFINED LLVM_ABITYPE
OR NOT DEFINED LLVM_CPUTYPE)
message(
WARNING
"Missing required LLVM parameters for Rust support. Skipping Rust build logic. "
"Required: LLVM_ARCHTYPE='${LLVM_ARCHTYPE}', LLVM_ABITYPE='${LLVM_ABITYPE}', LLVM_CPUTYPE='${LLVM_CPUTYPE}'"
)
return()
endif()
# ~~~
# Convert architecture type to Rust NuttX target
#
# Supported architectures:
# - aarch64: aarch64-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
# - 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 "${NUTTX_APPS_DIR}/tools/x86_64-unknown-nuttx.json")
elseif(ARCHTYPE STREQUAL "x86")
set(TARGET_TRIPLE "${NUTTX_APPS_DIR}/tools/i486-unknown-nuttx.json")
elseif(ARCHTYPE STREQUAL "aarch64")
set(TARGET_TRIPLE "aarch64-unknown-nuttx")
elseif(ARCHTYPE MATCHES "thumb")
if(ARCHTYPE MATCHES "thumbv8")
# Extract just the base architecture type (thumbv8m.main or thumbv8m.base)
# Handle both thumbv8m and thumbv8.1m variants
if(ARCHTYPE MATCHES "thumbv8.*\.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()
# Build flags for rust_unified_lib, this file will be included only once in the
# NuttX build system, so it's safe to set these variables globally
set(RUST_UNIFIED_CARGO_BUILD_FLAGS "")
if(NOT CONFIG_DEBUG_NOOPT)
list(APPEND RUST_UNIFIED_CARGO_BUILD_FLAGS "--release")
set(RUST_UNIFIED_CARGO_PROFILE "release")
else()
set(RUST_UNIFIED_CARGO_PROFILE "debug")
endif()
nuttx_rust_target_triple(${LLVM_ARCHTYPE} ${LLVM_ABITYPE} ${LLVM_CPUTYPE}
RUST_UNIFIED_CARGO_TARGET)
# Handle JSON target: use base name for target directory if needed
if(RUST_UNIFIED_CARGO_TARGET MATCHES ".json$")
get_filename_component(RUST_UNIFIED_TARGET_BASE ${RUST_UNIFIED_CARGO_TARGET}
NAME_WE)
else()
set(RUST_UNIFIED_TARGET_BASE ${RUST_UNIFIED_CARGO_TARGET})
endif()
list(APPEND RUST_UNIFIED_CARGO_BUILD_FLAGS
"--target=${RUST_UNIFIED_CARGO_TARGET}")
list(APPEND RUST_UNIFIED_CARGO_BUILD_FLAGS
"--manifest-path=${CMAKE_BINARY_DIR}/rust_unified_lib/Cargo.toml")
set(RUST_UNIFIED_LIBPATH
${CMAKE_BINARY_DIR}/rust_unified_lib/target/${RUST_UNIFIED_TARGET_BASE}/${RUST_UNIFIED_CARGO_PROFILE}/librust_unified_lib.a
)
# Create a global target to collect all Rust crate information Step 1: Generate
# the unified Rust library source
if(NOT TARGET rust_unified_lib)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/rust_unified_lib
DEPENDS ${NUTTX_APPS_DIR}/tools/generate_rust_unified_lib.py
COMMAND
python3 ${NUTTX_APPS_DIR}/tools/generate_rust_unified_lib.py
--crate-name=$<TARGET_PROPERTY:rust_unified_lib,RUST_CRATE_NAMES>
--crate-path=$<TARGET_PROPERTY:rust_unified_lib,RUST_CRATE_PATHS>
--output-dir ${CMAKE_BINARY_DIR}/rust_unified_lib
COMMENT "Generating unified Rust library sources"
VERBATIM)
# Step 2: Build the Rust static library
# Convert NUTTX_EXTRA_FLAGS to a space-separated string
string(REPLACE ";" " " CFLAGS_STRING "${NUTTX_EXTRA_FLAGS}")
# Get include directory for toolchain headers
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=include
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE TOOLCHAIN_INCLUDE_DIR)
add_custom_command(
OUTPUT ${RUST_UNIFIED_LIBPATH}
DEPENDS ${CMAKE_BINARY_DIR}/rust_unified_lib
COMMAND
${CMAKE_COMMAND} -E env
NUTTX_INCLUDE_DIR=${TOOLCHAIN_INCLUDE_DIR}:${PROJECT_SOURCE_DIR}/include:${CMAKE_BINARY_DIR}/include:${CMAKE_BINARY_DIR}/include/arch
CC=${CMAKE_C_COMPILER} AR=${CMAKE_AR} CFLAGS=${CFLAGS_STRING}
NUTTX_APPS_DIR=${NUTTX_APPS_DIR} NUTTX_BUILD_DIR=${CMAKE_BINARY_DIR} cargo
build ${RUST_UNIFIED_CARGO_BUILD_FLAGS}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/rust_unified_lib
COMMENT "Building unified Rust static library"
BYPRODUCTS ${CMAKE_BINARY_DIR}/rust_unified_lib/dummy_product
VERBATIM)
# Step 3: Create the target and make it depend on the static library
add_custom_target(rust_unified_lib DEPENDS ${RUST_UNIFIED_LIBPATH})
endif()
# ~~~
# 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})
# Import the unified Rust static library only once
if(NOT TARGET rust_unified_lib_static)
add_custom_target(rust_unified_lib_static)
add_dependencies(apps rust_unified_lib)
# Add the Rust library to NuttX build
nuttx_add_extra_library(${RUST_UNIFIED_LIBPATH})
endif()
# Add crate information to the unified target properties Store crate name and
# path as target properties
set_property(
TARGET rust_unified_lib
APPEND
PROPERTY RUST_CRATE_NAMES ${CRATE_NAME})
set_property(
TARGET rust_unified_lib
APPEND
PROPERTY RUST_CRATE_PATHS ${CRATE_PATH})
endfunction()