From c13865130054ae748c2f7f754ceabcdb966276ad Mon Sep 17 00:00:00 2001 From: Huang Qi Date: Thu, 7 Mar 2024 12:37:18 +0800 Subject: [PATCH] tools: New CMake based Wasm build system Introduce a new CMake based build system for Wasm. And target the Wasm ABI to wasm32-wasi, it should be a more commnly used and standard ABI for Wasm. Signed-off-by: Huang Qi --- CMakeLists.txt | 1 + Makefile | 21 +++++ examples/hello_wasm/CMakeLists.txt | 36 ++++++++ examples/hello_wasm/Kconfig | 12 +++ examples/hello_wasm/hello_main.c | 1 - tools/CMakeLists.txt | 65 ++++++++++++++ tools/Kconfig | 20 +++++ tools/Wasm/.gitignore | 1 + tools/Wasm/CMakeLists.txt | 100 +++++++++++++++++++++ tools/Wasm/Kconfig | 4 + tools/Wasm/README.md | 52 +++++++++++ tools/Wasm/WASI-SDK.cmake | 135 +++++++++++++++++++++++++++++ 12 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 examples/hello_wasm/CMakeLists.txt create mode 100644 tools/CMakeLists.txt create mode 100644 tools/Kconfig create mode 100644 tools/Wasm/.gitignore create mode 100644 tools/Wasm/CMakeLists.txt create mode 100644 tools/Wasm/Kconfig create mode 100644 tools/Wasm/README.md create mode 100644 tools/Wasm/WASI-SDK.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 383820fd6..5b2fd150f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ add_subdirectory(platform) add_subdirectory(sdr) add_subdirectory(system) add_subdirectory(testing) +add_subdirectory(tools) add_subdirectory(wireless) # add examples and external at the end to allow library dependencies diff --git a/Makefile b/Makefile index 690e35f56..4da8a1528 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,25 @@ $(INCDIR): $(TOPDIR)/tools/incdir.c IMPORT_TOOLS = $(MKDEP) $(INCDIR) +ifeq ($(CONFIG_TOOLS_WASM_BUILD),y) + +configure_wasm: + $(Q) cmake -B$(APPDIR)$(DELIM)tools$(DELIM)Wasm$(DELIM)build \ + $(APPDIR)$(DELIM)tools$(DELIM)Wasm \ + -DAPPDIR=$(APPDIR) -DTOPDIR=$(TOPDIR) \ + -DWASI_SDK_PATH=$(WASI_SDK_PATH) \ + -DKCONFIG_FILE_PATH=$(TOPDIR)$(DELIM).config + +context_wasm: configure_wasm + $(Q) cmake --build $(APPDIR)$(DELIM)tools$(DELIM)Wasm$(DELIM)build + +else + +context_wasm: + +endif + + # In the KERNEL build, we must build and install all of the modules. No # symbol table is needed @@ -155,6 +174,7 @@ staging: context: | staging $(Q) $(MAKE) context_all $(Q) $(MAKE) register_all + $(Q) $(MAKE) context_wasm Kconfig: $(foreach SDIR, $(CONFIGDIRS), $(call MAKE_template,$(SDIR),preconfig)) @@ -205,4 +225,5 @@ distclean: $(foreach SDIR, $(CLEANDIRS), $(SDIR)_distclean) $(call DELDIR, $(BINDIR)) $(call DELDIR, staging) $(call DELDIR, wasm) + $(call DELDIR, $(APPDIR)$(DELIM)tools$(DELIM)Wasm$(DELIM)build) $(call CLEAN) diff --git a/examples/hello_wasm/CMakeLists.txt b/examples/hello_wasm/CMakeLists.txt new file mode 100644 index 000000000..556aafd5b --- /dev/null +++ b/examples/hello_wasm/CMakeLists.txt @@ -0,0 +1,36 @@ +# ############################################################################## +# apps/examples/hello_wasm/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_WASM_BUILD_NATIVE) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_HELLO_WASM_PROGNAME} + SRCS + hello_main.c + STACKSIZE + ${CONFIG_EXAMPLES_HELLO_WASM_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_HELLO_WASM_PRIORITY}) +endif() + +if(CONFIG_EXAMPLES_HELLO_WASM_BUILD_WASM) + wasm_add_application(NAME ${CONFIG_EXAMPLES_HELLO_WASM_PROGNAME} SRCS + hello_main.c) +endif() diff --git a/examples/hello_wasm/Kconfig b/examples/hello_wasm/Kconfig index fedb8d828..d4ed93523 100644 --- a/examples/hello_wasm/Kconfig +++ b/examples/hello_wasm/Kconfig @@ -26,4 +26,16 @@ config EXAMPLES_HELLO_WASM_STACKSIZE int "Hello wasm stack size" default DEFAULT_TASK_STACKSIZE +config EXAMPLES_HELLO_WASM_BUILD_WASM + bool "Build as WebAssembly module" + default y + ---help--- + Build the WebAssembly binary from the C source code. + +config EXAMPLES_HELLO_WASM_BUILD_NATIVE + bool "Build as native program" + default n + ---help--- + Build the native binary from the C source code. + endif diff --git a/examples/hello_wasm/hello_main.c b/examples/hello_wasm/hello_main.c index a98dcf7e4..c351cf939 100644 --- a/examples/hello_wasm/hello_main.c +++ b/examples/hello_wasm/hello_main.c @@ -22,7 +22,6 @@ * Included Files ****************************************************************************/ -#include #include /**************************************************************************** diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000..c473bdbe0 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,65 @@ +# ############################################################################## +# apps/tools/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. +# +# ############################################################################## + +# Fake wasm_add_application function to suppress error from native build process +function(wasm_add_application) + +endfunction() + +# Fake wasm_add_library function to suppress error from native build process +function(wasm_add_library) + +endfunction() + +if(CONFIG_TOOLS_WASM_BUILD) + + include(ExternalProject) + + set(TOPDIR + ${CMAKE_SOURCE_DIR} + CACHE INTERNAL "") + + set(KCONFIG_FILE_PATH + ${CMAKE_BINARY_DIR}/.config + CACHE INTERNAL "") + + # Get parent dir of CMAKE_CURRENT_SOURCE_DIR + get_filename_component(APPDIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) + + # Configure and build the Wasm based application + add_custom_target( + configure_wasm_build + COMMAND + ${CMAKE_COMMAND} -B${CMAKE_BINARY_DIR}/Wasm + ${CMAKE_CURRENT_SOURCE_DIR}/Wasm -DAPPDIR=${APPDIR} -DTOPDIR=${TOPDIR} + -DKCONFIG_FILE_PATH=${KCONFIG_FILE_PATH} + -DWASI_SDK_PATH=$ENV{WASI_SDK_PATH}) + + add_custom_target(wasm_build COMMAND ${CMAKE_COMMAND} --build + ${CMAKE_BINARY_DIR}/Wasm) + + add_dependencies(wasm_build configure_wasm_build) + + # Add the Wasm based application to the build. Notice: Wasm build will be + # triggered by the native build process each time, but it's ok since the + # incremental build is very fast in CMake. + add_dependencies(apps wasm_build) + +endif() diff --git a/tools/Kconfig b/tools/Kconfig new file mode 100644 index 000000000..d427908a6 --- /dev/null +++ b/tools/Kconfig @@ -0,0 +1,20 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menu "Extra Tools" + +menu "Wasm Build Options" + +config TOOLS_WASM_BUILD + bool "Enable Wasm build support" + default n + ---help--- + If enabled, then then build system will trigger the Wasm build + process. This will require the WASI-SDK to be installed on the + host system. + +endmenu + +endmenu diff --git a/tools/Wasm/.gitignore b/tools/Wasm/.gitignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/tools/Wasm/.gitignore @@ -0,0 +1 @@ +build diff --git a/tools/Wasm/CMakeLists.txt b/tools/Wasm/CMakeLists.txt new file mode 100644 index 000000000..667583457 --- /dev/null +++ b/tools/Wasm/CMakeLists.txt @@ -0,0 +1,100 @@ +# ############################################################################## +# apps/tools/Wasm/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. +# +# ############################################################################## + +# ~~~ +# This file is used to manage the WebAssembly (Wasm) build process as entry +# point. All the wasm apps are built using this file as a sub target. +# +# Necessary input for this file is the APPDIR, TOPDIR and KCONFIG_FILE_PATH. +# The APPDIR is the directory where the Wasm apps are located. +# The TOPDIR is the root directory of the NuttX source code. +# The KCONFIG_FILE_PATH is the path to the .config file of the NuttX build. +# ~~~ + +cmake_minimum_required(VERSION 3.5) + +include(WASI-SDK.cmake) + +project(WasmApps) + +# Check whether the APPDIR is defined or not. If not, then set it to the parent +# directory of the current CMakeLists.txt file. +if(NOT DEFINED APPDIR) + message(FATAL_ERROR "APPDIR is not defined") +endif() + +# Check wether the TOPDIR is defined or not. If not, then raise an error. +if(NOT DEFINED TOPDIR) + message(FATAL_ERROR "TOPDIR is not defined") +endif() + +# Check wether the KCONFIG_FILE_PATH is defined or not. If not, then raise an +# error. +if(NOT DEFINED KCONFIG_FILE_PATH) + message(FATAL_ERROR "KCONFIG_FILE_PATH is not defined") +endif() + +# Include the NuttX kconfig parser to shared the configuration between the NuttX +# build and the Wasm build. And then parse the input KCONFIG_FILE_PATH to get +# the configuration. +include(${TOPDIR}/cmake/nuttx_kconfig.cmake) +nuttx_export_kconfig(${KCONFIG_FILE_PATH}) + +# Provide FAR macro from command line since it is not supported in wasi-sdk, but +# it is used in NuttX code. +# ~~~ +# #define FAR +# ~~~ +# It usually defined in nuttx/compiler.h. +set(NUTTX_MACRO_FAR "") +add_definitions(-DFAR=${NUTTX_MACRO_FAR}) + +# Fake nuttx_add_application to avoid error in the Wasm build process. +function(nuttx_add_application) + +endfunction() + +# Fake nuttx_add_library to avoid error in the Wasm build process. +function(nuttx_add_library) + +endfunction() + +# Recursively find all the CMakeLists.txt files in the ${APPDIR} and add it by +# add_subdirectory, but exclude the CMakeLists.txt file in the ${APPDIR}/tools +# directory. +file(GLOB_RECURSE WASM_APPS ${APPDIR}/*/CMakeLists.txt) +list(FILTER WASM_APPS EXCLUDE REGEX ".*/tools/.*") + +# Read and check if wasm_add_application is called in the CMakeLists.txt file in +# WASM_APPS If true, then add the directory to the build process +foreach(WASM_APP ${WASM_APPS}) + file(READ ${WASM_APP} WASM_APP_CONTENTS) + string(FIND "${WASM_APP_CONTENTS}" "wasm_add_application" WASM_APP_FOUND) + string(FIND "${WASM_APP_CONTENTS}" "wasm_add_library" WASM_LIB_FOUND) + if(WASM_APP_FOUND GREATER -1 OR WASM_LIB_FOUND GREATER -1) + get_filename_component(WASM_APP_DIR ${WASM_APP} DIRECTORY) + # Add subdirectory to the build process and put the build directory in the + # current build directory with the name same as the relative path of the + # ${APPDIR} + string(REPLACE ${APPDIR} "" WASM_APP_BUILD_DIR ${WASM_APP_DIR}) + add_subdirectory(${WASM_APP_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/Wasm/${WASM_APP_BUILD_DIR}) + endif() +endforeach() diff --git a/tools/Wasm/Kconfig b/tools/Wasm/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/tools/Wasm/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/tools/Wasm/README.md b/tools/Wasm/README.md new file mode 100644 index 000000000..6f5cd9d46 --- /dev/null +++ b/tools/Wasm/README.md @@ -0,0 +1,52 @@ +# Wasm build system in NuttX + +## Overview + +The files in this directory are used to build the Wasm module in NuttX: +* CMakeLists.txt - The project configuration file for all Wasm targets +* WASI-SDK.cmake - Provides the toolchain definition and utility functions for the Wasm build + +Since the Wasm module is built with dedicated toolchain and flags, the main NuttX build system will treat the Wasm module as an external project, but share the same source tree and configuration (.config file). + +## Design goals + +* **Consistency**: The Wasm module can be built with the same build system as NuttX, and share the same source tree and configuration. +* **Flexibility**: Can be built with CMake or Makefile, and can be integrated into the NuttX build system easily (until Makefile based build system is deprecated) +* **Maintainability**: The Wasm module build system should be simple and less dependent on the NuttX build system. +* **Portability**: The Wasm module can be built on any platform with CMake and WASI-SDK installed. + +## Build process + +Each Wasm target (such as examples/hello_wasm) will have its own CMakeLists.txt file, which will be included in the main CMakeLists.txt file in this directory. + +Each target will have its own build directory (Wasm) inside the NuttX build directory, such as: +``` +* apps +* nuttx +* cmake_build_dir + * other NuttX native targets + * Wasm + * examples + * hello_wasm + * hello_wasm.wasm + * other build files + * benchmarks + * coremark + * coremark.wasm + * netutils + * cjson + * libcJSON.a + * libWasm.a + * other build files +``` + +Each target can be built with private source files, or shared source files with other targets. The shared source files will be built into a static global library (libWasm.a) or a custom library and linked to the Wasm targets. + +Each target will be visible to other targets, so that the module level CMakelists.txt can define the dependencies between targets. + +## Limitations + +Now the Wasm module is targeted to wasm32-wasi, instead of legacy custom build with NuttX sysroot. +So the source files should not call any NuttX APIs directly, and should not include any NuttX header files. +It will limit the usage that some applications inside apps directory can not be built as Wasm targets directly.But still +can be used for many POSIX compatible applications. diff --git a/tools/Wasm/WASI-SDK.cmake b/tools/Wasm/WASI-SDK.cmake new file mode 100644 index 000000000..9d06fd99b --- /dev/null +++ b/tools/Wasm/WASI-SDK.cmake @@ -0,0 +1,135 @@ +# ############################################################################## +# apps/tools/Wasm/WASI-SDK.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. +# +# ############################################################################## + +# This file is to used for finding the WASI SDK and setting up the necessary +# flags for building WebAssembly applications with CMake or Makefile in NuttX +# build system. +# +# This file is intended to be included in the top-level CMakeLists.txt file of +# the application. +# +# For legacy Makefile-based build system, CMake will be called in the Makefile +# to do actual build. + +# If no WASI_SDK_PATH is provided, the raise an error and stop the build +if(NOT DEFINED WASI_SDK_PATH) + message(FATAL_ERROR "WASI_SDK_PATH is not defined." + "Please set it to the path of the WASI SDK.") +endif() + +# Set the system name, version and processor to WASI These are from original +# WASI SDK's cmake toolchain file +set(CMAKE_SYSTEM_NAME WASI) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR wasm32) + +# Fetch the wasm compiler and linker from the WASI SDK +set(CMAKE_C_COMPILER ${WASI_SDK_PATH}/bin/clang) +set(CMAKE_CXX_COMPILER ${WASI_SDK_PATH}/bin/clang++) + +# ~~~ +# Function "wasm_add_application" to add a WebAssembly application to the +# build system. +# +# This function is used to add a WebAssembly application to the build system. +# It creates an executable target for the application and sets the necessary +# properties for building the application. +# +# Usage: +# wasm_add_application(NAME SRCS +# [STACK_SIZE ] [INITIAL_MEMORY_SIZE ]) +# +# Parameters: +# NAME: The name of the application (NAME.wasm). +# SRCS: The source files of the application. +# STACK_SIZE: The stack size of the application. Default is 2048. +# INITIAL_MEMORY_SIZE: The initial memory size of the application. +# Default is 65536 (One page), and must be a multiple of 65536. +# ~~~ + +function(wasm_add_application) + + # Parse the APP_NAME and APP_SRCS from the arguments + set(APP_NAME "") + set(APP_SRCS "") + set(APP_STACK_SIZE 2048) + set(APP_INITIAL_MEMORY_SIZE 65536) + + cmake_parse_arguments(APP "" "NAME;STACK_SIZE;INITIAL_MEMORY_SIZE" "SRCS" + ${ARGN}) + + # Check if the APP_NAME (NAME) is provided + if(NOT APP_NAME) + message(FATAL_ERROR "NAME is not provided.") + endif() + + # Check if the APP_SRCS (SRCS) is provided + if(NOT APP_SRCS) + message(FATAL_ERROR "SRCS is not provided.") + endif() + + # Create the executable target for the application + add_executable(${APP_NAME} ${APP_SRCS}) + + # Set the target properties + set_target_properties(${APP_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME}.wasm) + +endfunction() + +# ~~~ +# Function "wasm_add_library" to add a WebAssembly library to the build system. +# +# This function is used to add a WebAssembly library to the build system. +# It creates a static library target for the library and sets the necessary +# properties for building the library. +# +# Usage: +# wasm_add_library(NAME SRCS ) +# +# Parameters: +# NAME: The name of the library (libNAME.a). +# SRCS: The source files of the library. +# ~~~ + +function(wasm_add_library) + + # Parse the LIB_NAME and LIB_SRCS from the arguments + set(LIB_NAME "") + set(LIB_SRCS "") + + cmake_parse_arguments(LIB "" "NAME" "SRCS" ${ARGN}) + + # Check if the LIB_NAME (NAME) is provided + if(NOT LIB_NAME) + message(FATAL_ERROR "NAME is not provided.") + endif() + + # Check if the LIB_SRCS (SRCS) is provided + if(NOT LIB_SRCS) + message(FATAL_ERROR "SRCS is not provided.") + endif() + + # Create the static library target for the library + add_library(${LIB_NAME} STATIC ${LIB_SRCS}) + + # Set the target properties + set_target_properties(${LIB_NAME} PROPERTIES OUTPUT_NAME lib${LIB_NAME}.a) + +endfunction()