ci: add riscv64-linux build using native RISE runner

Add riscv64gc-unknown-linux-musl to the build matrix using a native
ubuntu-24.04-riscv runner (RISE Project), mirroring the arm64-linux
pattern which uses ubuntu-22.04-arm.

Changes:
- Use ubuntu-24.04-riscv runner instead of cross-compilation on x86_64
- Dockerfile.riscv64-linux: Ubuntu 24.04 base (AlmaLinux 8 has no
  riscv64 image); native build-essential, no cross-compiler env vars
- docker-build.sh: skip wasmtime volume mount when WASI_SDK_CI_SKIP_SYSROOT
  is set (wasmtime is not available on riscv64 runners yet)
- WASI_SDK_CI_SKIP_SYSROOT: 1 and WASI_SDK_LLDB=OFF kept as before;
  sysroot tests require running wasm binaries which needs wasmtime

RISE runners are GitHub Actions runners for riscv64, free for open
source projects: https://github.com/apps/rise-risc-v-runners

Closes #607

Signed-off-by: Bruno Verachten <gounthar@gmail.com>
pull/619/head
Bruno Verachten 1 week ago
commit d4e56f2ffc
No known key found for this signature in database
GPG Key ID: E7EAD7209D3ECCD3

@ -0,0 +1,4 @@
# Our docker builds do not require the submodule sources so exclude them as
# they can be very big.
/src
.git

2
.gitattributes vendored

@ -0,0 +1,2 @@
# Bourne-style shell scripts: These are Unix-style scripts. Don't add CR's.
*.sh text eol=lf

@ -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

@ -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

@ -0,0 +1,281 @@
name: CI
on:
push:
tags:
- 'wasi-sdk-*'
branches:
- main
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build ${{ matrix.artifact }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- artifact: x86_64-linux
os: ubuntu-24.04
- artifact: arm64-linux
os: ubuntu-22.04-arm
- artifact: riscv64-linux
os: ubuntu-24.04-riscv
rust_target: riscv64gc-unknown-linux-gnu
cross_cmake_args: -DWASI_SDK_LLDB=OFF
env:
WASI_SDK_CI_SKIP_SYSROOT: 1
- 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/checkout@v6
with:
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: ${{ runner.tool_cache }}/ccache
key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }}
restore-keys: |
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
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@v6
with:
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
# 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:
matrix:
include:
- name: default
- name: exceptions
defines: -DWASI_SDK_EXCEPTIONS=ON
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
- uses: ./.github/actions/install-deps
- run: cargo install wasm-component-ld@0.5.21
- run: sudo apt-get update -y && sudo apt-get install -y clang-20 lld-20
- run: |
cmake -G Ninja -B build -S . \
-DCMAKE_C_COMPILER=/usr/lib/llvm-20/bin/clang \
-DCMAKE_SYSTEM_NAME=WASI \
-DWASI_SDK_INCLUDE_TESTS=ON \
-DWASI_SDK_CPU_CFLAGS="" \
-DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF \
-DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF \
${{ matrix.defines }}
- run: ninja -C build
- run: ctest --output-on-failure --parallel 10 --test-dir build/tests
# Once all of the above matrix entries have completed this job will run and
# 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/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/checkout
# 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:
images: ghcr.io/${{ github.repository }}
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=sha
- name: Build and push wasi-sdk docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' }}
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
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

2
.gitignore vendored

@ -0,0 +1,2 @@
build
dist

9
.gitmodules vendored

