├── .dockerignore ├── .gitattributes ├── .github ├── actions │ ├── checkout │ │ └── action.yml │ └── install-deps │ │ └── action.yml └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── RELEASING.md ├── SetjmpLongjmp.md ├── ci ├── build.sh ├── docker-build.sh ├── docker │ ├── Dockerfile │ └── README.md └── merge-artifacts.sh ├── clang.cfg ├── cmake ├── Platform │ └── WASI.cmake ├── wasi-sdk-dist.cmake ├── wasi-sdk-enable-ccache.cmake ├── wasi-sdk-sysroot.cmake └── wasi-sdk-toolchain.cmake ├── docker └── Dockerfile ├── tests ├── .gitignore ├── CMakeLists.txt ├── compile-only │ ├── CMakeLists.txt │ ├── addresses.c │ ├── printf-long-double.c │ └── test.cc ├── exit_status_zero ├── general │ ├── CMakeLists.txt │ ├── abort.c │ ├── abort.c.exit_status.expected │ ├── abort.c.stderr.expected │ ├── abort.c.stderr.expected.filter │ ├── argc_argv_main.c │ ├── argc_argv_main.c.stdout.expected │ ├── argc_argv_main.cc │ ├── argc_argv_main.cc.stdout.expected │ ├── assert-fail.c │ ├── assert-fail.c.exit_status.expected │ ├── assert-fail.c.stderr.expected │ ├── assert-fail.c.stderr.expected.filter │ ├── assert-pass.c │ ├── clocks.c │ ├── ctors_dtors.c │ ├── ctors_dtors.c.stdout.expected │ ├── ctors_dtors.cc │ ├── ctors_dtors.cc.stdout.expected │ ├── empty.c │ ├── env-absent.c │ ├── env-absent.c.stdout.expected │ ├── env.c │ ├── env.c.env │ ├── env.c.stdout.expected │ ├── environ.c │ ├── environ.c.env │ ├── environ.c.stdout.expected │ ├── getentropy.c │ ├── iostream_main.cc │ ├── iostream_main.cc.stdout.expected │ ├── main_errno.c │ ├── main_errno.c.stdout.expected │ ├── mmap.c │ ├── mmap.c.dir │ │ ├── .gitattributes │ │ └── input.txt │ ├── mmap.c.stdout.expected │ ├── no_arg_main.c │ ├── no_arg_main.c.stdout.expected │ ├── no_arg_main.cc │ ├── no_arg_main.cc.stdout.expected │ ├── opendir.c │ ├── opendir.c.dir │ │ ├── dir-symlink │ │ ├── dir │ │ │ └── file.txt │ │ ├── file-symlink │ │ └── file.md │ ├── printf-long-double-enabled.c │ ├── printf-long-double-enabled.c.stdout.expected │ ├── printf-no-float.c │ ├── printf-no-float.c.stdout.expected │ ├── printf-no-long-double.c │ ├── printf-no-long-double.c.stdout.expected │ ├── sigabrt.c │ ├── sigabrt.c.exit_status.expected │ ├── sigabrt.c.stderr.expected │ ├── sigabrt.c.stderr.expected.filter │ ├── sigabrt.c.wasm32-wasi-preview2.stderr.expected │ ├── sigabrt.c.wasm32-wasip2.stderr.expected │ ├── signals.c │ ├── signals.c.stderr.expected │ ├── signals.c.stdout.expected │ ├── stat.c │ ├── stat.c.dir │ │ └── empty │ ├── void_main.c │ ├── void_main.c.stdout.expected │ ├── void_main.cc │ └── void_main.cc.stdout.expected └── testcase.sh ├── version.py ├── wasi-sdk-p1.cmake ├── wasi-sdk-p2.cmake ├── wasi-sdk-pthread.cmake ├── wasi-sdk.cmake └── wasi-sdk.control /.dockerignore: -------------------------------------------------------------------------------- 1 | # Our docker builds do not require the submodule sources so exclude them as 2 | # they can be very big. 3 | /src 4 | .git 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Bourne-style shell scripts: These are Unix-style scripts. Don't add CR's. 2 | *.sh text eol=lf 3 | -------------------------------------------------------------------------------- /.github/actions/checkout/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Prepare wasi-sdk git directory' 2 | description: 'Prepare wasi-sdk git directory' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - run: git fetch --tags --force 8 | name: Force-fetch tags to work around actions/checkout#290 9 | shell: bash 10 | # We can't use `--depth 1` here sadly because the GNU config 11 | # submodule is not pinned to a particular tag/branch. Please 12 | # bump depth (or even better, the submodule), in case of "error: 13 | # Server does not allow request for unadvertised object" in the 14 | # future. 15 | - run: git submodule update --init --depth 64 --jobs 3 16 | shell: bash 17 | -------------------------------------------------------------------------------- /.github/actions/install-deps/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Install wasi-sdk dependencies' 2 | description: 'Install wasi-sdk dependencies' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup `wasmtime` for tests 8 | uses: bytecodealliance/actions/wasmtime/setup@v1 9 | with: 10 | version: "29.0.1" 11 | - name: Install ccache, ninja (macOS) 12 | run: brew install ccache ninja 13 | if: runner.os == 'macOS' 14 | shell: bash 15 | - name: Install ccache, ninja (Windows) 16 | run: choco install ccache ninja 17 | if: startsWith(matrix.os, 'windows') 18 | shell: bash 19 | # 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) 20 | # but the x86 ones do (see https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md) 21 | - name: Install cargo (Windows-arm) 22 | run: choco install rust 23 | if: matrix.os == 'windows-11-arm' 24 | shell: bash 25 | - name: Install ccache, ninja (Linux) 26 | run: sudo apt-get install -y ccache ninja-build 27 | if: runner.os == 'Linux' 28 | shell: bash 29 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'wasi-sdk-*' 7 | branches: 8 | - main 9 | 10 | pull_request: 11 | workflow_dispatch: 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build: 19 | name: Build ${{ matrix.artifact }} 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | include: 25 | - artifact: x86_64-linux 26 | os: ubuntu-24.04 27 | 28 | - artifact: arm64-linux 29 | os: ubuntu-22.04-arm 30 | 31 | - artifact: arm64-macos 32 | os: macos-14 33 | rust_target: aarch64-apple-darwin 34 | env: 35 | WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >- 36 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 37 | -DCMAKE_OSX_ARCHITECTURES=arm64 38 | 39 | - artifact: x86_64-macos 40 | os: macos-14 41 | rust_target: x86_64-apple-darwin 42 | env: 43 | WASI_SDK_CI_SKIP_SYSROOT: 1 44 | WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >- 45 | -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 46 | -DCMAKE_OSX_ARCHITECTURES=x86_64 47 | 48 | - artifact: x86_64-windows 49 | os: windows-2022 50 | 51 | - artifact: arm64-windows 52 | os: windows-11-arm 53 | 54 | env: ${{ matrix.env || fromJSON('{}') }} 55 | steps: 56 | - uses: actions/checkout@v4 57 | with: 58 | fetch-depth: 0 59 | - uses: ./.github/actions/checkout 60 | - uses: ./.github/actions/install-deps 61 | 62 | # Persist ccache-based caches across builds. This directory is configured 63 | # via the CCACHE_DIR env var below for ccache to use. 64 | # 65 | # Bump the prefix number to evict all previous caches and enforce a clean 66 | # build, in the unlikely case that some weird build error occur and ccache 67 | # becomes a potential suspect. 68 | - uses: actions/cache@v4 69 | id: cache-restore 70 | with: 71 | path: ${{ runner.tool_cache }}/ccache 72 | key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }} 73 | restore-keys: | 74 | 0-cache-${{ matrix.artifact }}- 75 | - run: | 76 | mkdir -p '${{ runner.tool_cache }}/ccache' 77 | echo 'CCACHE_DIR=${{ runner.tool_cache }}/ccache' >> $GITHUB_ENV 78 | shell: bash 79 | 80 | # Configure CMake flags for `ci/build.sh` as necessary for each 81 | # matrix entry. 82 | - run: | 83 | cmake_args=-DWASI_SDK_ARTIFACT=${{ matrix.artifact }} 84 | if [ "${{ matrix.rust_target }}" != "" ]; then 85 | rustup target add ${{ matrix.rust_target }} 86 | cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}" 87 | fi 88 | echo WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS="$cmake_args" >> $GITHUB_ENV 89 | shell: bash 90 | 91 | - name: Clear ccache statistics 92 | run: ccache --zero-stats 93 | 94 | - name: Build and test (macOS) 95 | run: ./ci/build.sh 96 | if: runner.os == 'macOS' 97 | 98 | - name: Build and test (Linux) 99 | run: ./ci/docker-build.sh ${{ matrix.artifact }} 100 | if: runner.os == 'Linux' 101 | 102 | # Setup the VS Developoer Prompt environment variables to explicitly use 103 | # MSVC to compile LLVM as that avoids extra runtime dependencies 104 | # msys/mingw might bring. 105 | # 106 | # As of 2024-07-22 this sha is the "v1.13.0" tag. 107 | - uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 108 | if: startsWith(matrix.os, 'windows') 109 | - name: Build and test (Windows) 110 | run: | 111 | # Delete a troublesome binary as recommended here 112 | # https://github.com/ilammy/msvc-dev-cmd?tab=readme-ov-file#name-conflicts-with-shell-bash 113 | rm /usr/bin/link 114 | # Use a shorter build directory than the default on Windows to avoid 115 | # hitting path length and command line length limits. See 116 | # WebAssembly/wasi-libc#514. Despite using a different build directory 117 | # though still move the `dist` folder to `build/dist` so the upload 118 | # step below doesn't need a windows-specific hook. 119 | ./ci/build.sh C:/wasi-sdk 120 | mkdir build 121 | cp -r C:/wasi-sdk/dist build 122 | shell: bash 123 | if: startsWith(matrix.os, 'windows') 124 | 125 | # Upload the `dist` folder from the build as the artifacts for this 126 | # runner. 127 | - name: Upload artifacts 128 | uses: actions/upload-artifact@v4 129 | with: 130 | name: ${{ format( 'dist-{0}', matrix.artifact) }} 131 | path: build/dist 132 | 133 | # Caches are persisted across runs by restoring the latest cache which 134 | # means that quite a lot of cruft can accumulate. Prune older entries that 135 | # haven't been used by this run to avoid the cache continuously getting 136 | # larger. In theory this should use `--evict-older-than $dur` where `$dur` 137 | # is the time since the start of the run, but I'm not sure how to easily 138 | # calculate that so pick something loose like one day instead. 139 | - name: Prune ccache objects 140 | run: ccache --evict-older-than 1d 141 | 142 | # Help debug ccache issues by showing what happened. 143 | - if: always() 144 | name: Show ccache statistics 145 | run: ccache --show-stats 146 | 147 | # Always save a cache, even if the build failed. This ensures that if 148 | # live-debugging via CI the build gets to pick up where it left off last 149 | # time instead of having to recreate everything each time a failure 150 | # happens. 151 | - if: always() && steps.cache-restore.outputs.cache-hit != 'true' 152 | uses: actions/cache/save@v4 153 | with: 154 | path: ${{ runner.tool_cache }}/ccache 155 | key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }} 156 | 157 | build-only-sysroot: 158 | name: Build only sysroot 159 | runs-on: ubuntu-24.04 160 | steps: 161 | - uses: actions/checkout@v4 162 | with: 163 | fetch-depth: 0 164 | - uses: ./.github/actions/checkout 165 | - uses: ./.github/actions/install-deps 166 | - run: cargo install wasm-component-ld@0.5.12 167 | - run: | 168 | cmake -G Ninja -B build -S . \ 169 | -DCMAKE_C_COMPILER=/usr/lib/llvm-18/bin/clang \ 170 | -DCMAKE_SYSTEM_NAME=WASI \ 171 | -DWASI_SDK_INCLUDE_TESTS=ON \ 172 | -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF \ 173 | -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF 174 | - run: ninja -C build 175 | - run: ctest --output-on-failure --parallel 10 --test-dir build/tests 176 | 177 | # Once all of the above matrix entries have completed this job will run and 178 | # assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot 179 | # artifacts. 180 | finalize: 181 | name: Finalize wasi-sdk artifacts 182 | needs: build 183 | runs-on: ubuntu-24.04 184 | steps: 185 | - uses: actions/checkout@v4 186 | with: 187 | fetch-depth: 0 188 | - uses: ./.github/actions/checkout 189 | 190 | # Download all artifacts from all platforms in `build`, merge them into 191 | # final wasi-sdk-* artifacts, and then upload them. 192 | - uses: actions/download-artifact@v4 193 | - run: ./ci/merge-artifacts.sh 194 | - uses: actions/upload-artifact@v4 195 | with: 196 | name: release-artifacts 197 | path: dist 198 | 199 | # Use the `wasi-sdk-*` artifacts just created to create a docker image 200 | # with a toolchain pre-installed. 201 | - uses: docker/login-action@v2 202 | with: 203 | registry: ghcr.io 204 | username: ${{ github.actor }} 205 | password: ${{ secrets.GITHUB_TOKEN }} 206 | - uses: docker/setup-qemu-action@v2 207 | - uses: docker/setup-buildx-action@v2 208 | - uses: docker/metadata-action@v4 209 | id: meta 210 | with: 211 | images: ghcr.io/${{ github.repository }} 212 | tags: | 213 | type=schedule 214 | type=ref,event=branch 215 | type=ref,event=tag 216 | type=ref,event=pr 217 | type=sha 218 | - name: Build and push wasi-sdk docker image 219 | uses: docker/build-push-action@v3 220 | with: 221 | context: . 222 | file: docker/Dockerfile 223 | push: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' }} 224 | platforms: linux/amd64,linux/arm64 225 | tags: ${{ steps.meta.outputs.tags }} 226 | labels: ${{ steps.meta.outputs.labels }} 227 | cache-from: type=gha 228 | cache-to: type=gha,mode=max 229 | 230 | - name: Publish a draft release 231 | if: startsWith(github.ref, 'refs/tags') 232 | run: gh release create --draft --prerelease --generate-notes ${{ github.ref_name }} ./dist/* 233 | env: 234 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 235 | 236 | # Test the final artifacts as-is without passing `--sysroot` or 237 | # `-resource-dir` or any extra flags. This exercises running the compiler 238 | # as-is from the distribution tarballs and ensuring that it can build and pass 239 | # all tests. 240 | test-standalone: 241 | name: Test standalone toolchain 242 | needs: build 243 | runs-on: ubuntu-24.04 244 | steps: 245 | - uses: actions/checkout@v4 246 | with: 247 | fetch-depth: 0 248 | - uses: ./.github/actions/checkout 249 | - uses: ./.github/actions/install-deps 250 | - uses: actions/download-artifact@v4 251 | with: 252 | name: dist-x86_64-linux 253 | path: dist-x86_64-linux 254 | - run: ./ci/merge-artifacts.sh 255 | - run: tar xf dist/wasi-sdk-*.tar.gz 256 | - run: | 257 | cmake -G Ninja -B build -S . \ 258 | -DWASI_SDK_INCLUDE_TESTS=ON \ 259 | -DWASI_SDK_TEST_HOST_TOOLCHAIN=ON \ 260 | -DCMAKE_TOOLCHAIN_FILE=$(ls ./wasi-sdk-*/share/cmake/wasi-sdk.cmake) 261 | - run: ninja -C build build-tests 262 | - run: ctest --output-on-failure --parallel 10 --test-dir build/tests 263 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/llvm-project"] 2 | path = src/llvm-project 3 | url = https://github.com/llvm/llvm-project 4 | [submodule "src/wasi-libc"] 5 | path = src/wasi-libc 6 | url = https://github.com/WebAssembly/wasi-libc 7 | [submodule "src/config"] 8 | path = src/config 9 | url = https://git.savannah.gnu.org/git/config.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Build logic for building both a toolchain and a sysroot for WASI. 2 | # 3 | # This top level `CMakeLists.txt` file can be used either to build a clang 4 | # toolchain or a WASI sysroot. Note that this can't be done at the same time. 5 | # A toolchain build requires a compiler for the target architecture. A 6 | # WASI sysroot build requires this previous compiler and must be runnable on 7 | # the host. 8 | 9 | cmake_minimum_required(VERSION 3.26) 10 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 11 | project(wasi-sdk) 12 | include(ExternalProject) 13 | 14 | set(WASI_SDK_TARGETS "wasm32-wasi;wasm32-wasip1;wasm32-wasip2;wasm32-wasip1-threads;wasm32-wasi-threads" 15 | CACHE STRING "List of WASI targets to build") 16 | option(WASI_SDK_BUILD_TOOLCHAIN "Build a toolchain instead of the sysroot" OFF) 17 | 18 | set(llvm_proj_dir ${CMAKE_CURRENT_SOURCE_DIR}/src/llvm-project) 19 | set(wasi_libc ${CMAKE_CURRENT_SOURCE_DIR}/src/wasi-libc) 20 | 21 | include(wasi-sdk-enable-ccache) 22 | 23 | find_program(PYTHON python3 python REQUIRED) 24 | 25 | # Set some variables based on the `version.py` script 26 | set(version_script ${CMAKE_CURRENT_SOURCE_DIR}/version.py) 27 | execute_process( 28 | COMMAND ${PYTHON} ${version_script} 29 | OUTPUT_VARIABLE wasi_sdk_version 30 | OUTPUT_STRIP_TRAILING_WHITESPACE) 31 | 32 | message(STATUS "wasi-sdk version is ${wasi_sdk_version}") 33 | 34 | # Only include one version of the build logic as pulling in both isn't 35 | # supported at this time. 36 | if(WASI_SDK_BUILD_TOOLCHAIN) 37 | include(wasi-sdk-toolchain) 38 | else() 39 | include(wasi-sdk-sysroot) 40 | endif() 41 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | 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. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | 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. 28 | 29 | 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. 30 | 31 | ## Scope 32 | 33 | 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. 34 | 35 | ## Enforcement 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | --- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. 220 | 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WASI SDK 2 | 3 | ## Quick Start 4 | 5 | [Download SDK packages here.][releases] 6 | 7 | [releases]: https://github.com/WebAssembly/wasi-sdk/releases 8 | 9 | ## About this repository 10 | 11 | This repository contains no compiler or library code itself; it uses 12 | git submodules to pull in the upstream Clang and LLVM tree, as well as the 13 | wasi-libc tree. 14 | 15 | The libc portion of this SDK is maintained in [wasi-libc]. 16 | 17 | [wasi-libc]: https://github.com/WebAssembly/wasi-libc 18 | 19 | Upstream Clang and LLVM (from 9.0 onwards) can compile for WASI out of the box, 20 | and WebAssembly support is included in them by default. So, all that's done here 21 | is to provide builds configured to set the default target and sysroot for 22 | convenience. 23 | 24 | One could also use a standard Clang installation, build a sysroot from the 25 | sources mentioned above, and compile with `--target=wasm32-wasi 26 | --sysroot=/path/to/sysroot`. In this scenario, one would also need the 27 | `libclang_rt.builtins-wasm32.a` objects available separately in the [release 28 | downloads][releases] which must be extracted into 29 | `$CLANG_INSTALL_DIR/$CLANG_VERSION/lib/wasi/`. 30 | 31 | ## Clone 32 | 33 | This repository uses git submodule, to clone it you need use the command below : 34 | 35 | ```shell script 36 | git clone --recursive https://github.com/WebAssembly/wasi-sdk.git 37 | ``` 38 | 39 | ## Requirements 40 | 41 | The Wasm-sdk's build process needs some packages : 42 | 43 | * `cmake` 44 | * `clang` 45 | * `ninja` 46 | * `python3` 47 | 48 | Please refer to your OS documentation to install those packages. 49 | 50 | ## Build 51 | 52 | Building `wasi-sdk` uses CMake and is split into two halves. First you can build 53 | the toolchain itself: 54 | 55 | ```shell script 56 | cmake -G Ninja -B build/toolchain -S . -DWASI_SDK_BUILD_TOOLCHAIN=ON -DCMAKE_INSTALL_PREFIX=build/install 57 | cmake --build build/toolchain --target install 58 | ``` 59 | 60 | When you're developing locally you may also wish to pass 61 | `-DCMAKE_CXX_COMPILER_LAUNCHER=ccache` to assist with rebuilds. Other supported 62 | CMake flags are: 63 | 64 | * `-DLLVM_CMAKE_FLAGS` - extra flags to pass to `cmake` when building 65 | LLVM/Clang. 66 | * `-DRUST_TARGET` - the specific Rust target triple to build `wasm-component-ld` 67 | for, useful for cross-compiles. 68 | 69 | The `clang` compiler should now be located at `build/install/bin/clang` but it's 70 | just a compiler, the sysroot isn't built yet. Next the second step of the build 71 | is to build the sysroot: 72 | 73 | ```shell script 74 | cmake -G Ninja -B build/sysroot -S . \ 75 | -DCMAKE_INSTALL_PREFIX=build/install \ 76 | -DCMAKE_TOOLCHAIN_FILE=build/install/share/cmake/wasi-sdk.cmake \ 77 | -DCMAKE_C_COMPILER_WORKS=ON \ 78 | -DCMAKE_CXX_COMPILER_WORKS=ON 79 | cmake --build build/sysroot --target install 80 | ``` 81 | 82 | A full toolchain should now be present at `build/install` and is ready for use 83 | in compiling WebAssembly code. Supported CMake flags are: 84 | 85 | * `-DWASI_SDK_DEBUG_PREFIX_MAKE=OFF` - disable `-fdebug-prefix-map` when 86 | building C/C++ code to use full host paths instead. 87 | * `-DWASI_SDK_INCLUDE_TESTS=ON` - used for building tests. 88 | * `-DWASI_SDK_TEST_HOST_TOOLCHAIN=ON` - test the host toolchain's wasi-libc and 89 | sysroot libraries, don't build or use fresh libraries for tests. 90 | * `-DWASI_SDK_TARGETS=..` - a list of targets to build, by default all WASI 91 | targets are compiled. 92 | * `-DWASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR=ON` - install compiler-rt 93 | to the compiler's resource directory. might be convenient if you want to 94 | use the toolchain (eg. `./build/install/bin/clang`) in-place. 95 | 96 | If you'd like to build distribution artifacts you can use the `dist` target like 97 | so: 98 | 99 | ```shell script 100 | cmake --build build/toolchain --target dist 101 | cmake --build build/sysroot --target dist 102 | ``` 103 | 104 | Tarballs will be created under `build/toolchain/dist` and `build/sysroot/dist`. 105 | Note that these are separate tarballs for the toolchain and sysroot. To create a 106 | single tarball for the entire SDK you'll first want to copy all tarballs into a 107 | new folder and then run the `./ci/merge-artifacts.sh` script: 108 | 109 | ```shell script 110 | mkdir dist-my-platform 111 | cp build/toolchain/dist/* build/sysroot/dist/* dist-my-platform 112 | ./ci/merge-artifacts.sh 113 | ``` 114 | 115 | This will produce `dist/wasi-sdk-*.tar.gz` which is the same as the release 116 | artifacts for this repository. 117 | 118 | Finally you can additionally bundle many of the above steps, minus 119 | `merge-artifact.sh` by using the CI script to perform both the toolchain and 120 | sysroot build: 121 | 122 | ```shell script 123 | ./ci/build.sh 124 | ``` 125 | 126 | The built package can be found into `build/dist` directory. 127 | For releasing a new version of the package on GitHub, 128 | see [RELEASING.md](RELEASING.md). 129 | 130 | ## Install 131 | 132 | A typical installation from the release binaries might look like the following: 133 | 134 | ```shell script 135 | WASI_OS=linux 136 | WASI_ARCH=x86_64 # or 'arm64' if running on arm64 host 137 | WASI_VERSION=25 138 | WASI_VERSION_FULL=${WASI_VERSION}.0 139 | wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz 140 | tar xvf wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS}.tar.gz 141 | ``` 142 | 143 | ## Use 144 | 145 | Use the clang installed in the `wasi-sdk` directory: 146 | 147 | ```shell script 148 | WASI_SDK_PATH=`pwd`/wasi-sdk-${WASI_VERSION_FULL}-${WASI_ARCH}-${WASI_OS} 149 | CC="${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot" 150 | $CC foo.c -o foo.wasm 151 | ``` 152 | 153 | Note: `${WASI_SDK_PATH}/share/wasi-sysroot` contains the WASI-specific 154 | includes/libraries/etc. The `--sysroot=...` option is not necessary if 155 | `WASI_SDK_PATH` is `/opt/wasi-sdk`. For troubleshooting, one can replace the 156 | `--sysroot` path with a manual build of [wasi-libc]. 157 | 158 | ### Integrating with a CMake build system 159 | 160 | Use a toolchain file to setup the *wasi-sdk* platform. 161 | 162 | ``` 163 | $ cmake -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk.cmake ... 164 | ``` 165 | 166 | or the *wasi-sdk-thread* platform 167 | 168 | ``` 169 | $ cmake -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_PATH}/share/cmake/wasi-sdk-pthread.cmake ... 170 | ``` 171 | 172 | ## Notes for Autoconf 173 | 174 | [Autoconf] 2.70 now [recognizes WASI]. 175 | 176 | [Autoconf]: https://www.gnu.org/software/autoconf/autoconf.html 177 | [recognizes WASI]: https://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=blob;f=build-aux/config.sub;h=19c9553b1825cafb182115513bc628e0ee801bd0;hb=97fbc5c184acc6fa591ad094eae86917f03459fa#l1723 178 | 179 | For convenience when building packages that aren't yet updated, updated 180 | config.sub and config.guess files are installed at `share/misc/config.*` 181 | in the install directory. 182 | 183 | ## Docker Image 184 | 185 | We provide a [docker image] including WASI SDK that can be used for building 186 | projects without a separate installation of the SDK. Autotools, CMake, and Ninja 187 | are included in this image, and standard environment variables are set to use 188 | WASI SDK for building. 189 | 190 | [docker image]: https://github.com/WebAssembly/wasi-sdk/pkgs/container/wasi-sdk 191 | 192 | For example, this command can build a make-based project with the Docker 193 | image. 194 | 195 | ``` 196 | docker run -v `pwd`:/src -w /src ghcr.io/webassembly/wasi-sdk make 197 | ``` 198 | 199 | Take note of the [notable limitations](#notable-limitations) below when 200 | building projects, for example many projects will need threads support 201 | disabled in a configure step before building with WASI SDK. 202 | 203 | ## Notable Limitations 204 | 205 | This repository does not yet support __C++ exceptions__. C++ code is supported 206 | only with -fno-exceptions for now. 207 | Work on support for [exception handling] is underway at the 208 | language level which will support the features. 209 | 210 | [exception handling]: https://github.com/WebAssembly/exception-handling/ 211 | 212 | See [C setjmp/longjmp support] about setjmp/longjmp support. 213 | 214 | [C setjmp/longjmp support]: SetjmpLongjmp.md 215 | 216 | This repository experimentally supports __threads__ with 217 | `--target=wasm32-wasi-threads`. It uses WebAssembly's [threads] primitives 218 | (atomics, `wait`/`notify`, shared memory) and [wasi-threads] for spawning 219 | threads. Note: this is experimental — do not expect long-term stability! 220 | 221 | [threads]: https://github.com/WebAssembly/threads 222 | [wasi-threads]: https://github.com/WebAssembly/wasi-threads 223 | 224 | This repository does not yet support __dynamic libraries__. While there are 225 | [some efforts] to design a system for dynamic libraries in wasm, it is still in 226 | development and not yet generally usable. 227 | 228 | [some efforts]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md 229 | 230 | There is no support for __networking__. It is a goal of WASI to support 231 | networking in the future though. 232 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | To publish a new version of `wasi-sdk` as a GitHub release: 4 | 5 | 1. Tag a commit with an annotated tag. Note that this must be an annotated tag, 6 | not a lightweight tag, so that `version.py` can use it for calculating the 7 | package version (use `git show wasi-sdk-...` to show other tag messages). 8 | Note that you may need to clear the repository cache to avoid problems with 9 | cached artifacts [^cache]. 10 | 11 | ```shell script 12 | TAG=wasi-sdk-1 13 | git tag -a $TAG 14 | git push origin $TAG 15 | ``` 16 | 17 | 2. Wait for the CI build of the tag to finish. This will automatically publish 18 | a draft pre-release to [GitHub Releases](https://github.com/WebAssembly/wasi-sdk/releases). 19 | Release notes are auto-generated and should be reviewed for accuracy. Once 20 | everything looks good manually publish the release through the GitHub UI. 21 | 22 | 3. Remember to tag the wasi-libc repository with the new `$TAG` version. 23 | 24 | ```shell script 25 | git submodule status -- src/wasi-libc # grab $WASI_LIBC_COMMIT from the output 26 | cd $WASI_LIBC_REPO_DIR 27 | git tag $TAG $WASI_LIBC_COMMIT 28 | git push origin $TAG 29 | ``` 30 | 31 | [^cache]: Here is an example of how to clear a cache with the GitHub CLI: 32 | 33 | ```shell script 34 | URL=/repos/WebAssembly/wasi-sdk/actions/caches 35 | gh api $URL -q '.actions_caches[].id' \ 36 | | xargs -I {} gh api --method DELETE $URL/{} 37 | ``` 38 | -------------------------------------------------------------------------------- /SetjmpLongjmp.md: -------------------------------------------------------------------------------- 1 | # C setjmp/longjmp support 2 | 3 | WASI-SDK provides basic setjmp/longjmp support. 4 | 5 | Note that it's still under active development and may change in 6 | future versions. 7 | 8 | ## Build an application 9 | 10 | To build an application using setjmp/longjmp, you need two things: 11 | 12 | * Enable the necessary LLVM translation (`-mllvm -wasm-enable-sjlj`) 13 | 14 | * Link the setjmp library (`-lsetjmp`) 15 | 16 | ### Example without LTO 17 | 18 | ```shell 19 | clang -Os -mllvm -wasm-enable-sjlj -o your_app.legacy.wasm your_app.c -lsetjmp 20 | ``` 21 | 22 | ### Example with LTO 23 | 24 | ```shell 25 | clang -Os -flto=full -mllvm -wasm-enable-sjlj -Wl,-mllvm,-wasm-enable-sjlj -o your_app.legacy.wasm your_app.c -lsetjmp 26 | ``` 27 | 28 | ## Run an application 29 | 30 | To run the application built as in the previous section, 31 | you need to use a runtime with [exception handling proposal] support. 32 | 33 | Unfortunately, there are two incompatible versions of 34 | [exception handling proposal], which is commonly implemented by runtimes. 35 | 36 | * The latest version with `exnref` 37 | 38 | * The legacy [phase3] version 39 | 40 | ### Example with the latest exception handling proposal 41 | 42 | By default, the current version of WASI-SDK produces the legacy 43 | "phase3" version of [exception handling proposal] instructions. 44 | 45 | You can tell the llvm to produce the latest version of proposal by 46 | specifying `-mllvm -wasm-use-legacy-eh=false`. This is expected 47 | to be the default in a future version. 48 | 49 | Alternatively, you can use binaryen `wasm-opt` command to convert 50 | existing modules from the legacy "phase3" version to the "exnref" version. 51 | 52 | ```shell 53 | wasm-opt --translate-to-exnref -all -o your_app.wasm your_app.legacy.wasm 54 | ``` 55 | 56 | Then you can run it with a runtime supporting the "exnref" version of 57 | the proposal. 58 | [toywasm] is an example of such runtimes. 59 | 60 | ```shell 61 | toywasm --wasi your_app.wasm 62 | ``` 63 | (You may need to enable the support with `-D TOYWASM_ENABLE_WASM_EXCEPTION_HANDLING=ON`.) 64 | 65 | ### Example with the legacy phase3 exception handling proposal 66 | 67 | If your runtime supports the legacy [phase3] version of 68 | [exception handling proposal], which is the same version as what WASI-SDK 69 | currently produces by default, you can run the produced module as it is. 70 | 71 | For example, the classic interpreter of [wasm-micro-runtime] is 72 | one of such runtimes. 73 | 74 | ```shell 75 | iwasm your_app.legacy.wasm 76 | ``` 77 | (You may need to enable the support with `-D WAMR_BUILD_EXCE_HANDLING=1 -D WAMR_BUILD_FAST_INTERP=0`.) 78 | 79 | [exception handling proposal]: https://github.com/WebAssembly/exception-handling/ 80 | [phase3]: https://github.com/WebAssembly/exception-handling/tree/main/proposals/exception-handling/legacy 81 | [toywasm]: https://github.com/yamt/toywasm 82 | [wasm-micro-runtime]: https://github.com/bytecodealliance/wasm-micro-runtime 83 | -------------------------------------------------------------------------------- /ci/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Build logic executed in CI. This is intentionally kept relatively minimal to 4 | # one day not live in bash to have a bash-less build on Windows. For now though 5 | # this will unconditionally build a toolchain and then optionally build a 6 | # sysroot. Builders which can't actually execute the toolchain they produce 7 | # skip the sysroot step below. 8 | 9 | set -ex 10 | 11 | # Optionally allow the first argument to this script to be the install 12 | # location. 13 | if [ "$1" = "" ]; then 14 | build_dir=`pwd`/build 15 | else 16 | build_dir="$1" 17 | fi 18 | 19 | cmake -G Ninja -B $build_dir/toolchain -S . \ 20 | -DWASI_SDK_BUILD_TOOLCHAIN=ON \ 21 | -DCMAKE_BUILD_TYPE=MinSizeRel \ 22 | "-DCMAKE_INSTALL_PREFIX=$build_dir/install" \ 23 | $WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS \ 24 | "-DLLVM_CMAKE_FLAGS=$WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS" 25 | ninja -C $build_dir/toolchain install dist -v 26 | 27 | mv $build_dir/toolchain/dist $build_dir/dist 28 | 29 | if [ "$WASI_SDK_CI_SKIP_SYSROOT" = "1" ]; then 30 | exit 0 31 | fi 32 | 33 | # Use the just-built toolchain and its `CMAKE_TOOLCHAIN_FILE` to build a 34 | # sysroot. 35 | cmake -G Ninja -B $build_dir/sysroot -S . \ 36 | "-DCMAKE_TOOLCHAIN_FILE=$build_dir/install/share/cmake/wasi-sdk.cmake" \ 37 | -DCMAKE_C_COMPILER_WORKS=ON \ 38 | -DCMAKE_CXX_COMPILER_WORKS=ON \ 39 | -DWASI_SDK_INCLUDE_TESTS=ON \ 40 | "-DCMAKE_INSTALL_PREFIX=$build_dir/install" 41 | ninja -C $build_dir/sysroot install dist -v 42 | 43 | mv $build_dir/sysroot/dist/* $build_dir/dist 44 | 45 | if [ "$WASI_SDK_CI_SKIP_TESTS" = "1" ]; then 46 | exit 0 47 | fi 48 | 49 | # Run tests to ensure that the sysroot works. 50 | ctest --output-on-failure --parallel 10 --test-dir $build_dir/sysroot/tests \ 51 | --timeout 60 52 | -------------------------------------------------------------------------------- /ci/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This is a helper script invoked from CI which will execute the `ci/build.sh` 4 | # script within a docker container. This contain is built using the Dockerfile located at `ci/docker/Dockerfile` 5 | # This container is then used to execute `ci/build.sh`. 6 | 7 | set -e 8 | 9 | if [ "$1" = "" ]; then 10 | echo "Usage: $0 " 11 | echo "" 12 | echo "example: $0 x86_64-linux" 13 | exit 1 14 | fi 15 | 16 | set -x 17 | 18 | # Build the Docker imager 19 | docker build --tag wasi-sdk-builder ci/docker 20 | 21 | # Perform the build in `/src`. The current directory is mounted read-write at 22 | # this location as well. To ensure that container-created files are reasonable 23 | # on the host as well the `--user` is passed to configure various permissions. 24 | args="--workdir /src --volume `pwd`:/src:Z" 25 | args="$args --user $(id -u):$(id -g)" 26 | 27 | # Persist the ccache directory on the host to ensure repeated runs/debugging 28 | # of this container don't take forever. Also enables caching in CI. 29 | ccache_dir=$CCACHE_DIR 30 | if [ "$ccache_dir" = "" ]; then 31 | ccache_dir=$HOME/.ccache 32 | fi 33 | args="$args --volume $ccache_dir:/ccache:Z --env CCACHE_DIR=/ccache" 34 | 35 | # Inherit some tools from the host into this container. This ensures that the 36 | # decision made on CI of what versions to use is the canonical source of truth 37 | # for theset ools 38 | args="$args --volume `rustc --print sysroot`:/rustc:ro" 39 | args="$args --volume $(dirname $(which wasmtime)):/wasmtime:ro" 40 | 41 | # Pass through some env vars that `build.sh` reads 42 | args="$args --env WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS" 43 | args="$args --env WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS" 44 | args="$args --env WASI_SDK_CI_SKIP_SYSROOT" 45 | args="$args --env WASI_SDK_CI_SKIP_TESTS" 46 | 47 | # Before running `ci/build.sh` set up some rust/PATH related info to use what 48 | # was just mounted above, and then execute the build. 49 | docker run \ 50 | $args \ 51 | --tty \ 52 | --init \ 53 | wasi-sdk-builder \ 54 | bash -c 'CARGO_HOME=/tmp/cargo-home PATH=$PATH:/rustc/bin:/wasmtime exec ci/build.sh' 55 | -------------------------------------------------------------------------------- /ci/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use a relatively old/stable distro here to maximize the supported platforms 2 | # and avoid depending on more recent version of, say, libc. 3 | # Here we choose Ubuntu 20.04. 4 | 5 | FROM ubuntu:20.04 6 | 7 | # Various build tooling and such necessary to build LLVM and a wasi-sysroot 8 | RUN apt-get update \ 9 | && apt-get install -y --no-install-recommends \ 10 | ccache \ 11 | curl \ 12 | ca-certificates \ 13 | build-essential \ 14 | clang \ 15 | python3 \ 16 | git \ 17 | unzip \ 18 | xz-utils 19 | 20 | # Install a more recent version of CMake than what 18.04 has since that's what 21 | # LLVM requires. 22 | RUN ARCH=$(uname -m) \ 23 | && curl -sSLO https://github.com/Kitware/CMake/releases/download/v3.29.5/cmake-3.29.5-linux-${ARCH}.tar.gz \ 24 | && tar xf cmake-3.29.5-linux-${ARCH}.tar.gz \ 25 | && rm cmake-3.29.5-linux-${ARCH}.tar.gz \ 26 | && mkdir -p /opt \ 27 | && mv cmake-3.29.5-linux-${ARCH} /opt/cmake 28 | 29 | ENV PATH /opt/cmake/bin:$PATH 30 | 31 | # As with CMake install a later version of Ninja than waht 18.04 has. 32 | RUN ARCH=$(uname -m) \ 33 | && if [ "$ARCH" = "aarch64" ]; then SUFFIX=-aarch64; fi \ 34 | && curl -sSL -o ninja.zip https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux${SUFFIX}.zip \ 35 | && unzip ninja.zip \ 36 | && rm *.zip \ 37 | && mv ninja /opt/cmake/bin 38 | 39 | # Tell programs to cache in a location that both isn't a `--volume` mounted root 40 | # and isn't `/root` in the container as that won't be writable during the build. 41 | ENV XDG_CACHE_HOME /tmp/cache 42 | -------------------------------------------------------------------------------- /ci/docker/README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This folder contains the docker images that are used in CI to build the wasi-sdk 4 | release toolchains. Docker is used to intentionally use older Linux 5 | distributions to build the toolchain to have a more maximal set of glibc 6 | compatibility. 7 | 8 | These images are intended to be used on an x86\_64 host. Images start from the 9 | `Dockerfile.common` file and then layer on target-specific 10 | toolchains/options/etc as necessary. 11 | -------------------------------------------------------------------------------- /ci/merge-artifacts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Helper script executed on CI once all builds have completed. This takes 4 | # `wasi-toolchain-*` artifacts and `wasi-sysroot-*` artifacts and merges 5 | # them together into a single `wasi-sdk-*` artifact. Toolchains which don't 6 | # have a sysroot that they themselves built use a sysroot from the x86_64-linux 7 | # toolchain. 8 | 9 | set -ex 10 | 11 | rm -rf dist 12 | mkdir dist 13 | version=$(./version.py) 14 | 15 | make_deb() { 16 | build=$1 17 | dir=$2 18 | 19 | if ! command -v dpkg-deb >/dev/null; then 20 | return 21 | fi 22 | 23 | case $build in 24 | dist-x86_64-linux) deb_arch=amd64 ;; 25 | dist-arm64-linux) deb_arch=arm64 ;; 26 | *) 27 | echo "unknown build $build" 28 | exit 1 29 | esac 30 | 31 | mkdir dist/pkg 32 | mkdir dist/pkg/opt 33 | mkdir dist/pkg/DEBIAN 34 | sed s/VERSION/$version/ wasi-sdk.control | \ 35 | sed s/ARCH/$deb_arch/ > dist/pkg/DEBIAN/control 36 | cp -R $dir dist/pkg/opt/wasi-sdk 37 | deb_name=$(echo $(basename $dir) | sed 's/.tar.gz//') 38 | (cd dist && dpkg-deb -b pkg $deb_name.deb) 39 | rm -rf dist/pkg 40 | } 41 | 42 | for build in dist-*; do 43 | toolchain=`ls $build/wasi-toolchain-*` 44 | if [ -f $build/wasi-sysroot-* ]; then 45 | sysroot=`ls $build/wasi-sysroot-*` 46 | else 47 | sysroot=`ls dist-x86_64-linux/wasi-sysroot-*` 48 | fi 49 | if [ -f $build/libclang_rt* ]; then 50 | compiler_rt=`ls $build/libclang_rt*` 51 | else 52 | compiler_rt=`ls dist-x86_64-linux/libclang_rt*` 53 | fi 54 | 55 | sdk_dir=`basename $toolchain | sed 's/.tar.gz//' | sed s/toolchain/sdk/` 56 | mkdir dist/$sdk_dir 57 | 58 | # Start with the toolchain and then overlay the sysroot into 59 | # `share/wasi-sysroot`, the default sysroot. 60 | tar xf $toolchain -C dist/$sdk_dir --strip-components 1 61 | mkdir -p dist/$sdk_dir/share/wasi-sysroot 62 | tar xf $sysroot -C dist/$sdk_dir/share/wasi-sysroot --strip-components 1 63 | mv dist/$sdk_dir/share/wasi-sysroot/VERSION dist/$sdk_dir 64 | 65 | # Setup the compiler-rt library for wasi,wasip1,wasip2 66 | rtlibdir=$(dirname $(find dist/$sdk_dir/lib -name include))/lib 67 | mkdir -p $rtlibdir/wasi 68 | tar xf $compiler_rt -C $rtlibdir/wasi --strip-components 1 69 | cp -r $rtlibdir/wasi $rtlibdir/wasip1 70 | cp -r $rtlibdir/wasi $rtlibdir/wasip2 71 | 72 | tar czf dist/$sdk_dir.tar.gz -C dist $sdk_dir 73 | 74 | if echo $build | grep -q linux; then 75 | make_deb $build dist/$sdk_dir 76 | fi 77 | rm -rf dist/$sdk_dir 78 | done 79 | 80 | # In addition to `wasi-sdk-*` also preserve artifacts for just the sysroot 81 | # and just compiler-rt. 82 | if [ -d dist-x86_64-linux ]; then 83 | cp dist-x86_64-linux/wasi-sysroot-* dist 84 | cp dist-x86_64-linux/libclang_rt* dist 85 | fi 86 | -------------------------------------------------------------------------------- /clang.cfg: -------------------------------------------------------------------------------- 1 | --sysroot=/../share/wasi-sysroot 2 | -------------------------------------------------------------------------------- /cmake/Platform/WASI.cmake: -------------------------------------------------------------------------------- 1 | set(WASI 1) 2 | -------------------------------------------------------------------------------- /cmake/wasi-sdk-dist.cmake: -------------------------------------------------------------------------------- 1 | # Helper function to create tarballs for wasi-sdk. 2 | # 3 | # The `target` is the name of the CMake target to create for the creation of 4 | # this tarball. The `tarball` argument is where the final tarball will be 5 | # located. The name of the tarball is also used for the name of the root folder 6 | # in the tarball. The `dir` argument is is the directory that will get packaged 7 | # up within the tarball. 8 | function(wasi_sdk_add_tarball target tarball dir) 9 | cmake_path(GET tarball PARENT_PATH tarball_dir) 10 | 11 | # Run STEM twice to chop of both `.gz` and `.tar` in `.tar.gz` 12 | cmake_path(GET tarball STEM LAST_ONLY tarball_stem) 13 | cmake_path(GET tarball_stem STEM LAST_ONLY tarball_stem) 14 | 15 | if(CMAKE_SYSTEM_NAME MATCHES Windows) 16 | # Copy the contents of symlinks on Windows to avoid dealing with symlink 17 | set(copy_dir ${CMAKE_COMMAND} -E copy_directory ${dir} ${tarball_stem}) 18 | else() 19 | # ... but on non-Windows copy symlinks themselves to cut down on 20 | # distribution size. 21 | set(copy_dir cp -R ${dir} ${tarball_stem}) 22 | endif() 23 | 24 | add_custom_command( 25 | OUTPUT ${tarball} 26 | # First copy the directory under a different name, the filestem of the 27 | # tarball. 28 | COMMAND ${copy_dir} 29 | # Next use CMake to create the tarball itself 30 | COMMAND ${CMAKE_COMMAND} -E tar cfz ${tarball} ${tarball_stem} 31 | # Finally delete the temporary directory created above. 32 | COMMAND ${CMAKE_COMMAND} -E rm -rf ${tarball_stem} 33 | WORKING_DIRECTORY ${tarball_dir} 34 | COMMENT "Creating ${tarball}..." 35 | ) 36 | add_custom_target(${target} DEPENDS ${tarball}) 37 | endfunction() 38 | -------------------------------------------------------------------------------- /cmake/wasi-sdk-enable-ccache.cmake: -------------------------------------------------------------------------------- 1 | # Helper module to auto-enable ccache if detected. 2 | 3 | find_program(CCACHE ccache) 4 | 5 | option(WASI_SDK_DISABLE_CCACHE "Force disable ccache even if it's found" OFF) 6 | 7 | if(NOT CMAKE_C_COMPILER_LAUNCHER) 8 | if(NOT WASI_SDK_DISABLE_CCACHE) 9 | if(CCACHE) 10 | set(CMAKE_C_COMPILER_LAUNCHER ccache) 11 | set(CMAKE_CXX_COMPILER_LAUNCHER ccache) 12 | message(STATUS "Auto-enabling ccache") 13 | else() 14 | message(STATUS "Failed to auto-enable ccache, not found on system") 15 | endif() 16 | endif() 17 | endif() 18 | -------------------------------------------------------------------------------- /cmake/wasi-sdk-sysroot.cmake: -------------------------------------------------------------------------------- 1 | # Build logic for building a sysroot for wasi-sdk which includes compiler-rt, 2 | # wasi-libc, libcxx, and libcxxabi. 3 | 4 | if(NOT CMAKE_BUILD_TYPE) 5 | set(CMAKE_BUILD_TYPE RelWithDebInfo) 6 | endif() 7 | 8 | if(NOT CMAKE_C_COMPILER_ID MATCHES Clang) 9 | message(FATAL_ERROR "C compiler ${CMAKE_C_COMPILER} is not `Clang`, it is ${CMAKE_C_COMPILER_ID}") 10 | endif() 11 | 12 | set(minimum_clang_required 18.0.0) 13 | 14 | if(CMAKE_C_COMPILER_VERSION VERSION_LESS ${minimum_clang_required}) 15 | message(FATAL_ERROR "compiler version ${CMAKE_C_COMPILER_VERSION} is less than the required version ${minimum_clang_required}") 16 | endif() 17 | 18 | message(STATUS "Found executable for `nm`: ${CMAKE_NM}") 19 | message(STATUS "Found executable for `ar`: ${CMAKE_AR}") 20 | 21 | find_program(MAKE make REQUIRED) 22 | 23 | option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" ON) 24 | option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF) 25 | option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF) 26 | option(WASI_SDK_LTO "Whether or not to build LTO assets" ON) 27 | 28 | set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) 29 | set(wasi_sysroot ${wasi_tmp_install}/share/wasi-sysroot) 30 | set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir) 31 | 32 | if(WASI_SDK_DEBUG_PREFIX_MAP) 33 | add_compile_options( 34 | -fdebug-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=wasisdk://v${wasi_sdk_version}) 35 | endif() 36 | 37 | # Default arguments for builds of cmake projects (mostly LLVM-based) to forward 38 | # along much of our own configuration into these projects. 39 | set(default_cmake_args 40 | -DCMAKE_SYSTEM_NAME=WASI 41 | -DCMAKE_SYSTEM_VERSION=1 42 | -DCMAKE_SYSTEM_PROCESSOR=wasm32 43 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 44 | -DCMAKE_AR=${CMAKE_AR} 45 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 46 | -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} 47 | -DCMAKE_C_COMPILER_WORKS=ON 48 | -DCMAKE_CXX_COMPILER_WORKS=ON 49 | -DCMAKE_SYSROOT=${wasi_sysroot} 50 | -DCMAKE_MODULE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/cmake 51 | # CMake detects this based on `CMAKE_C_COMPILER` alone and when that compiler 52 | # is just a bare "clang" installation then it can mistakenly deduce that this 53 | # feature is supported when it's not actually supported for WASI targets. 54 | # Currently `wasm-ld` does not support the linker flag for this. 55 | -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF 56 | -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF) 57 | 58 | if(CMAKE_C_COMPILER_LAUNCHER) 59 | list(APPEND default_cmake_args -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}) 60 | endif() 61 | if(CMAKE_CXX_COMPILER_LAUNCHER) 62 | list(APPEND default_cmake_args -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}) 63 | endif() 64 | 65 | # ============================================================================= 66 | # compiler-rt build logic 67 | # ============================================================================= 68 | 69 | ExternalProject_Add(compiler-rt-build 70 | SOURCE_DIR "${llvm_proj_dir}/compiler-rt" 71 | CMAKE_ARGS 72 | ${default_cmake_args} 73 | -DCOMPILER_RT_BAREMETAL_BUILD=ON 74 | -DCOMPILER_RT_BUILD_XRAY=OFF 75 | -DCOMPILER_RT_INCLUDE_TESTS=OFF 76 | -DCOMPILER_RT_HAS_FPIC_FLAG=OFF 77 | -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON 78 | -DCOMPILER_RT_BUILD_SANITIZERS=OFF 79 | -DCOMPILER_RT_BUILD_XRAY=OFF 80 | -DCOMPILER_RT_BUILD_LIBFUZZER=OFF 81 | -DCOMPILER_RT_BUILD_PROFILE=OFF 82 | -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF 83 | -DCOMPILER_RT_BUILD_MEMPROF=OFF 84 | -DCOMPILER_RT_BUILD_ORC=OFF 85 | -DCOMPILER_RT_BUILD_GWP_ASAN=OFF 86 | -DCMAKE_C_COMPILER_TARGET=wasm32-wasi 87 | -DCOMPILER_RT_OS_DIR=wasi 88 | -DCMAKE_INSTALL_PREFIX=${wasi_resource_dir} 89 | EXCLUDE_FROM_ALL ON 90 | USES_TERMINAL_CONFIGURE ON 91 | USES_TERMINAL_BUILD ON 92 | USES_TERMINAL_INSTALL ON 93 | ) 94 | 95 | # In addition to the default installation of `compiler-rt` itself also copy 96 | # around some headers and make copies of the `wasi` directory as `wasip1` and 97 | # `wasip2` 98 | execute_process( 99 | COMMAND ${CMAKE_C_COMPILER} -print-resource-dir 100 | OUTPUT_VARIABLE clang_resource_dir 101 | OUTPUT_STRIP_TRAILING_WHITESPACE) 102 | add_custom_target(compiler-rt-post-build 103 | # The `${wasi_resource_dir}` folder is going to get used as `-resource-dir` 104 | # for future compiles. Copy the host compiler's own headers into this 105 | # directory to ensure that all host-defined headers all work as well. 106 | COMMAND ${CMAKE_COMMAND} -E copy_directory 107 | ${clang_resource_dir}/include ${wasi_resource_dir}/include 108 | 109 | # Copy the `lib/wasi` folder to `libc/wasi{p1,p2}` to ensure that those 110 | # OS-strings also work for looking up the compiler-rt.a file. 111 | COMMAND ${CMAKE_COMMAND} -E copy_directory 112 | ${wasi_resource_dir}/lib/wasi ${wasi_resource_dir}/lib/wasip1 113 | COMMAND ${CMAKE_COMMAND} -E copy_directory 114 | ${wasi_resource_dir}/lib/wasi ${wasi_resource_dir}/lib/wasip2 115 | 116 | COMMENT "finalizing compiler-rt installation" 117 | ) 118 | add_dependencies(compiler-rt-post-build compiler-rt-build) 119 | 120 | add_custom_target(compiler-rt DEPENDS compiler-rt-build compiler-rt-post-build) 121 | 122 | 123 | # ============================================================================= 124 | # wasi-libc build logic 125 | # ============================================================================= 126 | 127 | function(define_wasi_libc_sub target target_suffix lto) 128 | set(build_dir ${CMAKE_CURRENT_BINARY_DIR}/wasi-libc-${target}${target_suffix}) 129 | 130 | if(${target} MATCHES threads) 131 | if(lto) 132 | set(extra_make_flags LTO=full THREAD_MODEL=posix) 133 | else() 134 | set(extra_make_flags THREAD_MODEL=posix) 135 | endif() 136 | elseif(${target} MATCHES p2) 137 | if(lto) 138 | set(extra_make_flags LTO=full WASI_SNAPSHOT=p2 default) 139 | else() 140 | set(extra_make_flags WASI_SNAPSHOT=p2 default libc_so) 141 | endif() 142 | else() 143 | if(lto) 144 | set(extra_make_flags LTO=full default) 145 | else() 146 | set(extra_make_flags default libc_so) 147 | endif() 148 | endif() 149 | 150 | string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER) 151 | get_property(directory_cflags DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS) 152 | list(APPEND directory_cflags -resource-dir ${wasi_resource_dir}) 153 | set(extra_cflags_list 154 | "${CMAKE_C_FLAGS} ${directory_cflags} ${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") 155 | list(JOIN extra_cflags_list " " extra_cflags) 156 | 157 | ExternalProject_Add(wasi-libc-${target}${target_suffix}-build 158 | # Currently wasi-libc doesn't support out-of-tree builds so feign a 159 | # "download command" which copies the source tree to a different location 160 | # so out-of-tree builds are supported. 161 | DOWNLOAD_COMMAND 162 | ${CMAKE_COMMAND} -E copy_directory ${wasi_libc} ${build_dir} 163 | SOURCE_DIR "${build_dir}" 164 | CONFIGURE_COMMAND "" 165 | BUILD_COMMAND 166 | ${MAKE} -j8 -C ${build_dir} 167 | CC=${CMAKE_C_COMPILER} 168 | AR=${CMAKE_AR} 169 | NM=${CMAKE_NM} 170 | SYSROOT=${wasi_sysroot} 171 | EXTRA_CFLAGS=${extra_cflags} 172 | TARGET_TRIPLE=${target} 173 | ${extra_make_flags} 174 | INSTALL_COMMAND "" 175 | DEPENDS compiler-rt 176 | EXCLUDE_FROM_ALL ON 177 | USES_TERMINAL_CONFIGURE ON 178 | USES_TERMINAL_BUILD ON 179 | USES_TERMINAL_INSTALL ON 180 | ) 181 | endfunction() 182 | 183 | function(define_wasi_libc target) 184 | define_wasi_libc_sub (${target} "" OFF) 185 | if(WASI_SDK_LTO) 186 | define_wasi_libc_sub (${target} "-lto" ON) 187 | endif() 188 | 189 | add_custom_target(wasi-libc-${target} 190 | DEPENDS wasi-libc-${target}-build $<$:wasi-libc-${target}-lto-build>) 191 | endfunction() 192 | 193 | foreach(target IN LISTS WASI_SDK_TARGETS) 194 | define_wasi_libc(${target}) 195 | endforeach() 196 | 197 | # ============================================================================= 198 | # libcxx build logic 199 | # ============================================================================= 200 | 201 | execute_process( 202 | COMMAND ${CMAKE_C_COMPILER} -dumpversion 203 | OUTPUT_VARIABLE llvm_version 204 | OUTPUT_STRIP_TRAILING_WHITESPACE) 205 | 206 | function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix) 207 | if(${target} MATCHES threads) 208 | set(threads ON) 209 | set(pic OFF) 210 | set(target_flags -pthread) 211 | else() 212 | set(threads OFF) 213 | set(pic ON) 214 | set(target_flags "") 215 | endif() 216 | if(${target_suffix} MATCHES lto) 217 | set(pic OFF) 218 | endif() 219 | list(APPEND target_flags ${extra_target_flags}) 220 | 221 | set(runtimes "libcxx;libcxxabi") 222 | 223 | get_property(dir_compile_opts DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS) 224 | get_property(dir_link_opts DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY LINK_OPTIONS) 225 | set(extra_flags 226 | ${target_flags} 227 | --target=${target} 228 | ${dir_compile_opts} 229 | ${dir_link_opts} 230 | --sysroot ${wasi_sysroot} 231 | -resource-dir ${wasi_resource_dir}) 232 | 233 | set(extra_cflags_list ${CMAKE_C_FLAGS} ${extra_flags}) 234 | list(JOIN extra_cflags_list " " extra_cflags) 235 | set(extra_cxxflags_list ${CMAKE_CXX_FLAGS} ${extra_flags}) 236 | list(JOIN extra_cxxflags_list " " extra_cxxflags) 237 | 238 | ExternalProject_Add(libcxx-${target}${target_suffix}-build 239 | SOURCE_DIR ${llvm_proj_dir}/runtimes 240 | CMAKE_ARGS 241 | ${default_cmake_args} 242 | # Ensure headers are installed in a target-specific path instead of a 243 | # target-generic path. 244 | -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target} 245 | -DCMAKE_STAGING_PREFIX=${wasi_sysroot} 246 | -DCMAKE_POSITION_INDEPENDENT_CODE=${pic} 247 | -DCXX_SUPPORTS_CXX11=ON 248 | -DLIBCXX_ENABLE_THREADS:BOOL=${threads} 249 | -DLIBCXX_HAS_PTHREAD_API:BOOL=${threads} 250 | -DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF 251 | -DLIBCXX_BUILD_EXTERNAL_THREAD_LIBRARY:BOOL=OFF 252 | -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF 253 | -DLLVM_COMPILER_CHECKED=ON 254 | -DLIBCXX_ENABLE_SHARED:BOOL=${pic} 255 | -DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY:BOOL=OFF 256 | -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=OFF 257 | -DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON 258 | -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF 259 | -DLIBCXX_CXX_ABI=libcxxabi 260 | -DLIBCXX_CXX_ABI_INCLUDE_PATHS=${llvm_proj_dir}/libcxxabi/include 261 | -DLIBCXX_HAS_MUSL_LIBC:BOOL=ON 262 | -DLIBCXX_ABI_VERSION=2 263 | -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=OFF 264 | -DLIBCXXABI_ENABLE_SHARED:BOOL=${pic} 265 | -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON 266 | -DLIBCXXABI_ENABLE_THREADS:BOOL=${threads} 267 | -DLIBCXXABI_HAS_PTHREAD_API:BOOL=${threads} 268 | -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF 269 | -DLIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY:BOOL=OFF 270 | -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF 271 | -DLIBCXXABI_ENABLE_PIC:BOOL=${pic} 272 | -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=OFF 273 | -DUNIX:BOOL=ON 274 | -DCMAKE_C_FLAGS=${extra_cflags} 275 | -DCMAKE_CXX_FLAGS=${extra_cxxflags} 276 | -DLIBCXX_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} 277 | -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} 278 | -DLIBCXX_INCLUDE_TESTS=OFF 279 | -DLIBCXX_INCLUDE_BENCHMARKS=OFF 280 | 281 | # See https://www.scivision.dev/cmake-externalproject-list-arguments/ for 282 | # why this is in `CMAKE_CACHE_ARGS` instead of above 283 | CMAKE_CACHE_ARGS 284 | -DLLVM_ENABLE_RUNTIMES:STRING=${runtimes} 285 | DEPENDS 286 | wasi-libc-${target} 287 | compiler-rt 288 | EXCLUDE_FROM_ALL ON 289 | USES_TERMINAL_CONFIGURE ON 290 | USES_TERMINAL_BUILD ON 291 | USES_TERMINAL_INSTALL ON 292 | ) 293 | endfunction() 294 | 295 | function(define_libcxx target) 296 | define_libcxx_sub(${target} "" "" "") 297 | if(WASI_SDK_LTO) 298 | # Note: clang knows this /llvm-lto/${llvm_version} convention. 299 | # https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/clang/lib/Driver/ToolChains/WebAssembly.cpp#L204-L210 300 | define_libcxx_sub(${target} "-lto" "-flto=full" "/llvm-lto/${llvm_version}") 301 | endif() 302 | 303 | # As of this writing, `clang++` will ignore the target-specific include dirs 304 | # unless this one also exists: 305 | add_custom_target(libcxx-${target}-extra-dir 306 | COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1 307 | COMMENT "creating libcxx-specific header file folder") 308 | add_custom_target(libcxx-${target} 309 | DEPENDS libcxx-${target}-build $<$:libcxx-${target}-lto-build> libcxx-${target}-extra-dir) 310 | endfunction() 311 | 312 | foreach(target IN LISTS WASI_SDK_TARGETS) 313 | define_libcxx(${target}) 314 | endforeach() 315 | 316 | # ============================================================================= 317 | # misc build logic 318 | # ============================================================================= 319 | 320 | install(DIRECTORY ${wasi_tmp_install}/share 321 | USE_SOURCE_PERMISSIONS 322 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 323 | if(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR) 324 | install(DIRECTORY ${wasi_resource_dir}/lib 325 | USE_SOURCE_PERMISSIONS 326 | DESTINATION ${clang_resource_dir}) 327 | else() 328 | install(DIRECTORY ${wasi_resource_dir}/lib 329 | USE_SOURCE_PERMISSIONS 330 | DESTINATION ${CMAKE_INSTALL_PREFIX}/clang-resource-dir) 331 | endif() 332 | 333 | # Add a top-level `build` target as well as `build-$target` targets. 334 | add_custom_target(build ALL) 335 | foreach(target IN LISTS WASI_SDK_TARGETS) 336 | add_custom_target(build-${target}) 337 | add_dependencies(build-${target} libcxx-${target} wasi-libc-${target} compiler-rt) 338 | add_dependencies(build build-${target}) 339 | endforeach() 340 | 341 | # Install a `VERSION` file in the output prefix with a dump of version 342 | # information. 343 | execute_process( 344 | COMMAND ${PYTHON} ${version_script} dump 345 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 346 | OUTPUT_VARIABLE version_dump) 347 | set(version_file_tmp ${wasi_sysroot}/VERSION) 348 | file(GENERATE OUTPUT ${version_file_tmp} CONTENT ${version_dump}) 349 | add_custom_target(version-file DEPENDS ${version_file_tmp}) 350 | add_dependencies(build version-file) 351 | 352 | if(WASI_SDK_INCLUDE_TESTS) 353 | add_subdirectory(tests) 354 | endif() 355 | 356 | include(wasi-sdk-dist) 357 | 358 | set(dist_dir ${CMAKE_CURRENT_BINARY_DIR}/dist) 359 | 360 | # Tarball with just `compiler-rt` builtins within it 361 | wasi_sdk_add_tarball(dist-compiler-rt 362 | ${dist_dir}/libclang_rt.builtins-wasm32-wasi-${wasi_sdk_version}.tar.gz 363 | ${wasi_resource_dir}/lib/wasi) 364 | add_dependencies(dist-compiler-rt compiler-rt) 365 | 366 | # Tarball with the whole sysroot 367 | wasi_sdk_add_tarball(dist-sysroot 368 | ${dist_dir}/wasi-sysroot-${wasi_sdk_version}.tar.gz 369 | ${wasi_sysroot}) 370 | add_dependencies(dist-sysroot build) 371 | 372 | add_custom_target(dist DEPENDS dist-compiler-rt dist-sysroot) 373 | -------------------------------------------------------------------------------- /cmake/wasi-sdk-toolchain.cmake: -------------------------------------------------------------------------------- 1 | # Build logic and support for building a Clang toolchain that can target 2 | # WebAssembly and build a WASI sysroot. 3 | 4 | set(LLVM_CMAKE_FLAGS "" CACHE STRING "Extra cmake flags to pass to LLVM's build") 5 | set(RUST_TARGET "" CACHE STRING "Target to build Rust code for, if not the host") 6 | set(WASI_SDK_ARTIFACT "" CACHE STRING "Name of the wasi-sdk artifact being produced") 7 | 8 | string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}") 9 | 10 | set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) 11 | 12 | if(NOT CMAKE_BUILD_TYPE) 13 | set(CMAKE_BUILD_TYPE MinSizeRel) 14 | endif() 15 | 16 | set(default_cmake_args 17 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 18 | -DCMAKE_AR=${CMAKE_AR} 19 | -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} 20 | -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} 21 | -DCMAKE_INSTALL_PREFIX=${wasi_tmp_install}) 22 | 23 | if(CMAKE_C_COMPILER_LAUNCHER) 24 | list(APPEND default_cmake_args -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}) 25 | endif() 26 | if(CMAKE_CXX_COMPILER_LAUNCHER) 27 | list(APPEND default_cmake_args -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}) 28 | endif() 29 | 30 | set(links_to_create clang-cl clang-cpp clang++) 31 | foreach(target IN LISTS WASI_SDK_TARGETS) 32 | list(APPEND links_to_create ${target}-clang) 33 | list(APPEND links_to_create ${target}-clang++) 34 | endforeach() 35 | 36 | set(projects "lld;clang;clang-tools-extra") 37 | 38 | set(tools 39 | clang 40 | clang-format 41 | clang-tidy 42 | clang-apply-replacements 43 | lld 44 | llvm-mc 45 | llvm-ranlib 46 | llvm-strip 47 | llvm-dwarfdump 48 | llvm-dwp 49 | clang-resource-headers 50 | ar 51 | ranlib 52 | strip 53 | nm 54 | size 55 | strings 56 | objdump 57 | objcopy 58 | c++filt 59 | llvm-config) 60 | 61 | # By default link LLVM dynamically to all the various tools. This greatly 62 | # reduces the binary size of all the tools through a shared library rather than 63 | # statically linking LLVM to each individual tool. This requires a few other 64 | # install targets as well to ensure the appropriate libraries are all installed. 65 | # 66 | # Also note that the `-wasi-sdk` version suffix is intended to help prevent 67 | # these dynamic libraries from clashing with other system libraries in case the 68 | # `lib` dir gets put on `LD_LIBRARY_PATH` or similar. 69 | if(NOT WIN32) 70 | list(APPEND default_cmake_args -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_VERSION_SUFFIX=-wasi-sdk) 71 | list(APPEND tools LLVM clang-cpp) 72 | endif() 73 | 74 | list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets) 75 | list(TRANSFORM tools PREPEND --target=install- OUTPUT_VARIABLE install_targets) 76 | 77 | ExternalProject_Add(llvm-build 78 | SOURCE_DIR "${llvm_proj_dir}/llvm" 79 | CMAKE_ARGS 80 | ${default_cmake_args} 81 | -DLLVM_ENABLE_ZLIB=OFF 82 | -DLLVM_ENABLE_ZSTD=OFF 83 | -DLLVM_STATIC_LINK_CXX_STDLIB=ON 84 | -DLLVM_INCLUDE_TESTS=OFF 85 | -DLLVM_INCLUDE_UTILS=OFF 86 | -DLLVM_INCLUDE_BENCHMARKS=OFF 87 | -DLLVM_INCLUDE_EXAMPLES=OFF 88 | -DLLVM_TARGETS_TO_BUILD=WebAssembly 89 | -DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasi 90 | -DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE 91 | -DLLVM_ENABLE_LIBXML2=OFF 92 | # Pass `-s` to strip symbols by default and shrink the size of the 93 | # distribution 94 | -DCMAKE_EXE_LINKER_FLAGS=-s 95 | ${llvm_cmake_flags_list} 96 | # See https://www.scivision.dev/cmake-externalproject-list-arguments/ for 97 | # why this is in `CMAKE_CACHE_ARGS` instead of above 98 | CMAKE_CACHE_ARGS 99 | -DLLVM_ENABLE_PROJECTS:STRING=${projects} 100 | -DCLANG_LINKS_TO_CREATE:STRING=${links_to_create} 101 | BUILD_COMMAND 102 | cmake --build . ${build_targets} 103 | INSTALL_COMMAND 104 | cmake --build . ${install_targets} 105 | USES_TERMINAL_CONFIGURE ON 106 | USES_TERMINAL_BUILD ON 107 | USES_TERMINAL_INSTALL ON 108 | ) 109 | 110 | add_custom_target(build ALL DEPENDS llvm-build) 111 | 112 | # Installation target for this outer project for installing the toolchain to the 113 | # system. 114 | install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_install}/share 115 | USE_SOURCE_PERMISSIONS 116 | DESTINATION ${CMAKE_INSTALL_PREFIX}) 117 | 118 | # Build logic for `wasm-component-ld` installed from Rust code. 119 | set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld) 120 | set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX}) 121 | set(wasm_component_ld_version 0.5.13) 122 | if(RUST_TARGET) 123 | set(rust_target_flag --target=${RUST_TARGET}) 124 | endif() 125 | add_custom_command( 126 | OUTPUT ${wasm_component_ld} 127 | COMMAND 128 | cargo install --root ${wasm_component_ld_root} ${rust_target_flag} 129 | wasm-component-ld@${wasm_component_ld_version} 130 | COMMAND 131 | cmake -E make_directory ${wasi_tmp_install}/bin 132 | COMMAND 133 | cmake -E copy ${wasm_component_ld} ${wasi_tmp_install}/bin 134 | COMMENT "Building `wasm-component-ld` ...") 135 | add_custom_target(wasm-component-ld DEPENDS ${wasm_component_ld}) 136 | add_dependencies(build wasm-component-ld) 137 | 138 | # Setup installation logic for CMake support files. 139 | add_custom_target(misc-files) 140 | add_dependencies(build misc-files) 141 | 142 | function(copy_misc_file src dst_folder) 143 | cmake_path(GET src FILENAME src_filename) 144 | set(dst ${wasi_tmp_install}/share/${dst_folder}/${src_filename}) 145 | add_custom_command( 146 | OUTPUT ${dst} 147 | COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${src} ${dst}) 148 | add_custom_target(copy-${src_filename} DEPENDS ${dst}) 149 | add_dependencies(misc-files copy-${src_filename}) 150 | endfunction() 151 | 152 | copy_misc_file(src/config/config.sub misc) 153 | copy_misc_file(src/config/config.guess misc) 154 | copy_misc_file(wasi-sdk.cmake cmake) 155 | copy_misc_file(wasi-sdk-pthread.cmake cmake) 156 | copy_misc_file(wasi-sdk-p1.cmake cmake) 157 | copy_misc_file(wasi-sdk-p2.cmake cmake) 158 | copy_misc_file(cmake/Platform/WASI.cmake cmake/Platform) 159 | 160 | function(copy_cfg_file compiler) 161 | set(dst ${wasi_tmp_install}/bin/${compiler}.cfg) 162 | add_custom_command( 163 | OUTPUT ${dst} 164 | COMMAND cmake -E copy ${CMAKE_CURRENT_SOURCE_DIR}/clang.cfg ${dst}) 165 | add_custom_target(copy-${compiler} DEPENDS ${dst}) 166 | add_dependencies(misc-files copy-${compiler}) 167 | endfunction() 168 | 169 | copy_cfg_file(clang) 170 | copy_cfg_file(clang++) 171 | 172 | include(wasi-sdk-dist) 173 | 174 | # Figure out the name of the artifact which is either explicitly specified or 175 | # inferred from CMake default variables. 176 | if(WASI_SDK_ARTIFACT) 177 | set(wasi_sdk_artifact ${WASI_SDK_ARTIFACT}) 178 | else() 179 | if(APPLE) 180 | set(wasi_sdk_os macos) 181 | else() 182 | string(TOLOWER ${CMAKE_SYSTEM_NAME} wasi_sdk_os) 183 | endif() 184 | set(wasi_sdk_arch ${CMAKE_SYSTEM_PROCESSOR}) 185 | set(wasi_sdk_artifact ${wasi_sdk_arch}-${wasi_sdk_os}) 186 | endif() 187 | 188 | set(dist_dir ${CMAKE_CURRENT_BINARY_DIR}/dist) 189 | wasi_sdk_add_tarball(dist-toolchain 190 | ${dist_dir}/wasi-toolchain-${wasi_sdk_version}-${wasi_sdk_artifact}.tar.gz 191 | ${wasi_tmp_install}) 192 | add_dependencies(dist-toolchain build) 193 | add_custom_target(dist DEPENDS dist-toolchain) 194 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # A container which has a number of build tools pre-installed plus a build of 2 | # `wasi-sdk` installed at `/opt/wasi-sdk`. This also has environment variables 3 | # pre-configued to use the installed toolchain. 4 | # 5 | # This container is built as the last step on CI for this repository and 6 | # pre-built versions of this container are pushed as a package to the repository 7 | # as well. 8 | 9 | FROM ubuntu:24.04 10 | 11 | RUN apt-get update && \ 12 | apt-get install -y cmake ninja-build make autoconf autogen automake libtool && \ 13 | rm -rf /var/lib/apt/lists/* 14 | 15 | ADD dist/wasi-sdk-*.deb . 16 | RUN case `dpkg --print-architecture` in \ 17 | amd64) dpkg -i wasi-sdk-*-x86_64-linux.deb ;; \ 18 | arm64) dpkg -i wasi-sdk-*-arm64-linux.deb ;; \ 19 | *) exit 1 ;; \ 20 | esac && \ 21 | rm wasi-sdk-*.deb 22 | 23 | ENV CC="/opt/wasi-sdk/bin/clang" 24 | ENV CXX="/opt/wasi-sdk/bin/clang++" 25 | ENV LD="/opt/wasi-sdk/bin/wasm-ld" 26 | ENV AR="/opt/wasi-sdk/bin/llvm-ar" 27 | ENV RANLIB="/opt/wasi-sdk/bin/llvm-ranlib" 28 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.observed 2 | *.observed.filtered 3 | *.wasm 4 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Support for running tests in the `tests/{compile-only,general}` folders 2 | cmake_minimum_required(VERSION 3.22) 3 | project(wasi-sdk-test) 4 | include(CTest) 5 | enable_testing() 6 | set(CMAKE_EXECUTABLE_SUFFIX ".wasm") 7 | 8 | option(WASI_SDK_TEST_HOST_TOOLCHAIN "Test against the host toolchain, not a fresh sysroot" OFF) 9 | 10 | if(NOT WASI_SDK_TEST_HOST_TOOLCHAIN) 11 | add_compile_options(--sysroot=${wasi_sysroot} -resource-dir ${wasi_resource_dir}) 12 | add_link_options(--sysroot=${wasi_sysroot} -resource-dir ${wasi_resource_dir}) 13 | endif() 14 | 15 | # Sanity check setup 16 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL WASI) 17 | message(FATAL_ERROR "Wrong system name (${CMAKE_SYSTEM_NAME}), wrong toolchain file in use?") 18 | endif() 19 | 20 | if(NOT DEFINED WASI) 21 | message(FATAL_ERROR "WASI is not set, platform file likely not loaded") 22 | endif() 23 | 24 | set(WASI_SDK_RUNWASI "wasmtime" CACHE STRING "Runner for tests") 25 | 26 | # Test everything at O0, O2, and O2+LTO 27 | set(opt_flags -O0 -O2 "-O2 -flto") 28 | 29 | add_custom_target(build-tests) 30 | 31 | # Executes a single `test` specified. 32 | # 33 | # This will compile `test` for all the various targets and with various 34 | # compiler options. If `runwasi` is non-empty then the test will be executed 35 | # in that runner as well. 36 | function(add_testcase runwasi test) 37 | foreach(target IN LISTS WASI_SDK_TARGETS) 38 | foreach(compile_flags IN LISTS opt_flags) 39 | # Mangle the options into something appropriate for a CMake rule name 40 | string(REGEX REPLACE " " "." target_name "${target}.${compile_flags}.${test}") 41 | 42 | # Add a new test executable based on `test` 43 | add_executable(${target_name} ${test}) 44 | add_dependencies(build-tests ${target_name}) 45 | 46 | # Configure all the compile options necessary. For example `--target` here 47 | # if the target doesn't look like it's already in the name of the compiler 48 | # as well. 49 | if(NOT(CMAKE_C_COMPILER MATCHES ${target})) 50 | target_compile_options(${target_name} PRIVATE --target=${target}) 51 | target_link_options(${target_name} PRIVATE --target=${target}) 52 | endif() 53 | 54 | # Apply test-specific compile options and link flags. 55 | if(test MATCHES "clocks.c$") 56 | target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_PROCESS_CLOCKS) 57 | target_link_options(${target_name} PRIVATE -lwasi-emulated-process-clocks) 58 | elseif(test MATCHES "mmap.c$") 59 | target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_MMAN) 60 | target_link_options(${target_name} PRIVATE -lwasi-emulated-mman) 61 | elseif(test MATCHES "(sigabrt|signals).c$") 62 | target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_SIGNAL) 63 | target_link_options(${target_name} PRIVATE -lwasi-emulated-signal) 64 | elseif(test MATCHES "printf-long-double-enabled.c$") 65 | target_link_options(${target_name} PRIVATE -lc-printscan-long-double) 66 | endif() 67 | 68 | # Apply language-specific options and dependencies. 69 | if(test MATCHES "cc$") 70 | target_compile_options(${target_name} PRIVATE -fno-exceptions) 71 | if(NOT WASI_SDK_TEST_HOST_TOOLCHAIN) 72 | add_dependencies(${target_name} libcxx-${target}) 73 | endif() 74 | else() 75 | if(NOT WASI_SDK_TEST_HOST_TOOLCHAIN) 76 | add_dependencies(${target_name} wasi-libc-${target}) 77 | endif() 78 | endif() 79 | 80 | # Apply target-specific options. 81 | if(target MATCHES threads) 82 | target_compile_options(${target_name} PRIVATE -pthread) 83 | target_link_options(${target_name} PRIVATE -pthread) 84 | endif() 85 | 86 | if(runwasi) 87 | add_test( 88 | NAME test-${target_name} 89 | COMMAND 90 | bash ../testcase.sh 91 | "${runwasi}" 92 | ${test} 93 | $ 94 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 95 | endif() 96 | endforeach() 97 | endforeach() 98 | endfunction() 99 | 100 | add_subdirectory(compile-only) 101 | add_subdirectory(general) 102 | -------------------------------------------------------------------------------- /tests/compile-only/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB c_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") 2 | file(GLOB cxx_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") 3 | 4 | set(compile_tests ${c_compile_tests} ${cxx_compile_tests}) 5 | 6 | foreach(test IN LISTS compile_tests) 7 | add_testcase("" ${test}) 8 | endforeach() 9 | 10 | -------------------------------------------------------------------------------- /tests/compile-only/addresses.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | extern char **environ; 5 | 6 | extern void __dso_handle; 7 | #if !defined(__clang_major__) || __clang_major__ >= 10 8 | extern void __data_end; 9 | extern void __global_base; 10 | extern void __heap_base; 11 | #endif 12 | 13 | int main(int argc, char *argv[]) { 14 | printf("NULL=%p\n", NULL); 15 | printf("__dso_handle=%p\n", &__dso_handle); 16 | #if !defined(__clang_major__) || __clang_major__ >= 10 17 | printf("__data_end=%p\n", &__data_end); 18 | printf("__global_base=%p\n", &__global_base); 19 | printf("__heap_base=%p\n", &__heap_base); 20 | #endif 21 | printf("__builtin_frame_address(0)=%p\n", __builtin_frame_address(0)); 22 | printf("__builtin_alloca(0)=%p\n", __builtin_alloca(0)); 23 | printf("__builtin_wasm_memory_size(0)=%p\n", (void *)(__builtin_wasm_memory_size(0) * PAGE_SIZE)); 24 | printf("&errno=%p\n", (void *)&errno); 25 | printf("&environ=%p\n", (void *)&environ); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/compile-only/printf-long-double.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | volatile long double x = 42.0L; 4 | 5 | int main(void) { 6 | printf("the answer is %Lf\n", x); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/compile-only/test.cc: -------------------------------------------------------------------------------- 1 | int main(){} 2 | -------------------------------------------------------------------------------- /tests/exit_status_zero: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/general/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") 2 | file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") 3 | 4 | set(general_tests ${c_general_tests} ${cxx_general_tests}) 5 | 6 | foreach(test IN LISTS general_tests) 7 | add_testcase(${WASI_SDK_RUNWASI} ${test}) 8 | endforeach() 9 | -------------------------------------------------------------------------------- /tests/general/abort.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | abort(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/abort.c.exit_status.expected: -------------------------------------------------------------------------------- 1 | 134 2 | -------------------------------------------------------------------------------- /tests/general/abort.c.stderr.expected: -------------------------------------------------------------------------------- 1 | Error: failed to run main module `abort.c.---.wasm` 2 | 3 | Caused by: 4 | 0: failed to invoke --- 5 | 1: error while executing at wasm backtrace: 6 | note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information 7 | 2: wasm trap: wasm `unreachable` instruction executed 8 | -------------------------------------------------------------------------------- /tests/general/abort.c.stderr.expected.filter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | cat \ 5 | | sed -e 's/main module `.*abort\.c\.wasm`/main module `abort.c.---.wasm`/' \ 6 | | sed -e 's/failed to invoke.*/failed to invoke ---/' \ 7 | | sed -E '/0x[[:xdigit:]]+/d' 8 | -------------------------------------------------------------------------------- /tests/general/argc_argv_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) { 4 | puts("hello from argc argv main!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/argc_argv_main.c.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from argc argv main! 2 | -------------------------------------------------------------------------------- /tests/general/argc_argv_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) { 4 | puts("hello from C++ argc argv main!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/argc_argv_main.cc.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from C++ argc argv main! 2 | -------------------------------------------------------------------------------- /tests/general/assert-fail.c: -------------------------------------------------------------------------------- 1 | #ifdef NDEBUG 2 | #undef NDEBUG 3 | #endif 4 | 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | assert(false); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /tests/general/assert-fail.c.exit_status.expected: -------------------------------------------------------------------------------- 1 | 134 2 | -------------------------------------------------------------------------------- /tests/general/assert-fail.c.stderr.expected: -------------------------------------------------------------------------------- 1 | Assertion failed: false (assert-fail.c: main: 9) 2 | Error: failed to run main module `assert-fail.c.---.wasm` 3 | 4 | Caused by: 5 | 0: failed to invoke --- 6 | 1: error while executing at wasm backtrace: 7 | note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information 8 | 2: wasm trap: wasm `unreachable` instruction executed 9 | -------------------------------------------------------------------------------- /tests/general/assert-fail.c.stderr.expected.filter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | cat \ 5 | | sed -e 's/main module `.*assert-fail\.c\.wasm`/main module `assert-fail.c.---.wasm`/' \ 6 | | sed -e 's/failed to invoke.*/failed to invoke ---/' \ 7 | | sed -e 's/Assertion failed: false (.*assert-fail.c/Assertion failed: false (assert-fail.c/' \ 8 | | sed -E '/0x[[:xdigit:]]+/d' 9 | -------------------------------------------------------------------------------- /tests/general/assert-pass.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | assert(true); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/general/clocks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void test_clock(void) { 7 | clock_t a = clock(); 8 | clock_t b = clock(); 9 | assert(a != -1); 10 | assert(b != -1); 11 | assert(a > 0); 12 | assert(b >= a); 13 | } 14 | 15 | static void test_times(void) { 16 | struct tms before; 17 | struct tms after; 18 | clock_t a = times(&before); 19 | clock_t b = times(&after); 20 | assert(a != -1); 21 | assert(b != -1); 22 | assert(b >= a); 23 | assert(after.tms_utime >= before.tms_utime); 24 | } 25 | 26 | static void test_getrusage(void) { 27 | struct rusage before; 28 | struct rusage after; 29 | int a = getrusage(RUSAGE_SELF, &before); 30 | int b = getrusage(RUSAGE_SELF, &after); 31 | assert(a != -1); 32 | assert(b != -1); 33 | assert(after.ru_utime.tv_sec >= before.ru_utime.tv_sec); 34 | assert(after.ru_utime.tv_sec != before.ru_utime.tv_sec || 35 | after.ru_utime.tv_usec >= before.ru_utime.tv_usec); 36 | } 37 | 38 | int main(void) { 39 | test_clock(); 40 | test_times(); 41 | test_getrusage(); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/general/ctors_dtors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | extern char **environ; 6 | 7 | static void from_atexit(void) { 8 | printf("hello from_atexit\n"); 9 | } 10 | 11 | static void another_from_atexit(void) { 12 | printf("hello another_from_atexit\n"); 13 | } 14 | 15 | __attribute__((constructor)) static void from_constructor(void) { 16 | printf("hello from_constructor\n"); 17 | } 18 | 19 | __attribute__((constructor(101))) static void from_constructor_101(void) { 20 | assert(errno == 0); 21 | printf("hello from_constructor101\n"); 22 | 23 | assert(environ && "environment should be initialized by this point"); 24 | } 25 | 26 | __attribute__((constructor(65535))) static void from_constructor_65535(void) { 27 | printf("hello from_constructor65535\n"); 28 | } 29 | 30 | __attribute__((destructor)) static void from_destructor(void) { 31 | printf("hello from_destructor\n"); 32 | } 33 | 34 | __attribute__((destructor(101))) static void from_destructor101(void) { 35 | printf("hello from_destructor101\n"); 36 | } 37 | 38 | __attribute__((destructor(65535))) static void from_destructor65535(void) { 39 | printf("hello from_destructor65535\n"); 40 | } 41 | 42 | int main(int argc, char *argv[]) { 43 | printf("hello main\n"); 44 | assert(argc != 0); 45 | assert(argv != NULL); 46 | assert(argv[argc] == NULL); 47 | 48 | atexit(from_atexit); 49 | atexit(another_from_atexit); 50 | printf("goodbye main\n"); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /tests/general/ctors_dtors.c.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from_constructor101 2 | hello from_constructor 3 | hello from_constructor65535 4 | hello main 5 | goodbye main 6 | hello another_from_atexit 7 | hello from_atexit 8 | hello from_destructor65535 9 | hello from_destructor 10 | hello from_destructor101 11 | -------------------------------------------------------------------------------- /tests/general/ctors_dtors.cc: -------------------------------------------------------------------------------- 1 | #include "ctors_dtors.c" 2 | 3 | struct StaticObject { 4 | StaticObject(); 5 | ~StaticObject(); 6 | }; 7 | 8 | StaticObject::StaticObject() { 9 | printf("hello StaticObject::StaticObject\n"); 10 | } 11 | 12 | StaticObject::~StaticObject() { 13 | printf("hello StaticObject::~StaticObject\n"); 14 | } 15 | 16 | static StaticObject static_object; 17 | -------------------------------------------------------------------------------- /tests/general/ctors_dtors.cc.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from_constructor101 2 | hello from_constructor 3 | hello from_constructor65535 4 | hello StaticObject::StaticObject 5 | hello main 6 | goodbye main 7 | hello another_from_atexit 8 | hello from_atexit 9 | hello from_destructor65535 10 | hello from_destructor 11 | hello StaticObject::~StaticObject 12 | hello from_destructor101 13 | -------------------------------------------------------------------------------- /tests/general/empty.c: -------------------------------------------------------------------------------- 1 | int main(void) { return 0; } 2 | -------------------------------------------------------------------------------- /tests/general/env-absent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | printf("HELLO = %s\n", getenv("HELLO")); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/general/env-absent.c.stdout.expected: -------------------------------------------------------------------------------- 1 | HELLO = (null) 2 | -------------------------------------------------------------------------------- /tests/general/env.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | printf("HELLO = %s\n", getenv("HELLO")); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/general/env.c.env: -------------------------------------------------------------------------------- 1 | HELLO=hello 2 | -------------------------------------------------------------------------------- /tests/general/env.c.stdout.expected: -------------------------------------------------------------------------------- 1 | HELLO = hello 2 | -------------------------------------------------------------------------------- /tests/general/environ.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | extern char **environ; 6 | 7 | int main(void) { 8 | assert(environ != NULL); 9 | for (char **p = environ; *p; ++p) { 10 | assert(p != NULL); 11 | } 12 | for (char **p = environ; *p; ++p) { 13 | if (strncmp(*p, "HELLO=", 5) == 0) { 14 | printf("HELLO = %s\n", *p + 6); 15 | } 16 | } 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/general/environ.c.env: -------------------------------------------------------------------------------- 1 | HELLO=hello 2 | -------------------------------------------------------------------------------- /tests/general/environ.c.stdout.expected: -------------------------------------------------------------------------------- 1 | HELLO = hello 2 | -------------------------------------------------------------------------------- /tests/general/getentropy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | char buf[256] = {0}; 7 | int ret = getentropy(buf, 256); 8 | assert(ret == 0); 9 | 10 | bool something_nonzero = false; 11 | for (int i = 0; i < 256; i++) { 12 | if (buf[i] != 0) 13 | something_nonzero = true; 14 | } 15 | 16 | assert(something_nonzero); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tests/general/iostream_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "hello from C++ main with cout!" << std::endl; 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/iostream_main.cc.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from C++ main with cout! 2 | -------------------------------------------------------------------------------- /tests/general/main_errno.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // It isn't required that errno be zero on entry to main, but 6 | // for tidiness' sake, if we ever do things during startup that 7 | // do set errno, we should reset it for tidiness' sake. 8 | int main(void) { 9 | int n = errno; 10 | printf("initial errno is %d: %s\n", n, strerror(n)); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/general/main_errno.c.stdout.expected: -------------------------------------------------------------------------------- 1 | initial errno is 0: Success 2 | -------------------------------------------------------------------------------- /tests/general/mmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef __GLIBC__ 9 | #include 10 | #endif 11 | #include 12 | 13 | #define perror_and_exit(message) \ 14 | do { \ 15 | perror(message); \ 16 | return EXIT_FAILURE; \ 17 | } while (0) 18 | 19 | #define OFFSET 10726 20 | #define LENGTH 143 21 | 22 | int main(int argc, char *argv[]) { 23 | if (argc != 2) { 24 | fprintf(stderr, "usage: %s \n", argv[0]); 25 | return EXIT_FAILURE; 26 | } 27 | char *filename; 28 | if (asprintf(&filename, "%s/input.txt", argv[1]) == -1) { 29 | fprintf(stderr, "can't allocate filename"); 30 | return EXIT_FAILURE; 31 | } 32 | 33 | int fd = open(filename, O_RDONLY); 34 | if (fd < 0) 35 | perror_and_exit("open"); 36 | 37 | struct stat stat_buf; 38 | if (fstat(fd, &stat_buf) != 0) 39 | perror_and_exit("fstat"); 40 | 41 | off_t offset = OFFSET; 42 | if (offset < 0) { 43 | fprintf(stderr, "negative offset\n"); 44 | return EXIT_FAILURE; 45 | } 46 | if (offset > (off_t)SIZE_MAX) { 47 | fprintf(stderr, "offset overflow\n"); 48 | return EXIT_FAILURE; 49 | } 50 | 51 | off_t aligned_offset = offset & -(off_t)PAGE_SIZE; 52 | 53 | if (offset >= stat_buf.st_size) { 54 | fprintf(stderr, "offset is past end of file\n"); 55 | return EXIT_FAILURE; 56 | } 57 | 58 | size_t length = LENGTH; 59 | if ((off_t)length < 0) { 60 | fprintf(stderr, "length overflow\n"); 61 | return EXIT_FAILURE; 62 | } 63 | if ((off_t)length > stat_buf.st_size - offset) 64 | length = (size_t)(stat_buf.st_size - offset); 65 | 66 | size_t mmap_length = length + (size_t)(offset - aligned_offset); 67 | char *addr = mmap(NULL, mmap_length, PROT_READ, MAP_PRIVATE, fd, aligned_offset); 68 | if (addr == MAP_FAILED) 69 | perror_and_exit("mmap"); 70 | 71 | ssize_t nwritten = write(STDOUT_FILENO, addr + (offset - aligned_offset), length); 72 | if (nwritten < 0) 73 | perror_and_exit("write"); 74 | 75 | if ((size_t)nwritten != length) { 76 | fprintf(stderr, "partial write"); 77 | return EXIT_FAILURE; 78 | } 79 | 80 | if (munmap(addr, mmap_length) != 0) 81 | perror_and_exit("munmap"); 82 | 83 | if (close(fd) != 0) 84 | perror_and_exit("close"); 85 | 86 | return EXIT_SUCCESS; 87 | } 88 | -------------------------------------------------------------------------------- /tests/general/mmap.c.dir/.gitattributes: -------------------------------------------------------------------------------- 1 | # This input is read at runtime during testing so ensure that the same input is 2 | # read on unix and windows by forcing just-a-newline for line endings. 3 | *.txt text eol=lf 4 | -------------------------------------------------------------------------------- /tests/general/mmap.c.dir/input.txt: -------------------------------------------------------------------------------- 1 | Alice’s Adventures in Wonderland 2 | by Lewis Carroll 3 | 4 | CHAPTER VI. 5 | Pig and Pepper 6 | 7 | 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. 8 | 9 | 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.” 10 | 11 | Then they both bowed low, and their curls got entangled together. 12 | 13 | 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. 14 | 15 | Alice went timidly up to the door, and knocked. 16 | 17 | “There’s no sort of use in knocking,” said the Footman, “and that for two reasons. First, because I’m on the same side of the door as you are; secondly, because they’re 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. 18 | 19 | “Please, then,” said Alice, “how am I to get in?” 20 | 21 | “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 can’t 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. 22 | 23 | “I shall sit here,” the Footman remarked, “till tomorrow—” 24 | 25 | At this moment the door of the house opened, and a large plate came skimming out, straight at the Footman’s head: it just grazed his nose, and broke to pieces against one of the trees behind him. 26 | 27 | “—or next day, maybe,” the Footman continued in the same tone, exactly as if nothing had happened. 28 | 29 | “How am I to get in?” asked Alice again, in a louder tone. 30 | 31 | “Are you to get in at all?” said the Footman. “That’s the first question, you know.” 32 | 33 | It was, no doubt: only Alice did not like to be told so. “It’s really dreadful,” she muttered to herself, “the way all the creatures argue. It’s enough to drive one crazy!” 34 | 35 | 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.” 36 | 37 | “But what am I to do?” said Alice. 38 | 39 | “Anything you like,” said the Footman, and began whistling. 40 | 41 | “Oh, there’s no use in talking to him,” said Alice desperately: “he’s perfectly idiotic!” And she opened the door and went in. 42 | 43 | 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. 44 | 45 | “There’s certainly too much pepper in that soup!” Alice said to herself, as well as she could for sneezing. 46 | 47 | 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 moment’s 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. 48 | 49 | “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?” 50 | 51 | “It’s a Cheshire cat,” said the Duchess, “and that’s why. Pig!” 52 | 53 | 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:— 54 | 55 | “I didn’t know that Cheshire cats always grinned; in fact, I didn’t know that cats could grin.” 56 | 57 | “They all can,” said the Duchess; “and most of ’em do.” 58 | 59 | “I don’t know of any that do,” Alice said very politely, feeling quite pleased to have got into a conversation. 60 | 61 | “You don’t know much,” said the Duchess; “and that’s a fact.” 62 | 63 | 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. 64 | 65 | “Oh, please mind what you’re 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. 66 | 67 | “If everybody minded their own business,” the Duchess said in a hoarse growl, “the world would go round a deal faster than it does.” 68 | 69 | “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—” 70 | 71 | “Talking of axes,” said the Duchess, “chop off her head!” 72 | 73 | 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—” 74 | 75 | “Oh, don’t 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: 76 | 77 | “Speak roughly to your little boy, 78 | And beat him when he sneezes: 79 | He only does it to annoy, 80 | Because he knows it teases.” 81 | 82 | CHORUS. 83 | (In which the cook and the baby joined): 84 | 85 | “Wow! wow! wow!” 86 | 87 | 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:— 88 | 89 | “I speak severely to my boy, 90 | I beat him when he sneezes; 91 | For he can thoroughly enjoy 92 | The pepper when he pleases!” 93 | 94 | CHORUS. 95 | 96 | “Wow! wow! wow!” 97 | 98 | “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. 99 | 100 | 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. 101 | 102 | 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 don’t take this child away with me,” thought Alice, “they’re sure to kill it in a day or two: wouldn’t 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). “Don’t grunt,” said Alice; “that’s not at all a proper way of expressing yourself.” 103 | 104 | 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. 105 | 106 | No, there were no tears. “If you’re going to turn into a pig, my dear,” said Alice, seriously, “I’ll 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. 107 | 108 | 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. 109 | 110 | 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. 111 | 112 | 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. 113 | 114 | “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, it’s pleased so far,” thought Alice, and she went on. “Would you tell me, please, which way I ought to go from here?” 115 | 116 | “That depends a good deal on where you want to get to,” said the Cat. 117 | 118 | “I don’t much care where—” said Alice. 119 | 120 | “Then it doesn’t matter which way you go,” said the Cat. 121 | 122 | “—so long as I get somewhere,” Alice added as an explanation. 123 | 124 | “Oh, you’re sure to do that,” said the Cat, “if you only walk long enough.” 125 | 126 | Alice felt that this could not be denied, so she tried another question. “What sort of people live about here?” 127 | 128 | “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: they’re both mad.” 129 | 130 | “But I don’t want to go among mad people,” Alice remarked. 131 | 132 | “Oh, you can’t help that,” said the Cat: “we’re all mad here. I’m mad. You’re mad.” 133 | 134 | “How do you know I’m mad?” said Alice. 135 | 136 | “You must be,” said the Cat, “or you wouldn’t have come here.” 137 | 138 | Alice didn’t think that proved it at all; however, she went on “And how do you know that you’re mad?” 139 | 140 | “To begin with,” said the Cat, “a dog’s not mad. You grant that?” 141 | 142 | “I suppose so,” said Alice. 143 | 144 | “Well, then,” the Cat went on, “you see, a dog growls when it’s angry, and wags its tail when it’s pleased. Now I growl when I’m pleased, and wag my tail when I’m angry. Therefore I’m mad.” 145 | 146 | “I call it purring, not growling,” said Alice. 147 | 148 | “Call it what you like,” said the Cat. “Do you play croquet with the Queen to-day?” 149 | 150 | “I should like it very much,” said Alice, “but I haven’t been invited yet.” 151 | 152 | “You’ll see me there,” said the Cat, and vanished. 153 | 154 | 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. 155 | 156 | “By-the-bye, what became of the baby?” said the Cat. “I’d nearly forgotten to ask.” 157 | 158 | “It turned into a pig,” Alice quietly said, just as if it had come back in a natural way. 159 | 160 | “I thought it would,” said the Cat, and vanished again. 161 | 162 | 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. “I’ve seen hatters before,” she said to herself; “the March Hare will be much the most interesting, and perhaps as this is May it won’t 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. 163 | 164 | “Did you say pig, or fig?” said the Cat. 165 | 166 | “I said pig,” replied Alice; “and I wish you wouldn’t keep appearing and vanishing so suddenly: you make one quite giddy.” 167 | 168 | “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. 169 | 170 | “Well! I’ve often seen a cat without a grin,” thought Alice; “but a grin without a cat! It’s the most curious thing I ever saw in my life!” 171 | 172 | 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 I’d gone to see the Hatter instead!” 173 | -------------------------------------------------------------------------------- /tests/general/mmap.c.stdout.expected: -------------------------------------------------------------------------------- 1 | “Would you tell me, please, which way I ought to go from here?” 2 | 3 | “That depends a good deal on where you want to get to,” said the Cat. 4 | -------------------------------------------------------------------------------- /tests/general/no_arg_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | puts("hello from no-arg main!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/no_arg_main.c.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from no-arg main! 2 | -------------------------------------------------------------------------------- /tests/general/no_arg_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | puts("hello from C++ no-arg main!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/no_arg_main.cc.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from C++ no-arg main! 2 | -------------------------------------------------------------------------------- /tests/general/opendir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define perror_and_exit(message) \ 15 | do { \ 16 | perror(message); \ 17 | return EXIT_FAILURE; \ 18 | } while (0) 19 | 20 | #define OFFSET 10726 21 | #define LENGTH 143 22 | 23 | int main(int argc, char *argv[]) { 24 | if (argc != 2) { 25 | fprintf(stderr, "usage: %s \n", argv[0]); 26 | return EXIT_FAILURE; 27 | } 28 | DIR *dir = opendir(argv[1]); 29 | if (dir == NULL) { 30 | perror_and_exit("opendir"); 31 | } 32 | 33 | int count = 0; 34 | int zeros = 0; 35 | errno = 0; 36 | for (;; count += 1) { 37 | struct dirent *ent = readdir(dir); 38 | if (ent == NULL) { 39 | if (errno == 0) { 40 | break; 41 | } 42 | perror_and_exit("readdir"); 43 | } 44 | 45 | if (strcmp(ent->d_name, "file.md") == 0) { 46 | assert(ent->d_type == DT_REG); 47 | } else if (strcmp(ent->d_name, "dir") == 0) { 48 | assert(ent->d_type == DT_DIR); 49 | } else if (strcmp(ent->d_name, "file-symlink") == 0) { 50 | assert(ent->d_type == DT_LNK); 51 | } else if (strcmp(ent->d_name, "dir-symlink") == 0) { 52 | assert(ent->d_type == DT_LNK); 53 | } else if (strcmp(ent->d_name, ".") == 0) { 54 | assert(ent->d_type == DT_DIR); 55 | } else if (strcmp(ent->d_name, "..") == 0) { 56 | assert(ent->d_type == DT_DIR); 57 | } else { 58 | assert(false); 59 | } 60 | if (ent->d_ino == 0) { 61 | zeros += 1; 62 | } 63 | } 64 | 65 | assert(count == 6); 66 | assert(zeros <= 1); 67 | 68 | if (closedir(dir) != 0) 69 | perror_and_exit("closedir"); 70 | 71 | return EXIT_SUCCESS; 72 | } 73 | -------------------------------------------------------------------------------- /tests/general/opendir.c.dir/dir-symlink: -------------------------------------------------------------------------------- 1 | dir -------------------------------------------------------------------------------- /tests/general/opendir.c.dir/dir/file.txt: -------------------------------------------------------------------------------- 1 | This is a file in a directory. 2 | -------------------------------------------------------------------------------- /tests/general/opendir.c.dir/file-symlink: -------------------------------------------------------------------------------- 1 | file.md -------------------------------------------------------------------------------- /tests/general/opendir.c.dir/file.md: -------------------------------------------------------------------------------- 1 | # This is a top-level file 2 | -------------------------------------------------------------------------------- /tests/general/printf-long-double-enabled.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | volatile long double x = 42.0L; 4 | 5 | int main(void) { 6 | printf("the answer is %Lf\n", x); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/general/printf-long-double-enabled.c.stdout.expected: -------------------------------------------------------------------------------- 1 | the answer is 42.000000 2 | -------------------------------------------------------------------------------- /tests/general/printf-no-float.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | volatile int x = 42; 4 | 5 | int main(void) { 6 | printf("the answer is %d\n", x); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/general/printf-no-float.c.stdout.expected: -------------------------------------------------------------------------------- 1 | the answer is 42 2 | -------------------------------------------------------------------------------- /tests/general/printf-no-long-double.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | volatile double x = 42.0; 4 | 5 | int main(void) { 6 | printf("the answer is %f\n", x); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/general/printf-no-long-double.c.stdout.expected: -------------------------------------------------------------------------------- 1 | the answer is 42.000000 2 | -------------------------------------------------------------------------------- /tests/general/sigabrt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) { 6 | fprintf(stderr, "raising SIGABRT...\n"); 7 | raise(SIGABRT); 8 | fprintf(stderr, "oops!\n"); 9 | return EXIT_FAILURE; 10 | } 11 | -------------------------------------------------------------------------------- /tests/general/sigabrt.c.exit_status.expected: -------------------------------------------------------------------------------- 1 | 134 2 | -------------------------------------------------------------------------------- /tests/general/sigabrt.c.stderr.expected: -------------------------------------------------------------------------------- 1 | raising SIGABRT... 2 | Program received fatal signal: Aborted 3 | Error: failed to run main module `sigabrt.c.---.wasm` 4 | 5 | Caused by: 6 | 0: failed to invoke --- 7 | -------------------------------------------------------------------------------- /tests/general/sigabrt.c.stderr.expected.filter: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | cat \ 5 | | sed -e 's/main module `.*sigabrt\.c\.wasm`/main module `sigabrt.c.---.wasm`/' \ 6 | | sed -e 's/source location: @[[:xdigit:]]*$/source location: @----/' \ 7 | | sed -e 's/failed to invoke.*/failed to invoke ---/' \ 8 | | head -n 6 9 | -------------------------------------------------------------------------------- /tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected: -------------------------------------------------------------------------------- 1 | raising SIGABRT... 2 | Program received fatal signal: Aborted 3 | Error: failed to run main module `sigabrt.c.---.wasm` 4 | 5 | Caused by: 6 | 0: failed to invoke `run` function 7 | -------------------------------------------------------------------------------- /tests/general/sigabrt.c.wasm32-wasip2.stderr.expected: -------------------------------------------------------------------------------- 1 | raising SIGABRT... 2 | Program received fatal signal: Aborted 3 | Error: failed to run main module `sigabrt.c.---.wasm` 4 | 5 | Caused by: 6 | 0: failed to invoke `run` function 7 | -------------------------------------------------------------------------------- /tests/general/signals.c: -------------------------------------------------------------------------------- 1 | #ifdef NDEBUG 2 | #undef NDEBUG 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Make sure this exists. 13 | #pragma clang diagnostic push 14 | #pragma clang diagnostic ignored "-W#warnings" 15 | #include 16 | #pragma clang diagnostic pop 17 | 18 | volatile sig_atomic_t flag = 0; 19 | 20 | static void handler(int n) { 21 | // This is undefined behavior by the spec, but this is just a testcase. 22 | fflush(stdout); 23 | printf("handler for signal %s\n", strsignal(n)); 24 | fflush(stdout); 25 | flag = 1; 26 | } 27 | 28 | int main(void) { 29 | // Test various raise cases that don't abort. 30 | assert(raise(SIGCHLD) == 0); 31 | #ifdef SIGCLD 32 | assert(raise(SIGCLD) == 0); 33 | #endif 34 | assert(raise(SIGURG) == 0); 35 | assert(raise(SIGWINCH) == 0); 36 | 37 | errno = 0; 38 | assert(raise(_NSIG) == -1 && errno == EINVAL); 39 | 40 | // Test psignal. 41 | psignal(SIGINT, "psignal message for SIGINT"); 42 | 43 | // Test strsignal. 44 | printf("strsignal for SIGHUP: '%s'\n", strsignal(SIGHUP)); 45 | 46 | // Some signals can't be ignored. 47 | errno = 0; 48 | assert(signal(SIGKILL, SIG_IGN) == SIG_ERR && errno == EINVAL); 49 | errno = 0; 50 | assert(signal(SIGSTOP, SIG_IGN) == SIG_ERR && errno == EINVAL); 51 | 52 | // Test that all the C-standard-required signals can be 53 | // ignored with `SIG_IGN`. 54 | int some_fatal_sigs[] = { 55 | SIGINT, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM 56 | }; 57 | for (size_t i = 0; 58 | i < sizeof(some_fatal_sigs) / sizeof(some_fatal_sigs[0]); 59 | ++i) 60 | { 61 | int sig = some_fatal_sigs[i]; 62 | assert(signal(sig, SIG_IGN) == SIG_DFL); 63 | raise(sig); 64 | assert(signal(sig, SIG_DFL) == SIG_IGN); 65 | assert(signal(sig, SIG_DFL) == SIG_DFL); 66 | } 67 | 68 | // Install a handler and invoke it. 69 | printf("beginning handler test:\n"); 70 | assert(signal(SIGWINCH, handler) == SIG_DFL); 71 | fflush(stdout); 72 | assert(raise(SIGWINCH) == 0); 73 | fflush(stdout); 74 | assert(flag == 1); 75 | printf("finished handler test\n"); 76 | 77 | // Check various API invariants. 78 | assert(signal(SIGWINCH, SIG_IGN) == handler); 79 | assert(raise(SIGWINCH) == 0); 80 | assert(signal(SIGWINCH, SIG_DFL) == SIG_IGN); 81 | assert(raise(SIGWINCH) == 0); 82 | assert(signal(SIGWINCH, SIG_DFL) == SIG_DFL); 83 | assert(raise(SIGWINCH) == 0); 84 | 85 | return EXIT_SUCCESS; 86 | } 87 | -------------------------------------------------------------------------------- /tests/general/signals.c.stderr.expected: -------------------------------------------------------------------------------- 1 | psignal message for SIGINT: Interrupt 2 | -------------------------------------------------------------------------------- /tests/general/signals.c.stdout.expected: -------------------------------------------------------------------------------- 1 | strsignal for SIGHUP: 'Hangup' 2 | beginning handler test: 3 | handler for signal Window changed 4 | finished handler test 5 | -------------------------------------------------------------------------------- /tests/general/stat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) { 10 | if (argc != 2) { 11 | fprintf(stderr, "usage: %s \n", argv[0]); 12 | return EXIT_FAILURE; 13 | } 14 | 15 | char *filename; 16 | int n = asprintf(&filename, "%s/file", argv[1]); 17 | assert(n > 0); 18 | 19 | n = creat(filename, S_IRUSR|S_IWUSR); 20 | assert(n >= 0); 21 | 22 | char *linkname; 23 | n = asprintf(&linkname, "%s/symlink", argv[1]); 24 | assert(n > 0); 25 | 26 | n = symlink("file", linkname); 27 | assert(n == 0); 28 | 29 | struct stat file_statbuf; 30 | struct stat link_statbuf; 31 | 32 | // Test stat. 33 | 34 | n = stat(filename, &file_statbuf); 35 | assert(n == 0); 36 | assert(file_statbuf.st_size == 0); 37 | assert(S_ISREG(file_statbuf.st_mode)); 38 | 39 | n = stat(linkname, &link_statbuf); 40 | assert(n == 0); 41 | assert(link_statbuf.st_size == 0); 42 | assert(S_ISREG(link_statbuf.st_mode)); 43 | 44 | assert(file_statbuf.st_dev == link_statbuf.st_dev); 45 | 46 | assert(file_statbuf.st_dev == link_statbuf.st_dev); 47 | assert(file_statbuf.st_ino == link_statbuf.st_ino); 48 | assert(file_statbuf.st_mode == link_statbuf.st_mode); 49 | assert(file_statbuf.st_uid == link_statbuf.st_uid); 50 | assert(file_statbuf.st_gid == link_statbuf.st_gid); 51 | assert(file_statbuf.st_rdev == link_statbuf.st_rdev); 52 | assert(file_statbuf.st_size == link_statbuf.st_size); 53 | assert(file_statbuf.st_blksize == link_statbuf.st_blksize); 54 | assert(file_statbuf.st_blocks == link_statbuf.st_blocks); 55 | // NB: `atim` is explicitly not compared here 56 | assert(file_statbuf.st_mtim.tv_sec == link_statbuf.st_mtim.tv_sec); 57 | assert(file_statbuf.st_mtim.tv_nsec == link_statbuf.st_mtim.tv_nsec); 58 | assert(file_statbuf.st_ctim.tv_sec == link_statbuf.st_ctim.tv_sec); 59 | assert(file_statbuf.st_ctim.tv_nsec == link_statbuf.st_ctim.tv_nsec); 60 | 61 | // Test lstat. 62 | 63 | n = lstat(filename, &file_statbuf); 64 | assert(n == 0); 65 | assert(file_statbuf.st_size == 0); 66 | assert(S_ISREG(file_statbuf.st_mode)); 67 | 68 | n = lstat(linkname, &link_statbuf); 69 | assert(n == 0); 70 | assert(link_statbuf.st_size != 0); 71 | assert(S_ISLNK(link_statbuf.st_mode)); 72 | 73 | assert(file_statbuf.st_dev == link_statbuf.st_dev); 74 | assert(link_statbuf.st_ino != file_statbuf.st_ino); 75 | 76 | n = unlink(filename); 77 | assert(n == 0); 78 | n = unlink(linkname); 79 | assert(n == 0); 80 | 81 | free(filename); 82 | free(linkname); 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /tests/general/stat.c.dir/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/wasi-sdk/38c97448e00facd18826bc73b5bb6c69219ffb4d/tests/general/stat.c.dir/empty -------------------------------------------------------------------------------- /tests/general/void_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | puts("hello from void main!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/void_main.c.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from void main! 2 | -------------------------------------------------------------------------------- /tests/general/void_main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | puts("hello from C++ void main!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/general/void_main.cc.stdout.expected: -------------------------------------------------------------------------------- 1 | hello from C++ void main! 2 | -------------------------------------------------------------------------------- /tests/testcase.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ueo pipefail 3 | 4 | # A simple testcase runner that runs a command, captures all its command-line 5 | # outputs, and compares them against expected outputs. 6 | 7 | # Command-line parsing; this script is meant to be run from a higher-level 8 | # script, so don't do anything fancy. 9 | runwasi="$1" 10 | input="$2" 11 | wasm="$3" 12 | 13 | # Compile names for generated files. 14 | stdout_observed="$wasm.stdout.observed" 15 | stderr_observed="$wasm.stderr.observed" 16 | exit_status_observed="$wasm.exit_status.observed" 17 | 18 | # Double-check that a runwasi command was specified since otherwise this script 19 | # was invoked with no arguments which isn't as intended. 20 | if [ "$runwasi" == "" ]; then 21 | exit 1 22 | fi 23 | 24 | # Determine the input file to write to stdin. 25 | if [ -e "$input.stdin" ]; then 26 | stdin="$input.stdin" 27 | else 28 | stdin="/dev/null" 29 | fi 30 | 31 | # Determine any environment variables to set. 32 | if [ -e "$input.env" ]; then 33 | env=$(sed -e 's/^/--env /' < "$input.env") 34 | else 35 | env="" 36 | fi 37 | 38 | # Determine a preopened directory to provide. 39 | if [ -e "$input.dir" ]; then 40 | dir="--dir $input.dir" 41 | dirarg="$input.dir" 42 | else 43 | dir="" 44 | dirarg="" 45 | fi 46 | 47 | # Run the test, capturing stdout, stderr, and the exit status. 48 | exit_status=0 49 | "$runwasi" $env $dir "$wasm" $dirarg \ 50 | < "$stdin" \ 51 | > "$stdout_observed" \ 52 | 2> "$stderr_observed" \ 53 | || exit_status=$? 54 | echo $exit_status > "$exit_status_observed" 55 | 56 | # On Windows Wasmtime will exit with error code 3 for aborts. On Unix Wasmtime 57 | # will exit with status 134. Paper over this difference by pretending to be Unix 58 | # on Windows and converting exit code 3 into 134 for the purposes of asserting 59 | # test output. 60 | if [ "$OSTYPE" = "msys" ] && [ "$exit_status" = "3" ]; then 61 | echo 134 > "$exit_status_observed" 62 | fi 63 | 64 | # Determine the reference files to compare with. 65 | if [ -e "$input.stdout.expected" ]; then 66 | stdout_expected="$input.stdout.expected" 67 | 68 | # Apply output filters. 69 | if [ -e "$input.stdout.expected.filter" ]; then 70 | cat "$stdout_observed" \ 71 | | "$input.stdout.expected.filter" \ 72 | > "${stdout_observed}.filtered" 73 | stdout_observed="${stdout_observed}.filtered" 74 | fi 75 | else 76 | stdout_expected="/dev/null" 77 | fi 78 | 79 | if [ -e "$input.stderr.expected" ]; then 80 | stderr_expected="$input.stderr.expected" 81 | 82 | # Apply output filters. 83 | if [ -e "$input.stderr.expected.filter" ]; then 84 | cat "$stderr_observed" \ 85 | | "./$input.stderr.expected.filter" \ 86 | > "${stderr_observed}.filtered" 87 | stderr_observed="${stderr_observed}.filtered" 88 | fi 89 | else 90 | stderr_expected="/dev/null" 91 | fi 92 | if [ -e "$input.exit_status.expected" ]; then 93 | exit_status_expected="$input.exit_status.expected" 94 | else 95 | exit_status_expected=../exit_status_zero 96 | fi 97 | 98 | # If there are any differences, diff will return a non-zero exit status, and 99 | # since this script uses "set -e", it will return a non-zero exit status too. 100 | diff --ignore-space-change -u "$stderr_expected" "$stderr_observed" 101 | diff --ignore-space-change -u "$stdout_expected" "$stdout_observed" 102 | diff --ignore-space-change -u "$exit_status_expected" "$exit_status_observed" 103 | -------------------------------------------------------------------------------- /version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script finds and prints the various versions in this project: wasi-sdk 4 | # itself, LLVM, and the Git revisions of dependencies. 5 | # 6 | # Usage: version [wasi-sdk|llvm|llvm-major|dump] [--llvm-dir=] 7 | 8 | import argparse 9 | import os 10 | import subprocess 11 | import sys 12 | 13 | # The number of characters to use for the abbreviated Git revision. 14 | GIT_REF_LEN = 12 15 | 16 | 17 | def exec(command, cwd): 18 | result = subprocess.run(command, stdout=subprocess.PIPE, 19 | universal_newlines=True, check=True, cwd=cwd) 20 | return result.stdout.strip() 21 | 22 | 23 | def git_commit(dir): 24 | return exec(['git', 'rev-parse', f'--short={GIT_REF_LEN}', 'HEAD'], dir) 25 | 26 | 27 | def parse_git_version(version): 28 | # Parse, e.g.: wasi-sdk-21-0-g317548590b40+m 29 | parts = version.replace('+', '-').split('-') 30 | assert parts.pop(0) == 'wasi' 31 | assert parts.pop(0) == 'sdk' 32 | 33 | major, minor = parts.pop(0), parts.pop(0) 34 | git = None 35 | dirty = False 36 | 37 | if parts: 38 | # Check: git|dirty. 39 | next = parts.pop(0) 40 | if next == 'm': 41 | dirty = True 42 | elif minor != '0': 43 | git = next[1:] 44 | 45 | # Check: dirty. 46 | if parts: 47 | assert parts.pop(0) == 'm', f'expected dirty flag: +m' 48 | dirty = True 49 | 50 | assert not parts, f'unexpected suffixes: {parts}' 51 | return major, minor, git, dirty 52 | 53 | 54 | # Some inline tests to check Git version parsing: 55 | assert parse_git_version( 56 | 'wasi-sdk-21-1-g317548590b40+m') == ('21', '1', '317548590b40', True) 57 | assert parse_git_version('wasi-sdk-21-2+m') == ('21', '2', None, True) 58 | assert parse_git_version( 59 | 'wasi-sdk-23-0-g317548590b40') == ('23', '0', None, False) 60 | 61 | 62 | def git_version(): 63 | version = exec(['git', 'describe', '--long', '--candidates=999', 64 | '--match=wasi-sdk-*', '--dirty=+m', f'--abbrev={GIT_REF_LEN}'], 65 | os.path.dirname(sys.argv[0])) 66 | major, minor, git, dirty = parse_git_version(version) 67 | version = f'{major}.{minor}' 68 | if git: 69 | version += f'g{git}' 70 | if dirty: 71 | version += '+m' 72 | return version 73 | 74 | 75 | def parse_cmake_set(line): 76 | return line.split(' ')[1].split(')')[0] 77 | 78 | 79 | def llvm_cmake_version(llvm_dir): 80 | path = f'{llvm_dir}/cmake/Modules/LLVMVersion.cmake' 81 | if not os.path.exists(path): 82 | # Handle older LLVM versions; see #399. 83 | path = f'{llvm_dir}/llvm/CMakeLists.txt' 84 | with open(path) as file: 85 | for line in file: 86 | line = line.strip() 87 | if line.startswith('set(LLVM_VERSION_MAJOR'): 88 | llvm_version_major = parse_cmake_set(line) 89 | elif line.startswith('set(LLVM_VERSION_MINOR'): 90 | llvm_version_minor = parse_cmake_set(line) 91 | elif line.startswith('set(LLVM_VERSION_PATCH'): 92 | llvm_version_patch = parse_cmake_set(line) 93 | return llvm_version_major, llvm_version_minor, llvm_version_patch 94 | 95 | 96 | def main(action, llvm_dir): 97 | if action == 'wasi-sdk': 98 | print(git_version()) 99 | elif action == 'llvm': 100 | major, minor, path = llvm_cmake_version(llvm_dir) 101 | print(f'{major}.{minor}.{path}') 102 | elif action == 'llvm-major': 103 | major, _, _ = llvm_cmake_version(llvm_dir) 104 | print(major) 105 | elif action == 'dump': 106 | print(git_version()) 107 | print(f'wasi-libc: {git_commit("src/wasi-libc")}') 108 | print(f'llvm: {git_commit(llvm_dir)}') 109 | major, minor, path = llvm_cmake_version(llvm_dir) 110 | print(f'llvm-version: {major}.{minor}.{path}') 111 | print(f'config: {git_commit("src/config")}') 112 | 113 | 114 | if __name__ == '__main__': 115 | parser = argparse.ArgumentParser( 116 | description='Print the various kinds of versions in wasi-sdk') 117 | parser.add_argument('action', 118 | choices=['wasi-sdk', 'llvm', 'llvm-major', 'dump'], 119 | nargs='?', 120 | default='wasi-sdk', 121 | help='Which kind of version to print (default: wasi-sdk).') 122 | parser.add_argument('--llvm-dir', 123 | nargs='?', 124 | default='src/llvm-project', 125 | help='Override the location of the LLVM source directory (default: src/llvm-project).') 126 | args = parser.parse_args() 127 | main(args.action, args.llvm_dir) 128 | sys.exit(0) 129 | -------------------------------------------------------------------------------- /wasi-sdk-p1.cmake: -------------------------------------------------------------------------------- 1 | # Cmake toolchain description file for the Makefile 2 | 3 | # Until Platform/WASI.cmake is upstream we need to inject the path to it 4 | # into CMAKE_MODULE_PATH. 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") 6 | 7 | set(CMAKE_SYSTEM_NAME WASI) 8 | set(CMAKE_SYSTEM_VERSION 1) 9 | set(CMAKE_SYSTEM_PROCESSOR wasm32) 10 | set(triple wasm32-wasip1) 11 | 12 | if(WIN32) 13 | set(WASI_HOST_EXE_SUFFIX ".exe") 14 | else() 15 | set(WASI_HOST_EXE_SUFFIX "") 16 | endif() 17 | 18 | # When building from source, WASI_SDK_PREFIX represents the generated directory 19 | if(NOT WASI_SDK_PREFIX) 20 | set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) 21 | endif() 22 | 23 | set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 24 | set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) 25 | set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 26 | set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) 27 | set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) 28 | set(CMAKE_C_COMPILER_TARGET ${triple}) 29 | set(CMAKE_CXX_COMPILER_TARGET ${triple}) 30 | set(CMAKE_ASM_COMPILER_TARGET ${triple}) 31 | 32 | # Don't look in the sysroot for executables to run during the build 33 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 34 | 35 | # Only look in the sysroot (not in the host paths) for the rest 36 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 37 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 38 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 39 | -------------------------------------------------------------------------------- /wasi-sdk-p2.cmake: -------------------------------------------------------------------------------- 1 | # Cmake toolchain description file for the Makefile 2 | 3 | # Until Platform/WASI.cmake is upstream we need to inject the path to it 4 | # into CMAKE_MODULE_PATH. 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") 6 | 7 | set(CMAKE_SYSTEM_NAME WASI) 8 | set(CMAKE_SYSTEM_VERSION 1) 9 | set(CMAKE_SYSTEM_PROCESSOR wasm32) 10 | set(triple wasm32-wasip2) 11 | 12 | if(WIN32) 13 | set(WASI_HOST_EXE_SUFFIX ".exe") 14 | else() 15 | set(WASI_HOST_EXE_SUFFIX "") 16 | endif() 17 | 18 | # When building from source, WASI_SDK_PREFIX represents the generated directory 19 | if(NOT WASI_SDK_PREFIX) 20 | set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) 21 | endif() 22 | 23 | set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 24 | set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) 25 | set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 26 | set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) 27 | set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) 28 | set(CMAKE_C_COMPILER_TARGET ${triple}) 29 | set(CMAKE_CXX_COMPILER_TARGET ${triple}) 30 | set(CMAKE_ASM_COMPILER_TARGET ${triple}) 31 | 32 | # Don't look in the sysroot for executables to run during the build 33 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 34 | # Only look in the sysroot (not in the host paths) for the rest 35 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 36 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 37 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 38 | -------------------------------------------------------------------------------- /wasi-sdk-pthread.cmake: -------------------------------------------------------------------------------- 1 | # Cmake toolchain description file for the Makefile 2 | 3 | set(CMAKE_SYSTEM_NAME WASI) 4 | set(CMAKE_SYSTEM_VERSION 1) 5 | set(CMAKE_SYSTEM_PROCESSOR wasm32) 6 | set(triple wasm32-wasi-threads) 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 9 | # wasi-threads requires --import-memory. 10 | # wasi requires --export-memory. 11 | # (--export-memory is implicit unless --import-memory is given) 12 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--import-memory") 13 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-memory") 14 | 15 | if(WIN32) 16 | set(WASI_HOST_EXE_SUFFIX ".exe") 17 | else() 18 | set(WASI_HOST_EXE_SUFFIX "") 19 | endif() 20 | 21 | # When building from source, WASI_SDK_PREFIX represents the generated directory 22 | if(NOT WASI_SDK_PREFIX) 23 | set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) 24 | endif() 25 | 26 | set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 27 | set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) 28 | set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 29 | set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) 30 | set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) 31 | set(CMAKE_C_COMPILER_TARGET ${triple}) 32 | set(CMAKE_CXX_COMPILER_TARGET ${triple}) 33 | set(CMAKE_ASM_COMPILER_TARGET ${triple}) 34 | 35 | # Don't look in the sysroot for executables to run during the build 36 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 37 | # Only look in the sysroot (not in the host paths) for the rest 38 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 39 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 40 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 41 | -------------------------------------------------------------------------------- /wasi-sdk.cmake: -------------------------------------------------------------------------------- 1 | # Cmake toolchain description file for the Makefile 2 | 3 | # Until Platform/WASI.cmake is upstream we need to inject the path to it 4 | # into CMAKE_MODULE_PATH. 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") 6 | 7 | set(CMAKE_SYSTEM_NAME WASI) 8 | set(CMAKE_SYSTEM_VERSION 1) 9 | set(CMAKE_SYSTEM_PROCESSOR wasm32) 10 | set(triple wasm32-wasi) 11 | 12 | if(WIN32) 13 | set(WASI_HOST_EXE_SUFFIX ".exe") 14 | else() 15 | set(WASI_HOST_EXE_SUFFIX "") 16 | endif() 17 | 18 | # When building from source, WASI_SDK_PREFIX represents the generated directory 19 | if(NOT WASI_SDK_PREFIX) 20 | set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) 21 | endif() 22 | 23 | set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 24 | set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) 25 | set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) 26 | set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) 27 | set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) 28 | set(CMAKE_C_COMPILER_TARGET ${triple}) 29 | set(CMAKE_CXX_COMPILER_TARGET ${triple}) 30 | set(CMAKE_ASM_COMPILER_TARGET ${triple}) 31 | 32 | # Don't look in the sysroot for executables to run during the build 33 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 34 | # Only look in the sysroot (not in the host paths) for the rest 35 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 36 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 37 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 38 | -------------------------------------------------------------------------------- /wasi-sdk.control: -------------------------------------------------------------------------------- 1 | Package: wasi-sdk 2 | Version: VERSION 3 | Architecture: ARCH 4 | Priority: optional 5 | Description: Clang toolchain with wasm32-wasi default target, and the wasi sysroot 6 | Maintainer: WASI SDK Maintainers 7 | --------------------------------------------------------------------------------