Merge branch 'main' into sy/experimental-threading

pull/640/head
Sy Brand 3 months ago
commit 1ace2ed4da

@ -7,7 +7,11 @@ runs:
- name: Setup `wasmtime` for tests - name: Setup `wasmtime` for tests
uses: bytecodealliance/actions/wasmtime/setup@v1 uses: bytecodealliance/actions/wasmtime/setup@v1
with: with:
<<<<<<< HEAD
version: "dev" version: "dev"
=======
version: "44.0.0"
>>>>>>> main
- name: Install ccache, ninja (macOS) - name: Install ccache, ninja (macOS)
run: brew install ccache ninja run: brew install ccache ninja
if: runner.os == 'macOS' if: runner.os == 'macOS'

@ -28,6 +28,21 @@ jobs:
- artifact: arm64-linux - artifact: arm64-linux
os: ubuntu-22.04-arm os: ubuntu-22.04-arm
- artifact: riscv64-linux
os: ubuntu-24.04
rust_target: riscv64gc-unknown-linux-gnu
cross_cmake_args: >-
-DCMAKE_SYSTEM_NAME=Linux
-DCMAKE_SYSTEM_PROCESSOR=riscv64
-DCMAKE_C_COMPILER=riscv64-linux-gnu-gcc
-DCMAKE_CXX_COMPILER=riscv64-linux-gnu-g++
-DWASI_SDK_LLDB=OFF
env:
WASI_SDK_CI_SKIP_SYSROOT: 1
WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >-
-DCMAKE_SYSTEM_NAME=Linux
-DCMAKE_SYSTEM_PROCESSOR=riscv64
- artifact: arm64-macos - artifact: arm64-macos
os: macos-14 os: macos-14
rust_target: aarch64-apple-darwin rust_target: aarch64-apple-darwin
@ -53,7 +68,7 @@ jobs:
env: ${{ matrix.env || fromJSON('{}') }} env: ${{ matrix.env || fromJSON('{}') }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: ./.github/actions/checkout - uses: ./.github/actions/checkout
@ -65,7 +80,7 @@ jobs:
# Bump the prefix number to evict all previous caches and enforce a clean # Bump the prefix number to evict all previous caches and enforce a clean
# build, in the unlikely case that some weird build error occur and ccache # build, in the unlikely case that some weird build error occur and ccache
# becomes a potential suspect. # becomes a potential suspect.
- uses: actions/cache@v4 - uses: actions/cache@v5
id: cache-restore id: cache-restore
with: with:
path: ${{ runner.tool_cache }}/ccache path: ${{ runner.tool_cache }}/ccache
@ -85,6 +100,9 @@ jobs:
rustup target add ${{ matrix.rust_target }} rustup target add ${{ matrix.rust_target }}
cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}" cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}"
fi fi
if [ "${{ matrix.cross_cmake_args }}" != "" ]; then
cmake_args="$cmake_args ${{ matrix.cross_cmake_args }}"
fi
echo WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS="$cmake_args" >> $GITHUB_ENV echo WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS="$cmake_args" >> $GITHUB_ENV
shell: bash shell: bash
@ -125,7 +143,7 @@ jobs:
# Upload the `dist` folder from the build as the artifacts for this # Upload the `dist` folder from the build as the artifacts for this
# runner. # runner.
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v6
with: with:
name: ${{ format( 'dist-{0}', matrix.artifact) }} name: ${{ format( 'dist-{0}', matrix.artifact) }}
path: build/dist path: build/dist
@ -149,11 +167,48 @@ jobs:
# time instead of having to recreate everything each time a failure # time instead of having to recreate everything each time a failure
# happens. # happens.
- if: always() && steps.cache-restore.outputs.cache-hit != 'true' - if: always() && steps.cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4 uses: actions/cache/save@v5
with: with:
path: ${{ runner.tool_cache }}/ccache path: ${{ runner.tool_cache }}/ccache
key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }} key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }}
build-only-sysroot:
name: Build only sysroot - ${{ matrix.name }}
runs-on: ubuntu-24.04
strategy:
matrix:
include:
- name: default
- name: exceptions
defines: -DWASI_SDK_EXCEPTIONS=ON
- name: static
defines: -DWASI_SDK_BUILD_SHARED=OFF
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
- uses: ./.github/actions/install-deps
- run: cargo install wasm-component-ld@0.5.21
- name: Install LLVM 22
run: |
v=22
rel=$(lsb_release -cs)
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/keyrings/llvm.asc
echo "deb [signed-by=/etc/apt/keyrings/llvm.asc] http://apt.llvm.org/$rel/ llvm-toolchain-$rel-$v main" | sudo tee /etc/apt/sources.list.d/llvm-$v.list
sudo apt-get update -y && sudo apt-get install -y clang-$v lld-$v
- run: |
cmake -G Ninja -B build -S . \
-DCMAKE_C_COMPILER=/usr/lib/llvm-22/bin/clang \
-DCMAKE_SYSTEM_NAME=WASI \
-DWASI_SDK_INCLUDE_TESTS=ON \
-DWASI_SDK_CPU_CFLAGS="" \
-DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF \
-DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF \
${{ matrix.defines }}
- run: ninja -C build
- run: ctest --output-on-failure --parallel 10 --test-dir build/tests
# Once all of the above matrix entries have completed this job will run and # Once all of the above matrix entries have completed this job will run and
# assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot # assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot
# artifacts. # artifacts.
@ -162,16 +217,16 @@ jobs:
needs: build needs: build
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: ./.github/actions/checkout - uses: ./.github/actions/checkout
# Download all artifacts from all platforms in `build`, merge them into # Download all artifacts from all platforms in `build`, merge them into
# final wasi-sdk-* artifacts, and then upload them. # final wasi-sdk-* artifacts, and then upload them.
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v8
- run: ./ci/merge-artifacts.sh - run: ./ci/merge-artifacts.sh
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v6
with: with:
name: release-artifacts name: release-artifacts
path: dist path: dist
@ -222,12 +277,12 @@ jobs:
needs: build needs: build
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: ./.github/actions/checkout - uses: ./.github/actions/checkout
- uses: ./.github/actions/install-deps - uses: ./.github/actions/install-deps
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v8
with: with:
name: dist-x86_64-linux name: dist-x86_64-linux
path: dist-x86_64-linux path: dist-x86_64-linux

1
.gitignore vendored

@ -1,2 +1,3 @@
build build
dist dist
experimental