@ -0,0 +1,9 @@
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/llvm/llvm-project
[submodule "src/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

@ -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()

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at either sunfish@mozilla.com or tyler@fastly.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

@ -0,0 +1,60 @@
# Support for C++ Exceptions
The released artifacts for wasi-sdk at this time do not support C++ exceptions.
LLVM and Clang, however, have support for C++ exceptions in WebAssembly and this
is intended to serve as documentation of the current state of affairs of using
C++ exceptions. It should be noted though that the current status of C++
exceptions support is not intended to be the final state of support, and this is
all continuing to be iterated on over time.
## 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.
## 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="-lunwind"
```
## Limitations
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.
## Future Plans
There are a few tracking issues with historical discussion about C++ exceptions
support in wasi-sdk such as [#334](https://github.com/WebAssembly/wasi-sdk/issues/334)
and [#565](https://github.com/WebAssembly/wasi-sdk/issues/565). The major
remaining items are:
* Figure out support for shared libraries.
* Determine how to ship a sysroot that supports both with-and-without
exceptions.
* Figure out how to avoid the need for extra compiler flags when using
exceptions.
* Figure out if a new wasm target is warranted.

@ -0,0 +1,220 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

@ -0,0 +1,238 @@
# WASI SDK
## Quick Start
[Download SDK packages here.][releases]
[releases]: https://github.com/WebAssembly/wasi-sdk/releases
## About this repository
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 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
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`. 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
This repository uses git submodule, to clone it you need use the command below :
```shell script
git clone --recursive https://github.com/WebAssembly/wasi-sdk.git
```
## Requirements
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.
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:
```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
```
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
mkdir dist-my-platform
cp build/toolchain/dist/* build/sysroot/dist/* dist-my-platform
./ci/merge-artifacts.sh
```
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
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:
```shell script
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`. 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] 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.*`
in the install directory.
## Docker Image
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.
```
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.
## Notable Limitations
* C++ exceptions are disabled by default. For more information 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 `<atomic>`, `<mutex>`, and `<thread>` 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
[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

@ -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/{}
```

@ -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 <assert.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stdio.h>
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
```

@ -0,0 +1,51 @@
#!/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 \
"-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

@ -0,0 +1,63 @@
#!/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 <image>"
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"
# Only mount wasmtime when the sysroot build (and its tests) will run.
# When WASI_SDK_CI_SKIP_SYSROOT is set wasmtime is not needed.
if [ -z "$WASI_SDK_CI_SKIP_SYSROOT" ]; then
args="$args --volume $(dirname $(which 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'

@ -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

@ -0,0 +1,20 @@
# Ubuntu 24.04 is used here (rather than AlmaLinux 8) because AlmaLinux 8
# does not publish riscv64 images. This runs natively on a riscv64 runner.
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
build-essential \
clang \
lld \
python3 \
git \
unzip \
cmake \
ninja-build \
ccache
# 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

@ -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.

@ -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

@ -0,0 +1,83 @@
#!/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 ;;
*)
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

@ -0,0 +1 @@
--sysroot=<CFGDIR>/../share/wasi-sysroot

@ -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()

@ -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()

@ -0,0 +1,416 @@
# 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)
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)
option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF)
set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable")
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 $<$<BOOL:${WASI_SDK_LTO}>: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)
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})
if (WASI_SDK_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)
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}
-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=${WASI_SDK_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=${WASI_SDK_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=${WASI_SDK_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}${extra_libdir_suffix}
-DLIBCXXABI_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix}
-DLIBUNWIND_LIBDIR_SUFFIX=/${target}${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
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"
)
endfunction()
function(define_libcxx target)
define_libcxx_sub(${target} "" "" "")
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} "-lto" "-flto=full" "/llvm-lto/${llvm_version}")
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_custom_target(libcxx-${target}
DEPENDS libcxx-${target}-build $<$<BOOL:${WASI_SDK_LTO}>:libcxx-${target}-lto-build> 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)

@ -0,0 +1,337 @@
# 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
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
<SOURCE_DIR>/configure
--prefix=${wasi_tmp_install}
--enable-pic
--disable-examples
--disable-static
--disable-silent-rules
CC=${CMAKE_C_COMPILER}
LDFLAGS=${libedit_ldflags}
${extra_configure_commands}
BUILD_COMMAND
${MAKE_EXECUTABLE} -j${nproc}
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
)
list(APPEND default_cmake_args
-DLLDB_ENABLE_LIBEDIT=ON
-DLibEdit_ROOT=${wasi_tmp_install}
)
else()
list(APPEND default_cmake_args -DLLDB_ENABLE_LIBEDIT=OFF)
add_custom_target(libedit)
endif()
set(libxml_cmake_args)
# Windows doesn't have iconv by default, so disable it for now.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
list(APPEND libxml_cmake_args -DLIBXML2_WITH_ICONV=OFF)
endif()
# Our AlmaLinux:8 container ends up using `lib64` instead of `lib` by default
# which doesn't match LLVM, so specifically use the same dir as LLVM.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
list(APPEND libxml_cmake_args -DCMAKE_INSTALL_LIBDIR=lib)
endif()
if (WASI_SDK_LIBXML2)
ExternalProject_Add(libxml2
URL https://download.gnome.org/sources/libxml2/2.15/libxml2-2.15.2.tar.xz
URL_HASH SHA256=c8b9bc81f8b590c33af8cc6c336dbff2f53409973588a351c95f1c621b13d09d
CMAKE_ARGS
${default_cmake_args}
-DLIBXML2_WITH_PROGRAMS=OFF
-DLIBXML2_WITH_DEBUG=OFF
-DLIBXML2_WITH_DOCS=OFF
-DLIBXML2_WITH_TESTS=OFF
${libxml_cmake_args}
${llvm_cmake_flags_list}
USES_TERMINAL_CONFIGURE ON
USES_TERMINAL_BUILD ON
USES_TERMINAL_INSTALL ON
)
list(APPEND default_cmake_args
-DLLDB_ENABLE_LIBXML2=ON
-DLibXml2_ROOT=${wasi_tmp_install}
)
else()
list(APPEND default_cmake_args -DLLDB_ENABLE_LIBXML2=OFF)
add_custom_target(libxml2)
endif()
else()
add_custom_target(libedit)
add_custom_target(libxml2)
endif()
list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets)
list(TRANSFORM tools PREPEND --target=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
)
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.21)
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)

@ -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"

@ -0,0 +1 @@
Subproject commit f992bcc08219edb283d2ab31dd3871a4a0e8220e

@ -0,0 +1,28 @@
diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h
index f8e83e138eff..c5097d25b0c6 100644
--- a/libunwind/src/assembly.h
+++ b/libunwind/src/assembly.h
@@ -249,6 +249,9 @@ aliasname: \
#define WEAK_ALIAS(name, aliasname)
#define NO_EXEC_STACK_DIRECTIVE
+#elif defined(__wasm__)
+#define NO_EXEC_STACK_DIRECTIVE
+
// clang-format on
#else
diff --git a/libunwind/src/config.h b/libunwind/src/config.h
index deb5a4d4d73d..23c9f012cbcf 100644
--- a/libunwind/src/config.h
+++ b/libunwind/src/config.h
@@ -66,7 +66,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

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

@ -0,0 +1 @@
Subproject commit 4434dabb69916856b824f68a64b029c67175e532

@ -0,0 +1 @@
Subproject commit 2fc32bc81b9f07f8d9525edea59bfbaf760c06d6

3
tests/.gitignore vendored

@ -0,0 +1,3 @@
*.observed
*.observed.filtered
*.wasm

@ -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(WASI_SDK_EXCEPTIONS)
target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false)
target_link_options(${target_name} PRIVATE -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}
$<TARGET_FILE:${target_name}>
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
endforeach()
endforeach()
endfunction()
add_subdirectory(compile-only)
add_subdirectory(general)

@ -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()

@ -0,0 +1,27 @@
#include <stdio.h>
#include <limits.h>
#include <errno.h>
extern char **environ;
extern void __dso_handle;
#if !defined(__clang_major__) || __clang_major__ >= 10
extern void __data_end;
extern void __global_base;
extern void __heap_base;
#endif
int main(int argc, char *argv[]) {
printf("NULL=%p\n", NULL);
printf("__dso_handle=%p\n", &__dso_handle);
#if !defined(__clang_major__) || __clang_major__ >= 10
printf("__data_end=%p\n", &__data_end);
printf("__global_base=%p\n", &__global_base);
printf("__heap_base=%p\n", &__heap_base);
#endif
printf("__builtin_frame_address(0)=%p\n", __builtin_frame_address(0));
printf("__builtin_alloca(0)=%p\n", __builtin_alloca(0));
printf("__builtin_wasm_memory_size(0)=%p\n", (void *)(__builtin_wasm_memory_size(0) * PAGE_SIZE));
printf("&errno=%p\n", (void *)&errno);
printf("&environ=%p\n", (void *)&environ);
return 0;
}

@ -0,0 +1,8 @@
#include <stdio.h>
volatile long double x = 42.0L;
int main(void) {
printf("the answer is %Lf\n", x);
return 0;
}

@ -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()

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

@ -0,0 +1,7 @@
Error: failed to run main module `abort.c.---.wasm`
Caused by:
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

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

@ -0,0 +1,6 @@
#include <stdio.h>
int main(int argc, char *argv[]) {
puts("hello from argc argv main!");
return 0;
}

@ -0,0 +1,6 @@
#include <stdio.h>
int main(int argc, char *argv[]) {
puts("hello from C++ argc argv main!");
return 0;
}

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

@ -0,0 +1,11 @@
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>
#include <stdbool.h>
int main(void) {
assert(false);
return 0;
}

@ -0,0 +1,8 @@
Assertion failed: false (assert-fail.c: main: 9)
Error: failed to run main module `assert-fail.c.---.wasm`
Caused by:
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

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

@ -0,0 +1,7 @@
#include <assert.h>
#include <stdbool.h>
int main(void) {
assert(true);
return 0;
}

@ -0,0 +1,43 @@
#include <time.h>
#include <sys/resource.h>
#include <sys/times.h>
#include <assert.h>
static void test_clock(void) {
clock_t a = clock();
clock_t b = clock();
assert(a != -1);
assert(b != -1);
assert(a > 0);
assert(b >= a);
}
static void test_times(void) {
struct tms before;
struct tms after;
clock_t a = times(&before);
clock_t b = times(&after);
assert(a != -1);
assert(b != -1);
assert(b >= a);
assert(after.tms_utime >= before.tms_utime);
}
static void test_getrusage(void) {
struct rusage before;
struct rusage after;
int a = getrusage(RUSAGE_SELF, &before);
int b = getrusage(RUSAGE_SELF, &after);
assert(a != -1);
assert(b != -1);
assert(after.ru_utime.tv_sec >= before.ru_utime.tv_sec);
assert(after.ru_utime.tv_sec != before.ru_utime.tv_sec ||
after.ru_utime.tv_usec >= before.ru_utime.tv_usec);
}
int main(void) {
test_clock();
test_times();
test_getrusage();
return 0;
}

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

@ -0,0 +1,52 @@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
extern char **environ;
static void from_atexit(void) {
printf("hello from_atexit\n");
}
static void another_from_atexit(void) {
printf("hello another_from_atexit\n");
}
__attribute__((constructor)) static void from_constructor(void) {
printf("hello from_constructor\n");
}
__attribute__((constructor(101))) static void from_constructor_101(void) {
assert(errno == 0);
printf("hello from_constructor101\n");
assert(environ && "environment should be initialized by this point");
}
__attribute__((constructor(65535))) static void from_constructor_65535(void) {
printf("hello from_constructor65535\n");
}
__attribute__((destructor)) static void from_destructor(void) {
printf("hello from_destructor\n");
}
__attribute__((destructor(101))) static void from_destructor101(void) {
printf("hello from_destructor101\n");
}
__attribute__((destructor(65535))) static void from_destructor65535(void) {
printf("hello from_destructor65535\n");
}
int main(int argc, char *argv[]) {
printf("hello main\n");
assert(argc != 0);
assert(argv != NULL);
assert(argv[argc] == NULL);
atexit(from_atexit);
atexit(another_from_atexit);
printf("goodbye main\n");
return 0;
}

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

@ -0,0 +1,16 @@
#include "ctors_dtors.c"
struct StaticObject {
StaticObject();
~StaticObject();
};
StaticObject::StaticObject() {
printf("hello StaticObject::StaticObject\n");
}
StaticObject::~StaticObject() {
printf("hello StaticObject::~StaticObject\n");
}
static StaticObject static_object;

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

@ -0,0 +1 @@
int main(void) { return 0; }

@ -0,0 +1,7 @@
#include <stdlib.h>
#include <stdio.h>
int main(void) {
printf("HELLO = %s\n", getenv("HELLO"));
return 0;
}

@ -0,0 +1,7 @@
#include <stdlib.h>
#include <stdio.h>
int main(void) {
printf("HELLO = %s\n", getenv("HELLO"));
return 0;
}

@ -0,0 +1,18 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
extern char **environ;
int main(void) {
assert(environ != NULL);
for (char **p = environ; *p; ++p) {
assert(p != NULL);
}
for (char **p = environ; *p; ++p) {
if (strncmp(*p, "HELLO=", 5) == 0) {
printf("HELLO = %s\n", *p + 6);
}
}
return 0;
}

@ -0,0 +1,17 @@
#include <iostream>
#include <stdlib.h>
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
}

@ -0,0 +1,19 @@
#include <assert.h>
#include <unistd.h>
#include <stdbool.h>
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;
}

@ -0,0 +1,6 @@
#include <iostream>
int main() {
std::cout << "hello from C++ main with cout!" << std::endl;
return 0;
}

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

@ -0,0 +1,12 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
// It isn't required that errno be zero on entry to main, but
// for tidiness' sake, if we ever do things during startup that
// do set errno, we should reset it for tidiness' sake.
int main(void) {
int n = errno;
printf("initial errno is %d: %s\n", n, strerror(n));
return 0;
}

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

@ -0,0 +1,87 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <sys/mman.h>
#include <sys/stat.h>
#ifdef __GLIBC__
#include <sys/user.h>
#endif
#include <unistd.h>
#define perror_and_exit(message) \
do { \
perror(message); \
return EXIT_FAILURE; \
} while (0)
#define OFFSET 10726
#define LENGTH 143
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "usage: %s <dir>\n", argv[0]);
return EXIT_FAILURE;
}
char *filename;
if (asprintf(&filename, "%s/input.txt", argv[1]) == -1) {
fprintf(stderr, "can't allocate filename");
return EXIT_FAILURE;
}
int fd = open(filename, O_RDONLY);
if (fd < 0)
perror_and_exit("open");
struct stat stat_buf;
if (fstat(fd, &stat_buf) != 0)
perror_and_exit("fstat");
off_t offset = OFFSET;
if (offset < 0) {
fprintf(stderr, "negative offset\n");
return EXIT_FAILURE;
}
if (offset > (off_t)SIZE_MAX) {
fprintf(stderr, "offset overflow\n");
return EXIT_FAILURE;
}
off_t aligned_offset = offset & -(off_t)PAGE_SIZE;
if (offset >= stat_buf.st_size) {
fprintf(stderr, "offset is past end of file\n");
return EXIT_FAILURE;
}
size_t length = LENGTH;
if ((off_t)length < 0) {
fprintf(stderr, "length overflow\n");
return EXIT_FAILURE;
}
if ((off_t)length > stat_buf.st_size - offset)
length = (size_t)(stat_buf.st_size - offset);
size_t mmap_length = length + (size_t)(offset - aligned_offset);
char *addr = mmap(NULL, mmap_length, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
if (addr == MAP_FAILED)
perror_and_exit("mmap");
ssize_t nwritten = write(STDOUT_FILENO, addr + (offset - aligned_offset), length);
if (nwritten < 0)
perror_and_exit("write");
if ((size_t)nwritten != length) {
fprintf(stderr, "partial write");
return EXIT_FAILURE;
}
if (munmap(addr, mmap_length) != 0)
perror_and_exit("munmap");
if (close(fd) != 0)
perror_and_exit("close");
return EXIT_SUCCESS;
}

@ -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

@ -0,0 +1,172 @@
Alices Adventures in Wonderland
by Lewis Carroll
CHAPTER VI.
Pig and Pepper
For a minute or two she stood looking at the house, and wondering what to do next, when suddenly a footman in livery came running out of the wood—(she considered him to be a footman because he was in livery: otherwise, judging by his face only, she would have called him a fish)—and rapped loudly at the door with his knuckles. It was opened by another footman in livery, with a round face, and large eyes like a frog; and both footmen, Alice noticed, had powdered hair that curled all over their heads. She felt very curious to know what it was all about, and crept a little way out of the wood to listen.
The Fish-Footman began by producing from under his arm a great letter, nearly as large as himself, and this he handed over to the other, saying, in a solemn tone, “For the Duchess. An invitation from the Queen to play croquet.” The Frog-Footman repeated, in the same solemn tone, only changing the order of the words a little, “From the Queen. An invitation for the Duchess to play croquet.”
Then they both bowed low, and their curls got entangled together.
Alice laughed so much at this, that she had to run back into the wood for fear of their hearing her; and when she next peeped out the Fish-Footman was gone, and the other was sitting on the ground near the door, staring stupidly up into the sky.
Alice went timidly up to the door, and knocked.
“Theres no sort of use in knocking,” said the Footman, “and that for two reasons. First, because Im on the same side of the door as you are; secondly, because theyre making such a noise inside, no one could possibly hear you.” And certainly there was a most extraordinary noise going on within—a constant howling and sneezing, and every now and then a great crash, as if a dish or kettle had been broken to pieces.
“Please, then,” said Alice, “how am I to get in?”
“There might be some sense in your knocking,” the Footman went on without attending to her, “if we had the door between us. For instance, if you were inside, you might knock, and I could let you out, you know.” He was looking up into the sky all the time he was speaking, and this Alice thought decidedly uncivil. “But perhaps he cant help it,” she said to herself; “his eyes are so very nearly at the top of his head. But at any rate he might answer questions.—How am I to get in?” she repeated, aloud.
“I shall sit here,” the Footman remarked, “till tomorrow—”
At this moment the door of the house opened, and a large plate came skimming out, straight at the Footmans head: it just grazed his nose, and broke to pieces against one of the trees behind him.
“—or next day, maybe,” the Footman continued in the same tone, exactly as if nothing had happened.
“How am I to get in?” asked Alice again, in a louder tone.
“Are you to get in at all?” said the Footman. “Thats the first question, you know.”
It was, no doubt: only Alice did not like to be told so. “Its really dreadful,” she muttered to herself, “the way all the creatures argue. Its enough to drive one crazy!”
The Footman seemed to think this a good opportunity for repeating his remark, with variations. “I shall sit here,” he said, “on and off, for days and days.”
“But what am I to do?” said Alice.
“Anything you like,” said the Footman, and began whistling.
“Oh, theres no use in talking to him,” said Alice desperately: “hes perfectly idiotic!” And she opened the door and went in.
The door led right into a large kitchen, which was full of smoke from one end to the other: the Duchess was sitting on a three-legged stool in the middle, nursing a baby; the cook was leaning over the fire, stirring a large cauldron which seemed to be full of soup.
“Theres certainly too much pepper in that soup!” Alice said to herself, as well as she could for sneezing.
There was certainly too much of it in the air. Even the Duchess sneezed occasionally; and as for the baby, it was sneezing and howling alternately without a moments pause. The only things in the kitchen that did not sneeze, were the cook, and a large cat which was sitting on the hearth and grinning from ear to ear.
“Please would you tell me,” said Alice, a little timidly, for she was not quite sure whether it was good manners for her to speak first, “why your cat grins like that?”
“Its a Cheshire cat,” said the Duchess, “and thats why. Pig!”
She said the last word with such sudden violence that Alice quite jumped; but she saw in another moment that it was addressed to the baby, and not to her, so she took courage, and went on again:—
“I didnt know that Cheshire cats always grinned; in fact, I didnt know that cats could grin.”
“They all can,” said the Duchess; “and most of em do.”
“I dont know of any that do,” Alice said very politely, feeling quite pleased to have got into a conversation.
“You dont know much,” said the Duchess; “and thats a fact.”
Alice did not at all like the tone of this remark, and thought it would be as well to introduce some other subject of conversation. While she was trying to fix on one, the cook took the cauldron of soup off the fire, and at once set to work throwing everything within her reach at the Duchess and the baby—the fire-irons came first; then followed a shower of saucepans, plates, and dishes. The Duchess took no notice of them even when they hit her; and the baby was howling so much already, that it was quite impossible to say whether the blows hurt it or not.
“Oh, please mind what youre doing!” cried Alice, jumping up and down in an agony of terror. “Oh, there goes his precious nose!” as an unusually large saucepan flew close by it, and very nearly carried it off.
“If everybody minded their own business,” the Duchess said in a hoarse growl, “the world would go round a deal faster than it does.”
“Which would not be an advantage,” said Alice, who felt very glad to get an opportunity of showing off a little of her knowledge. “Just think of what work it would make with the day and night! You see the earth takes twenty-four hours to turn round on its axis—”
“Talking of axes,” said the Duchess, “chop off her head!”
Alice glanced rather anxiously at the cook, to see if she meant to take the hint; but the cook was busily stirring the soup, and seemed not to be listening, so she went on again: “Twenty-four hours, I think; or is it twelve? I—”
“Oh, dont bother me,” said the Duchess; “I never could abide figures!” And with that she began nursing her child again, singing a sort of lullaby to it as she did so, and giving it a violent shake at the end of every line:
“Speak roughly to your little boy,
And beat him when he sneezes:
He only does it to annoy,
Because he knows it teases.”
CHORUS.
(In which the cook and the baby joined):
“Wow! wow! wow!”
While the Duchess sang the second verse of the song, she kept tossing the baby violently up and down, and the poor little thing howled so, that Alice could hardly hear the words:—
“I speak severely to my boy,
I beat him when he sneezes;
For he can thoroughly enjoy
The pepper when he pleases!”
CHORUS.
“Wow! wow! wow!”
“Here! you may nurse it a bit, if you like!” the Duchess said to Alice, flinging the baby at her as she spoke. “I must go and get ready to play croquet with the Queen,” and she hurried out of the room. The cook threw a frying-pan after her as she went out, but it just missed her.
Alice caught the baby with some difficulty, as it was a queer-shaped little creature, and held out its arms and legs in all directions, “just like a star-fish,” thought Alice. The poor little thing was snorting like a steam-engine when she caught it, and kept doubling itself up and straightening itself out again, so that altogether, for the first minute or two, it was as much as she could do to hold it.
As soon as she had made out the proper way of nursing it, (which was to twist it up into a sort of knot, and then keep tight hold of its right ear and left foot, so as to prevent its undoing itself,) she carried it out into the open air. “If I dont take this child away with me,” thought Alice, “theyre sure to kill it in a day or two: wouldnt it be murder to leave it behind?” She said the last words out loud, and the little thing grunted in reply (it had left off sneezing by this time). “Dont grunt,” said Alice; “thats not at all a proper way of expressing yourself.”
The baby grunted again, and Alice looked very anxiously into its face to see what was the matter with it. There could be no doubt that it had a very turn-up nose, much more like a snout than a real nose; also its eyes were getting extremely small for a baby: altogether Alice did not like the look of the thing at all. “But perhaps it was only sobbing,” she thought, and looked into its eyes again, to see if there were any tears.
No, there were no tears. “If youre going to turn into a pig, my dear,” said Alice, seriously, “Ill have nothing more to do with you. Mind now!” The poor little thing sobbed again (or grunted, it was impossible to say which), and they went on for some while in silence.
Alice was just beginning to think to herself, “Now, what am I to do with this creature when I get it home?” when it grunted again, so violently, that she looked down into its face in some alarm. This time there could be no mistake about it: it was neither more nor less than a pig, and she felt that it would be quite absurd for her to carry it further.
So she set the little creature down, and felt quite relieved to see it trot away quietly into the wood. “If it had grown up,” she said to herself, “it would have made a dreadfully ugly child: but it makes rather a handsome pig, I think.” And she began thinking over other children she knew, who might do very well as pigs, and was just saying to herself, “if one only knew the right way to change them—” when she was a little startled by seeing the Cheshire Cat sitting on a bough of a tree a few yards off.
The Cat only grinned when it saw Alice. It looked good-natured, she thought: still it had very long claws and a great many teeth, so she felt that it ought to be treated with respect.
“Cheshire Puss,” she began, rather timidly, as she did not at all know whether it would like the name: however, it only grinned a little wider. “Come, its pleased so far,” thought Alice, and she went on. “Would you tell me, please, which way I ought to go from here?”
“That depends a good deal on where you want to get to,” said the Cat.
“I dont much care where—” said Alice.
“Then it doesnt matter which way you go,” said the Cat.
“—so long as I get somewhere,” Alice added as an explanation.
“Oh, youre sure to do that,” said the Cat, “if you only walk long enough.”
Alice felt that this could not be denied, so she tried another question. “What sort of people live about here?”
“In that direction,” the Cat said, waving its right paw round, “lives a Hatter: and in that direction,” waving the other paw, “lives a March Hare. Visit either you like: theyre both mad.”
“But I dont want to go among mad people,” Alice remarked.
“Oh, you cant help that,” said the Cat: “were all mad here. Im mad. Youre mad.”
“How do you know Im mad?” said Alice.
“You must be,” said the Cat, “or you wouldnt have come here.”
Alice didnt think that proved it at all; however, she went on “And how do you know that youre mad?”
“To begin with,” said the Cat, “a dogs not mad. You grant that?”
“I suppose so,” said Alice.
“Well, then,” the Cat went on, “you see, a dog growls when its angry, and wags its tail when its pleased. Now I growl when Im pleased, and wag my tail when Im angry. Therefore Im mad.”
“I call it purring, not growling,” said Alice.
“Call it what you like,” said the Cat. “Do you play croquet with the Queen to-day?”
“I should like it very much,” said Alice, “but I havent been invited yet.”
“Youll see me there,” said the Cat, and vanished.
Alice was not much surprised at this, she was getting so used to queer things happening. While she was looking at the place where it had been, it suddenly appeared again.
“By-the-bye, what became of the baby?” said the Cat. “Id nearly forgotten to ask.”
“It turned into a pig,” Alice quietly said, just as if it had come back in a natural way.
“I thought it would,” said the Cat, and vanished again.
Alice waited a little, half expecting to see it again, but it did not appear, and after a minute or two she walked on in the direction in which the March Hare was said to live. “Ive seen hatters before,” she said to herself; “the March Hare will be much the most interesting, and perhaps as this is May it wont be raving mad—at least not so mad as it was in March.” As she said this, she looked up, and there was the Cat again, sitting on a branch of a tree.
“Did you say pig, or fig?” said the Cat.
“I said pig,” replied Alice; “and I wish you wouldnt keep appearing and vanishing so suddenly: you make one quite giddy.”
“All right,” said the Cat; and this time it vanished quite slowly, beginning with the end of the tail, and ending with the grin, which remained some time after the rest of it had gone.
“Well! Ive often seen a cat without a grin,” thought Alice; “but a grin without a cat! Its the most curious thing I ever saw in my life!”
She had not gone much farther before she came in sight of the house of the March Hare: she thought it must be the right house, because the chimneys were shaped like ears and the roof was thatched with fur. It was so large a house, that she did not like to go nearer till she had nibbled some more of the lefthand bit of mushroom, and raised herself to about two feet high: even then she walked up towards it rather timidly, saying to herself “Suppose it should be raving mad after all! I almost wish Id gone to see the Hatter instead!”

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

@ -0,0 +1,6 @@
#include <stdio.h>
int main() {
puts("hello from no-arg main!");
return 0;
}

@ -0,0 +1,6 @@
#include <stdio.h>
int main() {
puts("hello from C++ no-arg main!");
return 0;
}

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

@ -0,0 +1,72 @@
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
#define perror_and_exit(message) \
do { \
perror(message); \
return EXIT_FAILURE; \
} while (0)
#define OFFSET 10726
#define LENGTH 143
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "usage: %s <dir>\n", argv[0]);
return EXIT_FAILURE;
}
DIR *dir = opendir(argv[1]);
if (dir == NULL) {
perror_and_exit("opendir");
}
int count = 0;
int zeros = 0;
errno = 0;
for (;; count += 1) {
struct dirent *ent = readdir(dir);
if (ent == NULL) {
if (errno == 0) {
break;
}
perror_and_exit("readdir");
}
if (strcmp(ent->d_name, "file.md") == 0) {
assert(ent->d_type == DT_REG);
} else if (strcmp(ent->d_name, "dir") == 0) {
assert(ent->d_type == DT_DIR);
} else if (strcmp(ent->d_name, "file-symlink") == 0) {
assert(ent->d_type == DT_LNK);
} else if (strcmp(ent->d_name, "dir-symlink") == 0) {
assert(ent->d_type == DT_LNK);
} else if (strcmp(ent->d_name, ".") == 0) {
assert(ent->d_type == DT_DIR);
} else if (strcmp(ent->d_name, "..") == 0) {
assert(ent->d_type == DT_DIR);
} else {
assert(false);
}
if (ent->d_ino == 0) {
zeros += 1;
}
}
assert(count == 6);
assert(zeros <= 1);
if (closedir(dir) != 0)
perror_and_exit("closedir");
return EXIT_SUCCESS;
}

@ -0,0 +1 @@
This is a file in a directory.

@ -0,0 +1 @@
# This is a top-level file

@ -0,0 +1,8 @@
#include <stdio.h>
volatile long double x = 42.0L;
int main(void) {
printf("the answer is %Lf\n", x);
return 0;
}

@ -0,0 +1,8 @@
#include <stdio.h>
volatile int x = 42;
int main(void) {
printf("the answer is %d\n", x);
return 0;
}

@ -0,0 +1,8 @@
#include <stdio.h>
volatile double x = 42.0;
int main(void) {
printf("the answer is %f\n", x);
return 0;
}

@ -0,0 +1,10 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
fprintf(stderr, "raising SIGABRT...\n");
raise(SIGABRT);
fprintf(stderr, "oops!\n");
return EXIT_FAILURE;
}

@ -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 ---

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

@ -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

@ -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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save