| # Copyright 2026 The IREE Authors |
| # |
| # Licensed under the Apache License v2.0 with LLVM Exceptions. |
| # See https://llvm.org/LICENSE.txt for license information. |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| cmake_minimum_required(VERSION 3.26) |
| |
| # CMake invokes the toolchain file twice during the first build, but only once |
| # during subsequent rebuilds. This was causing the various flags to be added |
| # twice on the first build, and on a rebuild ninja would see only one set of the |
| # flags and rebuild the world. |
| # https://github.com/android-ndk/ndk/issues/323 |
| if(IREE_WASM32_WASI_TOOLCHAIN_INCLUDED) |
| return() |
| endif() |
| set(IREE_WASM32_WASI_TOOLCHAIN_INCLUDED true) |
| |
| # WASI is a hosted environment — musl libc, libc++, full C/C++ stdlib. |
| # For freestanding production builds, use wasm32.cmake instead. |
| set(CMAKE_SYSTEM_NAME WASI) |
| set(CMAKE_SYSTEM_PROCESSOR wasm32) |
| |
| #------------------------------------------------------------------------------- |
| # wasi-sdk download (shared with wasm32.cmake) |
| #------------------------------------------------------------------------------- |
| |
| # Parse version and SHA-256 from the shared Bazel/CMake config file. |
| # This is the single source of truth — do not duplicate version constants here. |
| set(_WASI_SDK_VERSION_FILE "${CMAKE_CURRENT_LIST_DIR}/../wasm/wasi_sdk_version.bzl") |
| file(READ "${_WASI_SDK_VERSION_FILE}" _WASI_SDK_BZL) |
| |
| string(REGEX MATCH "WASI_SDK_VERSION = \"([^\"]+)\"" _ "${_WASI_SDK_BZL}") |
| set(_WASI_SDK_VERSION "${CMAKE_MATCH_1}") |
| string(REGEX MATCH "WASI_SDK_TAG = \"([^\"]+)\"" _ "${_WASI_SDK_BZL}") |
| set(_WASI_SDK_TAG "${CMAKE_MATCH_1}") |
| |
| # Detect host platform. |
| if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") |
| set(_WASI_SDK_OS "linux") |
| elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") |
| set(_WASI_SDK_OS "macos") |
| else() |
| message(FATAL_ERROR "wasi-sdk: unsupported host OS: ${CMAKE_HOST_SYSTEM_NAME}") |
| endif() |
| |
| cmake_host_system_information(RESULT _HOST_ARCH QUERY OS_PLATFORM) |
| if(_HOST_ARCH MATCHES "x86_64|AMD64") |
| set(_WASI_SDK_ARCH "x86_64") |
| elseif(_HOST_ARCH MATCHES "aarch64|arm64") |
| set(_WASI_SDK_ARCH "arm64") |
| else() |
| message(FATAL_ERROR "wasi-sdk: unsupported host architecture: ${_HOST_ARCH}") |
| endif() |
| |
| # Extract the SHA-256 hash for this platform from the .bzl dict. |
| set(_WASI_SDK_PLATFORM_KEY "${_WASI_SDK_ARCH}-${_WASI_SDK_OS}") |
| string(REGEX MATCH "\"${_WASI_SDK_PLATFORM_KEY}\": \"([^\"]+)\"" _ "${_WASI_SDK_BZL}") |
| set(_WASI_SDK_SHA256 "${CMAKE_MATCH_1}") |
| if("${_WASI_SDK_SHA256}" STREQUAL "") |
| message(FATAL_ERROR "wasi-sdk: no SHA-256 hash for platform ${_WASI_SDK_PLATFORM_KEY}") |
| endif() |
| |
| #------------------------------------------------------------------------------- |
| # Compiler tools |
| #------------------------------------------------------------------------------- |
| |
| # Users can provide IREE_WASI_SDK_ROOT to skip the download. |
| # Otherwise we download wasi-sdk into the build directory. |
| if(NOT "${IREE_WASI_SDK_ROOT}" STREQUAL "") |
| set(_WASI_SDK_ROOT "${IREE_WASI_SDK_ROOT}") |
| else() |
| # Download wasi-sdk into the build tree (idempotent via stamp file). |
| set(_WASI_SDK_BASENAME "wasi-sdk-${_WASI_SDK_VERSION}-${_WASI_SDK_ARCH}-${_WASI_SDK_OS}") |
| set(_WASI_SDK_URL |
| "https://github.com/WebAssembly/wasi-sdk/releases/download/${_WASI_SDK_TAG}/${_WASI_SDK_BASENAME}.tar.gz") |
| |
| # Download location: inside the build directory so it persists across |
| # reconfigures without leaving generated files in the source tree. |
| get_filename_component(_WASI_SDK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/wasi-sdk" ABSOLUTE) |
| set(_WASI_SDK_ROOT "${_WASI_SDK_DOWNLOAD_DIR}/${_WASI_SDK_BASENAME}") |
| set(_WASI_SDK_ARCHIVE "${_WASI_SDK_DOWNLOAD_DIR}/${_WASI_SDK_BASENAME}.tar.gz") |
| set(_WASI_SDK_STAMP "${_WASI_SDK_DOWNLOAD_DIR}/${_WASI_SDK_BASENAME}.stamp") |
| set(_WASI_SDK_STAMP_CONTENT "${_WASI_SDK_URL} : ${_WASI_SDK_SHA256}") |
| |
| set(_NEEDS_DOWNLOAD ON) |
| if(EXISTS "${_WASI_SDK_STAMP}" AND IS_DIRECTORY "${_WASI_SDK_ROOT}") |
| file(READ "${_WASI_SDK_STAMP}" _STAMP_CONTENTS) |
| if("${_STAMP_CONTENTS}" STREQUAL "${_WASI_SDK_STAMP_CONTENT}") |
| set(_NEEDS_DOWNLOAD OFF) |
| endif() |
| endif() |
| |
| if(_NEEDS_DOWNLOAD) |
| message(STATUS "Downloading wasi-sdk ${_WASI_SDK_VERSION} for ${_WASI_SDK_PLATFORM_KEY} from ${_WASI_SDK_URL}") |
| file(MAKE_DIRECTORY "${_WASI_SDK_DOWNLOAD_DIR}") |
| file(DOWNLOAD "${_WASI_SDK_URL}" "${_WASI_SDK_ARCHIVE}" |
| EXPECTED_HASH SHA256=${_WASI_SDK_SHA256} |
| SHOW_PROGRESS) |
| message(STATUS "Extracting wasi-sdk to ${_WASI_SDK_ROOT}") |
| file(ARCHIVE_EXTRACT INPUT "${_WASI_SDK_ARCHIVE}" DESTINATION "${_WASI_SDK_DOWNLOAD_DIR}") |
| file(REMOVE "${_WASI_SDK_ARCHIVE}") |
| file(WRITE "${_WASI_SDK_STAMP}" "${_WASI_SDK_STAMP_CONTENT}") |
| else() |
| message(STATUS "Using cached wasi-sdk at ${_WASI_SDK_ROOT}") |
| endif() |
| endif() |
| |
| set(CMAKE_C_COMPILER "${_WASI_SDK_ROOT}/bin/clang") |
| set(CMAKE_CXX_COMPILER "${_WASI_SDK_ROOT}/bin/clang++") |
| set(CMAKE_AR "${_WASI_SDK_ROOT}/bin/llvm-ar") |
| set(CMAKE_RANLIB "${_WASI_SDK_ROOT}/bin/llvm-ranlib") |
| set(CMAKE_STRIP "${_WASI_SDK_ROOT}/bin/llvm-strip") |
| set(CMAKE_LINKER "${_WASI_SDK_ROOT}/bin/wasm-ld") |
| set(CMAKE_SYSROOT "${_WASI_SDK_ROOT}/share/wasi-sysroot") |
| |
| # Tell CMake we know the compiler works (skip test compile for cross toolchain). |
| set(CMAKE_C_COMPILER_WORKS ON) |
| set(CMAKE_CXX_COMPILER_WORKS ON) |
| |
| #------------------------------------------------------------------------------- |
| # Compiler and linker flags |
| #------------------------------------------------------------------------------- |
| |
| # Detect the clang resource directory version for explicit -resource-dir. |
| file(GLOB _CLANG_VERSION_DIRS "${_WASI_SDK_ROOT}/lib/clang/*") |
| list(LENGTH _CLANG_VERSION_DIRS _CLANG_VERSION_COUNT) |
| if(NOT _CLANG_VERSION_COUNT EQUAL 1) |
| message(FATAL_ERROR "Expected exactly one clang version directory under lib/clang/, got: ${_CLANG_VERSION_DIRS}") |
| endif() |
| list(GET _CLANG_VERSION_DIRS 0 _CLANG_RESOURCE_DIR) |
| |
| set(IREE_WASM32_WASI_COMPILE_FLAGS "\ |
| --target=wasm32-wasi \ |
| --sysroot=${CMAKE_SYSROOT} \ |
| -resource-dir=${_CLANG_RESOURCE_DIR} \ |
| -fno-exceptions \ |
| -fno-rtti \ |
| -fvisibility=hidden \ |
| -fno-short-wchar \ |
| -mbulk-memory \ |
| -msign-ext \ |
| -mnontrapping-fptoint \ |
| -DIREE_PLATFORM_WEB=1 \ |
| -DIREE_SYNCHRONIZATION_DISABLE_UNSAFE=1 \ |
| -DIREE_THREADING_ENABLE=0 \ |
| -D_WASI_EMULATED_SIGNAL \ |
| -D_WASI_EMULATED_PROCESS_CLOCKS \ |
| -DCLOCK_THREAD_CPUTIME_ID=CLOCK_MONOTONIC \ |
| -DBENCHMARK_OS_NACL \ |
| -DGTEST_HAS_STREAM_REDIRECTION=0 \ |
| -DGTEST_HAS_EXCEPTIONS=0") |
| |
| # C++ flags: explicit include paths for libc++ headers. |
| # clang's auto-detection of C++ include paths can fail in non-standard |
| # directory layouts (symlinks, relocated SDKs). Explicit -isystem |
| # makes the build independent of clang's InstalledDir probing. |
| set(IREE_WASM32_WASI_CXX_FLAGS "\ |
| -std=c++17 \ |
| -isystem ${CMAKE_SYSROOT}/include/wasm32-wasi/c++/v1 \ |
| -isystem ${CMAKE_SYSROOT}/include/c++/v1") |
| |
| set(IREE_WASM32_WASI_LINK_FLAGS "\ |
| --target=wasm32-wasi \ |
| --sysroot=${CMAKE_SYSROOT} \ |
| -resource-dir=${_CLANG_RESOURCE_DIR} \ |
| -fuse-ld=lld \ |
| -Wl,--undefined=__main_argc_argv \ |
| -lwasi-emulated-signal \ |
| -lwasi-emulated-process-clocks \ |
| -lc++ \ |
| -lc++abi") |
| |
| set(CMAKE_C_FLAGS "${IREE_WASM32_WASI_COMPILE_FLAGS} ${CMAKE_C_FLAGS}") |
| set(CMAKE_CXX_FLAGS "${IREE_WASM32_WASI_COMPILE_FLAGS} ${IREE_WASM32_WASI_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") |
| set(CMAKE_ASM_FLAGS "${IREE_WASM32_WASI_COMPILE_FLAGS} ${CMAKE_ASM_FLAGS}") |
| set(CMAKE_EXE_LINKER_FLAGS "${IREE_WASM32_WASI_LINK_FLAGS}" CACHE STRING "" FORCE) |
| set(CMAKE_SHARED_LINKER_FLAGS "${IREE_WASM32_WASI_LINK_FLAGS}" CACHE STRING "" FORCE) |
| set(CMAKE_MODULE_LINKER_FLAGS "${IREE_WASM32_WASI_LINK_FLAGS}" CACHE STRING "" FORCE) |
| |
| #------------------------------------------------------------------------------- |
| # IREE build configuration |
| #------------------------------------------------------------------------------- |
| |
| set(CMAKE_CROSSCOMPILING ON CACHE BOOL "") |
| |
| # Disable gtest features unavailable on WASI. gtest_disable_pthreads prevents |
| # gtest from adding -lpthread. GTEST_HAS_EXCEPTIONS is also set in CFLAGS |
| # above, but gtest's CMakeLists.txt adds -fexceptions explicitly unless we |
| # tell it not to via this cache variable. |
| set(gtest_disable_pthreads ON CACHE BOOL "" FORCE) |
| |
| # Wasm test binaries are not directly executable on the host. Tests that need |
| # to run under wasm use their own runner (e.g., iree_wasm_cc_test with the |
| # bundled Node.js entry point). No CMAKE_CROSSCOMPILING_EMULATOR is set. |
| find_program(NODE_EXECUTABLE node REQUIRED) |
| |
| # The compiler is not cross-compiled — it runs on the host. |
| set(IREE_BUILD_COMPILER OFF CACHE BOOL "" FORCE) |
| |
| set(IREE_BUILD_TESTS ON CACHE BOOL "" FORCE) |
| set(IREE_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) |
| set(IREE_BUILD_BINDINGS_TFLITE OFF CACHE BOOL "" FORCE) |
| set(IREE_BUILD_BINDINGS_TFLITE_JAVA OFF CACHE BOOL "" FORCE) |
| |
| # WASI preview1 is single-threaded. Disable synchronization and threading |
| # so iree_copts.cmake injects the correct -D flags for all IREE targets. |
| # The -D flags in IREE_WASM32_WASI_COMPILE_FLAGS above handle non-IREE |
| # targets (gtest, benchmark) that don't use iree_copts. |
| set(IREE_ENABLE_THREADING OFF CACHE BOOL "" FORCE) |
| set(IREE_SYNCHRONIZATION_DISABLE_UNSAFE ON CACHE BOOL "" FORCE) |
| |
| # HAL configuration: local-sync only (local-task requires threading). |
| set(IREE_HAL_DRIVER_DEFAULTS OFF CACHE BOOL "" FORCE) |
| set(IREE_HAL_DRIVER_LOCAL_SYNC ON CACHE BOOL "" FORCE) |
| set(IREE_HAL_DRIVER_LOCAL_TASK OFF CACHE BOOL "" FORCE) |
| |
| # Executable loaders are opt-in for wasm. Enabling VMVX here pulls the VM |
| # bytecode schema-generation path into cross builds, which requires host tools. |
| set(IREE_HAL_EXECUTABLE_LOADER_DEFAULTS OFF CACHE BOOL "" FORCE) |
| set(IREE_HAL_EXECUTABLE_LOADER_VMVX_MODULE OFF CACHE BOOL "" FORCE) |
| set(IREE_HAL_EXECUTABLE_PLUGIN_DEFAULTS OFF CACHE BOOL "" FORCE) |