From 4c4cb2c31b9d9853e739d0092dd2fe1b6e2deb53 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 21 Mar 2026 11:30:58 +0100 Subject: [PATCH] chore: cleanup WASM builds (#7196) --- .github/workflows/companion.yml | 4 ++ .gitignore | 2 + CMakeLists.txt | 84 +++++++-------------------- cmake/CopyWasmModules.cmake | 4 +- cmake/Macros.cmake | 30 +++++++++- cmake/QtDefs.cmake | 2 +- cmake/toolchain/wasi-threads.cmake | 20 +++++++ cmake/wasm/CMakeLists.txt | 48 +++++++++++++++ radio/src/targets/simu/CMakeLists.txt | 4 +- radio/util/build-firmware.py | 4 -- tools/build-wasm-modules.sh | 65 ++++++++++++++++++--- 11 files changed, 184 insertions(+), 83 deletions(-) create mode 100644 cmake/toolchain/wasi-threads.cmake create mode 100644 cmake/wasm/CMakeLists.txt diff --git a/.github/workflows/companion.yml b/.github/workflows/companion.yml index 7a1f52bca..44eb53e5e 100644 --- a/.github/workflows/companion.yml +++ b/.github/workflows/companion.yml @@ -10,12 +10,16 @@ on: - '.github/workflows/companion.yml' - 'companion/**' - 'tools/build-companion.sh' + - 'tools/build-wasm-modules.sh' + - 'cmake/wasm/CMakeLists.txt' pull_request: paths: - '.github/workflows/companion.yml' - 'companion/**' - 'tools/build-companion.sh' + - 'tools/build-wasm-modules.sh' + - 'cmake/wasm/CMakeLists.txt' workflow_dispatch: diff --git a/.gitignore b/.gitignore index e1ea4aadc..94dd30097 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ compile_commands.json /.vs CMakeUserPresets.json .DS_Store +_deps +_wasi_sdk_fetch diff --git a/CMakeLists.txt b/CMakeLists.txt index 621d85bda..ba2fec68f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,20 +36,8 @@ option(EdgeTX_SUPERBUILD "Cross-compile EdgeTX for each toolchain" ON) include(ExternalProject) if(EdgeTX_SUPERBUILD) - # Fetch all command line args into CMAKE_ARGS - get_cmake_property(CACHE_VARS CACHE_VARIABLES) - foreach(CACHE_VAR ${CACHE_VARS}) - get_property(CACHE_VAR_HELPSTRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING) - if(CACHE_VAR_HELPSTRING STREQUAL "No help, variable specified on the command line.") - get_property(CACHE_VAR_TYPE CACHE ${CACHE_VAR} PROPERTY TYPE) - if(CACHE_VAR_TYPE STREQUAL "UNINITIALIZED") - set(CACHE_VAR_TYPE) - else() - set(CACHE_VAR_TYPE :${CACHE_VAR_TYPE}) - endif() - list(APPEND CMAKE_ARGS "-D${CACHE_VAR}${CACHE_VAR_TYPE}=${${CACHE_VAR}}") - endif() - endforeach() + include(Macros) + CollectCommandLineArgs(CMAKE_ARGS) message("-- CMAKE_ARGS: ${CMAKE_ARGS}") message("-- CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") @@ -82,85 +70,58 @@ if(EdgeTX_SUPERBUILD) EXCLUDE_FROM_ALL TRUE ) - if(DEFINED CMAKE_BUILD_PARALLEL_LEVEL) - set(PARALLEL_FLAG --parallel ${CMAKE_BUILD_PARALLEL_LEVEL}) - else() - set(PARALLEL_FLAG --parallel) - endif() - - # Webasm targets - if(ENABLE_WASM) - include(FetchWasiSDK) - find_package(WasiSDK REQUIRED) - - ExternalProject_Add(wasm - SOURCE_DIR ${CMAKE_SOURCE_DIR} - BINARY_DIR ${CMAKE_BINARY_DIR}/wasm - CMAKE_ARGS - ${CMAKE_ARGS} - -Wno-dev - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk-pthread.cmake - CMAKE_CACHE_ARGS - -DEdgeTX_SUPERBUILD:BOOL=0 - -DNATIVE_BUILD:BOOL=1 - -DDISABLE_COMPANION:BOOL=1 - INSTALL_COMMAND "" - EXCLUDE_FROM_ALL TRUE - ) - endif() + # Webasm targets — deferred to a separate CMake project so that + # the WASI SDK is only fetched when wasi-module is actually built. add_custom_target(configure DEPENDS native-configure arm-none-eabi-configure) - add_custom_target(libsimulator - COMMAND ${CMAKE_COMMAND} --build native --target libsimulator ${PARALLEL_FLAG} - DEPENDS native-configure - ) - add_custom_target(simulator - COMMAND ${CMAKE_COMMAND} --build native --target simulator ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build native --target simulator DEPENDS native-configure ) add_custom_target(simu - COMMAND ${CMAKE_COMMAND} --build native --target simu ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build native --target simu ) - if(ENABLE_WASM) - add_custom_target(wasi-module - COMMAND ${CMAKE_COMMAND} --build wasm --target wasi-module --parallel - DEPENDS wasm-configure - ) - endif() + add_custom_target(wasi-module + COMMAND ${CMAKE_COMMAND} + -S ${CMAKE_SOURCE_DIR}/cmake/wasm + -B ${CMAKE_BINARY_DIR}/wasm + -DEDGETX_SOURCE_DIR=${CMAKE_SOURCE_DIR} + -DEDGETX_BUILD_TYPE=${CMAKE_BUILD_TYPE} + ${CMAKE_ARGS} + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/wasm + ) add_custom_target(companion - COMMAND ${CMAKE_COMMAND} --build native --target companion ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build native --target companion DEPENDS native-configure ) add_custom_target(gtests-radio - COMMAND ${CMAKE_COMMAND} --build native --target gtests-radio ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build native --target gtests-radio DEPENDS native-configure ) add_custom_target(tests-radio - COMMAND ${CMAKE_COMMAND} --build native --target tests-radio ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build native --target tests-radio DEPENDS native-configure ) add_custom_target(bootloader - COMMAND ${CMAKE_COMMAND} --build arm-none-eabi --target bootloader ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build arm-none-eabi --target bootloader DEPENDS arm-none-eabi-configure ) add_custom_target(firmware - COMMAND ${CMAKE_COMMAND} --build arm-none-eabi --target firmware ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build arm-none-eabi --target firmware DEPENDS arm-none-eabi-configure ) add_custom_target(firmware-size - COMMAND ${CMAKE_COMMAND} --build arm-none-eabi --target firmware-size ${PARALLEL_FLAG} + COMMAND ${CMAKE_COMMAND} --build arm-none-eabi --target firmware-size DEPENDS arm-none-eabi-configure ) @@ -184,7 +145,4 @@ else() set(IGNORE "${LIBUSB1_ROOT_DIR}" "${OPENSSL_ROOT_DIR}") endif() -# Prevent CMake warnings -set(IGNORE "${ENABLE_WASM}") - add_subdirectory(${RADIO_SRC_DIR}) diff --git a/cmake/CopyWasmModules.cmake b/cmake/CopyWasmModules.cmake index 6f5c9e853..c49550e62 100644 --- a/cmake/CopyWasmModules.cmake +++ b/cmake/CopyWasmModules.cmake @@ -1,5 +1,5 @@ # Copy all WASM simulator modules from SRC_DIR to DST_DIR at build time. # Invoked via cmake -P so the pattern is evaluated on every build. file(INSTALL "${SRC_DIR}/" DESTINATION "${DST_DIR}" - FILES_MATCHING PATTERN "edgetx-*-simulator.wasm" - MESSAGE_NEVER) + MESSAGE_NEVER + FILES_MATCHING PATTERN "edgetx-*-simulator.wasm") diff --git a/cmake/Macros.cmake b/cmake/Macros.cmake index 89d47ffab..d116cf2f8 100644 --- a/cmake/Macros.cmake +++ b/cmake/Macros.cmake @@ -80,7 +80,7 @@ function(GenerateDatacopy source output) add_custom_command( OUTPUT ${output} COMMAND ${GEN_DATACOPY_CMD} > ${output} - DEPENDS ${GEN_DATACOPY_DEPEND} + DEPENDS ${GEN_DATACOPY_DEPEND} ${CMAKE_BINARY_DIR}/CMakeCache.txt ) endfunction() @@ -128,7 +128,8 @@ function(AddHWGenTarget input template output) add_custom_command(OUTPUT ${output} COMMAND ${GEN_JSON} > ${output} - DEPENDS ${INPUT_JSON} ${TEMPLATE} ${GEN_PY_DEPS}) + DEPENDS ${INPUT_JSON} ${TEMPLATE} ${GEN_PY_DEPS} ${CMAKE_BINARY_DIR}/CMakeCache.txt + ) endfunction() macro(AddHeadersSources) @@ -146,3 +147,28 @@ macro(AddHeadersSources) ) endforeach() endmacro() + +# Collect all user-specified command-line cache variables into a list +# suitable for forwarding to ExternalProject or sub-project cmake invocations. +# Optional EXCLUDE parameter: regex to skip matching variable names. +function(CollectCommandLineArgs out_var) + cmake_parse_arguments(_cla "" "EXCLUDE" "" ${ARGN}) + set(_args "") + get_cmake_property(_cache_vars CACHE_VARIABLES) + foreach(_var ${_cache_vars}) + if(_cla_EXCLUDE AND _var MATCHES "${_cla_EXCLUDE}") + continue() + endif() + get_property(_help CACHE ${_var} PROPERTY HELPSTRING) + if(_help STREQUAL "No help, variable specified on the command line.") + get_property(_type CACHE ${_var} PROPERTY TYPE) + if(_type STREQUAL "UNINITIALIZED") + set(_type) + else() + set(_type :${_type}) + endif() + list(APPEND _args "-D${_var}${_type}=${${_var}}") + endif() + endforeach() + set(${out_var} ${_args} PARENT_SCOPE) +endfunction() diff --git a/cmake/QtDefs.cmake b/cmake/QtDefs.cmake index c298d67c4..307e27111 100644 --- a/cmake/QtDefs.cmake +++ b/cmake/QtDefs.cmake @@ -48,7 +48,7 @@ if(Qt6_FOUND) list(APPEND APP_COMMON_DEFINES -D__STDC_LIMIT_MACROS) endif() if(APP_CUSTOM_DBG_HANDLER) - # provide full qDebug log context to our custom handler. This may also affect libsimulator, which is why it is here + # provide full qDebug log context to our custom handler. list(APPEND APP_COMMON_DEFINES -DQT_MESSAGELOGCONTEXT) list(APPEND APP_COMMON_DEFINES -DAPP_DBG_HANDLER_ENABLE=1) if(APP_CUSTOM_DBG_LEVEL GREATER -1 AND APP_CUSTOM_DBG_LEVEL LESS 5) diff --git a/cmake/toolchain/wasi-threads.cmake b/cmake/toolchain/wasi-threads.cmake new file mode 100644 index 000000000..417ac0d2f --- /dev/null +++ b/cmake/toolchain/wasi-threads.cmake @@ -0,0 +1,20 @@ +# Wrapper around wasi-sdk's pthread toolchain that uses the non-deprecated +# wasm32-wasip1-threads triple (compatible with SDK 25+). + +# Propagate WASI_SDK_PREFIX into try_compile sub-projects (used during ABI detection) +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES WASI_SDK_PREFIX) + +if(NOT WASI_SDK_PREFIX) + if(DEFINED ENV{WASI_SDK_PATH}) + set(WASI_SDK_PREFIX $ENV{WASI_SDK_PATH}) + elseif(EXISTS "/opt/wasi-sdk") + set(WASI_SDK_PREFIX "/opt/wasi-sdk") + endif() +endif() + +include(${WASI_SDK_PREFIX}/share/cmake/wasi-sdk-pthread.cmake) + +set(triple wasm32-wasip1-threads) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) diff --git a/cmake/wasm/CMakeLists.txt b/cmake/wasm/CMakeLists.txt new file mode 100644 index 000000000..727c6ac8a --- /dev/null +++ b/cmake/wasm/CMakeLists.txt @@ -0,0 +1,48 @@ +# Standalone CMake project for building WASM simulator modules. +# Invoked on-demand by the superbuild's wasi-module target so that +# the WASI SDK is only fetched when WASM targets are actually built. +# +# Usage from superbuild: +# cmake -S . -B build -DPCB=X10 -DPCBREV=TX16S +# cmake --build build --target wasi-module + +cmake_minimum_required(VERSION 3.21) +project(EdgeTX-WASM NONE) # NONE = no compiler check (we set a toolchain below) + +# Inherit the module path from the main project +list(APPEND CMAKE_MODULE_PATH "${EDGETX_SOURCE_DIR}/cmake") + +# Collect command-line args to forward, excluding EDGETX_* (consumed here) +include(Macros) +CollectCommandLineArgs(_forwarded_args EXCLUDE "^EDGETX_") + +# Fetch / find WASI SDK +include(FetchWasiSDK) +find_package(WasiSDK REQUIRED) + +include(ExternalProject) + +ExternalProject_Add(wasm + SOURCE_DIR ${EDGETX_SOURCE_DIR} + BINARY_DIR ${CMAKE_BINARY_DIR}/wasm-build + CMAKE_ARGS + ${_forwarded_args} + -Wno-dev + -DCMAKE_BUILD_TYPE=${EDGETX_BUILD_TYPE} + -DWASI_SDK_PREFIX=${WASI_SDK_PATH} + -DCMAKE_TOOLCHAIN_FILE=${EDGETX_SOURCE_DIR}/cmake/toolchain/wasi-threads.cmake + CMAKE_CACHE_ARGS + -DEdgeTX_SUPERBUILD:BOOL=0 + -DNATIVE_BUILD:BOOL=1 + -DDISABLE_COMPANION:BOOL=1 + BUILD_COMMAND ${CMAKE_COMMAND} --build --target wasi-module + INSTALL_COMMAND "" +) + +# Copy .wasm files up after build +ExternalProject_Add_Step(wasm copy-wasm + DEPENDEES build + COMMAND ${CMAKE_COMMAND} + -DSRC_DIR=${CMAKE_BINARY_DIR}/wasm-build -DDST_DIR=${CMAKE_BINARY_DIR} + -P ${EDGETX_SOURCE_DIR}/cmake/CopyWasmModules.cmake +) diff --git a/radio/src/targets/simu/CMakeLists.txt b/radio/src/targets/simu/CMakeLists.txt index 6ec880ba0..f2c2ee523 100644 --- a/radio/src/targets/simu/CMakeLists.txt +++ b/radio/src/targets/simu/CMakeLists.txt @@ -1,7 +1,7 @@ -option(SIMU_TARGET "Configure libsimulator/simu targets" ON) +option(SIMU_TARGET "Configure simu target" ON) if(NOT SIMU_TARGET) - message(STATUS "libsimulator/simu targets disabled") + message(STATUS "simu target disabled") return() endif() diff --git a/radio/util/build-firmware.py b/radio/util/build-firmware.py index 3b6f0c0fe..0e669b4d1 100755 --- a/radio/util/build-firmware.py +++ b/radio/util/build-firmware.py @@ -221,10 +221,6 @@ def main(): binary = "firmware.bin" ext = ".bin" filename = "opentx" - elif target == "libsimulator": - binary = "libopentx-" + board_name + "-simulator.so" - ext = ".so" - filename = "libopentx" else: return INVALID_BOARD diff --git a/tools/build-wasm-modules.sh b/tools/build-wasm-modules.sh index da5c6a762..205010bb1 100755 --- a/tools/build-wasm-modules.sh +++ b/tools/build-wasm-modules.sh @@ -27,7 +27,6 @@ else fi COMMON_OPTIONS+=" -DCMAKE_BUILD_TYPE=Release -DCMAKE_MESSAGE_LOG_LEVEL=WARNING -Wno-dev" -COMMON_OPTIONS+=" -DCMAKE_MODULE_PATH=/opt/wasi-sdk/share/cmake/" COMMON_OPTIONS+=" -DEdgeTX_SUPERBUILD:BOOL=0 -DNATIVE_BUILD:BOOL=1" # Generate EDGETX_VERSION_SUFFIX if not already set @@ -54,6 +53,58 @@ fi BUILD_DIR="build/wasm" +# Resolve WASI SDK: use WASI_SDK_PATH env, /opt/wasi-sdk, or auto-fetch +resolve_wasi_sdk() { + if [[ -n "$WASI_SDK_PATH" ]] && [[ -d "$WASI_SDK_PATH" ]]; then + echo "Using WASI SDK from WASI_SDK_PATH=$WASI_SDK_PATH" + return 0 + fi + + if [[ -d "/opt/wasi-sdk" ]]; then + WASI_SDK_PATH="/opt/wasi-sdk" + echo "Using WASI SDK from /opt/wasi-sdk" + return 0 + fi + + echo "WASI SDK not found, fetching via cmake/FetchWasiSDK.cmake..." + + # Run a minimal cmake project that reuses FetchWasiSDK.cmake to download + # the SDK. Downloads go into _deps/ which persists across plugin builds. + local fetch_dir="_wasi_sdk_fetch" + mkdir -p "$fetch_dir" + cat > "$fetch_dir/CMakeLists.txt" << CMAKEOF +cmake_minimum_required(VERSION 3.14) +project(wasi_sdk_fetch NONE) +list(APPEND CMAKE_MODULE_PATH "${SRCDIR}/cmake") +include(FetchWasiSDK) +file(WRITE "\${CMAKE_CURRENT_BINARY_DIR}/wasi_sdk_path.txt" "\${WASI_SDK_PATH}") +CMAKEOF + + if ! cmake -S "$fetch_dir" -B "$fetch_dir/build" \ + -DFETCHCONTENT_BASE_DIR="$PWD/_deps" 2>&1; then + echo "❌ Failed to fetch WASI SDK" + return 1 + fi + + WASI_SDK_PATH=$(cat "$fetch_dir/build/wasi_sdk_path.txt") + if [[ -z "$WASI_SDK_PATH" ]] || [[ ! -d "$WASI_SDK_PATH" ]]; then + echo "❌ WASI SDK path not resolved" + return 1 + fi + + echo "Using auto-fetched WASI SDK at $WASI_SDK_PATH" + return 0 +} + +if ! resolve_wasi_sdk; then + echo "❌ Cannot proceed without WASI SDK" + exit 1 +fi + +export WASI_SDK_PATH +COMMON_OPTIONS+=" -DCMAKE_MODULE_PATH=${WASI_SDK_PATH}/share/cmake/" +COMMON_OPTIONS+=" -DWASI_SDK_PREFIX=${WASI_SDK_PATH}" + # Function to output error logs (works in both GitHub Actions and terminal) output_error_log() { local log_file="$1" @@ -88,10 +139,9 @@ run_pipeline() { local context="$2" local show_details="${3:-false}" local cmake_opts="--parallel ${MAX_JOBS} ${QUIET_FLAGS}" - local wasi_toolchain="/opt/wasi-sdk/share/cmake/wasi-sdk-pthread.cmake" - - clean_build - if ! execute_with_output "🔧 CMake config" "cmake -S ${SRCDIR} -B ${BUILD_DIR} --toolchain ${wasi_toolchain} ${BUILD_OPTIONS}" "$log_file" "$show_details"; then + local wasi_toolchain="${SRCDIR}/cmake/toolchain/wasi-threads.cmake" + + if ! execute_with_output "🔧 CMake config" "cmake --fresh -S ${SRCDIR} -B ${BUILD_DIR} --toolchain ${wasi_toolchain} ${BUILD_OPTIONS}" "$log_file" "$show_details"; then output_error_log "$log_file" "$context (Configuration)" return 1 fi @@ -113,13 +163,10 @@ execute_with_output() { echo " $description..." fi + rm -f "$log_file" eval "$command" >> "$log_file" 2>&1 } -clean_build() { - rm -f "${BUILD_DIR}/CMakeCache.txt" -} - # Enhanced plugin builder with better error handling build_plugin() { local plugin="$1"