@ -1,25 +1,14 @@
# Support for C++ Exceptions # Support for C++ Exceptions
The released artifacts for wasi-sdk at this time do not support C++ exceptions. From wasi-sdk-33 and onwards the artifacts produced by this repository support
LLVM and Clang, however, have support for C++ exceptions in WebAssembly and this compiling C++ code both with and without exceptions. The sysroot for wasm
is intended to serve as documentation of the current state of affairs of using targets contains two copies of the C++ standard library and headers -- one with
C++ exceptions. It should be noted though that the current status of C++ exceptions enabled and one with exceptions disabled. These are automatically
exceptions support is not intended to be the final state of support, and this is selected based on compilation flags. This means that wasi-sdk-produced binaries
all continuing to be iterated on over time. can avoid using wasm exceptions entirely by disabling C++ exceptions, or C++
exceptions can be enabled in which case wasm exceptions will be used.
## Building wasi-sdk with exceptions Currently the default is for C++ exceptions to be disabled.
When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON`
to enable support for C++ exceptions. For example:
```shell script
$ cmake -G Ninja -B build/sysroot -S . \
-DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \
-DWASI_SDK_EXCEPTIONS=ON
```
The C++ standard library will be compiled with support for exceptions for the
desired targets and the resulting sysroot supports using exceptions.
## Compiling code with C++ exceptions ## Compiling code with C++ exceptions
@ -36,25 +25,47 @@ This can be specified for example with:
```shell script ```shell script
$ export CFLAGS="-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false" $ export CFLAGS="-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false"
$ export LDFLAGS="-lunwind" $ export LDFLAGS="-fwasm-exceptions -lunwind"
``` ```
## Limitations Note that `-fwasm-exceptions` must be present when linking to select the
correct C++ standard library to link.
## Building wasi-sdk with exceptions
When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON`
to enable support for C++ exceptions. For example:
```shell script
$ cmake -G Ninja -B build/sysroot -S . \
-DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \
-DWASI_SDK_EXCEPTIONS=ON
```
Currently C++ exceptions support in wasi-sdk does not support shared libraries. The C++ standard library will be compiled with support for exceptions for the
Fixing this will require resolving some miscellaneous build issues in this desired targets and the resulting sysroot supports using exceptions. Note that
repository itself. enabling C++ exceptions requires LLVM 22 or later.
C++ exceptions are disabled by default for local builds. With a future release
of LLVM 23 the dual-sysroot nature will be on-by-default.
## Limitations
## Future Plans There are a few known limitations/bugs/todos around exceptions support in
wasi-sdk at this time:
There are a few tracking issues with historical discussion about C++ exceptions * Currently C++ exceptions support in wasi-sdk does not support shared
support in wasi-sdk such as [#334](https://github.com/WebAssembly/wasi-sdk/issues/334) libraries. Fixing this will require resolving some miscellaneous build
and [#565](https://github.com/WebAssembly/wasi-sdk/issues/565). The major issues in this repository itself as well as [resolving some upstream
remaining items are: issues](https://github.com/llvm/llvm-project/issues/188077).
* Currently `-fwasm-exceptions` is a required flag to enable C++ exceptions.
It's unclear whether `-fexceptions` should also be supported as a substitute.
* Currently LLVM defaults to using the legacy exception-handling proposal and
this will likely change in the future. Precompiled libraries for wasi-sdk are
all built with the standard exception-handling proposal.
* Currently `-lunwind` is required when linking, but this may become automatic
in the future.
* Currently exceptions and LTO have a known issue and may not work. See
[WebAssembly/wasi-sdk#629][#629] for more information.
* Figure out support for shared libraries. [#629]: https://github.com/WebAssembly/wasi-sdk/issues/629
* Determine how to ship a sysroot that supports both with-and-without
exceptions.
* Figure out how to avoid the need for extra compiler flags when using
exceptions.
* Figure out if a new wasm target is warranted.

@ -90,6 +90,9 @@ in compiling WebAssembly code. Supported CMake flags are:
to enable/disable. The default is `-mcpu=lime1`. to enable/disable. The default is `-mcpu=lime1`.
* `-DWASI_SDK_LTO=ON` - whether to enable/disable builds of LTO-capable * `-DWASI_SDK_LTO=ON` - whether to enable/disable builds of LTO-capable
libraries as part of the build. libraries as part of the build.
* `-DWASI_SDK_BUILD_SHARED=ON` - whether to build shared libraries (`libc.so`,
`libc++.so`, etc.) for targets that support them. Set to `OFF` to produce a
static-only sysroot.
* `-DWASI_SDK_EXCEPTIONS=ON` - whether to enable/disable support for C++ * `-DWASI_SDK_EXCEPTIONS=ON` - whether to enable/disable support for C++
exceptions, see [CppExceptions.md](./CppExceptions.md) for more information. exceptions, see [CppExceptions.md](./CppExceptions.md) for more information.
* `-DWASI_SDK_TEST_HOST_TOOLCHAIN=ON` - test the host toolchain's wasi-libc and * `-DWASI_SDK_TEST_HOST_TOOLCHAIN=ON` - test the host toolchain's wasi-libc and
@ -209,41 +212,30 @@ disabled in a configure step before building with WASI SDK.
## Notable Limitations ## Notable Limitations
This repository does not yet support __C++ exceptions__. C++ code is supported * C++ exceptions are disabled by default and require extra configuration to get
only with -fno-exceptions for now. working, see [CppExceptions.md].
Work on support for [exception handling] is underway at the * C `setjmp`/`longjmp` require some extra configuration to get working, see
language level which will support the features. [SetjmpLongjmp.md].
* Most targets do not support spawning a thread. Experimental support for
[exception handling]: https://github.com/WebAssembly/exception-handling/ spawning threads is available with the `wasm32-wasip1-threads` target which
uses [wasi-threads]. Note that the `pthread_*` family of functions, as well as
See [C setjmp/longjmp support] about setjmp/longjmp support. C++ threading primitives such as `<atomic>`, `<mutex>`, and `<thread>` are
available on all targets. Defining a macro `_WASI_STRICT_PTHREAD` will make
[C setjmp/longjmp support]: SetjmpLongjmp.md `pthread_create`, `pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, and
`pthread_timedjoin_np` fail with a compile time error when building for
This repository experimentally supports __threads__ with single-threaded targets.
`--target=wasm32-wasip1-threads`. It uses WebAssembly's [threads] primitives * Dynamic linking [is supported][dylink] but not as fully baked as static
(atomics, `wait`/`notify`, shared memory) and [wasi-threads] for spawning linking. There might be obscure bugs in some situations related to dynamic
threads. Note: this is experimental &mdash; do not expect long-term stability! linking.
* The WASIp1 targets do not support networking, but WASIp2/WASIp3 support
Note that the `pthread_*` family of functions, as well as C++ threading primitives networking.
such as `<atomic>`, `<mutex>`, and `<thread>` are available on all targets. * 64-bit linear memories (a "wasm64" target) are not supported at this time.
Any attempt to spawn a thread will fail on `--target=wasm32-wasip1` or Supporting this will require resolving [WebAssembly/component-model#22] first
`--target=wasm32-wasip2`, but other functionality, such as locks, still works. at which point it will be possible to add a `wasm64-wasip2` target. There are
This makes it easier to port C++ codebases, as only a fraction of code needs no plans to add support for `wasm64-wasi{,-threads,p1,p1-threads}` at this
to be modified to build for the single-threaded targets. time.
Defining a macro `_WASI_STRICT_PTHREAD` will make `pthread_create`,
`pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, and `pthread_timedjoin_np`
fail with a compile time error when building for single-threaded targets.
[threads]: https://github.com/WebAssembly/threads [threads]: https://github.com/WebAssembly/threads
[wasi-threads]: https://github.com/WebAssembly/wasi-threads [wasi-threads]: https://github.com/WebAssembly/wasi-threads
[dylink]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
This repository does not yet support __dynamic libraries__. While there are [WebAssembly/component-model#22]: https://github.com/WebAssembly/component-model/issues/22
[some efforts] to design a system for dynamic libraries in wasm, it is still in
development and not yet generally usable.
[some efforts]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
There is no support for __networking__. It is a goal of WASI to support
networking in the future though.

@ -37,6 +37,7 @@ cmake -G Ninja -B $build_dir/sysroot -S . \
-DCMAKE_C_COMPILER_WORKS=ON \ -DCMAKE_C_COMPILER_WORKS=ON \
-DCMAKE_CXX_COMPILER_WORKS=ON \ -DCMAKE_CXX_COMPILER_WORKS=ON \
-DWASI_SDK_INCLUDE_TESTS=ON \ -DWASI_SDK_INCLUDE_TESTS=ON \
-DWASI_SDK_EXCEPTIONS=DUAL \
"-DCMAKE_INSTALL_PREFIX=$build_dir/install" "-DCMAKE_INSTALL_PREFIX=$build_dir/install"
ninja -C $build_dir/sysroot install dist -v ninja -C $build_dir/sysroot install dist -v

@ -15,8 +15,13 @@ fi
set -x set -x
# Build the Docker imager # Build the Docker image. Use an artifact-specific Dockerfile if one exists
docker build --tag wasi-sdk-builder ci/docker # (e.g. ci/docker/Dockerfile.riscv64-linux), otherwise use the default.
dockerfile=ci/docker/Dockerfile
if [ -f "ci/docker/Dockerfile.$1" ]; then
dockerfile="ci/docker/Dockerfile.$1"
fi
docker build --tag wasi-sdk-builder --file "$dockerfile" ci/docker
# Perform the build in `/src`. The current directory is mounted read-write at # Perform the build in `/src`. The current directory is mounted read-write at
# this location as well. To ensure that container-created files are reasonable # this location as well. To ensure that container-created files are reasonable
@ -34,9 +39,11 @@ args="$args --volume $ccache_dir:/ccache:Z --env CCACHE_DIR=/ccache"
# Inherit some tools from the host into this container. This ensures that the # Inherit some tools from the host into this container. This ensures that the
# decision made on CI of what versions to use is the canonical source of truth # decision made on CI of what versions to use is the canonical source of truth
# for theset ools # for these tools.
args="$args --volume `rustc --print sysroot`:/rustc:ro" args="$args --volume `rustc --print sysroot`:/rustc:ro"
args="$args --volume $(dirname $(which wasmtime)):/wasmtime:ro" if [ "${WASI_SDK_CI_SKIP_SYSROOT:-}" != "1" ]; then
args="$args --volume $(dirname $(command -v wasmtime)):/wasmtime:ro"
fi
# Pass through some env vars that `build.sh` reads # Pass through some env vars that `build.sh` reads
args="$args --env WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS" args="$args --env WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS"

@ -12,7 +12,8 @@ RUN dnf install -y \
python3 \ python3 \
git \ git \
unzip \ unzip \
cmake cmake \
ncurses-devel
COPY ./install-ccache.sh . COPY ./install-ccache.sh .
RUN ./install-ccache.sh RUN ./install-ccache.sh

@ -0,0 +1,28 @@
# Ubuntu 24.04 is used here (rather than AlmaLinux 8) because it has
# riscv64 cross-compilation packages in its repositories.
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
crossbuild-essential-riscv64 \
clang \
lld \
python3 \
git \
unzip \
cmake \
ninja-build \
ccache \
&& rm -rf /var/lib/apt/lists/*
# Cargo needs an explicit linker when cross-compiling for riscv64.
# The C/C++ cross-compiler is passed via CMAKE_C/CXX_COMPILER cmake flags
# rather than CC/CXX env vars so that LLVM's native tblgen sub-build can
# still find the host compiler (cmake cache vars are not inherited by
# subprocess cmake invocations, but env vars are).
ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc
# Tell programs to cache in a location that both isn't a `--volume` mounted root
# and isn't `/root` in the container as that won't be writable during the build.
ENV XDG_CACHE_HOME=/tmp/cache

@ -34,8 +34,9 @@ make_deb() {
fi fi
case $build in case $build in
dist-x86_64-linux) deb_arch=amd64 ;; dist-x86_64-linux) deb_arch=amd64 ;;
dist-arm64-linux) deb_arch=arm64 ;; dist-arm64-linux) deb_arch=arm64 ;;
dist-riscv64-linux) deb_arch=riscv64 ;;
*) *)
echo "unknown build $build" echo "unknown build $build"
exit 1 exit 1

@ -20,14 +20,34 @@ message(STATUS "Found executable for `ar`: ${CMAKE_AR}")
find_program(MAKE make REQUIRED) find_program(MAKE make REQUIRED)
set(EXCEPTIONS_DEFAULT "OFF")
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 23.0.0)
set(EXCEPTIONS_DEFAULT "DUAL")
endif()
option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" ON) option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" ON)
option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF) option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF)
option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF) option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF)
option(WASI_SDK_LTO "Whether or not to build LTO assets" ON) option(WASI_SDK_LTO "Whether or not to build LTO assets" ON)
<<<<<<< HEAD
option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF) option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF)
option(WASI_SDK_COOP_THREADING "Whether or not to build with cooperative threading support" OFF) option(WASI_SDK_COOP_THREADING "Whether or not to build with cooperative threading support" OFF)
=======
option(WASI_SDK_BUILD_SHARED "Whether or not to build shared libraries when supported" ON)
set(WASI_SDK_EXCEPTIONS "${EXCEPTIONS_DEFAULT}" CACHE STRING "Whether or not C++ exceptions are enabled")
>>>>>>> main
set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable") set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable")
if ((WASI_SDK_EXCEPTIONS STREQUAL "DUAL") OR (WASI_SDK_EXCEPTIONS STREQUAL "ON"))
if(CMAKE_C_COMPILER_VERSION VERSION_LESS 22.0.0)
message(FATAL_ERROR "enabling C++ exceptions requires Clang 22 or later")
endif()
elseif(WASI_SDK_EXCEPTIONS STREQUAL "OFF")
# No extra validation needed
else()
message(FATAL_ERROR "unknown WASI_SDK_EXCEPTIONS value ${WASI_SDK_EXCEPTIONS}, expected one of: OFF, ON, DUAL")
endif()
set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install)
set(wasi_sysroot ${wasi_tmp_install}/share/wasi-sysroot) set(wasi_sysroot ${wasi_tmp_install}/share/wasi-sysroot)
set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir) set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir)
@ -102,11 +122,27 @@ function(define_compiler_rt target)
add_dependencies(compiler-rt-build compiler-rt-build-${target}) add_dependencies(compiler-rt-build compiler-rt-build-${target})
endfunction() endfunction()
<<<<<<< HEAD
define_compiler_rt(wasm32-wasip3) define_compiler_rt(wasm32-wasip3)
=======
# The `compiler-rt` for `wasm32-wasip1` will be reused for `wasm32-wasip2` and
# `wasm32-wasi`. The version for `wasm32-wasip1-threads` will be reused for
# `wasm32-wasi-threads`. Different builds are needed for different codegen flags
# and such across the threaded/not target.
define_compiler_rt(wasm32-wasip1)
define_compiler_rt(wasm32-wasip1-threads)
>>>>>>> main
# If a p3 target is requested, also build compiler-rt for that target. WASIp3
# will eventually have a different ABI than wasm32-wasip2, so this separate
# build is needed.
if(WASI_SDK_TARGETS MATCHES p3)
define_compiler_rt(wasm32-wasip3)
endif()
# In addition to the default installation of `compiler-rt` itself also copy # In addition to the default installation of `compiler-rt` itself also copy
# around some headers and make copies of the `wasi` directory as `wasip1` and # around some headers and make copies of the `wasi` directory as `wasip1` and
# `wasip2` # `wasip2` and `wasip3`
execute_process( execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-resource-dir COMMAND ${CMAKE_C_COMPILER} -print-resource-dir
OUTPUT_VARIABLE clang_resource_dir OUTPUT_VARIABLE clang_resource_dir
@ -124,7 +160,6 @@ add_dependencies(compiler-rt-post-build compiler-rt-build)
add_custom_target(compiler-rt DEPENDS compiler-rt-build compiler-rt-post-build) add_custom_target(compiler-rt DEPENDS compiler-rt-build compiler-rt-post-build)
# ============================================================================= # =============================================================================
# wasi-libc build logic # wasi-libc build logic
# ============================================================================= # =============================================================================
@ -134,8 +169,13 @@ function(define_wasi_libc_sub target target_suffix lto)
get_property(directory_cflags DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS) get_property(directory_cflags DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS)
set(extra_cflags_list "${WASI_SDK_CPU_CFLAGS} ${CMAKE_C_FLAGS} ${directory_cflags}") set(extra_cflags_list "${WASI_SDK_CPU_CFLAGS} ${CMAKE_C_FLAGS} ${directory_cflags}")
<<<<<<< HEAD
if(${target} MATCHES p2 OR ${target} MATCHES p3) if(${target} MATCHES p2 OR ${target} MATCHES p3)
# Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets. # Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets.
=======
if(${target} MATCHES "p[23]")
# Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets.
>>>>>>> main
# This makes `libc.a` more flexible and usable in dynamic linking situations. # This makes `libc.a` more flexible and usable in dynamic linking situations.
list(APPEND extra_cflags_list -fPIC) list(APPEND extra_cflags_list -fPIC)
endif() endif()
@ -157,9 +197,14 @@ function(define_wasi_libc_sub target target_suffix lto)
set(extra_cmake_args) set(extra_cmake_args)
# Configure LTO in wasi libc if it's enabled. Be sure to disable shared # Configure LTO in wasi libc if it's enabled. Be sure to disable shared
# libraries as well since that's not currently supported. # libraries as well since that's not currently supported with LTO.
if (lto) if (lto)
list(APPEND extra_cmake_args -DLTO=full -DBUILD_SHARED=OFF) list(APPEND extra_cmake_args -DLTO=full)
endif()
# WASI_SDK_BUILD_SHARED=OFF allows producing a static-only sysroot; the LTO
# branch above already implies shared-off, so the two conditions are combined.
if (lto OR NOT WASI_SDK_BUILD_SHARED)
list(APPEND extra_cmake_args -DBUILD_SHARED=OFF)
endif() endif()
ExternalProject_Add(wasi-libc-${target}${target_suffix}-build ExternalProject_Add(wasi-libc-${target}${target_suffix}-build
@ -207,8 +252,13 @@ execute_process(
OUTPUT_VARIABLE llvm_version OUTPUT_VARIABLE llvm_version
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE)
<<<<<<< HEAD
function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix) function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix)
if(${target} MATCHES threads OR ${target} MATCHES p3) if(${target} MATCHES threads OR ${target} MATCHES p3)
=======
function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix exceptions)
if(${target} MATCHES threads)
>>>>>>> main
set(pic OFF) set(pic OFF)
set(target_flags -pthread) set(target_flags -pthread)
else() else()
@ -233,7 +283,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
--sysroot ${wasi_sysroot} --sysroot ${wasi_sysroot}
-resource-dir ${wasi_resource_dir}) -resource-dir ${wasi_resource_dir})
if (WASI_SDK_EXCEPTIONS) set(exnsuffix "")
if (exceptions)
# TODO: lots of builds fail with shared libraries and `-fPIC`. Looks like # TODO: lots of builds fail with shared libraries and `-fPIC`. Looks like
# things are maybe changing in llvm/llvm-project#159143 but otherwise I'm at # things are maybe changing in llvm/llvm-project#159143 but otherwise I'm at
# least not really sure what the state of shared libraries and exceptions # least not really sure what the state of shared libraries and exceptions
@ -242,6 +294,13 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
set(pic OFF) set(pic OFF)
set(runtimes "libunwind;${runtimes}") set(runtimes "libunwind;${runtimes}")
list(APPEND extra_flags -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) list(APPEND extra_flags -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false)
if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL")
set(exnsuffix "/eh")
endif()
else()
if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL")
set(exnsuffix "/noeh")
endif()
endif() endif()
# The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation # The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation
@ -250,6 +309,15 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
list(APPEND extra_flags -Wno-deprecated) list(APPEND extra_flags -Wno-deprecated)
endif() endif()
# `shared` is computed here, after the exceptions branch above may have forced
# pic OFF, so that LIBCXX_ENABLE_SHARED/LIBCXXABI_ENABLE_SHARED/LIBUNWIND_ENABLE_SHARED
# stay consistent with the final value of CMAKE_POSITION_INDEPENDENT_CODE.
if(WASI_SDK_BUILD_SHARED AND pic)
set(shared ON)
else()
set(shared OFF)
endif()
set(extra_cflags_list ${CMAKE_C_FLAGS} ${extra_flags}) set(extra_cflags_list ${CMAKE_C_FLAGS} ${extra_flags})
list(JOIN extra_cflags_list " " extra_cflags) list(JOIN extra_cflags_list " " extra_cflags)
set(extra_cxxflags_list ${CMAKE_CXX_FLAGS} ${extra_flags}) set(extra_cxxflags_list ${CMAKE_CXX_FLAGS} ${extra_flags})
@ -261,7 +329,7 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
${default_cmake_args} ${default_cmake_args}
# Ensure headers are installed in a target-specific path instead of a # Ensure headers are installed in a target-specific path instead of a
# target-generic path. # target-generic path.
-DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target} -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target}${exnsuffix}
-DCMAKE_STAGING_PREFIX=${wasi_sysroot} -DCMAKE_STAGING_PREFIX=${wasi_sysroot}
-DCMAKE_POSITION_INDEPENDENT_CODE=${pic} -DCMAKE_POSITION_INDEPENDENT_CODE=${pic}
-DLIBCXX_ENABLE_THREADS:BOOL=ON -DLIBCXX_ENABLE_THREADS:BOOL=ON
@ -269,22 +337,22 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
-DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF -DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF
-DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF
-DLLVM_COMPILER_CHECKED=ON -DLLVM_COMPILER_CHECKED=ON
-DLIBCXX_ENABLE_SHARED:BOOL=${pic} -DLIBCXX_ENABLE_SHARED:BOOL=${shared}
-DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${exceptions}
-DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON -DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON
-DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF
-DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_CXX_ABI=libcxxabi
-DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF -DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF
-DLIBCXX_ABI_VERSION=2 -DLIBCXX_ABI_VERSION=2
-DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions}
-DLIBCXXABI_ENABLE_SHARED:BOOL=${pic} -DLIBCXXABI_ENABLE_SHARED:BOOL=${shared}
-DLIBCXXABI_SILENT_TERMINATE:BOOL=ON -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON
-DLIBCXXABI_ENABLE_THREADS:BOOL=ON -DLIBCXXABI_ENABLE_THREADS:BOOL=ON
-DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON -DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON
-DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF
-DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF
-DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${WASI_SDK_EXCEPTIONS} -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions}
-DLIBUNWIND_ENABLE_SHARED:BOOL=${pic} -DLIBUNWIND_ENABLE_SHARED:BOOL=${shared}
-DLIBUNWIND_ENABLE_THREADS:BOOL=ON -DLIBUNWIND_ENABLE_THREADS:BOOL=ON
-DLIBUNWIND_USE_COMPILER_RT:BOOL=ON -DLIBUNWIND_USE_COMPILER_RT:BOOL=ON
-DLIBUNWIND_INCLUDE_TESTS:BOOL=OFF -DLIBUNWIND_INCLUDE_TESTS:BOOL=OFF
@ -292,9 +360,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
-DCMAKE_C_FLAGS=${extra_cflags} -DCMAKE_C_FLAGS=${extra_cflags}
-DCMAKE_ASM_FLAGS=${extra_cflags} -DCMAKE_ASM_FLAGS=${extra_cflags}
-DCMAKE_CXX_FLAGS=${extra_cxxflags} -DCMAKE_CXX_FLAGS=${extra_cxxflags}
-DLIBCXX_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} -DLIBCXX_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix}
-DLIBCXXABI_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix}
-DLIBUNWIND_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} -DLIBUNWIND_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix}
-DLIBCXX_INCLUDE_TESTS=OFF -DLIBCXX_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_BENCHMARKS=OFF -DLIBCXX_INCLUDE_BENCHMARKS=OFF
@ -309,18 +377,45 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_
USES_TERMINAL_CONFIGURE ON USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON USES_TERMINAL_INSTALL ON
USES_TERMINAL_PATCH ON
PATCH_COMMAND PATCH_COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c ${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch -R --check" "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch -R --check"
COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch -R --check"
COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch -R --check"
) )
add_dependencies(libcxx-${target} libcxx-${target}${target_suffix}-build)
endfunction() endfunction()
function(define_libcxx target) function(define_libcxx_and_lto target target_suffix exceptions)
define_libcxx_sub(${target} "" "" "") define_libcxx_sub(${target} "${target_suffix}" "" "" ${exceptions})
if(WASI_SDK_LTO) if (WASI_SDK_LTO)
# Note: clang knows this /llvm-lto/${llvm_version} convention. # Note: clang knows this /llvm-lto/${llvm_version} convention.
# https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/clang/lib/Driver/ToolChains/WebAssembly.cpp#L204-L210 # https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/clang/lib/Driver/ToolChains/WebAssembly.cpp#L204-L210
define_libcxx_sub(${target} "-lto" "-flto=full" "/llvm-lto/${llvm_version}") define_libcxx_sub(${target} ${target_suffix}-lto "-flto=full" "/llvm-lto/${llvm_version}" ${exceptions})
endif()
endfunction()
function(define_libcxx target)
add_custom_target(libcxx-${target})
# For dual-mode exceptions-and-not there are two versions of libcxx which are
# compiled and placed into the sysroot. They're named slightly differently to
# have unique CMake rules.
#
# Otherwise there's only one build of libcxx and it's either got exceptions or
# it doesn't depending on configuration.
if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL")
define_libcxx_and_lto(${target} "" OFF)
define_libcxx_and_lto(${target} "-exn" ON)
elseif(WASI_SDK_EXCEPTIONS STREQUAL "ON")
define_libcxx_and_lto(${target} "" ON)
else()
define_libcxx_and_lto(${target} "" OFF)
endif() endif()
# As of this writing, `clang++` will ignore the target-specific include dirs # As of this writing, `clang++` will ignore the target-specific include dirs
@ -328,8 +423,7 @@ function(define_libcxx target)
add_custom_target(libcxx-${target}-extra-dir add_custom_target(libcxx-${target}-extra-dir
COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1 COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1
COMMENT "creating libcxx-specific header file folder") COMMENT "creating libcxx-specific header file folder")
add_custom_target(libcxx-${target} add_dependencies(libcxx-${target} libcxx-${target}-extra-dir)
DEPENDS libcxx-${target}-build $<$<BOOL:${WASI_SDK_LTO}>:libcxx-${target}-lto-build> libcxx-${target}-extra-dir)
endfunction() endfunction()
foreach(target IN LISTS WASI_SDK_TARGETS) foreach(target IN LISTS WASI_SDK_TARGETS)

@ -5,6 +5,22 @@ set(LLVM_CMAKE_FLAGS "" CACHE STRING "Extra cmake flags to pass to LLVM's build"
set(RUST_TARGET "" CACHE STRING "Target to build Rust code for, if not the host") set(RUST_TARGET "" CACHE STRING "Target to build Rust code for, if not the host")
set(WASI_SDK_ARTIFACT "" CACHE STRING "Name of the wasi-sdk artifact being produced") set(WASI_SDK_ARTIFACT "" CACHE STRING "Name of the wasi-sdk artifact being produced")
option(WASI_SDK_LLDB "Include a build of LLDB" ON)
set(LIBEDIT_DEFAULT ON)
# I don't want to deal with running a `./configure` script on Windows, disable
# it by default.
if(WIN32)
set(LIBEDIT_DEFAULT OFF)
endif()
# I don't know how to resolve build failures when building libedit for x86_64
# from arm64 on macos, so disable it for now.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" AND LLVM_CMAKE_FLAGS MATCHES "x86_64")
set(LIBEDIT_DEFAULT OFF)
endif()
option(WASI_SDK_LIBEDIT "Whether or not to build libedit for LLDB" ${LIBEDIT_DEFAULT})
option(WASI_SDK_LIBXML2 "Whether or not to build libxml2 for LLDB" ON)
string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}") string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}")
set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install)
@ -40,6 +56,7 @@ set(tools
clang-format clang-format
clang-tidy clang-tidy
clang-apply-replacements clang-apply-replacements
clang-scan-deps
lld lld
llvm-addr2line llvm-addr2line
llvm-mc llvm-mc
@ -57,7 +74,8 @@ set(tools
objdump objdump
objcopy objcopy
c++filt c++filt
llvm-config) llvm-config
libclang)
# By default link LLVM dynamically to all the various tools. This greatly # By default link LLVM dynamically to all the various tools. This greatly
# reduces the binary size of all the tools through a shared library rather than # reduces the binary size of all the tools through a shared library rather than
@ -72,6 +90,127 @@ if(NOT WIN32)
list(APPEND tools LLVM clang-cpp) list(APPEND tools LLVM clang-cpp)
endif() endif()
# Configure/add LLDB if requested.
#
# Note that LLDB depends on `libedit` which is more-or-less required to get a
# reasonable command-line experience, so this is built custom here to ensure
# that it's available for LLDB.
if(WASI_SDK_LLDB)
list(APPEND projects lldb)
list(APPEND tools lldb liblldb)
list(APPEND default_cmake_args
-DLLDB_INCLUDE_TESTS=OFF
-DLLDB_INCLUDE_UNITTESTS=OFF
-DLLDB_ENABLE_SWIG=OFF
-DLLDB_ENABLE_CURSES=OFF
-DLLDB_ENABLE_LZMA=OFF
-DLLDB_ENABLE_LUA=OFF
-DLLDB_ENABLE_PYTHON=OFF
-DLLDB_ENABLE_FBSDVMCORE=OFF
-DLLDB_ENABLE_LINUXPTY=OFF
)
set(extra_configure_commands)
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
set(extra_configure_commands
# By default it looks like `libedit` tries to link to `libncurses.so` and
# such on Linux. This is problematic as systems may not have that
# installed. Turns out though at least for AlmaLinux [1] they just edit
# makefile and pkg-config info and it works out. Who knew! I thought
# one of the millions of lines in `./configure` would take care of this
# but apparently we're still resorting to editing things raw...
#
# [1]: https://git.almalinux.org/rpms/libedit/src/commit/3f0893c4cd8e0cbb2f556d2fad48326c9c037a6c/SPECS/libedit.spec#L44-L48
COMMAND sed -i "s/lncurses/ltinfo/" src/Makefile
COMMAND sed -i "s/ -lncurses//" libedit.pc
)
endif()
if (WASI_SDK_LIBEDIT)
include(ProcessorCount)
ProcessorCount(nproc)
find_program(MAKE_EXECUTABLE make REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(libedit_ldflags -Wl,-install_name,@rpath/libedit.0.dylib)
endif()
ExternalProject_Add(libedit
URL https://thrysoee.dk/editline/libedit-20251016-3.1.tar.gz
URL_HASH SHA256=21362b00653bbfc1c71f71a7578da66b5b5203559d43134d2dd7719e313ce041
# Without this the build system tries to find and use `aclocal-1.18` where
# with this it doesn't so turn this on.
DOWNLOAD_EXTRACT_TIMESTAMP ON
CONFIGURE_COMMAND
<SOURCE_DIR>/configure
--prefix=${wasi_tmp_install}
--enable-pic
--disable-examples
--disable-static
--disable-silent-rules
CC=${CMAKE_C_COMPILER}
LDFLAGS=${libedit_ldflags}
${extra_configure_commands}
BUILD_COMMAND
${MAKE_EXECUTABLE} -j${nproc}
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
)
list(APPEND default_cmake_args
-DLLDB_ENABLE_LIBEDIT=ON
-DLibEdit_ROOT=${wasi_tmp_install}
)
else()
list(APPEND default_cmake_args -DLLDB_ENABLE_LIBEDIT=OFF)
add_custom_target(libedit)
endif()
set(libxml_cmake_args)
# Windows doesn't have iconv by default, so disable it for now.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
list(APPEND libxml_cmake_args -DLIBXML2_WITH_ICONV=OFF)
endif()
# Our AlmaLinux:8 container ends up using `lib64` instead of `lib` by default
# which doesn't match LLVM, so specifically use the same dir as LLVM.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
list(APPEND libxml_cmake_args -DCMAKE_INSTALL_LIBDIR=lib)
endif()
if (WASI_SDK_LIBXML2)
ExternalProject_Add(libxml2
URL https://download.gnome.org/sources/libxml2/2.15/libxml2-2.15.2.tar.xz
URL_HASH SHA256=c8b9bc81f8b590c33af8cc6c336dbff2f53409973588a351c95f1c621b13d09d
CMAKE_ARGS
${default_cmake_args}
-DLIBXML2_WITH_PROGRAMS=OFF
-DLIBXML2_WITH_DEBUG=OFF
-DLIBXML2_WITH_DOCS=OFF
-DLIBXML2_WITH_TESTS=OFF
${libxml_cmake_args}
${llvm_cmake_flags_list}
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
)
list(APPEND default_cmake_args
-DLLDB_ENABLE_LIBXML2=ON
-DLibXml2_ROOT=${wasi_tmp_install}
)
else()
list(APPEND default_cmake_args -DLLDB_ENABLE_LIBXML2=OFF)
add_custom_target(libxml2)
endif()
else()
add_custom_target(libedit)
add_custom_target(libxml2)
endif()
list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets) list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets)
list(TRANSFORM tools PREPEND --target=install- OUTPUT_VARIABLE install_targets) list(TRANSFORM tools PREPEND --target=install- OUTPUT_VARIABLE install_targets)
@ -87,12 +226,15 @@ ExternalProject_Add(llvm-build
-DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF
-DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_EXAMPLES=OFF
-DLLVM_TARGETS_TO_BUILD=WebAssembly -DLLVM_TARGETS_TO_BUILD=WebAssembly
-DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasi -DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasip1
-DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE -DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE
-DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_LIBXML2=OFF
# Pass `-s` to strip symbols by default and shrink the size of the # Pass `-s` to strip symbols by default and shrink the size of the
# distribution # distribution
-DCMAKE_EXE_LINKER_FLAGS=-s -DCMAKE_EXE_LINKER_FLAGS=-s
# Looks to be required on macOS for, at build time, the dynamic linker to
# find `libedit.dylib` when that's enabled.
-DCMAKE_BUILD_RPATH=${wasi_tmp_install}/lib
${llvm_cmake_flags_list} ${llvm_cmake_flags_list}
# See https://www.scivision.dev/cmake-externalproject-list-arguments/ for # See https://www.scivision.dev/cmake-externalproject-list-arguments/ for
# why this is in `CMAKE_CACHE_ARGS` instead of above # why this is in `CMAKE_CACHE_ARGS` instead of above
@ -106,9 +248,13 @@ ExternalProject_Add(llvm-build
USES_TERMINAL_CONFIGURE ON USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON USES_TERMINAL_INSTALL ON
PATCH_COMMAND
${CMAKE_COMMAND} -E chdir .. bash -c
"git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch -R --check"
) )
add_custom_target(build ALL DEPENDS llvm-build) add_custom_target(build ALL DEPENDS llvm-build)
ExternalProject_Add_StepDependencies(llvm-build configure libedit libxml2)
# Installation target for this outer project for installing the toolchain to the # Installation target for this outer project for installing the toolchain to the
# system. # system.
@ -119,7 +265,7 @@ install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_ins
# Build logic for `wasm-component-ld` installed from Rust code. # Build logic for `wasm-component-ld` installed from Rust code.
set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld) set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld)
set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX}) set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX})
set(wasm_component_ld_version 0.5.21) set(wasm_component_ld_version 0.5.24)
if(RUST_TARGET) if(RUST_TARGET)
set(rust_target_flag --target=${RUST_TARGET}) set(rust_target_flag --target=${RUST_TARGET})
endif() endif()

@ -1,22 +1,17 @@
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h From 852c8a2ebc0fdb1e781591e3e6e08d3a539bcfc3 Mon Sep 17 00:00:00 2001
index f8e83e138eff..c5097d25b0c6 100644 From: Yerzhan Zhamashev <yerzhan@novel.systems>
--- a/libunwind/src/assembly.h Date: Wed, 21 Jan 2026 16:50:41 +0200
+++ b/libunwind/src/assembly.h Subject: [PATCH] libunwind: exclude __declspec from wasm build
@@ -249,6 +249,9 @@ aliasname: \
#define WEAK_ALIAS(name, aliasname) ---
#define NO_EXEC_STACK_DIRECTIVE libunwind/src/config.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
+#elif defined(__wasm__)
+#define NO_EXEC_STACK_DIRECTIVE
+
// clang-format on
#else
diff --git a/libunwind/src/config.h b/libunwind/src/config.h diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index deb5a4d4d73d..23c9f012cbcf 100644 index f017403fa2234..6014a37e27212 100644
--- a/libunwind/src/config.h --- a/libunwind/src/config.h
+++ b/libunwind/src/config.h +++ b/libunwind/src/config.h
@@ -66,7 +66,8 @@ @@ -75,7 +75,8 @@
#define _LIBUNWIND_EXPORT #define _LIBUNWIND_EXPORT
#define _LIBUNWIND_HIDDEN #define _LIBUNWIND_HIDDEN
#else #else

@ -0,0 +1,105 @@
From d702761d9135ebbb83590d4dd1323be433701ebd Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Tue, 10 Mar 2026 15:49:55 -0700
Subject: [PATCH] [WebAssembly] Move __cpp_exception to libunwind
The `__cpp_exception` symbol is now defined in libunwind instead of
compiler-rt. This is moved for a few reasons, but the primary reason is
that compiler-rt is linked duplicate-ly into all shared objects meaning
that it's not suitable for define-once symbols such as
`__cpp_exception`. By moving the definition to the user of the symbol,
libunwind itself, that guarantees that the symbol should be defined
exactly once and only when appropriate. A secondary reason for this
movement is that it avoids the need to compile compiler-rt twice: once
with exception and once without, and instead the same build can be used
for both exceptions-and-not.
---
compiler-rt/lib/builtins/CMakeLists.txt | 1 -
.../lib/builtins/wasm/__cpp_exception.S | 26 -------------------
libunwind/src/Unwind-wasm.c | 15 +++++++++++
.../compiler-rt/lib/builtins/sources.gni | 1 -
4 files changed, 15 insertions(+), 28 deletions(-)
delete mode 100644 compiler-rt/lib/builtins/wasm/__cpp_exception.S
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 6c27f6d4d529e..f0570a9092f40 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -891,7 +891,6 @@ set(s390x_SOURCES
set(wasm_SOURCES
wasm/__c_longjmp.S
- wasm/__cpp_exception.S
${GENERIC_TF_SOURCES}
${GENERIC_SOURCES}
)
diff --git a/compiler-rt/lib/builtins/wasm/__cpp_exception.S b/compiler-rt/lib/builtins/wasm/__cpp_exception.S
deleted file mode 100644
index 0496e1dbf6158..0000000000000
--- a/compiler-rt/lib/builtins/wasm/__cpp_exception.S
+++ /dev/null
@@ -1,26 +0,0 @@
-//===-- __cpp_exception.S - Implement __cpp_exception ---------------------===//
-//
-// Part of the LLVM Project, 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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements __cpp_exception which LLVM uses to implement exception
-// handling when Wasm EH is enabled.
-//
-//===----------------------------------------------------------------------===//
-
-#ifdef __wasm_exception_handling__
-
-#ifdef __wasm64__
-#define PTR i64
-#else
-#define PTR i32
-#endif
-
-.globl __cpp_exception
-.tagtype __cpp_exception PTR
-__cpp_exception:
-
-#endif // !__wasm_exception_handling__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index 2f4498c3f3989..c0ca9b775d244 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -69,6 +69,21 @@ _Unwind_RaiseException(_Unwind_Exception *exception_object) {
__builtin_wasm_throw(0, exception_object);
}
+// Define the `__cpp_exception` symbol which `__builtin_wasm_throw` above will
+// reference. This is defined here in `libunwind` as the single canonical
+// definition for this API and it's required for users to ensure that there's
+// only one copy of `libunwind` within a wasm module to ensure this is only
+// defined once and exactly once.
+__asm__(".globl __cpp_exception\n"
+#if defined(__wasm32__)
+ ".tagtype __cpp_exception i32\n"
+#elif defined(__wasm64__)
+ ".tagtype __cpp_exception i64\n"
+#else
+#error "Unsupported Wasm architecture"
+#endif
+ "__cpp_exception:\n");
+
/// Called by __cxa_end_catch.
_LIBUNWIND_EXPORT void
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
index 2ac71aa8e8367..c9eeede16e3eb 100644
--- a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
+++ b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni
@@ -539,7 +539,6 @@ if (current_cpu == "ve") {
if (current_cpu == "wasm") {
builtins_sources += [
"wasm/__c_longjmp.S",
- "wasm/__cpp_exception.S",
]
}

@ -0,0 +1,152 @@
From 0e36e8f304cd5f3997916f5d85201bb17e340337 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Tue, 10 Mar 2026 16:14:36 -0700
Subject: [PATCH] [WebAssembly] Clang support for exception-based lookup paths
This commit is an attempt to make progress on WebAssembly/wasi-sdk#565
where with wasi-sdk I'd like to ship a single toolchain which is
capable of building binaries both with C++ exceptions and without. This
means that there can't be a single set of precompiled libraries that are
used because one set of libraries is wrong for the other mode. The
support added here is to use `-fwasm-exceptions` to automatically select
a lookup path in the sysroot. The intention is then that wasi-sdk will
ship both a "eh" set of C++ libraries as well as a "noeh" set of C++
libraries too. Clang will automatically select the correct one based on
compilation flags which means that the final distribution will be able
to build both binaries with exceptions and without.
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 51 ++++++++++++++-------
clang/test/Driver/wasm-toolchain.cpp | 35 ++++++++++++++
2 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b5fa5760a46a0..e532ef0743cc2 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -34,6 +34,15 @@ std::string WebAssembly::getMultiarchTriple(const Driver &D,
TargetTriple.getOSAndEnvironmentName()).str();
}
+/// Returns a directory name in which separate objects compile with/without
+/// exceptions may lie. This is used both for `#include` paths as well as lib
+/// paths.
+static std::string GetCXXExceptionsDir(const ArgList &DriverArgs) {
+ if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions))
+ return "eh";
+ return "noeh";
+}
+
std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
const ToolChain &ToolChain = getToolChain();
if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
@@ -230,12 +239,16 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}
-/// Given a base library directory, append path components to form the
-/// LTO directory.
-static std::string AppendLTOLibDir(const std::string &Dir) {
+/// Append `Dir` to `Paths`, but also include the LTO directories before that if
+/// LTO is eanbled.
+static void AppendLibDirAndLTODir(ToolChain::path_list &Paths, const Driver &D,
+ const std::string &Dir) {
+ if (D.isUsingLTO()) {
// The version allows the path to be keyed to the specific version of
// LLVM in used, as the bitcode format is not stable.
- return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
+ Paths.push_back(Dir + "/llvm-lto/" LLVM_VERSION_STRING);
+ }
+ Paths.push_back(Dir);
}
WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
@@ -256,14 +269,15 @@ WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
} else {
const std::string MultiarchTriple =
getMultiarchTriple(getDriver(), Triple, SysRoot);
- if (D.isUsingLTO()) {
- // For LTO, enable use of lto-enabled sysroot libraries too, if available.
- // Note that the directory is keyed to the LLVM revision, as LLVM's
- // bitcode format is not stable.
- auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple);
- getFilePaths().push_back(Dir);
- }
- getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple);
+ std::string TripleLibDir = SysRoot + "/lib/" + MultiarchTriple;
+ // Allow sysroots to segregate objects based on whether exceptions are
+ // enabled or not. This is intended to assist with distribution of pre-built
+ // sysroots that contain libraries that are capable of producing binaries
+ // entirely without exception-handling instructions but also with if
+ // exceptions are enabled, for example.
+ AppendLibDirAndLTODir(getFilePaths(), D,
+ TripleLibDir + "/" + GetCXXExceptionsDir(Args));
+ AppendLibDirAndLTODir(getFilePaths(), D, TripleLibDir);
}
if (getTriple().getOS() == llvm::Triple::WASI) {
@@ -580,13 +594,18 @@ void WebAssembly::addLibCxxIncludePaths(
if (Version.empty())
return;
- // First add the per-target include path if the OS is known.
+ // First add the per-target-per-exception-handling include path if the
+ // OS is known, then second add the per-target include path.
if (IsKnownOs) {
- std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version;
- addSystemInclude(DriverArgs, CC1Args, TargetDir);
+ std::string TargetDir = LibPath + "/" + MultiarchTriple;
+ std::string Suffix = "/c++/" + Version;
+ addSystemInclude(DriverArgs, CC1Args,
+ TargetDir + "/" + GetCXXExceptionsDir(DriverArgs) +
+ Suffix);
+ addSystemInclude(DriverArgs, CC1Args, TargetDir + Suffix);
}
- // Second add the generic one.
+ // Third add the generic one.
addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
}
diff --git a/clang/test/Driver/wasm-toolchain.cpp b/clang/test/Driver/wasm-toolchain.cpp
index d7ff76cedfd10..30a2f9397e3f4 100644
--- a/clang/test/Driver/wasm-toolchain.cpp
+++ b/clang/test/Driver/wasm-toolchain.cpp
@@ -111,3 +111,38 @@
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[RESOURCE_DIR]]{{(/|\\\\)}}include"
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-linux-muslwali"
// COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+
+// With a known OS "eh" and "noeh" directories are added to enable segregating
+// object built with/without exception-handling
+
+// RUN: %clangxx -### --target=wasm32-wasi --stdlib=libc++ %s 2>&1 \
+// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \
+// RUN: | FileCheck -check-prefix=EH_OFF %s
+// EH_OFF: "-cc1"
+// EH_OFF: "-isysroot" "[[SYSROOT:[^"]+]]"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1"
+// EH_OFF-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi"
+// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+
+// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --stdlib=libc++ %s 2>&1 \
+// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \
+// RUN: | FileCheck -check-prefix=EH_ON %s
+// EH_ON: "-cc1"
+// EH_ON: "-isysroot" "[[SYSROOT:[^"]+]]"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1"
+// EH_ON-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi"
+// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include"
+//
+// RUN: %clangxx -### --target=wasm32-wasi --sysroot=/foo --stdlib=libc++ %s 2>&1 \
+// RUN: | FileCheck -check-prefix=EH_OFF_LINK %s
+// EH_OFF_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/noeh" "-L/foo/lib/wasm32-wasi"
+//
+// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --sysroot=/foo --stdlib=libc++ %s 2>&1 \
+// RUN: | FileCheck -check-prefix=EH_ON_LINK %s
+// EH_ON_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/eh" "-L/foo/lib/wasm32-wasi"

@ -0,0 +1,48 @@
From f71fdfcbd6fcc7b521c74b5856ebeacdd6cf55d9 Mon Sep 17 00:00:00 2001
From: Catherine <whitequark@whitequark.org>
Date: Thu, 12 Mar 2026 08:19:49 +0000
Subject: [PATCH] [libc++abi] Revert gating of `__cxa_thread_atexit` on
Linux||Fuchsia
This was done in the commit 3c100d5d548d with the description
"Enable -Wmissing-prototypes" which seems incongruent to me.
Since then it's made its way into a release and broke the use of
`thread_local` variables with destructors on Wasm/WASI:
```cc
// repro.cc
struct c { ~c() {} };
thread_local c v;
int main() { (void)v; }
```
```console
$ ./wasi-sdk-31.0-x86_64-linux/bin/clang++ repro.cc
wasm-ld: error: /tmp/repro-dd1ad7.o: undefined symbol: __cxa_thread_atexit
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
```
---
libcxxabi/src/cxa_thread_atexit.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/libcxxabi/src/cxa_thread_atexit.cpp b/libcxxabi/src/cxa_thread_atexit.cpp
index 402a52c741012..1bdcb4ef192b4 100644
--- a/libcxxabi/src/cxa_thread_atexit.cpp
+++ b/libcxxabi/src/cxa_thread_atexit.cpp
@@ -106,7 +106,6 @@ namespace {
#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
-#if defined(__linux__) || defined(__Fuchsia__)
extern "C" {
_LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() {
@@ -141,6 +140,5 @@ extern "C" {
}
#endif // HAVE___CXA_THREAD_ATEXIT_IMPL
}
-} // extern "C"
-#endif // defined(__linux__) || defined(__Fuchsia__)
+ } // extern "C"
} // namespace __cxxabiv1

@ -1 +1 @@
Subproject commit fd5840e1ba9d23cbf610abe99f5ff59a4f1bec0e Subproject commit 161b3195fc2558d2b1ba3eb9ffae3b2b47407623

@ -28,12 +28,26 @@ set(opt_flags -O0 -O2 "-O2 -flto")
add_custom_target(build-tests) add_custom_target(build-tests)
# Executes a single `test` specified. # Registers `test` with CMake, compiling it with a number of flag combinations
# and for all enabled targets. This will register up to many tests with CTest.
# #
# This will compile `test` for all the various targets and with various # This function takes CMake-style arguments, specified as:
# compiler options. If `runwasi` is non-empty then the test will be executed #
# in that runner as well. # * `EMULATED_CLOCKS` - enables `-lwasi-emulated-process-clocks` when compiling.
function(add_testcase runwasi test) # * `EMULATED_MMAN` - enables `-lwasi-emulated-mman` when compiling.
# * `EMULATED_SIGNAL` - enables `-lwasi-emulated-signal` when compiling.
# * `PRINTSCAN_LONG_DOUBLE` - enables `-lc-printscan-long-double` when compiling.
# * `FSDIR` - requires `${test}.dir` to exist and mounts it when running the test
# * `PASS_REGULAR_EXPRESSION` - same as the CTest property
# * `ENV` - env vars (the `--env` flag in Wasmtime) to pass to the test.
# * `COMPILE_ONLY` - does not actually execute this test, just compiles it.
function(add_testcase test)
set(options EMULATED_CLOCKS EMULATED_MMAN EMULATED_SIGNAL
PRINTSCAN_LONG_DOUBLE FSDIR COMPILE_ONLY)
set(oneValueArgs PASS_REGULAR_EXPRESSION)
set(multiValueArgs ENV)
cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${oneValueArgs}" "${multiValueArgs}")
foreach(target IN LISTS WASI_SDK_TARGETS) foreach(target IN LISTS WASI_SDK_TARGETS)
foreach(compile_flags IN LISTS opt_flags) foreach(compile_flags IN LISTS opt_flags)
# Mangle the options into something appropriate for a CMake rule name # Mangle the options into something appropriate for a CMake rule name
@ -52,24 +66,27 @@ function(add_testcase runwasi test)
endif() endif()
# Apply test-specific compile options and link flags. # Apply test-specific compile options and link flags.
if(test MATCHES "clocks.c$") if(${arg_EMULATED_CLOCKS})
target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_PROCESS_CLOCKS) target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_PROCESS_CLOCKS)
target_link_options(${target_name} PRIVATE -lwasi-emulated-process-clocks) target_link_options(${target_name} PRIVATE -lwasi-emulated-process-clocks)
elseif(test MATCHES "mmap.c$") endif()
if(${arg_EMULATED_MMAN})
target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_MMAN) target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_MMAN)
target_link_options(${target_name} PRIVATE -lwasi-emulated-mman) target_link_options(${target_name} PRIVATE -lwasi-emulated-mman)
elseif(test MATCHES "(sigabrt|signals).c$") endif()
if(${arg_EMULATED_SIGNAL})
target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_SIGNAL) target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_SIGNAL)
target_link_options(${target_name} PRIVATE -lwasi-emulated-signal) target_link_options(${target_name} PRIVATE -lwasi-emulated-signal)
elseif(test MATCHES "printf-long-double-enabled.c$") endif()
if(${arg_PRINTSCAN_LONG_DOUBLE})
target_link_options(${target_name} PRIVATE -lc-printscan-long-double) target_link_options(${target_name} PRIVATE -lc-printscan-long-double)
endif() endif()
# Apply language-specific options and dependencies. # Apply language-specific options and dependencies.
if(test MATCHES "cc$") if(test MATCHES "cc$")
if(WASI_SDK_EXCEPTIONS) if(NOT (WASI_SDK_EXCEPTIONS STREQUAL "OFF"))
target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false)
target_link_options(${target_name} PRIVATE -lunwind) target_link_options(${target_name} PRIVATE -fwasm-exceptions -lunwind)
else() else()
target_compile_options(${target_name} PRIVATE -fno-exceptions) target_compile_options(${target_name} PRIVATE -fno-exceptions)
endif() endif()
@ -93,27 +110,42 @@ function(add_testcase runwasi test)
target_link_options(${target_name} PRIVATE -Wno-deprecated) target_link_options(${target_name} PRIVATE -Wno-deprecated)
endif() endif()
if(runwasi) if(arg_COMPILE_ONLY)
set(runner ${runwasi}) continue()
if(${runner} MATCHES wasmtime) endif()
if(target MATCHES threads)
set(runner "${runner} -Wshared-memory") set(runner ${WASI_SDK_RUNWASI})
endif() set(args)
if(WASI_SDK_EXCEPTIONS) if(${runner} MATCHES wasmtime)
set(runner "${runner} -Wexceptions") if(target MATCHES threads)
endif() list(APPEND runner -Wshared-memory)
if(target MATCHES p3)
set(runner "${runner} -Wcomponent-model-async -Wcomponent-model-threading -Sp3")
endif()
endif() endif()
add_test( if(WASI_SDK_EXCEPTIONS)
NAME test-${target_name} list(APPEND runner -Wexceptions)
COMMAND endif()
bash ../testcase.sh if(target MATCHES "wasip3")
${runner} list(APPEND runner -Wcomponent-model-async -Sp3)
${test} endif()
$<TARGET_FILE:${target_name}> endif()
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
foreach(env IN LISTS arg_ENV)
list(APPEND runner --env ${env})
endforeach()
if (${arg_FSDIR})
list(APPEND runner --dir ${CMAKE_CURRENT_SOURCE_DIR}/${test}.dir::${test}.dir)
list(APPEND args ${test}.dir)
endif()
add_test(
NAME test-${target_name}
COMMAND
${runner}
$<TARGET_FILE:${target_name}>
${args}
)
if (arg_PASS_REGULAR_EXPRESSION)
set_tests_properties(test-${target_name} PROPERTIES PASS_REGULAR_EXPRESSION ${arg_PASS_REGULAR_EXPRESSION})
endif() endif()
endforeach() endforeach()
endforeach() endforeach()

@ -1,9 +1,3 @@
file(GLOB c_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") add_testcase(addresses.c COMPILE_ONLY)
file(GLOB cxx_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") add_testcase(printf-long-double.c COMPILE_ONLY)
add_testcase(test.cc COMPILE_ONLY)
set(compile_tests ${c_compile_tests} ${cxx_compile_tests})
foreach(test IN LISTS compile_tests)
add_testcase("" ${test})
endforeach()

@ -1,8 +1,69 @@
file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") add_testcase(abort.c PASS_REGULAR_EXPRESSION "wasm trap.*unreachable")
file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") add_testcase(argc_argv_main.c PASS_REGULAR_EXPRESSION "hello from argc argv main")
add_testcase(argc_argv_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ argc argv main")
add_testcase(assert-fail.c PASS_REGULAR_EXPRESSION "Assertion failed: false.*wasm trap:.*unreachable")
add_testcase(assert-pass.c)
add_testcase(clocks.c EMULATED_CLOCKS)
add_testcase(cpp_thread_local.cc)
add_testcase(ctors_dtors.c PASS_REGULAR_EXPRESSION "\
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello from_destructor101
"
)
add_testcase(ctors_dtors.cc PASS_REGULAR_EXPRESSION "\
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello StaticObject::StaticObject
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello StaticObject::~StaticObject
hello from_destructor101
")
add_testcase(empty.c)
add_testcase(env-absent.c PASS_REGULAR_EXPRESSION "HELLO = \\(null\\)")
add_testcase(env.c ENV HELLO=hello PASS_REGULAR_EXPRESSION "HELLO = hello")
add_testcase(environ.c ENV HELLO=hello PASS_REGULAR_EXPRESSION "HELLO = hello")
add_testcase(exceptions.cc)
add_testcase(getentropy.c)
add_testcase(iostream_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ main with cout")
add_testcase(main_errno.c PASS_REGULAR_EXPRESSION "initial errno is 0: Success")
add_testcase(mmap.c EMULATED_MMAN FSDIR PASS_REGULAR_EXPRESSION "\
Would you tell me, please, which way I ought to go from here\\?
set(general_tests ${c_general_tests} ${cxx_general_tests}) That depends a good deal on where you want to get to, said the Cat.
"
foreach(test IN LISTS general_tests) )
add_testcase(${WASI_SDK_RUNWASI} ${test}) add_testcase(no_arg_main.c PASS_REGULAR_EXPRESSION "hello from no-arg main")
endforeach() add_testcase(no_arg_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ no-arg main")
add_testcase(opendir.c FSDIR)
add_testcase(printf-long-double-enabled.c PRINTSCAN_LONG_DOUBLE PASS_REGULAR_EXPRESSION "the answer is 42.000000")
add_testcase(printf-no-float.c PASS_REGULAR_EXPRESSION "the answer is 42")
add_testcase(printf-no-long-double.c PASS_REGULAR_EXPRESSION "the answer is 42.000000")
add_testcase(sigabrt.c EMULATED_SIGNAL PASS_REGULAR_EXPRESSION "\
raising SIGABRT...
Program received fatal signal: Aborted
.*failed to run main module.*
")
add_testcase(signals.c EMULATED_SIGNAL PASS_REGULAR_EXPRESSION "\
psignal message for SIGINT: Interrupt
strsignal for SIGHUP: 'Hangup'
beginning handler test:
handler for signal Window changed
finished handler test
")
add_testcase(stat.c FSDIR)
add_testcase(void_main.c PASS_REGULAR_EXPRESSION "hello from void main")
add_testcase(void_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ void main")

@ -1,6 +1,7 @@
#include <stdlib.h> #include <stdlib.h>
void __wasm_set_tls_base(void *base);
int main(void) { int main(void) {
__wasm_set_tls_base(NULL);
abort(); abort();
return 0; return 0;
} }

@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cat \
| sed -e 's/main module `.*abort\.c\.wasm`/main module `abort.c.---.wasm`/' \
| sed -e 's/failed to invoke.*/failed to invoke ---/' \
| sed -E '/0x[[:xdigit:]]+/d'

@ -1 +0,0 @@
hello from C++ argc argv main!

@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
cat \
| sed -e 's/main module `.*assert-fail\.c\.wasm`/main module `assert-fail.c.---.wasm`/' \
| sed -e 's/failed to invoke.*/failed to invoke ---/' \
| sed -e 's/Assertion failed: false (.*assert-fail.c/Assertion failed: false (assert-fail.c/' \
| sed -E '/0x[[:xdigit:]]+/d'

@ -0,0 +1,3 @@
struct c { ~c() {} };
thread_local c v;
int main() { (void)v; }

@ -1,10 +0,0 @@
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello from_destructor101

@ -1,12 +0,0 @@
hello from_constructor101
hello from_constructor
hello from_constructor65535
hello StaticObject::StaticObject
hello main
goodbye main
hello another_from_atexit
hello from_atexit
hello from_destructor65535
hello from_destructor
hello StaticObject::~StaticObject
hello from_destructor101

@ -1 +0,0 @@
hello from C++ main with cout!

@ -1 +0,0 @@
initial errno is 0: Success

@ -1,3 +0,0 @@
“Would you tell me, please, which way I ought to go from here?”
“That depends a good deal on where you want to get to,” said the Cat.

@ -1 +0,0 @@
hello from C++ no-arg main!

@ -1,8 +0,0 @@
#!/bin/bash
set -euo pipefail
cat \
| sed -e 's/main module `.*sigabrt\.c\.wasm`/main module `sigabrt.c.---.wasm`/' \
| sed -e 's/source location: @[[:xdigit:]]*$/source location: @----/' \
| sed -e 's/failed to invoke.*/failed to invoke ---/' \
| head -n 6

@ -1,6 +0,0 @@
raising SIGABRT...
Program received fatal signal: Aborted
Error: failed to run main module `sigabrt.c.---.wasm`
Caused by:
0: failed to invoke `run` function

@ -1,6 +0,0 @@
raising SIGABRT...
Program received fatal signal: Aborted
Error: failed to run main module `sigabrt.c.---.wasm`
Caused by:
0: failed to invoke `run` function

@ -1 +0,0 @@
psignal message for SIGINT: Interrupt

@ -1,4 +0,0 @@
strsignal for SIGHUP: 'Hangup'
beginning handler test:
handler for signal Window changed
finished handler test

@ -1 +0,0 @@
hello from C++ void main!

@ -1,102 +0,0 @@
#!/bin/bash
set -ueo pipefail
# A simple testcase runner that runs a command, captures all its command-line
# outputs, and compares them against expected outputs.
# Command-line parsing; this script is meant to be run from a higher-level
# script, so don't do anything fancy.
runwasi="$1"
input="$2"
wasm="$3"
# Compile names for generated files.
stdout_observed="$wasm.stdout.observed"
stderr_observed="$wasm.stderr.observed"
exit_status_observed="$wasm.exit_status.observed"
# Double-check that a runwasi command was specified since otherwise this script
# was invoked with no arguments which isn't as intended.
if [ "$runwasi" == "" ]; then
exit 1
fi
# Determine the input file to write to stdin.
if [ -e "$input.stdin" ]; then
stdin="$input.stdin"
else
stdin="/dev/null"
fi
# Determine any environment variables to set.
if [ -e "$input.env" ]; then
env=$(sed -e 's/^/--env /' < "$input.env")
else
env=""
fi
# Determine a preopened directory to provide.
if [ -e "$input.dir" ]; then
dir="--dir $input.dir"
dirarg="$input.dir"
else
dir=""
dirarg=""
fi
# Run the test, capturing stdout, stderr, and the exit status.
exit_status=0
$runwasi $env $dir "$wasm" $dirarg \
< "$stdin" \
> "$stdout_observed" \
2> "$stderr_observed" \
|| exit_status=$?
echo $exit_status > "$exit_status_observed"
# On Windows Wasmtime will exit with error code 3 for aborts. On Unix Wasmtime
# will exit with status 134. Paper over this difference by pretending to be Unix
# on Windows and converting exit code 3 into 134 for the purposes of asserting
# test output.
if [ "$OSTYPE" = "msys" ] && [ "$exit_status" = "3" ]; then
echo 134 > "$exit_status_observed"
fi
# Determine the reference files to compare with.
if [ -e "$input.stdout.expected" ]; then
stdout_expected="$input.stdout.expected"
# Apply output filters.
if [ -e "$input.stdout.expected.filter" ]; then
cat "$stdout_observed" \
| "$input.stdout.expected.filter" \
> "${stdout_observed}.filtered"
stdout_observed="${stdout_observed}.filtered"
fi
else
stdout_expected="/dev/null"
fi
if [ -e "$input.stderr.expected" ]; then
stderr_expected="$input.stderr.expected"
# Apply output filters.
if [ -e "$input.stderr.expected.filter" ]; then
cat "$stderr_observed" \
| "./$input.stderr.expected.filter" \
> "${stderr_observed}.filtered"
stderr_observed="${stderr_observed}.filtered"
fi
else
stderr_expected="/dev/null"
fi
if [ -e "$input.exit_status.expected" ]; then
exit_status_expected="$input.exit_status.expected"
else
exit_status_expected=../exit_status_zero
fi
# If there are any differences, diff will return a non-zero exit status, and
# since this script uses "set -e", it will return a non-zero exit status too.
diff --ignore-space-change -u "$stderr_expected" "$stderr_observed"
diff --ignore-space-change -u "$stdout_expected" "$stdout_observed"
diff --ignore-space-change -u "$exit_status_expected" "$exit_status_observed"

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32) set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasip1) set(triple wasm32-wasip1)
if(WIN32) if(WIN32)
@ -31,7 +32,6 @@ set(CMAKE_ASM_COMPILER_TARGET ${triple})
# Don't look in the sysroot for executables to run during the build # Don't look in the sysroot for executables to run during the build
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Only look in the sysroot (not in the host paths) for the rest # Only look in the sysroot (not in the host paths) for the rest
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32) set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasip2) set(triple wasm32-wasip2)
if(WIN32) if(WIN32)
@ -17,7 +18,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory # When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX) if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif() endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32) set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasip3) set(triple wasm32-wasip3)
if(WIN32) if(WIN32)
@ -17,7 +18,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory # When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX) if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif() endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
# (--export-memory is implicit unless --import-memory is given) # (--export-memory is implicit unless --import-memory is given)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--import-memory") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--import-memory")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-memory") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-memory")
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
if(WIN32) if(WIN32)
set(WASI_HOST_EXE_SUFFIX ".exe") set(WASI_HOST_EXE_SUFFIX ".exe")
@ -20,7 +21,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory # When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX) if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif() endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_NAME WASI)
set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR wasm32) set(CMAKE_SYSTEM_PROCESSOR wasm32)
set(CMAKE_EXECUTABLE_SUFFIX .wasm)
set(triple wasm32-wasi) set(triple wasm32-wasi)
if(WIN32) if(WIN32)
@ -17,7 +18,7 @@ endif()
# When building from source, WASI_SDK_PREFIX represents the generated directory # When building from source, WASI_SDK_PREFIX represents the generated directory
if(NOT WASI_SDK_PREFIX) if(NOT WASI_SDK_PREFIX)
set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../)
endif() endif()
set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX})

Loading…
Cancel
Save