Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,41 @@ jobs:
./build_SITL/*.exe
./build_SITL/cygwin1.dll

build-SITL-Webassembly:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt-get update && sudo apt-get -y install ninja-build
- name: Setup Emscripten
uses: pyodide/setup-emsdk@v15
- name: Setup environment
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
run: |
# This is the hash of the commit for the PR
# when the action is triggered by PR, empty otherwise
COMMIT_ID=${{ github.event.pull_request.head.sha }}
# This is the hash of the commit when triggered by push
# but the hash of refs/pull/<n>/merge, which is different
# from the hash of the latest commit in the PR, that's
# why we try github.event.pull_request.head.sha first
COMMIT_ID=${COMMIT_ID:-${{ github.sha }}}
BUILD_SUFFIX=ci-$(date '+%Y%m%d')-$(git rev-parse --short ${COMMIT_ID})
VERSION=$(grep project CMakeLists.txt|awk -F VERSION '{ gsub(/[ \t)]/, "", $2); print $2 }')
echo "BUILD_SUFFIX=${BUILD_SUFFIX}" >> $GITHUB_ENV
echo "BUILD_NAME=inav-${VERSION}-${BUILD_SUFFIX}" >> $GITHUB_ENV
echo "NUM_CORES=$(grep processor /proc/cpuinfo | wc -l)" >> $GITHUB_ENV
- name: Build SITL
run: mkdir -p build_WASM && cd build_WASM && cmake -DWASM=ON -DWARNINGS_AS_ERRORS=ON -G Ninja .. && ninja -j${{ env.NUM_CORES }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ env.BUILD_NAME }}_WASM
path: |
./build_WASM/*_WASM.*
./build_WASM/*.html

test:
#needs: [build]
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ startup_stm32f10x_md_gcc.s
#.vscode/
cov-int*
/build/
/build_SITL/
/[hs]itl/
/ninja/
/obj/
Expand All @@ -27,6 +26,7 @@ cov-int*
/debug/
/release/
/*_SITL/
/*_WASM/

# script-generated files
docs/Manual.pdf
Expand Down
16 changes: 11 additions & 5 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
"name": "Linux",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/src/main/**"
"${workspaceRoot}/src/main/**",
"${workspaceRoot}/../emsdk/upstream/emscripten/cache/sysroot/include",
"${workspaceRoot}/src/main/target/SITL/**",
],
"browse": {
"limitSymbolsToIncludedHeaders": false,
"path": [
"${workspaceRoot}/**"
]
},
"intelliSenseMode": "msvc-x64",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "gcc-arm",
"cStandard": "c23",
"cppStandard": "c++23",
"defines": [
"USE_OSD",
"USE_GYRO_NOTCH_1",
Expand Down Expand Up @@ -53,7 +55,11 @@
"USE_GYRO_FFT_FILTER",
"USE_BARO_DPS310",
"USE_ADAPTIVE_FILTER",
"MCU_FLASH_SIZE 1024"
"MCU_FLASH_SIZE 1024",
"WASM_BUILD",
"USE_BLACKBOX = 0",
"CONFIG_IN_FILE",
"EEPROM_FILENAME=\"eeprom.bin\""
],
"configurationProvider": "ms-vscode.cmake-tools"
}
Expand Down
24 changes: 17 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ set(DOWNLOADS_DIR "${MAIN_DIR}/downloads")
set(TOOLS_DIR "${MAIN_DIR}/tools")

option(SITL "SITL build for host system" OFF)
option(WASM "WebAssembly build using Emscripten" OFF)

set(TOOLCHAIN_OPTIONS none arm-none-eabi host)
if (SITL)
set(TOOLCHAIN_OPTIONS none arm-none-eabi host emscripten)
if (WASM)
if (CMAKE_HOST_APPLE)
set(MACOSX TRUE)
endif()
set(TOOLCHAIN "emscripten" CACHE STRING "Toolchain to use. Available: ${TOOLCHAIN_OPTIONS}")
elseif (SITL)
if (CMAKE_HOST_APPLE)
set(MACOSX TRUE)
endif()
Expand Down Expand Up @@ -43,7 +49,7 @@ include(settings)
if(TOOLCHAIN STREQUAL none)
add_subdirectory(src/test)
else()
if (SITL)
if (SITL OR WASM)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/${TOOLCHAIN}.cmake")
else()
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${TOOLCHAIN}.cmake")
Expand All @@ -54,10 +60,13 @@ endif()

project(INAV VERSION 9.0.0)

if(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
enable_language(ASM)
endif()

enable_language(ASM)

if(MACOSX AND SITL)
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(CMAKE_C_STANDARD 11)
elseif(MACOSX AND SITL)
set(CMAKE_C_STANDARD 11)
else()
set(CMAKE_C_STANDARD 99)
Expand All @@ -84,14 +93,15 @@ set(COMMON_COMPILE_DEFINITIONS
FC_VERSION_TYPE="${VERSION_TYPE}"
)

if (NOT SITL)
if (NOT SITL AND NOT WASM)
include(openocd)
include(svd)
endif()

include(stm32)
include(at32)
include(sitl)
include(wasm)

add_subdirectory(src)

Expand Down
Empty file modified cmake/docker.sh
100755 → 100644
Empty file.
64 changes: 64 additions & 0 deletions cmake/emscripten.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Emscripten WebAssembly Toolchain Configuration
# This toolchain enables building INAV as WebAssembly (WASM) using Emscripten

set(CMAKE_SYSTEM_NAME Emscripten)
set(CMAKE_SYSTEM_PROCESSOR wasm)

# Find Emscripten compiler
find_program(EMCC emcc DOC "Emscripten C compiler")
find_program(EMXX em++ DOC "Emscripten C++ compiler")

if(NOT EMCC OR NOT EMXX)
message(FATAL_ERROR
"Emscripten not found. Please install Emscripten SDK:\n"
" https://emscripten.org/docs/getting_started/downloads.html\n"
"And activate the environment:\n"
" source ~/emsdk/emsdk_env.sh (Linux/macOS)\n"
" Or use the Windows batch file"
)
endif()

# Set compiler
set(CMAKE_C_COMPILER ${EMCC} CACHE INTERNAL "Emscripten C compiler")
set(CMAKE_CXX_COMPILER ${EMXX} CACHE INTERNAL "Emscripten C++ compiler")

# Disable C++ extensions for consistency
set(CMAKE_CXX_EXTENSIONS OFF)

# Build type defaults
if(NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Build Type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_CONFIGURATION_TYPES})

# Compiler flags for different build types
set(emscripten_debug "-O0 -g -DDEBUG")
set(emscripten_release "-O3 -DNDEBUG")

set(CMAKE_C_FLAGS_DEBUG ${emscripten_debug} CACHE INTERNAL "emscripten C compiler flags debug")
set(CMAKE_CXX_FLAGS_DEBUG ${emscripten_debug} CACHE INTERNAL "emscripten C++ compiler flags debug")

set(CMAKE_C_FLAGS_RELEASE ${emscripten_release} CACHE INTERNAL "emscripten C compiler flags release")
set(CMAKE_CXX_FLAGS_RELEASE ${emscripten_release} CACHE INTERNAL "emscripten C++ compiler flags release")

# Generic flags for all builds
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -fPIC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")

# Link flags
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")

# File extension for binaries
set(CMAKE_EXECUTABLE_SUFFIX ".js")

# Skip compiler checks that don't work with Emscripten
set(CMAKE_C_COMPILER_WORKS 1)
set(CMAKE_CXX_COMPILER_WORKS 1)

# Disable ASM support
set(CMAKE_ASM_COMPILER_WORKS 1)
97 changes: 97 additions & 0 deletions cmake/fix_wasm_build_files.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Fix WebAssembly filename references in generated files
# This script updates both the .js file and HTML file with correct versioned filenames
#
# Usage: cmake -P fix_wasm_build_files.cmake --arg <js_file> <html_file> <wasm_basename> <js_filename> [<firmware_version>]
# Example: cmake -P fix_wasm_build_files.cmake --arg inav_9.0.0_WASM.js index.html inav_9.0.0_WASM inav_9.0.0_WASM.js 9.0.0

# Parse arguments (skip the script name and --arg flag)
set(argc ${CMAKE_ARGC})
set(argv ${CMAKE_ARGV})

if(argc LESS 7)
message(FATAL_ERROR "fix_wasm_build_files.cmake requires: js_file, html_file, wasm_basename, and js_filename")
endif()

# Arguments are at positions 4, 5, 6, and 7 (0-indexed)
set(js_file "${CMAKE_ARGV4}")
set(html_file "${CMAKE_ARGV5}")
set(wasm_basename "${CMAKE_ARGV6}")
set(js_filename "${CMAKE_ARGV7}")
# Optional firmware version at position 8
if(argc GREATER 7)
set(firmware_version "${CMAKE_ARGV8}")
else()
set(firmware_version "")
endif()

if(NOT EXISTS "${js_file}")
message(FATAL_ERROR "JavaScript file not found: ${js_file}")
endif()

if(NOT EXISTS "${html_file}")
message(FATAL_ERROR "HTML file not found: ${html_file}")
endif()

message(STATUS "Fixing WASM filenames in: ${js_file}")
message(STATUS "Fixing WASM HTML in: ${html_file}")
message(STATUS "Target WASM basename: ${wasm_basename}")
message(STATUS "Target JS filename: ${js_filename}")
if(firmware_version)
message(STATUS "Firmware version: ${firmware_version}")
endif()

# ==========================================
# Part 1: Fix JavaScript file
# ==========================================

# Read the entire .js file
file(READ "${js_file}" js_content)

# Use regex to replace ANY .wasm filename with the correct one
# CMake regex doesn't support non-greedy matching, so use simpler pattern
# Pattern: return locateFile('***.wasm') - match anything up to .wasm
string(REGEX REPLACE "return locateFile\\('[^']+\\.wasm'\\)" "return locateFile('${wasm_basename}.wasm')" js_content "${js_content}")
string(REGEX REPLACE "return locateFile\\(\"[^\"]+\\.wasm\"\\)" "return locateFile(\"${wasm_basename}.wasm\")" js_content "${js_content}")

# Also handle direct locateFile calls without return
string(REGEX REPLACE "locateFile\\('[^']+\\.wasm'\\)" "locateFile('${wasm_basename}.wasm')" js_content "${js_content}")
string(REGEX REPLACE "locateFile\\(\"[^\"]+\\.wasm\"\\)" "locateFile(\"${wasm_basename}.wasm\")" js_content "${js_content}")

# Handle new URL patterns for wasm file
string(REGEX REPLACE "new URL\\('[^']+\\.wasm'" "new URL('${wasm_basename}.wasm'" js_content "${js_content}")
string(REGEX REPLACE "new URL\\(\"[^\"]+\\.wasm\"" "new URL(\"${wasm_basename}.wasm\"" js_content "${js_content}")

# Handle new URL patterns for worker js file
string(REGEX REPLACE "new Worker\\(new URL\\('[^']+\\.js'" "new Worker(new URL('${js_filename}'" js_content "${js_content}")
string(REGEX REPLACE "new Worker\\(new URL\\(\"[^\"]+\\.js\"" "new Worker(new URL(\"${js_filename}\"" js_content "${js_content}")

# Write the modified content back
file(WRITE "${js_file}" "${js_content}")

message(STATUS "Successfully updated WASM filename references to: ${wasm_basename}.wasm")

# ==========================================
# Part 2: Fix HTML file
# ==========================================

# Read the entire HTML file
file(READ "${html_file}" html_content)

# Replace the script src to load the correct WASM JS file
# Pattern: script.src = 'inav_WASM.js?t=' or ./inav_WASM.js with cache-busting
# Matches: wasmScriptTag.src = 'inav_WASM.js?t=' + Date.now(); or import('./inav_WASM.js?t=' + Date.now()); etc.
# Preserve the ./ prefix if it exists by using group 2
string(REGEX REPLACE "(['\"])(\\./)?(inav_)[^'\"]*\\.js" "\\1\\2${js_filename}" html_content "${html_content}")

# Update the version in the footer if firmware_version is provided
if(firmware_version)
string(REGEX REPLACE "INAV [0-9]+\\.[0-9]+\\.[0-9]+" "INAV ${firmware_version}" html_content "${html_content}")
endif()

# Write the modified content back
file(WRITE "${html_file}" "${html_content}")

message(STATUS "Successfully updated HTML to load: ${js_filename}")
if(firmware_version)
message(STATUS "Updated version to: INAV ${firmware_version}")
endif()
4 changes: 2 additions & 2 deletions cmake/main.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ set(MAIN_DEFINITIONS


# Can't check for OSX yet at this point
if(SITL)
if(SITL OR WASM)
set(MAIN_COMPILE_OPTIONS
-Wall
-Wextra
Expand Down Expand Up @@ -96,7 +96,7 @@ function(setup_firmware_target exe name)
get_property(targets GLOBAL PROPERTY VALID_TARGETS)
list(APPEND targets ${name})
set_property(GLOBAL PROPERTY VALID_TARGETS "${targets}")
if(NOT SITL)
if(NOT SITL AND NOT WASM)
setup_openocd(${exe} ${name})
setup_svd(${exe} ${name})
endif()
Expand Down
7 changes: 6 additions & 1 deletion cmake/settings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@ function(enable_settings exe name)
set(USE_HOST_GCC "-g")
endif()
set(output ${dir}/${SETTINGS_GENERATED_H} ${dir}/${SETTINGS_GENERATED_C})
# For WASM builds, set SETTINGS_CXX to em++ (Emscripten C++)
set(settings_cxx_env "${args_SETTINGS_CXX}")
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten" AND settings_cxx_env STREQUAL "")
set(settings_cxx_env "em++")
endif()
add_custom_command(
OUTPUT ${output}
COMMAND
${CMAKE_COMMAND} -E env CFLAGS="${cflags}" TARGET=${name} PATH="$ENV{PATH}" SETTINGS_CXX=${args_SETTINGS_CXX}
${CMAKE_COMMAND} -E env CFLAGS="${cflags}" TARGET=${name} PATH="$ENV{PATH}" SETTINGS_CXX=${settings_cxx_env} WASM_BUILD=1
${RUBY_EXECUTABLE} ${SETTINGS_GENERATOR} ${MAIN_DIR} ${SETTINGS_FILE} -o "${dir}" ${USE_HOST_GCC}
DEPENDS ${SETTINGS_GENERATOR} ${SETTINGS_FILE}
)
Expand Down
21 changes: 3 additions & 18 deletions cmake/sitl.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,10 @@ function (target_sitl name)
endif()

add_custom_target(${name} ALL
cmake -E copy $<TARGET_FILE:${exe_target}> ${exe_filename}
COMMAND cmake -E copy $<TARGET_FILE:${exe_target}> ${exe_filename}
DEPENDS ${exe_target}
COMMENT "Copying executable: ${binary_name}"
)

setup_firmware_target(${exe_target} ${name} ${ARGN})
#clean_<target>
set(generator_cmd "")
if (CMAKE_GENERATOR STREQUAL "Unix Makefiles")
set(generator_cmd "make")
elseif(CMAKE_GENERATOR STREQUAL "Ninja")
set(generator_cmd "ninja")
endif()
if (NOT generator_cmd STREQUAL "")
set(clean_target "clean_${name}")
add_custom_target(${clean_target}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND ${generator_cmd} clean
COMMENT "Removing intermediate files for ${name}")
set_property(TARGET ${clean_target} PROPERTY
EXCLUDE_FROM_ALL 1
EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
endfunction()
Loading