blob: 89825c5a56c4b539f8677fefad6ccddb903fa44d [file]
# 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)