diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml new file mode 100644 index 000000000..aca1b722a --- /dev/null +++ b/.github/actions/checkout/action.yml @@ -0,0 +1,16 @@ +name: 'Prepare wasi-sdk git directory' +description: 'Prepare wasi-sdk git directory' + +runs: + using: composite + steps: + - run: git fetch --tags --force + name: Force-fetch tags to work around actions/checkout#290 + shell: bash + # We can't use `--depth 1` here sadly because the GNU config + # submodule is not pinned to a particular tag/branch. Please + # bump depth (or even better, the submodule), in case of "error: + # Server does not allow request for unadvertised object" in the + # future. + - run: git submodule update --init --depth 64 --jobs 3 + shell: bash diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml new file mode 100644 index 000000000..e3d00f823 --- /dev/null +++ b/.github/actions/install-deps/action.yml @@ -0,0 +1,28 @@ +name: 'Install wasi-sdk dependencies' +description: 'Install wasi-sdk dependencies' + +runs: + using: composite + steps: + - name: Setup `wasmtime` for tests + uses: bytecodealliance/actions/wasmtime/setup@v1 + with: + version: "41.0.3" + - name: Install ccache, ninja (macOS) + run: brew install ccache ninja + if: runner.os == 'macOS' + shell: bash + - name: Install ccache, ninja (Windows) + run: choco install ccache ninja + if: startsWith(matrix.os, 'windows') + shell: bash + # Windows arm runners don't come with rust by default (see https://github.com/actions/partner-runner-images/blob/main/images/arm-windows-11-image.md) + # but the x86 ones do (see https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md) + - name: Install cargo (Windows-arm) + run: choco install rust + if: matrix.os == 'windows-11-arm' + shell: bash + - name: Install ccache, ninja (Linux) + run: sudo apt-get install -y ccache ninja-build + if: runner.os == 'Linux' + shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 718cb423f..2644c8f2f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,151 +1,243 @@ name: CI on: - create: - tags: push: + tags: + - 'wasi-sdk-*' branches: - main + pull_request: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: - name: Native Build + name: Build ${{ matrix.artifact }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest + include: + - artifact: x86_64-linux + os: ubuntu-24.04 + + - artifact: arm64-linux + 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 + os: macos-14 + rust_target: aarch64-apple-darwin + env: + WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >- + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 + -DCMAKE_OSX_ARCHITECTURES=arm64 + + - artifact: x86_64-macos + os: macos-14 + rust_target: x86_64-apple-darwin + env: + WASI_SDK_CI_SKIP_SYSROOT: 1 + WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >- + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 + -DCMAKE_OSX_ARCHITECTURES=x86_64 + + - artifact: x86_64-windows + os: windows-2022 + + - artifact: arm64-windows + os: windows-11-arm + + env: ${{ matrix.env || fromJSON('{}') }} steps: - - uses: actions/cache@v3 + - uses: actions/checkout@v6 with: - path: ~/.cache/ccache - # 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 becomes a potential - # suspect. - key: 0-cache-ubuntu-latest-${{ github.run_id }} - restore-keys: | - 0-cache-ubuntu-latest - if: matrix.os == 'ubuntu-latest' - - uses: actions/cache@v3 + fetch-depth: 0 + - uses: ./.github/actions/checkout + - uses: ./.github/actions/install-deps + + # Persist ccache-based caches across builds. This directory is configured + # via the CCACHE_DIR env var below for ccache to use. + # + # 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 + # becomes a potential suspect. + - uses: actions/cache@v5 + id: cache-restore with: - path: ~/Library/Caches/ccache - key: 0-cache-macos-latest-${{ github.run_id }} + path: ${{ runner.tool_cache }}/ccache + key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }} restore-keys: | - 0-cache-macos-latest - if: matrix.os == 'macos-latest' - - name: Install wasmtime for tests - run: curl -f -L --retry 5 https://wasmtime.dev/install.sh | bash -s -- --version v2.0.2 - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - # We can't use `--depth 1` here sadly because the GNU config - # submodule is not pinned to a particular tag/branch. Please - # bump depth (or even better, the submodule), in case of "error: - # Server does not allow request for unadvertised object" in the - # future. - - run: git submodule update --init --depth 16 --jobs 3 - - name: Install ccache, ninja (macOS) - run: brew install ccache ninja - if: matrix.os == 'macos-latest' - - name: Install ccache, ninja (Linux) - run: sudo apt install ccache ninja-build - if: matrix.os == 'ubuntu-latest' - - name: Build - run: NINJA_FLAGS=-v make package LLVM_CMAKE_FLAGS=-DLLVM_CCACHE_BUILD=ON + 0-cache-${{ matrix.artifact }}- + - run: | + mkdir -p '${{ runner.tool_cache }}/ccache' + echo 'CCACHE_DIR=${{ runner.tool_cache }}/ccache' >> $GITHUB_ENV + shell: bash + + # Configure CMake flags for `ci/build.sh` as necessary for each + # matrix entry. + - run: | + cmake_args=-DWASI_SDK_ARTIFACT=${{ matrix.artifact }} + if [ "${{ matrix.rust_target }}" != "" ]; then + rustup target add ${{ matrix.rust_target }} + cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}" + 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 + shell: bash + + - name: Clear ccache statistics + run: ccache --zero-stats + + - name: Build and test (macOS) + run: ./ci/build.sh + if: runner.os == 'macOS' + + - name: Build and test (Linux) + run: ./ci/docker-build.sh ${{ matrix.artifact }} + if: runner.os == 'Linux' + + # Setup the VS Developoer Prompt environment variables to explicitly use + # MSVC to compile LLVM as that avoids extra runtime dependencies + # msys/mingw might bring. + # + # As of 2024-07-22 this sha is the "v1.13.0" tag. + - uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 + if: startsWith(matrix.os, 'windows') + - name: Build and test (Windows) + run: | + # Delete a troublesome binary as recommended here + # https://github.com/ilammy/msvc-dev-cmd?tab=readme-ov-file#name-conflicts-with-shell-bash + rm /usr/bin/link + # Use a shorter build directory than the default on Windows to avoid + # hitting path length and command line length limits. See + # WebAssembly/wasi-libc#514. Despite using a different build directory + # though still move the `dist` folder to `build/dist` so the upload + # step below doesn't need a windows-specific hook. + ./ci/build.sh C:/wasi-sdk + mkdir build + cp -r C:/wasi-sdk/dist build shell: bash - - name: Run the testsuite - run: NINJA_FLAGS=-v make check RUNTIME=~/.wasmtime/bin/wasmtime + if: startsWith(matrix.os, 'windows') + + # Upload the `dist` folder from the build as the artifacts for this + # runner. - name: Upload artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v6 with: - # Upload the dist folder. Give it a name according to the OS it was built for. - name: ${{ format( 'dist-{0}', matrix.os) }} - path: dist + name: ${{ format( 'dist-{0}', matrix.artifact) }} + path: build/dist + + # Caches are persisted across runs by restoring the latest cache which + # means that quite a lot of cruft can accumulate. Prune older entries that + # haven't been used by this run to avoid the cache continuously getting + # larger. In theory this should use `--evict-older-than $dur` where `$dur` + # is the time since the start of the run, but I'm not sure how to easily + # calculate that so pick something loose like one day instead. + - name: Prune ccache objects + run: ccache --evict-older-than 1d + + # Help debug ccache issues by showing what happened. + - if: always() + name: Show ccache statistics + run: ccache --show-stats - winbuild: - name: Windows Build - runs-on: windows-latest + # Always save a cache, even if the build failed. This ensures that if + # live-debugging via CI the build gets to pick up where it left off last + # time instead of having to recreate everything each time a failure + # happens. + - if: always() && steps.cache-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@v5 + with: + path: ${{ runner.tool_cache }}/ccache + key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }} + + build-only-sysroot: + name: Build only sysroot - ${{ matrix.name }} + runs-on: ubuntu-24.04 strategy: - fail-fast: false matrix: include: - - arch: x64 - sys: clang64 - env: clang-x86_64 - - arch: x86 - sys: clang32 - env: clang-i686 + - name: default + - name: exceptions + defines: -DWASI_SDK_EXCEPTIONS=ON steps: - - uses: actions/cache@v3 - with: - path: ~/AppData/Local/ccache - key: 0-${{ format( 'cache-windows-latest-{0}', matrix.arch) }}-${{ github.run_id }} - restore-keys: | - 0-${{ format( 'cache-windows-latest-{0}', matrix.arch) }} - - uses: msys2/setup-msys2@v2 - with: - install: >- - base-devel - git - mingw-w64-${{ matrix.env }}-ccache - mingw-w64-${{ matrix.env }}-cmake - mingw-w64-${{ matrix.env }}-ninja - mingw-w64-${{ matrix.env }}-toolchain - msystem: ${{ matrix.sys }} - update: true - release: false - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - run: git submodule update --init --depth 16 --jobs 3 - - name: Build - shell: msys2 {0} - run: | - make package LLVM_CMAKE_FLAGS=-DLLVM_CCACHE_BUILD=ON - make check - - name: Does it work sans msys2? + - uses: ./.github/actions/checkout + - uses: ./.github/actions/install-deps + - run: cargo install wasm-component-ld@0.5.21 + - name: Install LLVM 22 run: | - C:\wasi-sdk\bin\clang.exe --version - C:\wasi-sdk\bin\llvm-ar.exe --version - C:\wasi-sdk\bin\wasm-ld.exe --version - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - # Upload the dist folder. Give it a name according to the OS it was built for. - name: ${{ format( 'dist-windows-latest-{0}', matrix.arch) }} - path: dist + 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 - dockerbuild: - name: Docker Build - runs-on: ubuntu-latest + # 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 + # artifacts. + finalize: + name: Finalize wasi-sdk artifacts + needs: build + runs-on: ubuntu-24.04 steps: - - uses: actions/cache@v3 - with: - path: ~/.ccache - key: 0-cache-ubuntu-bionic-${{ github.run_id }} - restore-keys: | - 0-cache-ubuntu-bionic - - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 with: fetch-depth: 0 + - uses: ./.github/actions/checkout - - run: git submodule update --init --depth 16 --jobs 3 + # Download all artifacts from all platforms in `build`, merge them into + # final wasi-sdk-* artifacts, and then upload them. + - uses: actions/download-artifact@v8 + - run: ./ci/merge-artifacts.sh + - uses: actions/upload-artifact@v6 + with: + name: release-artifacts + path: dist + # Use the `wasi-sdk-*` artifacts just created to create a docker image + # with a toolchain pre-installed. - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: docker/setup-qemu-action@v2 - uses: docker/setup-buildx-action@v2 - - uses: docker/metadata-action@v4 id: meta with: @@ -156,16 +248,6 @@ jobs: type=ref,event=tag type=ref,event=pr type=sha - - - name: Run docker_build script - run: ./docker_build.sh - - name: Upload artifacts - uses: actions/upload-artifact@v1 - with: - # Upload the dist folder. Give it a name according to the OS it was built for. - name: dist-ubuntu-bionic - path: dist - - name: Build and push wasi-sdk docker image uses: docker/build-push-action@v3 with: @@ -177,3 +259,37 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + + - name: Publish a draft release + if: startsWith(github.ref, 'refs/tags') + run: gh release create --draft --prerelease --generate-notes ${{ github.ref_name }} ./dist/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Test the final artifacts as-is without passing `--sysroot` or + # `-resource-dir` or any extra flags. This exercises running the compiler + # as-is from the distribution tarballs and ensuring that it can build and pass + # all tests. + test-standalone: + name: Test standalone toolchain + needs: build + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: ./.github/actions/checkout + - uses: ./.github/actions/install-deps + - uses: actions/download-artifact@v8 + with: + name: dist-x86_64-linux + path: dist-x86_64-linux + - run: ./ci/merge-artifacts.sh + - run: tar xf dist/wasi-sdk-*.tar.gz + - run: | + cmake -G Ninja -B build -S . \ + -DWASI_SDK_INCLUDE_TESTS=ON \ + -DWASI_SDK_TEST_HOST_TOOLCHAIN=ON \ + -DCMAKE_TOOLCHAIN_FILE=$(ls ./wasi-sdk-*/share/cmake/wasi-sdk.cmake) + - run: ninja -C build build-tests + - run: ctest --output-on-failure --parallel 10 --test-dir build/tests diff --git a/.gitmodules b/.gitmodules index c7de08f7e..ca31b7a0b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,10 @@ [submodule "src/llvm-project"] - path = src/llvm-project - url = https://github.com/dotnet/llvm-project - branch = dotnet/llvm-project +path = src/llvm-project +url = https://github.com/dotnet/llvm-project +branch = dotnet/llvm-project [submodule "src/wasi-libc"] - path = src/wasi-libc - url = https://github.com/WebAssembly/wasi-libc +path = src/wasi-libc +url = https://github.com/WebAssembly/wasi-libc +[submodule "src/config"] +path = src/config +url = https://git.savannah.gnu.org/git/config.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..a0d3fca77 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +# Build logic for building both a toolchain and a sysroot for WASI. +# +# This top level `CMakeLists.txt` file can be used either to build a clang +# toolchain or a WASI sysroot. Note that this can't be done at the same time. +# A toolchain build requires a compiler for the target architecture. A +# WASI sysroot build requires this previous compiler and must be runnable on +# the host. + +cmake_minimum_required(VERSION 3.26) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +project(wasi-sdk) +include(ExternalProject) + +set(WASI_SDK_TARGETS "wasm32-wasi;wasm32-wasip1;wasm32-wasip2;wasm32-wasip1-threads;wasm32-wasi-threads" + CACHE STRING "List of WASI targets to build") +option(WASI_SDK_BUILD_TOOLCHAIN "Build a toolchain instead of the sysroot" OFF) + +set(llvm_proj_dir ${CMAKE_CURRENT_SOURCE_DIR}/src/llvm-project) +set(wasi_libc ${CMAKE_CURRENT_SOURCE_DIR}/src/wasi-libc) + +include(wasi-sdk-enable-ccache) + +find_program(PYTHON python3 python REQUIRED) + +# Set some variables based on the `version.py` script +set(version_script ${CMAKE_CURRENT_SOURCE_DIR}/version.py) +execute_process( + COMMAND ${PYTHON} ${version_script} + OUTPUT_VARIABLE wasi_sdk_version + OUTPUT_STRIP_TRAILING_WHITESPACE) + +message(STATUS "wasi-sdk version is ${wasi_sdk_version}") + +# Only include one version of the build logic as pulling in both isn't +# supported at this time. +if(WASI_SDK_BUILD_TOOLCHAIN) +include(wasi-sdk-toolchain) +else() +include(wasi-sdk-sysroot) +endif() diff --git a/CppExceptions.md b/CppExceptions.md new file mode 100644 index 000000000..1ca79bf3c --- /dev/null +++ b/CppExceptions.md @@ -0,0 +1,70 @@ +# Support for C++ Exceptions + +> **Note**: this documentation does not cover wasi-sdk-31, the latest version +> of wasi-sdk at this time. + +From wasi-sdk-33 and onwards the artifacts produced by this repository support +compiling C++ code both with and without exceptions. The sysroot for wasm +targets contains two copies of the C++ standard library and headers -- one with +exceptions enabled and one with exceptions disabled. These are automatically +selected based on compilation flags. This means that wasi-sdk-produced binaries +can avoid using wasm exceptions entirely by disabling C++ exceptions, or C++ +exceptions can be enabled in which case wasm exceptions will be used. + +Currently the default is for C++ exceptions to be disabled. + +## Compiling code with C++ exceptions + +Currently extra compilation flags are required to fully support C++ exceptions. +Without these flags programs using C++ exceptions will not work correctly: + +* `-fwasm-exceptions` - needed to enable the WebAssembly exception-handling + proposal. +* `-mllvm -wasm-use-legacy-eh=false` - indicates that the standard WebAssembly + exception-handling instructions should be used. +* `-lunwind` - links in support for unwinding which C++ exceptions requires. + +This can be specified for example with: + +```shell script +$ export CFLAGS="-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false" +$ export LDFLAGS="-fwasm-exceptions -lunwind" +``` + +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 +``` + +The C++ standard library will be compiled with support for exceptions for the +desired targets and the resulting sysroot supports using exceptions. Note that +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 + +There are a few known limitations/bugs/todos around exceptions support in +wasi-sdk at this time: + +* Currently C++ exceptions support in wasi-sdk does not support shared + libraries. Fixing this will require resolving some miscellaneous build + issues in this repository itself as well as [resolving some upstream + 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. diff --git a/Makefile b/Makefile deleted file mode 100644 index 761ccbb8e..000000000 --- a/Makefile +++ /dev/null @@ -1,221 +0,0 @@ -# Any copyright is dedicated to the Public Domain. -# http://creativecommons.org/publicdomain/zero/1.0/ - -ROOT_DIR=${CURDIR} -LLVM_PROJ_DIR?=$(ROOT_DIR)/src/llvm-project - -# Windows needs munging -ifeq ($(OS),Windows_NT) - -PREFIX?=c:/wasi-sdk -# we need to explicitly call bash -c for makefile $(shell ...), otherwise we'll try under -# who knows what -BASH=bash -c - -ifeq (x$(MSYSTEM),x) -$(error On Windows, this Makefile only works in MSYS2 environments such as git-bash.) -endif - -# msys needs any /-prefixed arguments, or =/ containing, to turn into // -# because it tries to path-expand the / into the msys root. // escapes this. -ESCAPE_SLASH=/ - -BUILD_PREFIX=$(PREFIX) - -# assuming we're running under msys2 (git-bash), PATH needs /c/foo format directories (because -# it itself is :-delimited) -PATH_PREFIX=$(shell cygpath.exe -u $(BUILD_PREFIX)) - -else - -PREFIX?=/opt/wasi-sdk -DESTDIR=$(abspath build/install) -BUILD_PREFIX=$(DESTDIR)$(PREFIX) -PATH_PREFIX=$(BUILD_PREFIX) -ESCAPE_SLASH?= -BASH= - -endif - -CLANG_VERSION=$(shell $(BASH) ./llvm_version.sh $(LLVM_PROJ_DIR)) -VERSION:=$(shell $(BASH) ./version.sh) -DEBUG_PREFIX_MAP=-fdebug-prefix-map=$(ROOT_DIR)=wasisdk://v$(VERSION) - -default: build - @echo "Use -fdebug-prefix-map=$(ROOT_DIR)=wasisdk://v$(VERSION)" - -check: - CC="clang --sysroot=$(BUILD_PREFIX)/share/wasi-sysroot" \ - CXX="clang++ --sysroot=$(BUILD_PREFIX)/share/wasi-sysroot -fno-exceptions" \ - PATH="$(PATH_PREFIX)/bin:$$PATH" tests/run.sh $(RUNTIME) - -clean: - rm -rf build $(DESTDIR) - -build/llvm.BUILT: - mkdir -p build/llvm - cd build/llvm && cmake -G Ninja \ - -DCMAKE_BUILD_TYPE=MinSizeRel \ - -DLLVM_ENABLE_TERMINFO=OFF \ - -DLLVM_ENABLE_ZLIB=OFF \ - -DLLVM_ENABLE_ZSTD=OFF \ - -DLLVM_STATIC_LINK_CXX_STDLIB=ON \ - -DLLVM_HAVE_LIBXAR=OFF \ - -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \ - -DCMAKE_INSTALL_PREFIX=$(PREFIX) \ - -DLLVM_TARGETS_TO_BUILD=WebAssembly \ - -DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasi \ - -DLLVM_ENABLE_PROJECTS="lld;clang;clang-tools-extra" \ - $(if $(patsubst 9.%,,$(CLANG_VERSION)), \ - $(if $(patsubst 10.%,,$(CLANG_VERSION)), \ - -DDEFAULT_SYSROOT=../share/wasi-sysroot, \ - -DDEFAULT_SYSROOT=$(PREFIX)/share/wasi-sysroot), \ - -DDEFAULT_SYSROOT=$(PREFIX)/share/wasi-sysroot) \ - -DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE \ - -DLLVM_ENABLE_LIBXML2=OFF \ - $(LLVM_CMAKE_FLAGS) \ - $(LLVM_PROJ_DIR)/llvm - DESTDIR=$(DESTDIR) ninja $(NINJA_FLAGS) -C build/llvm \ - install-clang \ - install-clang-format \ - install-clang-tidy \ - install-clang-apply-replacements \ - install-lld \ - install-llvm-mc \ - install-llvm-ranlib \ - install-llvm-strip \ - install-llvm-dwarfdump \ - install-clang-resource-headers \ - install-ar \ - install-ranlib \ - install-strip \ - install-nm \ - install-size \ - install-strings \ - install-objdump \ - install-objcopy \ - install-c++filt \ - llvm-config - touch build/llvm.BUILT - -build/wasi-libc.BUILT: build/llvm.BUILT - $(MAKE) -C $(ROOT_DIR)/src/wasi-libc \ - CC=$(BUILD_PREFIX)/bin/clang \ - AR=$(BUILD_PREFIX)/bin/llvm-ar \ - NM=$(BUILD_PREFIX)/bin/llvm-nm \ - SYSROOT=$(BUILD_PREFIX)/share/wasi-sysroot - $(MAKE) -C $(ROOT_DIR)/src/wasi-libc \ - CC=$(BUILD_PREFIX)/bin/clang \ - AR=$(BUILD_PREFIX)/bin/llvm-ar \ - NM=$(BUILD_PREFIX)/bin/llvm-nm \ - SYSROOT=$(BUILD_PREFIX)/share/wasi-sysroot \ - THREAD_MODEL=posix - touch build/wasi-libc.BUILT - -build/compiler-rt.BUILT: build/llvm.BUILT build/wasi-libc.BUILT - # Do the build, and install it. - mkdir -p build/compiler-rt - cd build/compiler-rt && cmake -G Ninja \ - -DCMAKE_SYSROOT=$(BUILD_PREFIX)/share/wasi-sysroot \ - -DCMAKE_C_COMPILER_WORKS=ON \ - -DCMAKE_CXX_COMPILER_WORKS=ON \ - -DCMAKE_AR=$(BUILD_PREFIX)/bin/ar \ - -DCMAKE_MODULE_PATH=$(ROOT_DIR)/cmake \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_TOOLCHAIN_FILE=$(ROOT_DIR)/wasi-sdk.cmake \ - -DCOMPILER_RT_BAREMETAL_BUILD=On \ - -DCOMPILER_RT_BUILD_XRAY=OFF \ - -DCOMPILER_RT_INCLUDE_TESTS=OFF \ - -DCOMPILER_RT_HAS_FPIC_FLAG=OFF \ - -DCOMPILER_RT_ENABLE_IOS=OFF \ - -DCOMPILER_RT_DEFAULT_TARGET_ONLY=On \ - -DWASI_SDK_PREFIX=$(BUILD_PREFIX) \ - -DCMAKE_C_FLAGS="$(DEBUG_PREFIX_MAP)" \ - -DLLVM_CONFIG_PATH=$(ROOT_DIR)/build/llvm/bin/llvm-config \ - -DCOMPILER_RT_OS_DIR=wasi \ - -DCMAKE_INSTALL_PREFIX=$(PREFIX)/lib/clang/$(CLANG_VERSION)/ \ - -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ - $(LLVM_PROJ_DIR)/compiler-rt/lib/builtins - DESTDIR=$(DESTDIR) ninja $(NINJA_FLAGS) -C build/compiler-rt install - # Install clang-provided headers. - cp -R $(ROOT_DIR)/build/llvm/lib/clang $(BUILD_PREFIX)/lib/ - touch build/compiler-rt.BUILT - -# Flags for libcxx and libcxxabi. -LIBCXX_CMAKE_FLAGS = \ - -DCMAKE_C_COMPILER_WORKS=ON \ - -DCMAKE_CXX_COMPILER_WORKS=ON \ - -DCMAKE_AR=$(BUILD_PREFIX)/bin/ar \ - -DCMAKE_MODULE_PATH=$(ROOT_DIR)/cmake \ - -DCMAKE_TOOLCHAIN_FILE=$(ROOT_DIR)/wasi-sdk.cmake \ - -DCMAKE_STAGING_PREFIX=$(PREFIX)/share/wasi-sysroot \ - -DLLVM_CONFIG_PATH=$(ROOT_DIR)/build/llvm/bin/llvm-config \ - -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \ - -DCXX_SUPPORTS_CXX11=ON \ - -DLIBCXX_ENABLE_THREADS:BOOL=OFF \ - -DLIBCXX_HAS_PTHREAD_API:BOOL=OFF \ - -DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF \ - -DLIBCXX_BUILD_EXTERNAL_THREAD_LIBRARY:BOOL=OFF \ - -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF \ - -DLLVM_COMPILER_CHECKED=ON \ - -DCMAKE_BUILD_TYPE=RelWithDebugInfo \ - -DLIBCXX_ENABLE_SHARED:BOOL=OFF \ - -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY:BOOL=OFF \ - -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=OFF \ - -DLIBCXX_ENABLE_FILESYSTEM:BOOL=OFF \ - -DLIBCXX_CXX_ABI=libcxxabi \ - -DLIBCXX_CXX_ABI_INCLUDE_PATHS=$(LLVM_PROJ_DIR)/libcxxabi/include \ - -DLIBCXX_HAS_MUSL_LIBC:BOOL=ON \ - -DLIBCXX_ABI_VERSION=2 \ - -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=OFF \ - -DLIBCXXABI_ENABLE_SHARED:BOOL=OFF \ - -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON \ - -DLIBCXXABI_ENABLE_THREADS:BOOL=OFF \ - -DLIBCXXABI_HAS_PTHREAD_API:BOOL=OFF \ - -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF \ - -DLIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY:BOOL=OFF \ - -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF \ - -DLIBCXXABI_ENABLE_PIC:BOOL=OFF \ - -DWASI_SDK_PREFIX=$(BUILD_PREFIX) \ - -DUNIX:BOOL=ON \ - --debug-trycompile - -build/libcxx.BUILT: build/llvm.BUILT build/compiler-rt.BUILT build/wasi-libc.BUILT - # Do the build. - mkdir -p build/libcxx - cd build/libcxx && cmake -G Ninja $(LIBCXX_CMAKE_FLAGS) \ - -DCMAKE_SYSROOT=$(BUILD_PREFIX)/share/wasi-sysroot \ - -DCMAKE_C_FLAGS="$(DEBUG_PREFIX_MAP)" \ - -DCMAKE_CXX_FLAGS="$(DEBUG_PREFIX_MAP)" \ - -DLIBCXX_LIBDIR_SUFFIX=$(ESCAPE_SLASH)/wasm32-wasi \ - -DLIBCXXABI_LIBDIR_SUFFIX=$(ESCAPE_SLASH)/wasm32-wasi \ - -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi" \ - $(LLVM_PROJ_DIR)/runtimes - ninja $(NINJA_FLAGS) -C build/libcxx - # Do the install. - DESTDIR=$(DESTDIR) ninja $(NINJA_FLAGS) -C build/libcxx install - touch build/libcxx.BUILT - -build/config.BUILT: - mkdir -p $(BUILD_PREFIX)/share/misc - cp src/config/config.sub src/config/config.guess $(BUILD_PREFIX)/share/misc - mkdir -p $(BUILD_PREFIX)/share/cmake - cp wasi-sdk.cmake $(BUILD_PREFIX)/share/cmake - cp wasi-sdk-pthread.cmake $(BUILD_PREFIX)/share/cmake - touch build/config.BUILT - -build: build/llvm.BUILT build/wasi-libc.BUILT build/compiler-rt.BUILT build/libcxx.BUILT build/config.BUILT - -strip: build/llvm.BUILT - ./strip_symbols.sh $(BUILD_PREFIX)/bin - -package: build/package.BUILT - -build/package.BUILT: build strip - mkdir -p dist - ./deb_from_installation.sh $(shell pwd)/dist "$(VERSION)" "$(BUILD_PREFIX)" - ./tar_from_installation.sh "$(shell pwd)/dist" "$(VERSION)" "$(BUILD_PREFIX)" - touch build/package.BUILT - -.PHONY: default clean build strip package check diff --git a/README.md b/README.md index 6e6ecbfea..7893c9639 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ ## Quick Start -[Download SDK packages here.](https://github.com/WebAssembly/wasi-sdk/releases) +[Download SDK packages here.][releases] + +[releases]: https://github.com/WebAssembly/wasi-sdk/releases ## About this repository @@ -10,8 +12,9 @@ This repository contains no compiler or library code itself; it uses git submodules to pull in the upstream Clang and LLVM tree, as well as the wasi-libc tree. -The libc portion of this SDK is the -[wasi-libc](https://github.com/WebAssembly/wasi-libc). +The libc portion of this SDK is maintained in [wasi-libc]. + +[wasi-libc]: https://github.com/WebAssembly/wasi-libc Upstream Clang and LLVM (from 9.0 onwards) can compile for WASI out of the box, and WebAssembly support is included in them by default. So, all that's done here @@ -19,8 +22,11 @@ is to provide builds configured to set the default target and sysroot for convenience. One could also use a standard Clang installation, build a sysroot from the -sources mentioned above, and compile with -"--target=wasm32-wasi --sysroot=/path/to/sysroot". +sources mentioned above, and compile with `--target=wasm32-wasi +--sysroot=/path/to/sysroot`. In this scenario, one would also need the +`libclang_rt.*.a` objects available separately in the [release +downloads][releases] which must be extracted into +`$CLANG_INSTALL_DIR/$CLANG_VERSION/lib/`. ## Clone @@ -37,45 +43,145 @@ The Wasm-sdk's build process needs some packages : * `cmake` * `clang` * `ninja` +* `python3` +* `cargo` + +Please refer to your OS documentation to install those packages. + +## Build + +Building `wasi-sdk` uses CMake and is split into two halves. First you can build +the toolchain itself: + +```shell script +cmake -G Ninja -B build/toolchain -S . -DWASI_SDK_BUILD_TOOLCHAIN=ON -DCMAKE_INSTALL_PREFIX=build/install +cmake --build build/toolchain --target install +``` + +When you're developing locally you may also wish to pass +`-DCMAKE_CXX_COMPILER_LAUNCHER=ccache` to assist with rebuilds. Other supported +CMake flags are: + +* `-DLLVM_CMAKE_FLAGS` - extra flags to pass to `cmake` when building + LLVM/Clang. +* `-DRUST_TARGET` - the specific Rust target triple to build `wasm-component-ld` + for, useful for cross-compiles. -Please refer to your OS documentation to install those packages. +The `clang` compiler should now be located at `build/install/bin/clang` but it's +just a compiler, the sysroot isn't built yet. Next the second step of the build +is to build the sysroot: -## Build +```shell script +cmake -G Ninja -B build/sysroot -S . \ + -DCMAKE_INSTALL_PREFIX=build/install \ + -DCMAKE_TOOLCHAIN_FILE=build/install/share/cmake/wasi-sdk.cmake \ + -DCMAKE_C_COMPILER_WORKS=ON \ + -DCMAKE_CXX_COMPILER_WORKS=ON +cmake --build build/sysroot --target install +``` + +A full toolchain should now be present at `build/install` and is ready for use +in compiling WebAssembly code. Supported CMake flags are: + +* `-DWASI_SDK_DEBUG_PREFIX_MAKE=OFF` - disable `-fdebug-prefix-map` when + building C/C++ code to use full host paths instead. +* `-DWASI_SDK_INCLUDE_TESTS=ON` - used for building tests. +* `-DWASI_SDK_CPU_CFLAGS=..` - used to specify CFLAGS to tweak wasm features + to enable/disable. The default is `-mcpu=lime1`. +* `-DWASI_SDK_LTO=ON` - whether to enable/disable builds of LTO-capable + libraries as part of the build. +* `-DWASI_SDK_EXCEPTIONS=ON` - whether to enable/disable support for C++ + exceptions, see [CppExceptions.md](./CppExceptions.md) for more information. +* `-DWASI_SDK_TEST_HOST_TOOLCHAIN=ON` - test the host toolchain's wasi-libc and + sysroot libraries, don't build or use fresh libraries for tests. +* `-DWASI_SDK_TARGETS=..` - a list of targets to build, by default all WASI + targets are compiled. +* `-DWASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR=ON` - install compiler-rt + to the compiler's resource directory. might be convenient if you want to + use the toolchain (eg. `./build/install/bin/clang`) in-place. + +If you'd like to build distribution artifacts you can use the `dist` target like +so: + +```shell script +cmake --build build/toolchain --target dist +cmake --build build/sysroot --target dist +``` -To build the full package +Tarballs will be created under `build/toolchain/dist` and `build/sysroot/dist`. +Note that these are separate tarballs for the toolchain and sysroot. To create a +single tarball for the entire SDK you'll first want to copy all tarballs into a +new folder and then run the `./ci/merge-artifacts.sh` script: ```shell script -cd wasi-sdk -NINJA_FLAGS=-v make package +mkdir dist-my-platform +cp build/toolchain/dist/* build/sysroot/dist/* dist-my-platform +./ci/merge-artifacts.sh ``` -The built package can be found into `dist` directory. +This will produce `dist/wasi-sdk-*.tar.gz` which is the same as the release +artifacts for this repository. + +Finally you can additionally bundle many of the above steps, minus +`merge-artifact.sh` by using the CI script to perform both the toolchain and +sysroot build: + +```shell script +./ci/build.sh +``` + +The built package can be found into `build/dist` directory. +For releasing a new version of the package on GitHub, +see [RELEASING.md](RELEASING.md). ## Install A typical installation from the release binaries might look like the following: + ```shell script -export WASI_VERSION=14 -export WASI_VERSION_FULL=${WASI_VERSION}.0 -wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz -tar xvf wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz +WASI_OS=linux +WASI_ARCH=x86_64 # or 'arm64' if running on arm64 host +WASI_VERSION=27 +WASI_VERSION_FULL=${WASI_VERSION}.0 +wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz +tar xvf wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz ``` ## Use -Use the clang installed in the wasi-sdk directory: +Use the clang installed in the `wasi-sdk` directory: + ```shell script -export WASI_SDK_PATH=`pwd`/wasi-sdk-${WASI_VERSION_FULL} +WASI_SDK_PATH=`pwd`/wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS} CC="${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot" $CC foo.c -o foo.wasm ``` -Note: `${WASI_SDK_PATH}/share/wasi-sysroot` contains the WASI-specific includes/libraries/etc. The `--sysroot=...` option -is not necessary if `WASI_SDK_PATH` is `/opt/wasi-sdk`. + +Note: `${WASI_SDK_PATH}/share/wasi-sysroot` contains the WASI-specific +includes/libraries/etc. The `--sysroot=...` option is not necessary if +`WASI_SDK_PATH` is `/opt/wasi-sdk`. For troubleshooting, one can replace the +`--sysroot` path with a manual build of [wasi-libc]. + +### Integrating with a CMake build system + +Use a toolchain file to setup the *wasi-sdk* platform. + +``` +$ cmake -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk.cmake ... +``` + +or the *wasi-sdk-thread* platform + +``` +$ cmake -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk-pthread.cmake ... +``` ## Notes for Autoconf -[Autoconf](https://www.gnu.org/software/autoconf/autoconf.html) 2.70 now -[recognizes WASI](https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=blob;f=build-aux/config.sub;h=19c9553b1825cafb182115513bc628e0ee801bd0;hb=97fbc5c184acc6fa591ad094eae86917f03459fa#l1723). +[Autoconf] 2.70 now [recognizes WASI]. + +[Autoconf]: https://www.gnu.org/software/autoconf/autoconf.html +[recognizes WASI]: https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=blob;f=build-aux/config.sub;h=19c9553b1825cafb182115513bc628e0ee801bd0;hb=97fbc5c184acc6fa591ad094eae86917f03459fa#l1723 For convenience when building packages that aren't yet updated, updated config.sub and config.guess files are installed at `share/misc/config.*` @@ -83,11 +189,12 @@ in the install directory. ## Docker Image -We provide a [docker image](https://github.com/WebAssembly/wasi-sdk/pkgs/container/wasi-sdk) -including wasi-sdk that can be used for building projects without a -separate installation of the SDK. Autotools, CMake, and Ninja are included -in this image, and standard environment variables are set to use wask-sdk -for building. +We provide a [docker image] including WASI SDK that can be used for building +projects without a separate installation of the SDK. Autotools, CMake, and Ninja +are included in this image, and standard environment variables are set to use +WASI SDK for building. + +[docker image]: https://github.com/WebAssembly/wasi-sdk/pkgs/container/wasi-sdk For example, this command can build a make-based project with the Docker image. @@ -98,28 +205,34 @@ docker run -v `pwd`:/src -w /src ghcr.io/webassembly/wasi-sdk make Take note of the [notable limitations](#notable-limitations) below when building projects, for example many projects will need threads support -disabled in a configure step before building with wasi-sdk. +disabled in a configure step before building with WASI SDK. ## Notable Limitations -This repository does not yet support C++ exceptions. C++ code is -supported only with -fno-exceptions for now. Similarly, there is not -yet support for setjmp/longjmp. Work on support for [exception handling] -is underway at the language level which will support both of these -features. - -[exception handling]: https://github.com/WebAssembly/exception-handling/ - -This repository does not yet support [threads]. Specifically, WASI does -not yet have an API for creating and managing threads yet, and WASI libc -does not yet have pthread support. +* C++ exceptions are disabled by default and require extra configuration to get + working, see [CppExceptions.md]. +* C `setjmp`/`longjmp` require some extra configuration to get working, see + [SetjmpLongjmp.md]. +* Most targets do not support spawning a thread. Experimental support for + spawning threads is available with the `wasm32-wasip1-threads` target which + uses [wasi-threads]. Note that the `pthread_*` family of functions, as well as + C++ threading primitives such as ``, ``, and `` are + available on all targets. 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. +* Dynamic linking [is supported][dylink] but not as fully baked as static + linking. There might be obscure bugs in some situations related to dynamic + linking. +* The WASIp1 targets do not support networking, but WASIp2/WASIp3 support + networking. +* 64-bit linear memories (a "wasm64" target) are not supported at this time. + Supporting this will require resolving [WebAssembly/component-model#22] first + at which point it will be possible to add a `wasm64-wasip2` target. There are + no plans to add support for `wasm64-wasi{,-threads,p1,p1-threads}` at this + time. [threads]: https://github.com/WebAssembly/threads - -This repository does not yet support dynamic libraries. While there are -[some efforts](https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md) -to design a system for dynamic libraries in wasm, it is still in development -and not yet generally usable. - -There is no support for networking. It is a goal of WASI to support networking -in the future though. +[wasi-threads]: https://github.com/WebAssembly/wasi-threads +[dylink]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md +[WebAssembly/component-model#22]: https://github.com/WebAssembly/component-model/issues/22 diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 000000000..72989dd17 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,43 @@ +# Release Process + +We (maintainers) plan to release a new version of wasi-sdk every three months, +coinciding with our monthly meeting to discuss latest issues and pull requests. +This provides regularity to the release cadence, though we also reserve the +right to publish at any intervening time if there is a pressing need (i.e., open +an issue to discuss). + +To publish a new version of `wasi-sdk` as a GitHub release: + +1. Tag a commit with an annotated tag. Note that this must be an annotated tag, + not a lightweight tag, so that `version.py` can use it for calculating the + package version (use `git show wasi-sdk-...` to show other tag messages). + Note that you may need to clear the repository cache to avoid problems with + cached artifacts [^cache]. + + ```shell script + TAG=wasi-sdk-1 + git tag -a $TAG + git push origin $TAG + ``` + +2. Wait for the CI build of the tag to finish. This will automatically publish + a draft pre-release to [GitHub Releases](https://github.com/WebAssembly/wasi-sdk/releases). + Release notes are auto-generated and should be reviewed for accuracy. Once + everything looks good manually publish the release through the GitHub UI. + +3. Remember to tag the wasi-libc repository with the new `$TAG` version. + + ```shell script + git submodule status -- src/wasi-libc # grab $WASI_LIBC_COMMIT from the output + cd $WASI_LIBC_REPO_DIR + git tag $TAG $WASI_LIBC_COMMIT + git push origin $TAG + ``` + +[^cache]: Here is an example of how to clear a cache with the GitHub CLI: + + ```shell script + URL=/repos/WebAssembly/wasi-sdk/actions/caches + gh api $URL -q '.actions_caches[].id' \ + | xargs -I {} gh api --method DELETE $URL/{} + ``` diff --git a/SetjmpLongjmp.md b/SetjmpLongjmp.md new file mode 100644 index 000000000..364a06181 --- /dev/null +++ b/SetjmpLongjmp.md @@ -0,0 +1,117 @@ +# C setjmp/longjmp support + +WASI-SDK provides basic setjmp/longjmp support. + +Note that it's still under active development and may change in +future versions. The tl;dr; version of this document is to pass these flags to +the C compiler: + +``` +-mllvm -wasm-enable-sjlj -lsetjmp -mllvm -wasm-use-legacy-eh=false +``` + +## Implementation Primitives + +Support for `setjmp` and `longjmp` is built on top of the +[exception-handling](https://github.com/WebAssembly/exception-handling) +WebAssembly proposal. This proposal is now [phase +5](https://github.com/WebAssembly/proposals) and becoming part of the official +specification. Note, however, that the exception-handling proposal has a long +history and has a "legacy" version which shipped in browsers as well. This means +that there are two different, but similar, sets of instructions that can be +emitted to support `setjmp` and `longjmp`. Clang 20 and later (wasi-sdk-26 and +later) is capable of emitting both at this time via `-mllvm +-wasm-use-legacy-eh={false,true}` compiler flags. + +Another important point is that exception-handling only provides structured +control flow primitives for exceptions. This means it is not possible to purely +define `setjmp` in C as otherwise it must be a function that returns twice. This +means that support for `setjmp` and `longjmp` in WebAssembly relies on a +compiler pass to transform invocations of `setjmp` at a compiler IR level. This +means that the `setjmp` symbol is not defined in wasi-libc, for example, but +instead primitives used to implement `setjmp`, in conjunction with LLVM, are +found in wasi-libc. + +## Build an application + +To build an application using setjmp/longjmp, you need three sets of compiler +flags: + +1. `-mllvm -wasm-enable-sjlj`: Enable LLVM compiler pass which replaces calls to + `setjmp` and `longjmp` with a different implementation that wasi-libc + implements and hooks into. +2. `-lsetjmp`: Link the setjmp library that wasi-libc provides which contains + these hooks that LLVM uses. +2. `-mllvm -wasm-use-legacy-eh=false`: Specify which version of the + exception-handling instructions will be emitted. Note that if this is omitted + it currently defaults to `true` meaning that the legacy instructions are + emitted, not the standard instructions. + +In short, these flags are required to use `setjmp`/`longjmp` + +``` +-mllvm -wasm-enable-sjlj -lsetjmp -mllvm -wasm-use-legacy-eh=false +``` + +### Examples + +This source code: + +```c +#include +#include +#include +#include + +static jmp_buf env; + +static bool test_if_longjmp(void(*f)(void)) { + if (setjmp(env)) + return true; + f(); + return false; +} + +static void do_not_longjmp() { +} + +static void do_longjmp() { + longjmp(env, 1); +} + +int main() { + bool longjmped = test_if_longjmp(do_not_longjmp); + assert(!longjmped); + longjmped = test_if_longjmp(do_longjmp); + assert(longjmped); + return 0; +} +``` + +can be compiled using the standard set of instructions as: + +```shell +clang -Os -o test.wasm test.c \ + -mllvm -wasm-enable-sjlj -lsetjmp -mllvm -wasm-use-legacy-eh=false +``` + +and then `test.wasm` can be executed in a WebAssembly runtime supporting WASI. + +You can also compile for the legacy exceptions proposal with: + +```shell +clang -Os -o test.wasm test.c \ + -mllvm -wasm-enable-sjlj -lsetjmp -mllvm -wasm-use-legacy-eh=true +``` + +and then `test.wasm` can be executed in a WebAssembly runtime supporting the +legacy WebAssembly instructions. + +Note that when compiling with LTO you'll need to pass `-mllvm` flags to the +linker in addition to Clang itself, such as: + +```shell +clang -Os -flto=full -o test.wasm test.c \ + -mllvm -wasm-enable-sjlj -lsetjmp -mllvm -wasm-use-legacy-eh=false \ + -Wl,-mllvm,-wasm-enable-sjlj,-mllvm,-wasm-use-legacy-eh=false +``` diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 000000000..7c02fff8f --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Build logic executed in CI. This is intentionally kept relatively minimal to +# one day not live in bash to have a bash-less build on Windows. For now though +# this will unconditionally build a toolchain and then optionally build a +# sysroot. Builders which can't actually execute the toolchain they produce +# skip the sysroot step below. + +set -ex + +# Optionally allow the first argument to this script to be the install +# location. +if [ "$1" = "" ]; then + build_dir=`pwd`/build +else + build_dir="$1" +fi + +cmake -G Ninja -B $build_dir/toolchain -S . \ + -DWASI_SDK_BUILD_TOOLCHAIN=ON \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + "-DCMAKE_INSTALL_PREFIX=$build_dir/install" \ + $WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS \ + "-DLLVM_CMAKE_FLAGS=$WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS" +ninja -C $build_dir/toolchain install dist -v + +mv $build_dir/toolchain/dist $build_dir/dist + +if [ "$WASI_SDK_CI_SKIP_SYSROOT" = "1" ]; then + exit 0 +fi + +# Use the just-built toolchain and its `CMAKE_TOOLCHAIN_FILE` to build a +# sysroot. +cmake -G Ninja -B $build_dir/sysroot -S . \ + "-DCMAKE_TOOLCHAIN_FILE=$build_dir/install/share/cmake/wasi-sdk.cmake" \ + -DCMAKE_C_COMPILER_WORKS=ON \ + -DCMAKE_CXX_COMPILER_WORKS=ON \ + -DWASI_SDK_INCLUDE_TESTS=ON \ + -DWASI_SDK_EXCEPTIONS=DUAL \ + "-DCMAKE_INSTALL_PREFIX=$build_dir/install" +ninja -C $build_dir/sysroot install dist -v + +mv $build_dir/sysroot/dist/* $build_dir/dist + +if [ "$WASI_SDK_CI_SKIP_TESTS" = "1" ]; then + exit 0 +fi + +# Run tests to ensure that the sysroot works. +ctest --output-on-failure --parallel 10 --test-dir $build_dir/sysroot/tests \ + --timeout 60 diff --git a/ci/docker-build.sh b/ci/docker-build.sh new file mode 100755 index 000000000..2915e286c --- /dev/null +++ b/ci/docker-build.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +# This is a helper script invoked from CI which will execute the `ci/build.sh` +# script within a docker container. This contain is built using the Dockerfile located at `ci/docker/Dockerfile` +# This container is then used to execute `ci/build.sh`. + +set -e + +if [ "$1" = "" ]; then + echo "Usage: $0 " + echo "" + echo "example: $0 x86_64-linux" + exit 1 +fi + +set -x + +# Build the Docker image. Use an artifact-specific Dockerfile if one exists +# (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 +# this location as well. To ensure that container-created files are reasonable +# on the host as well the `--user` is passed to configure various permissions. +args="--workdir /src --volume `pwd`:/src:Z" +args="$args --user $(id -u):$(id -g)" + +# Persist the ccache directory on the host to ensure repeated runs/debugging +# of this container don't take forever. Also enables caching in CI. +ccache_dir=$CCACHE_DIR +if [ "$ccache_dir" = "" ]; then + ccache_dir=$HOME/.ccache +fi +args="$args --volume $ccache_dir:/ccache:Z --env CCACHE_DIR=/ccache" + +# 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 +# for these tools. +args="$args --volume `rustc --print sysroot`:/rustc: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 +args="$args --env WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS" +args="$args --env WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS" +args="$args --env WASI_SDK_CI_SKIP_SYSROOT" +args="$args --env WASI_SDK_CI_SKIP_TESTS" + +# Before running `ci/build.sh` set up some rust/PATH related info to use what +# was just mounted above, and then execute the build. +docker run \ + $args \ + --tty \ + --init \ + wasi-sdk-builder \ + bash -c 'CARGO_HOME=/tmp/cargo-home PATH=$PATH:/rustc/bin:/wasmtime exec ci/build.sh' diff --git a/ci/docker/Dockerfile b/ci/docker/Dockerfile new file mode 100644 index 000000000..71f4a1d61 --- /dev/null +++ b/ci/docker/Dockerfile @@ -0,0 +1,33 @@ +# Use a relatively old/stable distro here to maximize the supported platforms +# and avoid depending on more recent version of, say, libc. +# Here we choose AlmaLinux 8 + +FROM almalinux:8 + +# Various build tooling and such necessary to build LLVM and a wasi-sysroot +RUN dnf install -y \ + curl \ + ca-certificates \ + clang \ + python3 \ + git \ + unzip \ + cmake \ + ncurses-devel + +COPY ./install-ccache.sh . +RUN ./install-ccache.sh + +ENV PATH /opt/ccache/bin:$PATH + +# AlmaLinux 8 doesn't seem to have ninja, so install it manually. +RUN ARCH=$(uname -m) \ + && if [ "$ARCH" = "aarch64" ]; then SUFFIX=-aarch64; fi \ + && curl -sSL -o ninja.zip https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux${SUFFIX}.zip \ + && unzip ninja.zip \ + && rm *.zip \ + && mv ninja /opt/ccache/bin + +# 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 diff --git a/ci/docker/Dockerfile.riscv64-linux b/ci/docker/Dockerfile.riscv64-linux new file mode 100644 index 000000000..f4456ecf7 --- /dev/null +++ b/ci/docker/Dockerfile.riscv64-linux @@ -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 diff --git a/ci/docker/README.md b/ci/docker/README.md new file mode 100644 index 000000000..09ed7bcf4 --- /dev/null +++ b/ci/docker/README.md @@ -0,0 +1,10 @@ +# About + +This folder contains the docker images that are used in CI to build the wasi-sdk +release toolchains. Docker is used to intentionally use older Linux +distributions to build the toolchain to have a more maximal set of glibc +compatibility. + +These images are intended to be used on an x86\_64 host. Images start from the +`Dockerfile.common` file and then layer on target-specific +toolchains/options/etc as necessary. diff --git a/ci/docker/install-ccache.sh b/ci/docker/install-ccache.sh new file mode 100755 index 000000000..e70fb6482 --- /dev/null +++ b/ci/docker/install-ccache.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# AlmaLinux 8, the container this script runs in, does not have ccache in its +# package repositories. The ccache project publishes both x86_64 and aarch64 +# binaries, however. The x86_64 binaries for ccache are themselves built in +# AlmaLinux 8 so they're compatible, but the aarch64 binaries are built in a +# newer container and don't run on AlmaLinux 8. +# +# Thus this script downloads precompiled binaries for x86_64 but builds from +# source on aarch64. + +ARCH=$(uname -m) +ver=4.12.1 + +if [ "x$ARCH" = "x86_64" ]; then + curl -sSLO https://github.com/ccache/ccache/releases/download/v${ver}/ccache-${ver}-linux-${ARCH}.tar.xz + tar -xf ccache-${ver}-linux-${ARCH}.tar.xz + rm ccache-${ver}-linux-${ARCH}.tar.xz + mv ccache-${ver}-linux-${ARCH} /opt/ccache/bin +else + curl -sSLO https://github.com/ccache/ccache/releases/download/v${ver}/ccache-${ver}.tar.xz + tar -xf ccache-${ver}.tar.xz + + cd ccache-${ver} + mkdir build + cd build + cmake .. \ + -DCMAKE_INSTALL_PREFIX=/opt/ccache \ + -DCMAKE_BUILD_TYPE=Release \ + -DHTTP_STORAGE_BACKEND=OFF \ + -DENABLE_TESTING=OFF \ + -DREDIS_STORAGE_BACKEND=OFF + make -j$(nproc) + make install +fi diff --git a/ci/merge-artifacts.sh b/ci/merge-artifacts.sh new file mode 100755 index 000000000..9a1e68386 --- /dev/null +++ b/ci/merge-artifacts.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +# Helper script executed on CI once all builds have completed. This takes +# `wasi-toolchain-*` artifacts and `wasi-sysroot-*` artifacts and merges +# them together into a single `wasi-sdk-*` artifact. Toolchains which don't +# have a sysroot that they themselves built use a sysroot from the x86_64-linux +# toolchain. + +set -ex + +rm -rf dist +mkdir dist +version=$(./version.py) + +make_deb() { + build=$1 + dir=$2 + + if ! command -v dpkg-deb >/dev/null; then + return + fi + + case $build in + dist-x86_64-linux) deb_arch=amd64 ;; + dist-arm64-linux) deb_arch=arm64 ;; + dist-riscv64-linux) deb_arch=riscv64 ;; + *) + echo "unknown build $build" + exit 1 + esac + + mkdir dist/pkg + mkdir dist/pkg/opt + mkdir dist/pkg/DEBIAN + sed s/VERSION/$version/ wasi-sdk.control | \ + sed s/ARCH/$deb_arch/ > dist/pkg/DEBIAN/control + cp -R $dir dist/pkg/opt/wasi-sdk + deb_name=$(echo $(basename $dir) | sed 's/.tar.gz//') + (cd dist && dpkg-deb -b pkg $deb_name.deb) + rm -rf dist/pkg +} + +for build in dist-*; do + toolchain=`ls $build/wasi-toolchain-*` + if [ -f $build/wasi-sysroot-* ]; then + sysroot=`ls $build/wasi-sysroot-*` + else + sysroot=`ls dist-x86_64-linux/wasi-sysroot-*` + fi + if [ -f $build/libclang_rt* ]; then + compiler_rt=`ls $build/libclang_rt*` + else + compiler_rt=`ls dist-x86_64-linux/libclang_rt*` + fi + + sdk_dir=`basename $toolchain | sed 's/.tar.gz//' | sed s/toolchain/sdk/` + mkdir dist/$sdk_dir + + # Start with the toolchain and then overlay the sysroot into + # `share/wasi-sysroot`, the default sysroot. + tar xf $toolchain -C dist/$sdk_dir --strip-components 1 + mkdir -p dist/$sdk_dir/share/wasi-sysroot + tar xf $sysroot -C dist/$sdk_dir/share/wasi-sysroot --strip-components 1 + mv dist/$sdk_dir/share/wasi-sysroot/VERSION dist/$sdk_dir + + # Setup the compiler-rt library for all targets. + rtlibdir=$(dirname $(find dist/$sdk_dir/lib -name include))/lib + mkdir -p $rtlibdir + tar xf $compiler_rt -C $rtlibdir --strip-components 1 + + tar czf dist/$sdk_dir.tar.gz -C dist $sdk_dir + + if echo $build | grep -q linux; then + make_deb $build dist/$sdk_dir + fi + rm -rf dist/$sdk_dir +done + +# In addition to `wasi-sdk-*` also preserve artifacts for just the sysroot +# and just compiler-rt. +if [ -d dist-x86_64-linux ]; then + cp dist-x86_64-linux/wasi-sysroot-* dist + cp dist-x86_64-linux/libclang_rt* dist +fi diff --git a/clang.cfg b/clang.cfg new file mode 100644 index 000000000..38ddf5439 --- /dev/null +++ b/clang.cfg @@ -0,0 +1 @@ +--sysroot=/../share/wasi-sysroot diff --git a/cmake/wasi-sdk-dist.cmake b/cmake/wasi-sdk-dist.cmake new file mode 100644 index 000000000..8a9b2b3a1 --- /dev/null +++ b/cmake/wasi-sdk-dist.cmake @@ -0,0 +1,37 @@ +# Helper function to create tarballs for wasi-sdk. +# +# The `target` is the name of the CMake target to create for the creation of +# this tarball. The `tarball` argument is where the final tarball will be +# located. The name of the tarball is also used for the name of the root folder +# in the tarball. The `dir` argument is is the directory that will get packaged +# up within the tarball. +function(wasi_sdk_add_tarball target tarball dir) + cmake_path(GET tarball PARENT_PATH tarball_dir) + + # Run STEM twice to chop of both `.gz` and `.tar` in `.tar.gz` + cmake_path(GET tarball STEM LAST_ONLY tarball_stem) + cmake_path(GET tarball_stem STEM LAST_ONLY tarball_stem) + + if(CMAKE_SYSTEM_NAME MATCHES Windows) + # Copy the contents of symlinks on Windows to avoid dealing with symlink + set(copy_dir ${CMAKE_COMMAND} -E copy_directory ${dir} ${tarball_stem}) + else() + # ... but on non-Windows copy symlinks themselves to cut down on + # distribution size. + set(copy_dir cp -R ${dir} ${tarball_stem}) + endif() + + add_custom_command( + OUTPUT ${tarball} + # First copy the directory under a different name, the filestem of the + # tarball. + COMMAND ${copy_dir} + # Next use CMake to create the tarball itself + COMMAND ${CMAKE_COMMAND} -E tar cfz ${tarball} ${tarball_stem} + # Finally delete the temporary directory created above. + COMMAND ${CMAKE_COMMAND} -E rm -rf ${tarball_stem} + WORKING_DIRECTORY ${tarball_dir} + COMMENT "Creating ${tarball}..." + ) + add_custom_target(${target} DEPENDS ${tarball}) +endfunction() diff --git a/cmake/wasi-sdk-enable-ccache.cmake b/cmake/wasi-sdk-enable-ccache.cmake new file mode 100644 index 000000000..f5e1f304e --- /dev/null +++ b/cmake/wasi-sdk-enable-ccache.cmake @@ -0,0 +1,17 @@ +# Helper module to auto-enable ccache if detected. + +find_program(CCACHE ccache) + +option(WASI_SDK_DISABLE_CCACHE "Force disable ccache even if it's found" OFF) + +if(NOT CMAKE_C_COMPILER_LAUNCHER) + if(NOT WASI_SDK_DISABLE_CCACHE) + if(CCACHE) + set(CMAKE_C_COMPILER_LAUNCHER ccache) + set(CMAKE_CXX_COMPILER_LAUNCHER ccache) + message(STATUS "Auto-enabling ccache") + else() + message(STATUS "Failed to auto-enable ccache, not found on system") + endif() + endif() +endif() diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake new file mode 100644 index 000000000..491180ee4 --- /dev/null +++ b/cmake/wasi-sdk-sysroot.cmake @@ -0,0 +1,463 @@ +# Build logic for building a sysroot for wasi-sdk which includes compiler-rt, +# wasi-libc, libcxx, and libcxxabi. + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo) +endif() + +if(NOT CMAKE_C_COMPILER_ID MATCHES Clang) + message(FATAL_ERROR "C compiler ${CMAKE_C_COMPILER} is not `Clang`, it is ${CMAKE_C_COMPILER_ID}") +endif() + +set(minimum_clang_required 18.0.0) + +if(CMAKE_C_COMPILER_VERSION VERSION_LESS ${minimum_clang_required}) + message(FATAL_ERROR "compiler version ${CMAKE_C_COMPILER_VERSION} is less than the required version ${minimum_clang_required}") +endif() + +message(STATUS "Found executable for `nm`: ${CMAKE_NM}") +message(STATUS "Found executable for `ar`: ${CMAKE_AR}") + +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_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_LTO "Whether or not to build LTO assets" ON) +set(WASI_SDK_EXCEPTIONS "${EXCEPTIONS_DEFAULT}" CACHE STRING "Whether or not C++ exceptions are enabled") +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_sysroot ${wasi_tmp_install}/share/wasi-sysroot) +set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir) + +if(WASI_SDK_DEBUG_PREFIX_MAP) + add_compile_options( + -fdebug-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=wasisdk://v${wasi_sdk_version}) +endif() + +# Default arguments for builds of cmake projects (mostly LLVM-based) to forward +# along much of our own configuration into these projects. +set(default_cmake_args + -DCMAKE_SYSTEM_NAME=WASI + -DCMAKE_SYSTEM_VERSION=1 + -DCMAKE_SYSTEM_PROCESSOR=wasm32 + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_AR=${CMAKE_AR} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_C_COMPILER_WORKS=ON + -DCMAKE_CXX_COMPILER_WORKS=ON + -DCMAKE_SYSROOT=${wasi_sysroot} + -DCMAKE_MODULE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/cmake + # CMake detects this based on `CMAKE_C_COMPILER` alone and when that compiler + # is just a bare "clang" installation then it can mistakenly deduce that this + # feature is supported when it's not actually supported for WASI targets. + # Currently `wasm-ld` does not support the linker flag for this. + -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF + -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF) + +if(CMAKE_C_COMPILER_LAUNCHER) + list(APPEND default_cmake_args -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}) +endif() +if(CMAKE_CXX_COMPILER_LAUNCHER) + list(APPEND default_cmake_args -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}) +endif() + +# ============================================================================= +# compiler-rt build logic +# ============================================================================= + +add_custom_target(compiler-rt-build) +function(define_compiler_rt target) + ExternalProject_Add(compiler-rt-build-${target} + SOURCE_DIR "${llvm_proj_dir}/compiler-rt" + CMAKE_ARGS + ${default_cmake_args} + -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON + -DCOMPILER_RT_BAREMETAL_BUILD=ON + -DCOMPILER_RT_BUILD_XRAY=OFF + -DCOMPILER_RT_INCLUDE_TESTS=OFF + -DCOMPILER_RT_HAS_FPIC_FLAG=OFF + -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON + -DCOMPILER_RT_BUILD_SANITIZERS=OFF + -DCOMPILER_RT_BUILD_XRAY=OFF + -DCOMPILER_RT_BUILD_LIBFUZZER=OFF + -DCOMPILER_RT_BUILD_PROFILE=OFF + -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF + -DCOMPILER_RT_BUILD_MEMPROF=OFF + -DCOMPILER_RT_BUILD_ORC=OFF + -DCOMPILER_RT_BUILD_GWP_ASAN=OFF + -DCMAKE_C_COMPILER_TARGET=${target} + -DCMAKE_C_FLAGS=${WASI_SDK_CPU_CFLAGS} + -DCMAKE_CXX_FLAGS=${WASI_SDK_CPU_CFLAGS} + -DCMAKE_ASM_FLAGS=${WASI_SDK_CPU_CFLAGS} + -DCMAKE_INSTALL_PREFIX=${wasi_resource_dir} + EXCLUDE_FROM_ALL ON + USES_TERMINAL_CONFIGURE ON + USES_TERMINAL_BUILD ON + USES_TERMINAL_INSTALL ON + ) + add_dependencies(compiler-rt-build compiler-rt-build-${target}) +endfunction() + +# 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) + +# 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 +# around some headers and make copies of the `wasi` directory as `wasip1` and +# `wasip2` and `wasip3` +execute_process( + COMMAND ${CMAKE_C_COMPILER} -print-resource-dir + OUTPUT_VARIABLE clang_resource_dir + OUTPUT_STRIP_TRAILING_WHITESPACE) +add_custom_target(compiler-rt-post-build + # The `${wasi_resource_dir}` folder is going to get used as `-resource-dir` + # for future compiles. Copy the host compiler's own headers into this + # directory to ensure that all host-defined headers all work as well. + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${clang_resource_dir}/include ${wasi_resource_dir}/include + + # Copy the `lib/wasm32-unknown-wasip1` folder to `lib/wasm32-unknown-wasi{,p2}` to ensure that those + # OS-strings also work for looking up the compiler-rt.a file. + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${wasi_resource_dir}/lib/wasm32-unknown-wasip1 ${wasi_resource_dir}/lib/wasm32-unknown-wasi + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${wasi_resource_dir}/lib/wasm32-unknown-wasip1 ${wasi_resource_dir}/lib/wasm32-unknown-wasip2 + # Copy the `lib/wasm32-unknown-wasip1-threads` folder to `lib/wasm32-unknown-wasi-threads` + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${wasi_resource_dir}/lib/wasm32-unknown-wasip1-threads ${wasi_resource_dir}/lib/wasm32-unknown-wasi-threads + + COMMENT "finalizing compiler-rt installation" +) +add_dependencies(compiler-rt-post-build compiler-rt-build) + +add_custom_target(compiler-rt DEPENDS compiler-rt-build compiler-rt-post-build) + +# ============================================================================= +# wasi-libc build logic +# ============================================================================= + +function(define_wasi_libc_sub target target_suffix lto) + string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER) + 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}") + + if(${target} MATCHES "p[23]") + # Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets. + # This makes `libc.a` more flexible and usable in dynamic linking situations. + list(APPEND extra_cflags_list -fPIC) + endif() + + # The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation + # warnings for now. + if(${target} STREQUAL wasm32-wasi OR ${target} STREQUAL wasm32-wasi-threads) + list(APPEND extra_cflags_list -Wno-deprecated) + endif() + + list(JOIN extra_cflags_list " " extra_cflags) + + if(${target} MATCHES threads) + set(libcompiler_rt_a ${wasi_resource_dir}/lib/wasm32-unknown-wasip1-threads/libclang_rt.builtins.a) + else() + set(libcompiler_rt_a ${wasi_resource_dir}/lib/wasm32-unknown-wasip1/libclang_rt.builtins.a) + endif() + + set(extra_cmake_args) + + # Configure LTO in wasi libc if it's enabled. Be sure to disable shared + # libraries as well since that's not currently supported. + if (lto) + list(APPEND extra_cmake_args -DLTO=full -DBUILD_SHARED=OFF) + endif() + + ExternalProject_Add(wasi-libc-${target}${target_suffix}-build + SOURCE_DIR ${wasi_libc} + CMAKE_ARGS + ${default_cmake_args} + ${extra_cmake_args} + -DTARGET_TRIPLE=${target} + -DCMAKE_INSTALL_PREFIX=${wasi_sysroot} + -DCMAKE_C_FLAGS=${extra_cflags} + -DCMAKE_ASM_FLAGS=${extra_cflags} + -DBUILTINS_LIB=${libcompiler_rt_a} + -DUSE_WASM_COMPONENT_LD=OFF + -DWASI_SDK_VERSION=${wasi_sdk_version} + DEPENDS compiler-rt + EXCLUDE_FROM_ALL ON + USES_TERMINAL_CONFIGURE ON + USES_TERMINAL_BUILD ON + USES_TERMINAL_INSTALL ON + ) +endfunction() + +function(define_wasi_libc target) + define_wasi_libc_sub (${target} "" OFF) + if(WASI_SDK_LTO) + define_wasi_libc_sub (${target} "-lto" ON) + endif() + + add_custom_target(wasi-libc-${target} + DEPENDS wasi-libc-${target}-build $<$:wasi-libc-${target}-lto-build>) +endfunction() + +foreach(target IN LISTS WASI_SDK_TARGETS) + define_wasi_libc(${target}) +endforeach() + +# ============================================================================= +# libcxx build logic +# ============================================================================= + +execute_process( + COMMAND ${CMAKE_C_COMPILER} -dumpversion + OUTPUT_VARIABLE llvm_version + OUTPUT_STRIP_TRAILING_WHITESPACE) + +function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix exceptions) + if(${target} MATCHES threads) + set(pic OFF) + set(target_flags -pthread) + else() + set(pic ON) + set(target_flags "") + endif() + if(${target_suffix} MATCHES lto) + set(pic OFF) + endif() + list(APPEND target_flags ${extra_target_flags}) + + set(runtimes "libcxx;libcxxabi") + + get_property(dir_compile_opts DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS) + get_property(dir_link_opts DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY LINK_OPTIONS) + set(extra_flags + ${WASI_SDK_CPU_CFLAGS} + ${target_flags} + --target=${target} + ${dir_compile_opts} + ${dir_link_opts} + --sysroot ${wasi_sysroot} + -resource-dir ${wasi_resource_dir}) + + set(exnsuffix "") + + if (exceptions) + # 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 + # least not really sure what the state of shared libraries and exceptions + # are. For now shared libraries are disabled and supporting them is left for + # a future endeavor. + set(pic OFF) + set(runtimes "libunwind;${runtimes}") + 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() + + # The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation + # warnings for now. + if(${target} STREQUAL wasm32-wasi OR ${target} STREQUAL wasm32-wasi-threads) + list(APPEND extra_flags -Wno-deprecated) + endif() + + set(extra_cflags_list ${CMAKE_C_FLAGS} ${extra_flags}) + list(JOIN extra_cflags_list " " extra_cflags) + set(extra_cxxflags_list ${CMAKE_CXX_FLAGS} ${extra_flags}) + list(JOIN extra_cxxflags_list " " extra_cxxflags) + + ExternalProject_Add(libcxx-${target}${target_suffix}-build + SOURCE_DIR ${llvm_proj_dir}/runtimes + CMAKE_ARGS + ${default_cmake_args} + # Ensure headers are installed in a target-specific path instead of a + # target-generic path. + -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target}${exnsuffix} + -DCMAKE_STAGING_PREFIX=${wasi_sysroot} + -DCMAKE_POSITION_INDEPENDENT_CODE=${pic} + -DLIBCXX_ENABLE_THREADS:BOOL=ON + -DLIBCXX_HAS_PTHREAD_API:BOOL=ON + -DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF + -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF + -DLLVM_COMPILER_CHECKED=ON + -DLIBCXX_ENABLE_SHARED:BOOL=${pic} + -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${exceptions} + -DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON + -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF + -DLIBCXX_CXX_ABI=libcxxabi + -DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF + -DLIBCXX_ABI_VERSION=2 + -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions} + -DLIBCXXABI_ENABLE_SHARED:BOOL=${pic} + -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON + -DLIBCXXABI_ENABLE_THREADS:BOOL=ON + -DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON + -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF + -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF + -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions} + -DLIBUNWIND_ENABLE_SHARED:BOOL=${pic} + -DLIBUNWIND_ENABLE_THREADS:BOOL=ON + -DLIBUNWIND_USE_COMPILER_RT:BOOL=ON + -DLIBUNWIND_INCLUDE_TESTS:BOOL=OFF + -DUNIX:BOOL=ON + -DCMAKE_C_FLAGS=${extra_cflags} + -DCMAKE_ASM_FLAGS=${extra_cflags} + -DCMAKE_CXX_FLAGS=${extra_cxxflags} + -DLIBCXX_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBUNWIND_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBCXX_INCLUDE_TESTS=OFF + -DLIBCXX_INCLUDE_BENCHMARKS=OFF + + # See https://www.scivision.dev/cmake-externalproject-list-arguments/ for + # why this is in `CMAKE_CACHE_ARGS` instead of above + CMAKE_CACHE_ARGS + -DLLVM_ENABLE_RUNTIMES:STRING=${runtimes} + DEPENDS + wasi-libc-${target} + compiler-rt + EXCLUDE_FROM_ALL ON + USES_TERMINAL_CONFIGURE ON + USES_TERMINAL_BUILD ON + USES_TERMINAL_INSTALL ON + USES_TERMINAL_PATCH ON + PATCH_COMMAND + ${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" + 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() + +function(define_libcxx_and_lto target target_suffix exceptions) + define_libcxx_sub(${target} "${target_suffix}" "" "" ${exceptions}) + if (WASI_SDK_LTO) + # 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 + 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() + + # As of this writing, `clang++` will ignore the target-specific include dirs + # unless this one also exists: + add_custom_target(libcxx-${target}-extra-dir + COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1 + COMMENT "creating libcxx-specific header file folder") + add_dependencies(libcxx-${target} libcxx-${target}-extra-dir) +endfunction() + +foreach(target IN LISTS WASI_SDK_TARGETS) + define_libcxx(${target}) +endforeach() + +# ============================================================================= +# misc build logic +# ============================================================================= + +install(DIRECTORY ${wasi_tmp_install}/share + USE_SOURCE_PERMISSIONS + DESTINATION ${CMAKE_INSTALL_PREFIX}) +if(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR) + install(DIRECTORY ${wasi_resource_dir}/lib + USE_SOURCE_PERMISSIONS + DESTINATION ${clang_resource_dir}) +else() + install(DIRECTORY ${wasi_resource_dir}/lib + USE_SOURCE_PERMISSIONS + DESTINATION ${CMAKE_INSTALL_PREFIX}/clang-resource-dir) +endif() + +# Add a top-level `build` target as well as `build-$target` targets. +add_custom_target(build ALL) +foreach(target IN LISTS WASI_SDK_TARGETS) + add_custom_target(build-${target}) + add_dependencies(build-${target} libcxx-${target} wasi-libc-${target} compiler-rt) + add_dependencies(build build-${target}) +endforeach() + +# Install a `VERSION` file in the output prefix with a dump of version +# information. +execute_process( + COMMAND ${PYTHON} ${version_script} dump + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE version_dump) +set(version_file_tmp ${wasi_sysroot}/VERSION) +file(GENERATE OUTPUT ${version_file_tmp} CONTENT ${version_dump}) +add_custom_target(version-file DEPENDS ${version_file_tmp}) +add_dependencies(build version-file) + +if(WASI_SDK_INCLUDE_TESTS) + add_subdirectory(tests) +endif() + +include(wasi-sdk-dist) + +set(dist_dir ${CMAKE_CURRENT_BINARY_DIR}/dist) + +# Tarball with just `compiler-rt` libraries within it +wasi_sdk_add_tarball(dist-compiler-rt + ${dist_dir}/libclang_rt-${wasi_sdk_version}.tar.gz + ${wasi_resource_dir}/lib) +add_dependencies(dist-compiler-rt compiler-rt) + +# Tarball with the whole sysroot +wasi_sdk_add_tarball(dist-sysroot + ${dist_dir}/wasi-sysroot-${wasi_sdk_version}.tar.gz + ${wasi_sysroot}) +add_dependencies(dist-sysroot build) + +add_custom_target(dist DEPENDS dist-compiler-rt dist-sysroot) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake new file mode 100644 index 000000000..1d023c879 --- /dev/null +++ b/cmake/wasi-sdk-toolchain.cmake @@ -0,0 +1,341 @@ +# Build logic and support for building a Clang toolchain that can target +# WebAssembly and build a WASI sysroot. + +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(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}") + +set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE MinSizeRel) +endif() + +set(default_cmake_args + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_AR=${CMAKE_AR} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_INSTALL_PREFIX=${wasi_tmp_install}) + +if(CMAKE_C_COMPILER_LAUNCHER) + list(APPEND default_cmake_args -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}) +endif() +if(CMAKE_CXX_COMPILER_LAUNCHER) + list(APPEND default_cmake_args -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}) +endif() + +set(links_to_create clang-cl clang-cpp clang++) +foreach(target IN LISTS WASI_SDK_TARGETS) + list(APPEND links_to_create ${target}-clang) + list(APPEND links_to_create ${target}-clang++) +endforeach() + +set(projects "lld;clang;clang-tools-extra") + +set(tools + clang + clang-format + clang-tidy + clang-apply-replacements + clang-scan-deps + lld + llvm-addr2line + llvm-mc + llvm-ranlib + llvm-strip + llvm-dwarfdump + llvm-dwp + clang-resource-headers + ar + ranlib + strip + nm + size + strings + objdump + objcopy + c++filt + llvm-config + libclang) + +# 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 +# statically linking LLVM to each individual tool. This requires a few other +# install targets as well to ensure the appropriate libraries are all installed. +# +# Also note that the `-wasi-sdk` version suffix is intended to help prevent +# these dynamic libraries from clashing with other system libraries in case the +# `lib` dir gets put on `LD_LIBRARY_PATH` or similar. +if(NOT WIN32) + list(APPEND default_cmake_args -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_VERSION_SUFFIX=-wasi-sdk) + list(APPEND tools LLVM clang-cpp) +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 + /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=install- OUTPUT_VARIABLE install_targets) + +ExternalProject_Add(llvm-build + SOURCE_DIR "${llvm_proj_dir}/llvm" + CMAKE_ARGS + ${default_cmake_args} + -DLLVM_ENABLE_ZLIB=OFF + -DLLVM_ENABLE_ZSTD=OFF + -DLLVM_STATIC_LINK_CXX_STDLIB=ON + -DLLVM_INCLUDE_TESTS=OFF + -DLLVM_INCLUDE_UTILS=OFF + -DLLVM_INCLUDE_BENCHMARKS=OFF + -DLLVM_INCLUDE_EXAMPLES=OFF + -DLLVM_TARGETS_TO_BUILD=WebAssembly + -DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasip1 + -DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE + -DLLVM_ENABLE_LIBXML2=OFF + # Pass `-s` to strip symbols by default and shrink the size of the + # distribution + -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} + # See https://www.scivision.dev/cmake-externalproject-list-arguments/ for + # why this is in `CMAKE_CACHE_ARGS` instead of above + CMAKE_CACHE_ARGS + -DLLVM_ENABLE_PROJECTS:STRING=${projects} + -DCLANG_LINKS_TO_CREATE:STRING=${links_to_create} + BUILD_COMMAND + cmake --build . ${build_targets} + INSTALL_COMMAND + cmake --build . ${install_targets} + USES_TERMINAL_CONFIGURE ON + USES_TERMINAL_BUILD 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) +ExternalProject_Add_StepDependencies(llvm-build configure libedit libxml2) + +# Installation target for this outer project for installing the toolchain to the +# system. +install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_install}/share + USE_SOURCE_PERMISSIONS + DESTINATION ${CMAKE_INSTALL_PREFIX}) + +# 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 ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX}) +set(wasm_component_ld_version 0.5.22) +if(RUST_TARGET) + set(rust_target_flag --target=${RUST_TARGET}) +endif() +add_custom_command( + OUTPUT ${wasm_component_ld} + COMMAND + cargo install --root ${wasm_component_ld_root} ${rust_target_flag} + wasm-component-ld@${wasm_component_ld_version} + COMMAND + cmake -E make_directory ${wasi_tmp_install}/bin + COMMAND + cmake -E copy ${wasm_component_ld} ${wasi_tmp_install}/bin + COMMENT "Building `wasm-component-ld` ...") +add_custom_target(wasm-component-ld DEPENDS ${wasm_component_ld}) +add_dependencies(build wasm-component-ld) + +# Setup installation logic for CMake support files. +add_custom_target(misc-files) +add_dependencies(build misc-files) + +function(copy_misc_file src dst_folder) + cmake_path(GET src FILENAME src_filename) + set(dst ${wasi_tmp_install}/share/${dst_folder}/${src_filename}) + add_custom_command( + OUTPUT ${dst} + COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${src} ${dst}) + add_custom_target(copy-${src_filename} DEPENDS ${dst}) + add_dependencies(misc-files copy-${src_filename}) +endfunction() + +copy_misc_file(src/config/config.sub misc) +copy_misc_file(src/config/config.guess misc) +copy_misc_file(wasi-sdk.cmake cmake) +copy_misc_file(wasi-sdk-pthread.cmake cmake) +copy_misc_file(wasi-sdk-p1.cmake cmake) +copy_misc_file(wasi-sdk-p2.cmake cmake) +copy_misc_file(wasi-sdk-p3.cmake cmake) +copy_misc_file(cmake/Platform/WASI.cmake cmake/Platform) + +function(copy_cfg_file compiler) + set(dst ${wasi_tmp_install}/bin/${compiler}.cfg) + add_custom_command( + OUTPUT ${dst} + COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/clang.cfg ${dst}) + add_custom_target(copy-${compiler} DEPENDS ${dst}) + add_dependencies(misc-files copy-${compiler}) +endfunction() + +copy_cfg_file(clang) +copy_cfg_file(clang++) + +include(wasi-sdk-dist) + +# Figure out the name of the artifact which is either explicitly specified or +# inferred from CMake default variables. +if(WASI_SDK_ARTIFACT) + set(wasi_sdk_artifact ${WASI_SDK_ARTIFACT}) +else() + if(APPLE) + set(wasi_sdk_os macos) + else() + string(TOLOWER ${CMAKE_SYSTEM_NAME} wasi_sdk_os) + endif() + set(wasi_sdk_arch ${CMAKE_SYSTEM_PROCESSOR}) + set(wasi_sdk_artifact ${wasi_sdk_arch}-${wasi_sdk_os}) +endif() + +set(dist_dir ${CMAKE_CURRENT_BINARY_DIR}/dist) +wasi_sdk_add_tarball(dist-toolchain + ${dist_dir}/wasi-toolchain-${wasi_sdk_version}-${wasi_sdk_artifact}.tar.gz + ${wasi_tmp_install}) +add_dependencies(dist-toolchain build) +add_custom_target(dist DEPENDS dist-toolchain) diff --git a/deb_from_installation.sh b/deb_from_installation.sh deleted file mode 100755 index 053de421c..000000000 --- a/deb_from_installation.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env sh -set -x - -command -v dpkg-deb >/dev/null -if [ $? -ne 0 ]; then - echo "required tool dpkg-deb missing. exiting" - exit 0 -fi - -set -ex - -if [ -n "$1" ]; then - OUTDIR=$1 -else - OUTDIR=$PWD/dist -fi - -if [ -n "$2" ]; then - VERSION="$2" -else - VERSION=`./version.sh` -fi - -if [ -n "$3" ]; then - INSTALL_DIR="$3" -else - INSTALL_DIR=/opt/wasi-sdk -fi - -if [ ! -d $INSTALL_DIR ] ; then - echo "Directory $INSTALL_DIR doesn't exist. Nothing to copy from." - exit 1 -fi - -rm -rf build/pkg -mkdir -p build/pkg/opt -mkdir -p build/pkg/DEBIAN -sed -e s/VERSION/$VERSION/ wasi-sdk.control > build/pkg/DEBIAN/control -cp -R $INSTALL_DIR build/pkg/opt/ -cd build && dpkg-deb -b pkg wasi-sdk_$VERSION\_amd64.deb && cd .. -mv build/wasi-sdk_$VERSION\_amd64.deb $OUTDIR/ diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..ba1c6f956 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,27 @@ +# A container which has a number of build tools pre-installed plus a build of +# `wasi-sdk` installed at `/opt/wasi-sdk`. This also has environment variables +# pre-configued to use the installed toolchain. +# +# This container is built as the last step on CI for this repository and +# pre-built versions of this container are pushed as a package to the repository +# as well. + +FROM ubuntu:24.04 + +RUN apt-get update && \ + apt-get install -y cmake ninja-build make autoconf autogen automake libtool && \ + rm -rf /var/lib/apt/lists/* + +ADD dist/wasi-sdk-*.deb . +RUN case `dpkg --print-architecture` in \ + amd64) dpkg -i wasi-sdk-*-x86_64-linux.deb ;; \ + arm64) dpkg -i wasi-sdk-*-arm64-linux.deb ;; \ + *) exit 1 ;; \ + esac && \ + rm wasi-sdk-*.deb + +ENV CC="/opt/wasi-sdk/bin/clang" +ENV CXX="/opt/wasi-sdk/bin/clang++" +ENV LD="/opt/wasi-sdk/bin/wasm-ld" +ENV AR="/opt/wasi-sdk/bin/llvm-ar" +ENV RANLIB="/opt/wasi-sdk/bin/llvm-ranlib" diff --git a/llvm_version.sh b/llvm_version.sh deleted file mode 100755 index 41434eb79..000000000 --- a/llvm_version.sh +++ /dev/null @@ -1,6 +0,0 @@ -#/bin/bash -LLVM_PROJ_DIR=${1:-./src/llvm-project} -MAJOR=`grep "set(LLVM_VERSION_MAJOR" $LLVM_PROJ_DIR/llvm/CMakeLists.txt | awk '{print substr($2, 1, length($2) - 1)}'` -MINOR=`grep "set(LLVM_VERSION_MINOR" $LLVM_PROJ_DIR/llvm/CMakeLists.txt | awk '{print substr($2, 1, length($2) - 1)}'` -PATCH=`grep "set(LLVM_VERSION_PATCH" $LLVM_PROJ_DIR/llvm/CMakeLists.txt | awk '{print substr($2, 1, length($2) - 1)}'` -echo $MAJOR.$MINOR.$PATCH diff --git a/src/llvm-pr-168449.patch b/src/llvm-pr-168449.patch new file mode 100644 index 000000000..f5b873844 --- /dev/null +++ b/src/llvm-pr-168449.patch @@ -0,0 +1,23 @@ +From 852c8a2ebc0fdb1e781591e3e6e08d3a539bcfc3 Mon Sep 17 00:00:00 2001 +From: Yerzhan Zhamashev +Date: Wed, 21 Jan 2026 16:50:41 +0200 +Subject: [PATCH] libunwind: exclude __declspec from wasm build + +--- + libunwind/src/config.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libunwind/src/config.h b/libunwind/src/config.h +index f017403fa2234..6014a37e27212 100644 +--- a/libunwind/src/config.h ++++ b/libunwind/src/config.h +@@ -75,7 +75,8 @@ + #define _LIBUNWIND_EXPORT + #define _LIBUNWIND_HIDDEN + #else +- #if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX) ++ #if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX) && \ ++ !defined(__wasm__) + #define _LIBUNWIND_EXPORT __declspec(dllexport) + #define _LIBUNWIND_HIDDEN + #else diff --git a/src/llvm-pr-185770.patch b/src/llvm-pr-185770.patch new file mode 100644 index 000000000..2d47923ef --- /dev/null +++ b/src/llvm-pr-185770.patch @@ -0,0 +1,105 @@ +From d702761d9135ebbb83590d4dd1323be433701ebd Mon Sep 17 00:00:00 2001 +From: Alex Crichton +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", + ] + } + diff --git a/src/llvm-pr-185775.patch b/src/llvm-pr-185775.patch new file mode 100644 index 000000000..f1fdb6a57 --- /dev/null +++ b/src/llvm-pr-185775.patch @@ -0,0 +1,152 @@ +From 0e36e8f304cd5f3997916f5d85201bb17e340337 Mon Sep 17 00:00:00 2001 +From: Alex Crichton +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" diff --git a/src/llvm-pr-186054.patch b/src/llvm-pr-186054.patch new file mode 100644 index 000000000..c7756d77f --- /dev/null +++ b/src/llvm-pr-186054.patch @@ -0,0 +1,48 @@ +From f71fdfcbd6fcc7b521c74b5856ebeacdd6cf55d9 Mon Sep 17 00:00:00 2001 +From: Catherine +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 diff --git a/src/wasi-libc b/src/wasi-libc index b4814997f..2fc32bc81 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit b4814997f61ee352d8c1ae397561a813fb30c701 +Subproject commit 2fc32bc81b9f07f8d9525edea59bfbaf760c06d6 diff --git a/strip_symbols.sh b/strip_symbols.sh deleted file mode 100755 index c161f60aa..000000000 --- a/strip_symbols.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -e - -DIRECTORY=${1:-/opt/wasi-sdk/bin} -if [[ "$OSTYPE" == "darwin"* ]]; then -# macos find doesnt support -executable so we fall back on having a permission -# bit to execute: -EXECUTABLES=$(find ${DIRECTORY} -type f -perm +111) -else -EXECUTABLES=$(find ${DIRECTORY} -type f -executable) -fi -for e in ${EXECUTABLES}; do - echo "Stripping symbols: ${e}" - strip ${e} || echo "Failed to strip symbols for ${e}; continuing on." -done diff --git a/tar_from_installation.sh b/tar_from_installation.sh deleted file mode 100755 index f90000bf5..000000000 --- a/tar_from_installation.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash -set -ex - -if [ -n "$1" ]; then - OUTDIR=$1 -else - OUTDIR=$PWD/dist -fi - -if [ -n "$2" ]; then - VERSION="$2" -else - VERSION=`./version.sh` -fi - -if [ -n "$3" ]; then - INSTALL_DIR="$3" -else - INSTALL_DIR=/opt/wasi-sdk -fi - -case "$(uname -s)" in - Linux*) MACHINE=linux;; - Darwin*) MACHINE=macos;; - CYGWIN*) MACHINE=cygwin;; - MINGW*) MACHINE=mingw;; - MSYS*) MACHINE=msys;; #MSYS_NT-10.0-19043 - *) MACHINE="UNKNOWN" -esac - -if [ ! -d $INSTALL_DIR ] ; then - echo "Directory $INSTALL_DIR doesn't exist. Nothing to copy from." - exit 1 -fi - -PKGDIR=build/wasi-sdk-$VERSION -rm -rf $PKGDIR -if [ "$MACHINE" == "cygwin" ] || [ "$MACHINE" == "mingw" ] || [ "$MACHINE" == "msys" ]; then - # Copy with -L to avoid trying to create symlinks on Windows. - cp -R -L $INSTALL_DIR $PKGDIR -else - cp -R $INSTALL_DIR $PKGDIR -fi -cd build -tar czf $OUTDIR/wasi-sdk-$VERSION\-$MACHINE.tar.gz wasi-sdk-$VERSION - -# As well as the full SDK package, also create archives of libclang_rt.builtins -# and the sysroot. These are made available for users who have an existing clang -# installation. -tar czf $OUTDIR/libclang_rt.builtins-wasm32-wasi-$VERSION.tar.gz -C compiler-rt lib/wasi -tar czf $OUTDIR/wasi-sysroot-$VERSION.tar.gz -C wasi-sdk-$VERSION/share wasi-sysroot diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..9bb5fc2eb --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,123 @@ +# Support for running tests in the `tests/{compile-only,general}` folders +cmake_minimum_required(VERSION 3.22) +project(wasi-sdk-test) +include(CTest) +enable_testing() +set(CMAKE_EXECUTABLE_SUFFIX ".wasm") + +option(WASI_SDK_TEST_HOST_TOOLCHAIN "Test against the host toolchain, not a fresh sysroot" OFF) + +if(NOT WASI_SDK_TEST_HOST_TOOLCHAIN) + add_compile_options(--sysroot=${wasi_sysroot} -resource-dir ${wasi_resource_dir}) + add_link_options(--sysroot=${wasi_sysroot} -resource-dir ${wasi_resource_dir}) +endif() + +# Sanity check setup +if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL WASI) + message(FATAL_ERROR "Wrong system name (${CMAKE_SYSTEM_NAME}), wrong toolchain file in use?") +endif() + +if(NOT DEFINED WASI) + message(FATAL_ERROR "WASI is not set, platform file likely not loaded") +endif() + +set(WASI_SDK_RUNWASI "wasmtime" CACHE STRING "Runner for tests") + +# Test everything at O0, O2, and O2+LTO +set(opt_flags -O0 -O2 "-O2 -flto") + +add_custom_target(build-tests) + +# Executes a single `test` specified. +# +# This will compile `test` for all the various targets and with various +# compiler options. If `runwasi` is non-empty then the test will be executed +# in that runner as well. +function(add_testcase runwasi test) + foreach(target IN LISTS WASI_SDK_TARGETS) + foreach(compile_flags IN LISTS opt_flags) + # Mangle the options into something appropriate for a CMake rule name + string(REGEX REPLACE " " "." target_name "${target}.${compile_flags}.${test}") + + # Add a new test executable based on `test` + add_executable(${target_name} ${test}) + add_dependencies(build-tests ${target_name}) + + # Configure all the compile options necessary. For example `--target` here + # if the target doesn't look like it's already in the name of the compiler + # as well. + if(NOT(CMAKE_C_COMPILER MATCHES ${target})) + target_compile_options(${target_name} PRIVATE --target=${target}) + target_link_options(${target_name} PRIVATE --target=${target}) + endif() + + # Apply test-specific compile options and link flags. + if(test MATCHES "clocks.c$") + target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_PROCESS_CLOCKS) + target_link_options(${target_name} PRIVATE -lwasi-emulated-process-clocks) + elseif(test MATCHES "mmap.c$") + target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_MMAN) + target_link_options(${target_name} PRIVATE -lwasi-emulated-mman) + elseif(test MATCHES "(sigabrt|signals).c$") + target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_SIGNAL) + target_link_options(${target_name} PRIVATE -lwasi-emulated-signal) + elseif(test MATCHES "printf-long-double-enabled.c$") + target_link_options(${target_name} PRIVATE -lc-printscan-long-double) + endif() + + # Apply language-specific options and dependencies. + if(test MATCHES "cc$") + if(NOT (WASI_SDK_EXCEPTIONS STREQUAL "OFF")) + target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) + target_link_options(${target_name} PRIVATE -fwasm-exceptions -lunwind) + else() + target_compile_options(${target_name} PRIVATE -fno-exceptions) + endif() + if(NOT WASI_SDK_TEST_HOST_TOOLCHAIN) + add_dependencies(${target_name} libcxx-${target}) + endif() + else() + if(NOT WASI_SDK_TEST_HOST_TOOLCHAIN) + add_dependencies(${target_name} wasi-libc-${target}) + endif() + endif() + + # Apply target-specific options. + if(target MATCHES threads) + target_compile_options(${target_name} PRIVATE -pthread) + target_link_options(${target_name} PRIVATE -pthread) + endif() + + if(target STREQUAL wasm32-wasi OR target STREQUAL wasm32-wasi-threads) + target_compile_options(${target_name} PRIVATE -Wno-deprecated) + target_link_options(${target_name} PRIVATE -Wno-deprecated) + endif() + + if(runwasi) + set(runner ${runwasi}) + if(${runner} MATCHES wasmtime) + if(target MATCHES threads) + set(runner "${runner} -Wshared-memory") + endif() + if(WASI_SDK_EXCEPTIONS) + set(runner "${runner} -Wexceptions") + endif() + if(target MATCHES "wasip3") + set(runner "${runner} -Wcomponent-model-async -Sp3") + endif() + endif() + add_test( + NAME test-${target_name} + COMMAND + bash ../testcase.sh + ${runner} + ${test} + $ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + endforeach() + endforeach() +endfunction() + +add_subdirectory(compile-only) +add_subdirectory(general) diff --git a/tests/compile-only/CMakeLists.txt b/tests/compile-only/CMakeLists.txt new file mode 100644 index 000000000..484a9cddc --- /dev/null +++ b/tests/compile-only/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB c_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") +file(GLOB cxx_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") + +set(compile_tests ${c_compile_tests} ${cxx_compile_tests}) + +foreach(test IN LISTS compile_tests) + add_testcase("" ${test}) +endforeach() + diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt new file mode 100644 index 000000000..2954fd197 --- /dev/null +++ b/tests/general/CMakeLists.txt @@ -0,0 +1,8 @@ +file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.c") +file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.cc") + +set(general_tests ${c_general_tests} ${cxx_general_tests}) + +foreach(test IN LISTS general_tests) + add_testcase(${WASI_SDK_RUNWASI} ${test}) +endforeach() diff --git a/tests/general/abort.c.stderr.expected b/tests/general/abort.c.stderr.expected index bfba4982d..d533e582f 100644 --- a/tests/general/abort.c.stderr.expected +++ b/tests/general/abort.c.stderr.expected @@ -1,6 +1,7 @@ Error: failed to run main module `abort.c.---.wasm` Caused by: - 0: failed to invoke command default - 1: wasm trap: wasm `unreachable` instruction executed - wasm backtrace: + 0: failed to invoke --- + 1: error while executing at wasm backtrace: + note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information + 2: wasm trap: wasm `unreachable` instruction executed diff --git a/tests/general/abort.c.stderr.expected.filter b/tests/general/abort.c.stderr.expected.filter index b9f9c6665..98233a1ee 100755 --- a/tests/general/abort.c.stderr.expected.filter +++ b/tests/general/abort.c.stderr.expected.filter @@ -1,7 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail cat \ - | sed -e 's/main module `abort\.c\.[^`]*\.wasm`/main module `abort.c.---.wasm`/' \ - | sed -e 's/source location: @[[:xdigit:]]*$/source location: @----/' \ - | head -n 6 + | 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' diff --git a/tests/general/assert-fail.c b/tests/general/assert-fail.c index 07f455784..54479f9b5 100644 --- a/tests/general/assert-fail.c +++ b/tests/general/assert-fail.c @@ -1,3 +1,7 @@ +#ifdef NDEBUG +#undef NDEBUG +#endif + #include #include diff --git a/tests/general/assert-fail.c.stderr.expected b/tests/general/assert-fail.c.stderr.expected index 806abf9f4..fcf1f75ac 100644 --- a/tests/general/assert-fail.c.stderr.expected +++ b/tests/general/assert-fail.c.stderr.expected @@ -1,7 +1,8 @@ -Assertion failed: false (assert-fail.c: main: 5) +Assertion failed: false (assert-fail.c: main: 9) Error: failed to run main module `assert-fail.c.---.wasm` Caused by: - 0: failed to invoke command default - 1: wasm trap: wasm `unreachable` instruction executed - wasm backtrace: + 0: failed to invoke --- + 1: error while executing at wasm backtrace: + note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information + 2: wasm trap: wasm `unreachable` instruction executed diff --git a/tests/general/assert-fail.c.stderr.expected.filter b/tests/general/assert-fail.c.stderr.expected.filter index df075b130..77849a1ca 100755 --- a/tests/general/assert-fail.c.stderr.expected.filter +++ b/tests/general/assert-fail.c.stderr.expected.filter @@ -1,7 +1,8 @@ -#!/bin/bash +#!/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/source location: @[[:xdigit:]]*$/source location: @----/' \ - | head -n 7 + | 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' diff --git a/tests/general/clocks.c.options b/tests/general/clocks.c.options deleted file mode 100644 index 03953a9dc..000000000 --- a/tests/general/clocks.c.options +++ /dev/null @@ -1 +0,0 @@ --D_WASI_EMULATED_PROCESS_CLOCKS -lwasi-emulated-process-clocks diff --git a/tests/general/cpp_thread_local.cc b/tests/general/cpp_thread_local.cc new file mode 100644 index 000000000..30a151846 --- /dev/null +++ b/tests/general/cpp_thread_local.cc @@ -0,0 +1,3 @@ +struct c { ~c() {} }; +thread_local c v; +int main() { (void)v; } diff --git a/tests/general/exceptions.cc b/tests/general/exceptions.cc new file mode 100644 index 000000000..3daed98ce --- /dev/null +++ b/tests/general/exceptions.cc @@ -0,0 +1,17 @@ +#include +#include + +int main() { +#ifdef __wasm_exception_handling__ + try { + throw std::runtime_error("An error occurred"); + abort(); + } catch (const std::runtime_error& e) { + // .. + return 0; + } + abort(); +#else + return 0; +#endif +} diff --git a/tests/general/getentropy.c b/tests/general/getentropy.c new file mode 100644 index 000000000..d52bfbc7a --- /dev/null +++ b/tests/general/getentropy.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int main() { + char buf[256] = {0}; + int ret = getentropy(buf, 256); + assert(ret == 0); + + bool something_nonzero = false; + for (int i = 0; i < 256; i++) { + if (buf[i] != 0) + something_nonzero = true; + } + + assert(something_nonzero); + + return 0; +} diff --git a/tests/general/iostream_main.cc.options b/tests/general/iostream_main.cc.options deleted file mode 100644 index f7f5b39e8..000000000 --- a/tests/general/iostream_main.cc.options +++ /dev/null @@ -1 +0,0 @@ --fno-exceptions diff --git a/tests/general/mmap.c.dir/.gitattributes b/tests/general/mmap.c.dir/.gitattributes new file mode 100644 index 000000000..94a9658d0 --- /dev/null +++ b/tests/general/mmap.c.dir/.gitattributes @@ -0,0 +1,3 @@ +# This input is read at runtime during testing so ensure that the same input is +# read on unix and windows by forcing just-a-newline for line endings. +*.txt text eol=lf diff --git a/tests/general/mmap.c.options b/tests/general/mmap.c.options deleted file mode 100644 index 63236a027..000000000 --- a/tests/general/mmap.c.options +++ /dev/null @@ -1 +0,0 @@ --D_WASI_EMULATED_MMAN -lwasi-emulated-mman diff --git a/tests/general/printf-long-double-enabled.c.options b/tests/general/printf-long-double-enabled.c.options deleted file mode 100644 index 2b2ec8e65..000000000 --- a/tests/general/printf-long-double-enabled.c.options +++ /dev/null @@ -1 +0,0 @@ --lc-printscan-long-double diff --git a/tests/general/sigabrt.c.options b/tests/general/sigabrt.c.options deleted file mode 100644 index 4f07a3ec6..000000000 --- a/tests/general/sigabrt.c.options +++ /dev/null @@ -1 +0,0 @@ --D_WASI_EMULATED_SIGNAL -lwasi-emulated-signal diff --git a/tests/general/sigabrt.c.stderr.expected b/tests/general/sigabrt.c.stderr.expected index 72c425d0c..d702a8897 100644 --- a/tests/general/sigabrt.c.stderr.expected +++ b/tests/general/sigabrt.c.stderr.expected @@ -3,4 +3,4 @@ Program received fatal signal: Aborted Error: failed to run main module `sigabrt.c.---.wasm` Caused by: - 0: failed to invoke command default + 0: failed to invoke --- diff --git a/tests/general/sigabrt.c.stderr.expected.filter b/tests/general/sigabrt.c.stderr.expected.filter index 425b060c2..726f0241e 100755 --- a/tests/general/sigabrt.c.stderr.expected.filter +++ b/tests/general/sigabrt.c.stderr.expected.filter @@ -2,6 +2,7 @@ set -euo pipefail cat \ - | sed -e 's/main module `sigabrt\.c\.[^`]*\.wasm`/main module `sigabrt.c.---.wasm`/' \ + | 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 diff --git a/tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected b/tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected new file mode 100644 index 000000000..f437a8214 --- /dev/null +++ b/tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected @@ -0,0 +1,6 @@ +raising SIGABRT... +Program received fatal signal: Aborted +Error: failed to run main module `sigabrt.c.---.wasm` + +Caused by: + 0: failed to invoke `run` function diff --git a/tests/general/sigabrt.c.wasm32-wasip2.stderr.expected b/tests/general/sigabrt.c.wasm32-wasip2.stderr.expected new file mode 100644 index 000000000..f437a8214 --- /dev/null +++ b/tests/general/sigabrt.c.wasm32-wasip2.stderr.expected @@ -0,0 +1,6 @@ +raising SIGABRT... +Program received fatal signal: Aborted +Error: failed to run main module `sigabrt.c.---.wasm` + +Caused by: + 0: failed to invoke `run` function diff --git a/tests/general/signals.c b/tests/general/signals.c index 27c8e9eae..be4f54eef 100644 --- a/tests/general/signals.c +++ b/tests/general/signals.c @@ -1,3 +1,7 @@ +#ifdef NDEBUG +#undef NDEBUG +#endif + #include #include #include @@ -6,7 +10,10 @@ #include // Make sure this exists. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-W#warnings" #include +#pragma clang diagnostic pop volatile sig_atomic_t flag = 0; diff --git a/tests/general/signals.c.options b/tests/general/signals.c.options deleted file mode 100644 index 4f07a3ec6..000000000 --- a/tests/general/signals.c.options +++ /dev/null @@ -1 +0,0 @@ --D_WASI_EMULATED_SIGNAL -lwasi-emulated-signal diff --git a/tests/general/stat.c b/tests/general/stat.c index dc007c120..393921f70 100644 --- a/tests/general/stat.c +++ b/tests/general/stat.c @@ -43,10 +43,20 @@ int main(int argc, char *argv[]) { assert(file_statbuf.st_dev == link_statbuf.st_dev); - // Clear out the access time fields, and they should be the same. - memset(&file_statbuf.st_atim, 0, sizeof(struct timespec)); - memset(&link_statbuf.st_atim, 0, sizeof(struct timespec)); - assert(memcmp(&file_statbuf, &link_statbuf, sizeof(struct stat)) == 0); + assert(file_statbuf.st_dev == link_statbuf.st_dev); + assert(file_statbuf.st_ino == link_statbuf.st_ino); + assert(file_statbuf.st_mode == link_statbuf.st_mode); + assert(file_statbuf.st_uid == link_statbuf.st_uid); + assert(file_statbuf.st_gid == link_statbuf.st_gid); + assert(file_statbuf.st_rdev == link_statbuf.st_rdev); + assert(file_statbuf.st_size == link_statbuf.st_size); + assert(file_statbuf.st_blksize == link_statbuf.st_blksize); + assert(file_statbuf.st_blocks == link_statbuf.st_blocks); + // NB: `atim` is explicitly not compared here + assert(file_statbuf.st_mtim.tv_sec == link_statbuf.st_mtim.tv_sec); + assert(file_statbuf.st_mtim.tv_nsec == link_statbuf.st_mtim.tv_nsec); + assert(file_statbuf.st_ctim.tv_sec == link_statbuf.st_ctim.tv_sec); + assert(file_statbuf.st_ctim.tv_nsec == link_statbuf.st_ctim.tv_nsec); // Test lstat. diff --git a/tests/run.sh b/tests/run.sh deleted file mode 100755 index 6379b581c..000000000 --- a/tests/run.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -set -ueo pipefail - -# Top-level test runner. Usage is "run.sh" to run tests in compile-only mode, -# or "run.sh " where is a WASI-capable runtime to run the -# tests in full compile and execute mode. -# -# By default this script will look for `clang` and `clang++` in $PATH and -# assume that they are correctly configured with the sysroot in the default -# location. Alternatively, exporting $CC and $CXX allow more flexibility. e.g: -# -# export CXX="/bin/clang++ --sysroot /share/wasi-sysroot" -# export CC="/bin/clang --sysroot /share/wasi-sysroot" -# - -# Determine the wasm runtime to use, if one is provided. -if [ $# -gt 0 ]; then - runwasi="$1" -else - runwasi="" -fi - -testdir=$(dirname $0) -CC=${CC:=clang} -CXX=${CXX:=clang++} - -echo $CC -echo $CXX - -cd $testdir/compile-only -for options in -O0 -O2 "-O2 -flto"; do - echo "===== Testing compile-only with $options =====" - for file in *.c; do - echo "Testing compile-only $file..." - ../testcase.sh "" "$CC" "$options" "$file" - done - for file in *.cc; do - echo "Testing compile-only $file..." - ../testcase.sh "" "$CXX" "$options" "$file" - done -done -cd - >/dev/null - -cd $testdir/general -for options in -O0 -O2 "-O2 -flto"; do - echo "===== Testing with $options =====" - for file in *.c; do - echo "Testing $file..." - ../testcase.sh "$runwasi" "$CC" "$options" "$file" - done - for file in *.cc; do - echo "Testing $file..." - ../testcase.sh "$runwasi" "$CXX" "$options" "$file" - done -done -cd - >/dev/null diff --git a/tests/testcase.sh b/tests/testcase.sh index 38db9a37e..d2c5a8c34 100755 --- a/tests/testcase.sh +++ b/tests/testcase.sh @@ -7,31 +7,18 @@ set -ueo pipefail # Command-line parsing; this script is meant to be run from a higher-level # script, so don't do anything fancy. runwasi="$1" -compiler="$2" -options="$3" -input="$4" +input="$2" +wasm="$3" # Compile names for generated files. -wasm="$input.$options.wasm" -stdout_observed="$input.$options.stdout.observed" -stderr_observed="$input.$options.stderr.observed" -exit_status_observed="$input.$options.exit_status.observed" +stdout_observed="$wasm.stdout.observed" +stderr_observed="$wasm.stderr.observed" +exit_status_observed="$wasm.exit_status.observed" -# Optionally load compiler options from a file. -if [ -e "$input.options" ]; then - file_options=$(cat "$input.options") -else - file_options= -fi - -echo "Testing $input..." - -# Compile the testcase. -$compiler $options $file_options "$input" -o "$wasm" - -# If we don't have a runwasi command, we're just doing compile-only testing. +# 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 0 + exit 1 fi # Determine the input file to write to stdin. @@ -59,13 +46,21 @@ fi # Run the test, capturing stdout, stderr, and the exit status. exit_status=0 -"$runwasi" $env $dir "$wasm" $dirarg \ +$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" @@ -80,6 +75,7 @@ if [ -e "$input.stdout.expected" ]; then else stdout_expected="/dev/null" fi + if [ -e "$input.stderr.expected" ]; then stderr_expected="$input.stderr.expected" @@ -101,6 +97,6 @@ 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 -u "$stderr_expected" "$stderr_observed" -diff -u "$stdout_expected" "$stdout_observed" -diff -u "$exit_status_expected" "$exit_status_observed" +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" diff --git a/version.py b/version.py new file mode 100755 index 000000000..62f0ff9ef --- /dev/null +++ b/version.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +# This script finds and prints the various versions in this project: wasi-sdk +# itself, LLVM, and the Git revisions of dependencies. +# +# Usage: version [wasi-sdk|llvm|llvm-major|dump] [--llvm-dir=] + +import argparse +import os +import subprocess +import sys + +# The number of characters to use for the abbreviated Git revision. +GIT_REF_LEN = 12 + + +def exec(command, cwd): + result = subprocess.run(command, stdout=subprocess.PIPE, + universal_newlines=True, check=True, cwd=cwd) + return result.stdout.strip() + + +def git_commit(dir): + return exec(['git', 'rev-parse', f'--short={GIT_REF_LEN}', 'HEAD'], dir) + + +def parse_git_version(version): + # Parse, e.g.: wasi-sdk-21-0-g317548590b40+m + parts = version.replace('+', '-').split('-') + assert parts.pop(0) == 'wasi' + assert parts.pop(0) == 'sdk' + + major, minor = parts.pop(0), parts.pop(0) + git = None + dirty = False + + if parts: + # Check: git|dirty. + next = parts.pop(0) + if next == 'm': + dirty = True + elif minor != '0': + git = next[1:] + + # Check: dirty. + if parts: + assert parts.pop(0) == 'm', f'expected dirty flag: +m' + dirty = True + + assert not parts, f'unexpected suffixes: {parts}' + return major, minor, git, dirty + + +# Some inline tests to check Git version parsing: +assert parse_git_version( + 'wasi-sdk-21-1-g317548590b40+m') == ('21', '1', '317548590b40', True) +assert parse_git_version('wasi-sdk-21-2+m') == ('21', '2', None, True) +assert parse_git_version( + 'wasi-sdk-23-0-g317548590b40') == ('23', '0', None, False) + + +def git_version(): + version = exec(['git', 'describe', '--long', '--candidates=999', + '--match=wasi-sdk-*', '--dirty=+m', f'--abbrev={GIT_REF_LEN}'], + os.path.dirname(sys.argv[0])) + major, minor, git, dirty = parse_git_version(version) + version = f'{major}.{minor}' + if git: + version += f'g{git}' + if dirty: + version += '+m' + return version + + +def parse_cmake_set(line): + return line.split(' ')[1].split(')')[0] + + +def llvm_cmake_version(llvm_dir): + path = f'{llvm_dir}/cmake/Modules/LLVMVersion.cmake' + if not os.path.exists(path): + # Handle older LLVM versions; see #399. + path = f'{llvm_dir}/llvm/CMakeLists.txt' + with open(path) as file: + for line in file: + line = line.strip() + if line.startswith('set(LLVM_VERSION_MAJOR'): + llvm_version_major = parse_cmake_set(line) + elif line.startswith('set(LLVM_VERSION_MINOR'): + llvm_version_minor = parse_cmake_set(line) + elif line.startswith('set(LLVM_VERSION_PATCH'): + llvm_version_patch = parse_cmake_set(line) + return llvm_version_major, llvm_version_minor, llvm_version_patch + + +def main(action, llvm_dir): + if action == 'wasi-sdk': + print(git_version()) + elif action == 'llvm': + major, minor, path = llvm_cmake_version(llvm_dir) + print(f'{major}.{minor}.{path}') + elif action == 'llvm-major': + major, _, _ = llvm_cmake_version(llvm_dir) + print(major) + elif action == 'dump': + print(git_version()) + print(f'wasi-libc: {git_commit("src/wasi-libc")}') + print(f'llvm: {git_commit(llvm_dir)}') + major, minor, path = llvm_cmake_version(llvm_dir) + print(f'llvm-version: {major}.{minor}.{path}') + print(f'config: {git_commit("src/config")}') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Print the various kinds of versions in wasi-sdk') + parser.add_argument('action', + choices=['wasi-sdk', 'llvm', 'llvm-major', 'dump'], + nargs='?', + default='wasi-sdk', + help='Which kind of version to print (default: wasi-sdk).') + parser.add_argument('--llvm-dir', + nargs='?', + default='src/llvm-project', + help='Override the location of the LLVM source directory (default: src/llvm-project).') + args = parser.parse_args() + main(args.action, args.llvm_dir) + sys.exit(0) diff --git a/version.sh b/version.sh deleted file mode 100755 index 35c8a908e..000000000 --- a/version.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -set -e -git config --global --add safe.directory "/workspace" -GIT_DESCR=$(git describe --long --candidates=999 --match='wasi-sdk-*' --dirty='+m' --abbrev=12) -GIT_PACKAGE_VERSION=$(echo $GIT_DESCR | perl -ne 'if(/^wasi-sdk-(\d+)-(\d+)-g([0-9a-f]{7,12})([+]m)?$/) { if($2 == 0) { print "$1.$2$4" } else { print "$1.$2g$3$4" } exit } else { print "could not parse git description"; exit 1 }';) -echo $GIT_PACKAGE_VERSION diff --git a/wasi-sdk-p1.cmake b/wasi-sdk-p1.cmake new file mode 100644 index 000000000..903830132 --- /dev/null +++ b/wasi-sdk-p1.cmake @@ -0,0 +1,38 @@ +# Cmake toolchain description file for the Makefile + +# Until Platform/WASI.cmake is upstream we need to inject the path to it +# into CMAKE_MODULE_PATH. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +set(CMAKE_SYSTEM_NAME WASI) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(triple wasm32-wasip1) + +if(WIN32) + set(WASI_HOST_EXE_SUFFIX ".exe") +else() + set(WASI_HOST_EXE_SUFFIX "") +endif() + +# When building from source, WASI_SDK_PREFIX represents the generated directory +if(NOT WASI_SDK_PREFIX) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) +endif() + +set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) + +# Don't look in the sysroot for executables to run during the build +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# 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_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/wasi-sdk-p2.cmake b/wasi-sdk-p2.cmake new file mode 100644 index 000000000..aeed29350 --- /dev/null +++ b/wasi-sdk-p2.cmake @@ -0,0 +1,37 @@ +# Cmake toolchain description file for the Makefile + +# Until Platform/WASI.cmake is upstream we need to inject the path to it +# into CMAKE_MODULE_PATH. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +set(CMAKE_SYSTEM_NAME WASI) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(triple wasm32-wasip2) + +if(WIN32) + set(WASI_HOST_EXE_SUFFIX ".exe") +else() + set(WASI_HOST_EXE_SUFFIX "") +endif() + +# When building from source, WASI_SDK_PREFIX represents the generated directory +if(NOT WASI_SDK_PREFIX) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) +endif() + +set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) + +# Don't look in the sysroot for executables to run during the build +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# 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_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/wasi-sdk-p3.cmake b/wasi-sdk-p3.cmake new file mode 100644 index 000000000..f73756225 --- /dev/null +++ b/wasi-sdk-p3.cmake @@ -0,0 +1,37 @@ +# Cmake toolchain description file for the Makefile + +# Until Platform/WASI.cmake is upstream we need to inject the path to it +# into CMAKE_MODULE_PATH. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +set(CMAKE_SYSTEM_NAME WASI) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(triple wasm32-wasip3) + +if(WIN32) + set(WASI_HOST_EXE_SUFFIX ".exe") +else() + set(WASI_HOST_EXE_SUFFIX "") +endif() + +# When building from source, WASI_SDK_PREFIX represents the generated directory +if(NOT WASI_SDK_PREFIX) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) +endif() + +set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) + +# Don't look in the sysroot for executables to run during the build +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# 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_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/wasi-sdk-pthread.cmake b/wasi-sdk-pthread.cmake index e82622f97..c2cd83dbd 100644 --- a/wasi-sdk-pthread.cmake +++ b/wasi-sdk-pthread.cmake @@ -1,14 +1,16 @@ # Cmake toolchain description file for the Makefile -# This is arbitrary, AFAIK, for now. -cmake_minimum_required(VERSION 3.4.0) - set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR wasm32) set(triple wasm32-wasi-threads) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") +# wasi-threads requires --import-memory. +# wasi requires --export-memory. +# (--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,--export-memory") if(WIN32) set(WASI_HOST_EXE_SUFFIX ".exe") @@ -16,12 +18,19 @@ else() set(WASI_HOST_EXE_SUFFIX "") endif() +# When building from source, WASI_SDK_PREFIX represents the generated directory +if(NOT WASI_SDK_PREFIX) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) +endif() + set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) set(CMAKE_C_COMPILER_TARGET ${triple}) set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) # Don't look in the sysroot for executables to run during the build set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/wasi-sdk.cmake b/wasi-sdk.cmake index 3245a4c29..2131cfcd7 100644 --- a/wasi-sdk.cmake +++ b/wasi-sdk.cmake @@ -1,7 +1,8 @@ # Cmake toolchain description file for the Makefile -# This is arbitrary, AFAIK, for now. -cmake_minimum_required(VERSION 3.4.0) +# Until Platform/WASI.cmake is upstream we need to inject the path to it +# into CMAKE_MODULE_PATH. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_VERSION 1) @@ -14,12 +15,19 @@ else() set(WASI_HOST_EXE_SUFFIX "") endif() +# When building from source, WASI_SDK_PREFIX represents the generated directory +if(NOT WASI_SDK_PREFIX) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) +endif() + set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) set(CMAKE_C_COMPILER_TARGET ${triple}) set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) # Don't look in the sysroot for executables to run during the build set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) diff --git a/wasi-sdk.control b/wasi-sdk.control index 0586f37ba..e3859e7eb 100644 --- a/wasi-sdk.control +++ b/wasi-sdk.control @@ -1,6 +1,6 @@ Package: wasi-sdk Version: VERSION -Architecture: amd64 +Architecture: ARCH Priority: optional Description: Clang toolchain with wasm32-wasi default target, and the wasi sysroot -Maintainer: Pat Hickey +Maintainer: WASI SDK Maintainers