1name: CI
2on:
3  # Run CI for PRs to `main` and to release branches.
4  #
5  # Note that PRs to `main` will run a subset of tests and PRs to the
6  # `release-*` branches will run full CI.
7  pull_request:
8    branches:
9    - main
10    - 'release-*'
11
12  # Run full CI on the `main` branch once a day to prime the GitHub Actions
13  # caches used by PRs and the merge queue.
14  schedule:
15  - cron: '13 4 * * *'
16
17  # This is the CI that runs for PRs-to-merge.
18  merge_group:
19
20  push:
21    branches:
22    # Right now merge queues can't be used with wildcards in branch protections
23    # so full CI runs both on PRs to release branches as well as merges to
24    # release branches. Note that the merge to a release branch may produce a
25    # tag at the end of CI if successful and the tag will trigger the artifact
26    # uploads as well as publication to crates.io.
27    - 'release-*'
28
29defaults:
30  run:
31    shell: bash
32
33# Cancel any in-flight jobs for the same PR/branch so there's only one active
34# at a time
35concurrency:
36  group: ${{ github.workflow }}-${{ github.ref }}
37  cancel-in-progress: true
38
39jobs:
40  # Check Code style quickly by running `rustfmt` over all code
41  rustfmt:
42    name: Rustfmt
43    runs-on: ubuntu-latest
44    steps:
45    - uses: actions/checkout@v4
46      with:
47        submodules: true
48    - uses: ./.github/actions/install-rust
49    - run: rustup component add rustfmt
50    - run: cargo fmt --all -- --check
51
52    # common logic to cancel the entire run if this job fails
53    - run: gh run cancel ${{ github.run_id }}
54      if: failure() && github.event_name != 'pull_request'
55      env:
56        GH_TOKEN: ${{ github.token }}
57
58  # Check Code style quickly by running `clang-format` over all the C/C++ code
59  #
60  # Note that `wasmtime-platform.h` is excluded here as it's auto-generated.
61  clangformat:
62    name: Clang format
63    runs-on: ubuntu-latest
64    steps:
65    - uses: actions/checkout@v4
66      with:
67        submodules: true
68    - run: |
69        git ls-files '*.h' '*.c' '*.cpp' | \
70          grep -v wasmtime-platform.h | \
71          grep -v wasm.h | \
72          xargs clang-format-15 --dry-run --Werror --verbose
73
74    # common logic to cancel the entire run if this job fails
75    - run: gh run cancel ${{ github.run_id }}
76      if: failure() && github.event_name != 'pull_request'
77      env:
78        GH_TOKEN: ${{ github.token }}
79
80  # Lint dependency graph for security advisories, duplicate versions, and
81  # incompatible licences
82  cargo_deny:
83    name: Cargo deny
84    needs: determine
85    if: needs.determine.outputs.audit
86    runs-on: ubuntu-latest
87    steps:
88    - uses: actions/checkout@v4
89      with:
90        submodules: true
91    - uses: ./.github/actions/install-rust
92    - run: |
93        set -e
94        curl -L https://github.com/EmbarkStudios/cargo-deny/releases/download/0.14.5/cargo-deny-0.14.5-x86_64-unknown-linux-musl.tar.gz | tar xzf -
95        mv cargo-deny-*-x86_64-unknown-linux-musl/cargo-deny cargo-deny
96        echo `pwd` >> $GITHUB_PATH
97    - run: cargo deny check bans licenses
98
99    # common logic to cancel the entire run if this job fails
100    - run: gh run cancel ${{ github.run_id }}
101      if: failure() && github.event_name != 'pull_request'
102      env:
103        GH_TOKEN: ${{ github.token }}
104
105  # Ensure dependencies are vetted. See https://mozilla.github.io/cargo-vet/
106  #
107  # Note that this step, on PRs only, is allowed to fail. This is then followed
108  # up with the `cargo_vet_failure_for_prs` step below. The intention is to
109  # avoid causing this check to fail PRs while still enabling it to fail the
110  # merge queue checks. That way PRs can enter the merge queue when this step is
111  # failing if `main` has picked up `cargo vet` entries in the meantime for the
112  # failures.
113  cargo_vet:
114    name: Cargo vet
115    needs: determine
116    if: needs.determine.outputs.audit
117    runs-on: ubuntu-latest
118    outputs:
119      outcome: ${{ steps.vet.outcome }}
120    steps:
121    - uses: actions/checkout@v4
122      with:
123        submodules: true
124    - uses: ./.github/actions/install-rust
125    - uses: ./.github/actions/install-cargo-vet
126    - id: vet
127      run: cargo vet --locked
128      continue-on-error: ${{ github.event_name == 'pull_request' }}
129
130    # common logic to cancel the entire run if this job fails
131    - run: gh run cancel ${{ github.run_id }}
132      if: failure() && github.event_name != 'pull_request'
133      env:
134        GH_TOKEN: ${{ github.token }}
135
136  cargo_vet_failure_for_prs:
137    name: Cargo vet failed on a Pull Request
138    needs:
139    - determine
140    - cargo_vet
141    if: |
142      needs.determine.outputs.audit
143      && github.event_name == 'pull_request'
144      && needs.cargo_vet.outputs.outcome == 'failure'
145    runs-on: ubuntu-latest
146    steps:
147    # NB: this message ideally would link back to the previous step, but I'm not
148    # sure how to easily do that.
149    - run: |
150        echo 'failed to run "cargo vet", see previous `Cargo vet` step'
151        echo 'exiting with a nonzero status now to alert PR authors'
152        echo 'note, though, that this PR can still enter the merge queue'
153        echo ''
154        echo 'See https://docs.wasmtime.dev/contributing-coding-guidelines.html#cargo-vet-for-contributors'
155        echo 'for more information about the vetting process for Wasmtime'
156        exit 1
157
158  # This job is a dependency of many of the jobs below. This calculates what's
159  # actually being run for this workflow. For example:
160  #
161  # * Pushes to branches, which is currently both pushes to merge queue branches
162  #   as well as release branches, perform full CI.
163  # * PRs to release branches (not `main`) run full CI.
164  # * PRs to `main` will only run a few smoke tests above plus some elements of
165  #   the test matrix. The test matrix here is determined dynamically by the
166  #   `./ci/build-test-matrix.js` script given the commits that happened and
167  #   the files modified.
168  determine:
169    name: Determine CI jobs to run
170    runs-on: ubuntu-latest
171    outputs:
172      run-full: ${{ steps.calculate.outputs.run-full }}
173      test-matrix: ${{ steps.calculate.outputs.test-matrix }}
174      build-matrix: ${{ steps.calculate.outputs.build-matrix }}
175      test-capi: ${{ steps.calculate.outputs.test-capi }}
176      test-nightly: ${{ steps.calculate.outputs.test-nightly }}
177      audit: ${{ steps.calculate.outputs.audit }}
178      preview1-adapter: ${{ steps.calculate.outputs.preview1-adapter }}
179    steps:
180    - uses: actions/checkout@v4
181    - id: calculate
182      env:
183        GH_TOKEN: ${{ github.token }}
184      run: |
185        touch commits.log names.log
186        # Note that CI doesn't run on pushes to `main`, only pushes to merge
187        # queue branches and release branches, so this only runs full CI in
188        # those locations.
189        if [ "${{ github.event_name }}" != "pull_request" ]; then
190          run_full=true
191        else
192          pr=${{ github.event.number }}
193          gh pr view $pr --json commits | tee commits.log
194          gh pr diff $pr --name-only | tee names.log || echo "failed to get files"
195          if [ "${{ github.base_ref }}" != "main" ]; then
196            run_full=true
197          elif grep -q 'prtest:full' commits.log; then
198            run_full=true
199          fi
200          if grep -q crates.c-api names.log; then
201            echo test-capi=true >> $GITHUB_OUTPUT
202          fi
203          if grep -q fuzz names.log; then
204            echo test-nightly=true >> $GITHUB_OUTPUT
205          fi
206          if grep -q sys.custom names.log; then
207            echo test-nightly=true >> $GITHUB_OUTPUT
208          fi
209          if grep -q Cargo.lock names.log; then
210            echo audit=true >> $GITHUB_OUTPUT
211          fi
212          if grep -q supply-chain names.log; then
213            echo audit=true >> $GITHUB_OUTPUT
214          fi
215          if grep -q component-adapter names.log; then
216            echo preview1-adapter=true >> $GITHUB_OUTPUT
217          fi
218        fi
219        matrix="$(node ./ci/build-test-matrix.js ./commits.log ./names.log $run_full)"
220        echo "test-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
221        echo "$matrix"
222
223        matrix="$(node ./ci/build-build-matrix.js)"
224        echo "build-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
225
226        if [ "$run_full" = "true" ]; then
227            echo run-full=true >> $GITHUB_OUTPUT
228            echo test-capi=true >> $GITHUB_OUTPUT
229            echo test-nightly=true >> $GITHUB_OUTPUT
230            echo audit=true >> $GITHUB_OUTPUT
231            echo preview1-adapter=true >> $GITHUB_OUTPUT
232        fi
233
234  # Build all documentation of Wasmtime, including the C API documentation,
235  # mdbook documentation, etc. This produces a `gh-pages` artifact which is what
236  # gets uploaded to the `gh-pages` branch later on.
237  doc:
238    needs: determine
239    if: needs.determine.outputs.run-full
240    name: Doc build
241    runs-on: ubuntu-latest
242    env:
243      CARGO_MDBOOK_VERSION: 0.4.37
244      RUSTDOCFLAGS: -Dbroken_intra_doc_links --cfg docsrs
245      OPENVINO_SKIP_LINKING: 1
246    steps:
247    - uses: actions/checkout@v4
248      with:
249        submodules: true
250    - uses: ./.github/actions/install-rust
251      with:
252        toolchain: wasmtime-ci-pinned-nightly
253
254    # Build C API documentation
255    - run: curl -L https://sourceforge.net/projects/doxygen/files/rel-1.9.3/doxygen-1.9.3.linux.bin.tar.gz/download | tar xzf -
256    - run: echo "`pwd`/doxygen-1.9.3/bin" >> $GITHUB_PATH
257    - run: cd crates/c-api && doxygen doxygen.conf
258
259    # install mdbook, build the docs, and test the docs
260    - uses: actions/cache@v4
261      with:
262        path: ${{ runner.tool_cache }}/mdbook
263        key: cargo-mdbook-bin-${{ env.CARGO_MDBOOK_VERSION }}
264    - run: |
265        echo "${{ runner.tool_cache }}/mdbook/bin" >> $GITHUB_PATH
266        cargo install --root ${{ runner.tool_cache }}/mdbook --version ${{ env.CARGO_MDBOOK_VERSION }} mdbook --locked
267    - run: (cd docs && mdbook build)
268    - run: cargo build -p wasi-common --features wasmtime/wat,wasmtime/cranelift
269    - run: (cd docs && mdbook test -L ../target/debug/deps)
270
271    # Build Rust API documentation.
272    # We pass in the `component-model` feature
273    # to match the docs.rs metadata in
274    # crates/wasmtime/Cargo.toml.
275    - run: |
276        cargo doc --no-deps --workspace \
277          --exclude wasmtime-cli \
278          --exclude test-programs \
279          --exclude wasi-http-tests \
280          --exclude cranelift-codegen-meta \
281          --features component-model
282    - run: cargo doc --package cranelift-codegen-meta --document-private-items
283
284    # Assemble the documentation, and always upload it as an artifact for
285    # inspection on PRs and such.
286    - run: |
287        mv docs/book gh-pages
288        mv crates/c-api/html gh-pages/c-api
289        mv target/doc gh-pages/api
290        tar czf gh-pages.tar.gz gh-pages
291    - uses: actions/upload-artifact@v4
292      with:
293        name: gh-pages
294        path: gh-pages.tar.gz
295
296    # common logic to cancel the entire run if this job fails
297    - run: gh run cancel ${{ github.run_id }}
298      if: failure() && github.event_name != 'pull_request'
299      env:
300        GH_TOKEN: ${{ github.token }}
301
302  # Checks of various feature combinations and whether things compile. The goal
303  # here isn't to run tests, mostly just serve as a double-check that Rust code
304  # compiles and is likely to work everywhere else.
305  micro_checks:
306    needs: determine
307    name: Check ${{matrix.name}}
308    strategy:
309      fail-fast: true
310      matrix:
311        include:
312          - name: wasmtime
313            checks: |
314              -p wasmtime --no-default-features
315              -p wasmtime --no-default-features --features wat
316              -p wasmtime --no-default-features --features profiling
317              -p wasmtime --no-default-features --features cache
318              -p wasmtime --no-default-features --features async
319              -p wasmtime --no-default-features --features pooling-allocator
320              -p wasmtime --no-default-features --features cranelift
321              -p wasmtime --no-default-features --features component-model
322              -p wasmtime --no-default-features --features runtime,component-model
323              -p wasmtime --no-default-features --features cranelift,wat,async,cache
324              -p wasmtime --no-default-features --features winch
325              -p wasmtime --no-default-features --features wmemcheck
326              -p wasmtime --no-default-features --features wmemcheck,cranelift,runtime
327              -p wasmtime --no-default-features --features demangle
328              -p wasmtime --no-default-features --features addr2line
329              -p wasmtime --no-default-features --features gc
330              -p wasmtime --no-default-features --features runtime,gc
331              -p wasmtime --no-default-features --features cranelift,gc
332              -p wasmtime --no-default-features --features runtime
333              -p wasmtime --no-default-features --features threads
334              -p wasmtime --no-default-features --features runtime,threads
335              -p wasmtime --no-default-features --features cranelift,threads
336              -p wasmtime --features incremental-cache
337              -p wasmtime --all-features
338
339          - name: wasmtime-cli
340            checks: |
341              -p wasmtime-cli --no-default-features
342              -p wasmtime-cli --no-default-features --features run
343              -p wasmtime-cli --no-default-features --features run,component-model
344              -p wasmtime-cli --no-default-features --features compile
345              -p wasmtime-cli --no-default-features --features compile,cranelift
346              -p wasmtime-cli --no-default-features --features compile,cranelift,component-model
347              -p wasmtime-cli --all-features
348              -p wasmtime-cli --features component-model
349
350          - name: cranelift-codegen benches
351            checks: |
352              --benches -p cranelift-codegen
353
354          - name: wasmtime-bench-api
355            checks: |
356              -p wasmtime-bench-api
357
358          - name: wasmtime-c-api
359            checks: |
360              -p wasmtime-c-api --no-default-features
361              -p wasmtime-c-api --no-default-features --features wat
362              -p wasmtime-c-api --no-default-features --features wasi
363    runs-on: ubuntu-latest
364    steps:
365    - uses: actions/checkout@v4
366      with:
367        submodules: true
368    - uses: ./.github/actions/install-rust
369
370    # Run the check.
371    - run: |
372        checks=$(cat <<END
373        ${{ matrix.checks }}
374        END
375        )
376        echo "$checks" | xargs -I CHECK sh -c 'echo "=== cargo check CHECK ==="; cargo check CHECK'
377
378    # Common logic to cancel the entire run if this job fails.
379    - run: gh run cancel ${{ github.run_id }}
380      if: failure() && github.event_name != 'pull_request'
381      env:
382        GH_TOKEN: ${{ github.token }}
383
384  # Similar to `micro_checks` but where we need to install some more state
385  # (e.g. Android NDK) and we haven't factored support for those things out into
386  # a parallel jobs yet.
387  monolith_checks:
388    needs: determine
389    name: Monolith Checks
390    runs-on: ubuntu-latest
391    env:
392      CARGO_NDK_VERSION: 2.12.2
393    steps:
394    - uses: actions/checkout@v4
395      with:
396        submodules: true
397    - uses: ./.github/actions/install-rust
398
399    # Checks for no_std support, ensure that crates can build on a no_std
400    # target
401    - run: rustup target add x86_64-unknown-none
402    - run: cargo check -p wasmtime --no-default-features --features runtime,gc,component-model
403      env:
404        CARGO_BUILD_TARGET: x86_64-unknown-none
405
406    # Check that wasmtime compiles with panic=abort since there's some `#[cfg]`
407    # for specifically panic=abort there.
408    - run: cargo check -p wasmtime
409      env:
410        RUSTFLAGS: -Cpanic=abort
411
412    # Check a few builds of the cranelift backend
413    # - only x86 backend support,
414    # - only arm64 backend support,
415    # - no debug_assertions.
416    - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util --no-default-features --features=cranelift-codegen/arm64
417    - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util --no-default-features --features=cranelift-codegen/x86
418    - run: cargo check --manifest-path=./cranelift/Cargo.toml --bin clif-util
419      env:
420        CARGO_PROFILE_DEV_DEBUG_ASSERTIONS: false
421
422    # Check whether `wasmtime` cross-compiles to x86_64-unknown-freebsd
423    # TODO: We aren't building with default features since the `ittapi` crate fails to compile on freebsd.
424    - run: rustup target add x86_64-unknown-freebsd
425    - run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache --target x86_64-unknown-freebsd
426
427    # Run clippy configuration
428    - run: rustup component add clippy
429    - run: cargo clippy --workspace
430
431    # Re-vendor all WIT files and ensure that they're all up-to-date by ensuring
432    # that there's no git changes.
433    - name: Re-vendor WIT
434      run: ./ci/vendor-wit.sh
435    - run: git diff --exit-code
436
437    # Re-vendor the C API and make sure it's up-to-date.
438    - name: Re-vendor WIT
439      run: ./ci/vendor-c-api-headers.sh
440    - run: git diff --exit-code
441
442    # common logic to cancel the entire run if this job fails
443    - run: gh run cancel ${{ github.run_id }}
444      if: failure() && github.event_name != 'pull_request'
445      env:
446        GH_TOKEN: ${{ github.token }}
447
448  # Check whether `wasmtime` cross-compiles to aarch64-pc-windows-msvc
449  # We don't build nor test it because it lacks trap handling.
450  # Tracking issue: https://github.com/bytecodealliance/wasmtime/issues/4992
451  checks_winarm64:
452    needs: determine
453    if: needs.determine.outputs.run-full
454    name: Check Windows ARM64
455    runs-on: windows-latest
456    steps:
457    - uses: actions/checkout@v4
458      with:
459        submodules: true
460    - uses: ./.github/actions/install-rust
461    - run: rustup target add aarch64-pc-windows-msvc
462    - run: cargo check -p wasmtime --target aarch64-pc-windows-msvc
463
464    # common logic to cancel the entire run if this job fails
465    - run: gh run cancel ${{ github.run_id }}
466      if: failure() && github.event_name != 'pull_request'
467      env:
468        GH_TOKEN: ${{ github.token }}
469
470  # Run tests that require a nightly compiler, such as building fuzz targets.
471  test_nightly:
472    needs: determine
473    if: needs.determine.outputs.test-nightly
474    name: Nightly tests
475    runs-on: ubuntu-latest
476    steps:
477    - uses: actions/checkout@v4
478      with:
479        submodules: true
480    # Note that nightly is pinned here to insulate us from breakage that might
481    # happen upstream. This is periodically updated through a PR.
482    - uses: ./.github/actions/install-rust
483      with:
484        toolchain: wasmtime-ci-pinned-nightly
485
486    # Ensure that fuzzers still build.
487    #
488    # Install the OCaml packages necessary for fuzz targets that use the
489    # `wasm-spec-interpreter`.
490    - run: cargo install cargo-fuzz --vers "^0.11" --locked
491    - run: sudo apt-get update && sudo apt install -y ocaml-nox ocamlbuild ocaml-findlib libzarith-ocaml-dev
492    - run: cargo fetch
493      working-directory: ./fuzz
494    - run: cargo fuzz build --dev -s none
495    - run: cargo fuzz build --dev -s none --fuzz-dir ./cranelift/isle/fuzz
496    - run: cargo fuzz build --dev -s none --fuzz-dir ./crates/environ/fuzz --features component-model
497
498    # common logic to cancel the entire run if this job fails
499    - run: gh run cancel ${{ github.run_id }}
500      if: failure() && github.event_name != 'pull_request'
501      env:
502        GH_TOKEN: ${{ github.token }}
503
504  # Perform all tests of the c-api
505  test_capi:
506    needs: determine
507    name: Test C-API ${{ matrix.os }}
508    runs-on: ${{ matrix.os }}
509    if: needs.determine.outputs.test-capi
510
511    strategy:
512      fail-fast: true
513      matrix:
514        os: [ubuntu-latest, macos-latest, windows-latest]
515
516    steps:
517    - uses: actions/checkout@v4
518      with:
519        submodules: true
520    - uses: ./.github/actions/install-rust
521
522    # Build and test the C API with example C programs along with the example
523    # Rust programs. Note that this only executes if the `determine` step told
524    # us to test the capi which is off-by-default for PRs.
525    - run: cmake -Sexamples -Bexamples/build -DBUILD_SHARED_LIBS=OFF
526    - run: cmake --build examples/build --config Debug
527    - run: cmake -E env CTEST_OUTPUT_ON_FAILURE=1 cmake --build examples/build --config Debug --target RUN_TESTS
528      env:
529        RUST_BACKTRACE: 1
530      if: matrix.os == 'windows-latest'
531    - run: cmake -E env CTEST_OUTPUT_ON_FAILURE=1 cmake --build examples/build --config Debug --target test
532      env:
533        RUST_BACKTRACE: 1
534      if: matrix.os != 'windows-latest'
535
536  # Perform all tests (debug mode) for `wasmtime`.
537  #
538  # Note that the full matrix for what may run here is defined within
539  # `./ci/build-test-matrix.js` and the execution of the `determine` step will
540  # calculate whether the tests are actually run as part of PRs and such.
541  test:
542    needs: determine
543    name: ${{ matrix.name }}
544    runs-on: ${{ matrix.os }}
545    env:
546      QEMU_BUILD_VERSION: 8.1.5
547    strategy:
548      fail-fast: ${{ github.event_name != 'pull_request' }}
549      matrix: ${{ fromJson(needs.determine.outputs.test-matrix) }}
550    steps:
551    - uses: actions/checkout@v4
552      with:
553        submodules: true
554    - uses: ./.github/actions/install-rust
555      with:
556        toolchain: ${{ matrix.rust }}
557
558    # Install targets in order to build various tests throughout the repo
559    - run: rustup target add wasm32-wasi wasm32-unknown-unknown ${{ matrix.target }}
560    - run: echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV
561      if: matrix.target != ''
562
563    # Fix an ICE for now in gcc when compiling zstd with debuginfo (??)
564    - run: echo CFLAGS=-g0 >> $GITHUB_ENV
565      if: matrix.target == 'x86_64-pc-windows-gnu'
566
567    # Update binutils if MinGW due to https://github.com/rust-lang/rust/issues/112368
568    - run: C:/msys64/usr/bin/pacman.exe -S --needed mingw-w64-x86_64-gcc --noconfirm
569      if: matrix.target == 'x86_64-pc-windows-gnu'
570    - shell: pwsh
571      run: echo "C:\msys64\mingw64\bin" >> $Env:GITHUB_PATH
572      if: matrix.target == 'x86_64-pc-windows-gnu'
573
574    - run: cargo fetch --locked
575
576    - uses: actions/cache@v4
577      with:
578        path: ${{ runner.tool_cache }}/qemu
579        key: qemu-${{ matrix.target }}-${{ env.QEMU_BUILD_VERSION }}-patchcpuinfo
580      if: matrix.target != '' && matrix.os == 'ubuntu-latest'
581    - name: Install cross-compilation tools
582      run: |
583        set -ex
584        sudo apt-get update
585        sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build
586
587        # Configure Cargo for cross compilation and tell it how it can run
588        # cross executables
589        upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
590        echo CARGO_TARGET_${upcase}_RUNNER=${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} >> $GITHUB_ENV
591        echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV
592
593        # QEMU emulation is not always the speediest, so total testing time
594        # goes down if we build the libs in release mode when running tests.
595        echo CARGO_PROFILE_DEV_OPT_LEVEL=2 >> $GITHUB_ENV
596
597        # See comments in the source for why we enable this during QEMU
598        # emulation.
599        echo WASMTIME_TEST_NO_HOG_MEMORY=1 >> $GITHUB_ENV
600
601        # See if qemu is already in the cache
602        if [ -f ${{ runner.tool_cache }}/qemu/built ]; then
603          exit 0
604        fi
605
606        # Download and build qemu from source since the most recent release is
607        # way faster at arm emulation than the current version github actions'
608        # ubuntu image uses. Disable as much as we can to get it to build
609        # quickly.
610        curl https://download.qemu.org/qemu-$QEMU_BUILD_VERSION.tar.xz | tar xJf -
611        cd qemu-$QEMU_BUILD_VERSION
612        ./configure --target-list=${{ matrix.qemu_target }} --prefix=${{ runner.tool_cache}}/qemu --disable-tools --disable-slirp --disable-fdt --disable-capstone --disable-docs
613        ninja -C build install
614        touch ${{ runner.tool_cache }}/qemu/built
615      if: matrix.gcc != ''
616
617    # Record some CPU details; this is helpful information if tests fail due
618    # to CPU-specific features.
619    - name: CPU information
620      run: lscpu
621      if: matrix.os == 'ubuntu-latest'
622    - name: CPU information
623      run: sysctl hw
624      if: contains(matrix.os, 'macos')
625    - name: CPU information
626      run: wmic cpu list /format:list
627      shell: pwsh
628      if: matrix.os == 'windows-latest'
629
630    # Since MPK (PKU) is not present on some GitHub runners, we check if it is
631    # available before force-enabling it. This occasional testing is better than
632    # none at all; ideally we would test in a system-mode QEMU VM.
633    - name: Force-run with MPK enabled, if available
634      if: ${{ contains(matrix.name, 'MPK') }}
635      run: |
636        if cargo run --example mpk-available; then
637          echo "::notice::This CI run will force-enable MPK; this ensures tests conditioned with the \`WASMTIME_TEST_FORCE_MPK\` environment variable will run with MPK-protected memory pool stripes."
638          echo WASMTIME_TEST_FORCE_MPK=1 >> $GITHUB_ENV
639        else
640          echo "::warning::This CI run will not test MPK; it has been detected as not available on this machine (\`cargo run --example mpk-available\`)."
641        fi
642
643    # Build and test all features
644    - run: ./ci/run-tests.sh --locked ${{ matrix.bucket }}
645      env:
646        RUST_BACKTRACE: 1
647
648    # NB: the test job here is explicitly lacking in cancellation of this run if
649    # something goes wrong. These take the longest anyway and otherwise if
650    # Windows fails GitHub Actions will confusingly mark the failed Windows job
651    # as cancelled instead of failed.
652
653  # Test `wasmtime-wasi-nn` in its own job, as not all of its backends are
654  # compatible with all targets, and each must be tested separately anyways.
655  test_wasi_nn:
656    strategy:
657      matrix:
658        feature: ["openvino", "onnx"]
659        os: ["ubuntu-latest", "windows-latest"]
660    name: Test wasi-nn (${{ matrix.feature }}, ${{ matrix.os }})
661    runs-on: ${{ matrix.os }}
662    needs: determine
663    if: needs.determine.outputs.run-full
664    steps:
665    - uses: actions/checkout@v4
666      with:
667        submodules: true
668    - uses: ./.github/actions/install-rust
669
670    # Install OpenVINO
671    - uses: abrown/install-openvino-action@v8
672      if: runner.arch == 'X64'
673
674    # Install Rust targets.
675    - run: rustup target add wasm32-wasi
676
677    # Run the tests!
678    - run: cargo test -p wasmtime-wasi-nn --features ${{ matrix.feature }}
679      env:
680        RUST_BACKTRACE: 1
681
682    # common logic to cancel the entire run if this job fails
683    - run: gh run cancel ${{ github.run_id }}
684      if: failure() && github.event_name != 'pull_request'
685      env:
686        GH_TOKEN: ${{ github.token }}
687
688  # Test the `wasmtime-fuzzing` crate. Split out from the main tests because
689  # `--all-features` brings in OCaml, which is a pain to get setup for all
690  # targets.
691  test_fuzzing:
692    needs: determine
693    if: needs.determine.outputs.run-full
694    name: Test wasmtime-fuzzing
695    runs-on: 'ubuntu-latest'
696    steps:
697    - uses: actions/checkout@v4
698      with:
699        submodules: true
700    - uses: ./.github/actions/install-rust
701
702    # Run the tests
703    - run: |
704        cargo test -p wasmtime-fuzzing -p wasm-spec-interpreter
705      env:
706        RUST_BACKTRACE: 1
707
708    # common logic to cancel the entire run if this job fails
709    - run: gh run cancel ${{ github.run_id }}
710      if: failure() && github.event_name != 'pull_request'
711      env:
712        GH_TOKEN: ${{ github.token }}
713
714  # Test debug (DWARF) related functionality.
715  test_debug_dwarf:
716    needs: determine
717    if: needs.determine.outputs.run-full
718    name: Test DWARF debugging
719    runs-on: 'ubuntu-latest'
720    steps:
721    - uses: actions/checkout@v4
722      with:
723        submodules: true
724    - uses: ./.github/actions/install-rust
725    - run: rustup target add wasm32-wasi wasm32-unknown-unknown
726    - run: |
727        sudo apt-get update && sudo apt-get install -y gdb lldb-15 llvm
728        # woraround for https://bugs.launchpad.net/ubuntu/+source/llvm-defaults/+bug/1972855
729        sudo mkdir -p /usr/lib/local/lib/python3.10/dist-packages/lldb
730        sudo ln -s /usr/lib/llvm-15/lib/python3.10/dist-packages/lldb/* /usr/lib/python3/dist-packages/lldb/
731        cargo test test_debug_dwarf -- --ignored --test-threads 1
732      env:
733        RUST_BACKTRACE: 1
734        LLDB: lldb-15 # override default version, 14
735
736    # common logic to cancel the entire run if this job fails
737    - run: gh run cancel ${{ github.run_id }}
738      if: failure() && github.event_name != 'pull_request'
739      env:
740        GH_TOKEN: ${{ github.token }}
741
742  build-preview1-component-adapter:
743    name: Build wasi-preview1-component-adapter
744    needs: determine
745    if: needs.determine.outputs.preview1-adapter
746    runs-on: ubuntu-latest
747    permissions:
748      deployments: write
749      contents: write
750    steps:
751    - uses: actions/checkout@v4
752      with:
753        submodules: true
754    - uses: ./.github/actions/install-rust
755    - run: rustup target add wasm32-wasi wasm32-unknown-unknown
756
757    - name: Install wasm-tools
758      run: |
759        curl -L https://github.com/bytecodealliance/wasm-tools/releases/download/wasm-tools-1.0.27/wasm-tools-1.0.27-x86_64-linux.tar.gz | tar xfz -
760        echo `pwd`/wasm-tools-1.0.27-x86_64-linux >> $GITHUB_PATH
761
762    - run: ./ci/build-wasi-preview1-component-adapter.sh
763      env:
764        VERSION: ${{ github.sha }}
765
766    - uses: actions/upload-artifact@v4
767      with:
768        name: bins-wasi-preview1-component-adapter
769        path: target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.*.wasm
770
771
772    # common logic to cancel the entire run if this job fails
773    - run: gh run cancel ${{ github.run_id }}
774      if: failure() && github.event_name != 'pull_request'
775      env:
776        GH_TOKEN: ${{ github.token }}
777
778  # Verify the "min platform" example still works.
779  test-min-platform-example:
780    name: Test the min-platform example
781    needs: determine
782    if: needs.determine.outputs.run-full
783    runs-on: ubuntu-latest
784    steps:
785    - uses: actions/checkout@v4
786      with:
787        submodules: true
788    - uses: ./.github/actions/install-rust
789    - run: cargo install cbindgen --vers "^0.26" --locked
790    - run: rustup target add x86_64-unknown-none
791    - run: ./build.sh x86_64-unknown-none
792      working-directory: ./examples/min-platform
793
794    # Afterwards make sure the generated header file is up to date by ensuring
795    # that the regeneration process didn't change anything in-tree.
796    - run: git diff --exit-code
797
798    # Add the `wasmtime-platform.h` file as a release artifact
799    - uses: actions/upload-artifact@v4
800      with:
801        name: wasmtime-platform-header
802        path: examples/min-platform/embedding/wasmtime-platform.h
803
804    # common logic to cancel the entire run if this job fails
805    - run: gh run cancel ${{ github.run_id }}
806      if: failure() && github.event_name != 'pull_request'
807      env:
808        GH_TOKEN: ${{ github.token }}
809
810
811  build-wasmtime-target-wasm32:
812    name: Build wasmtime-target-wasm32
813    if: needs.determine.outputs.run-full
814    needs: determine
815    runs-on: ubuntu-latest
816    steps:
817    - uses: actions/checkout@v4
818      with:
819        submodules: true
820    - uses: ./.github/actions/install-rust
821    - run: rustup target add wasm32-wasi wasm32-unknown-unknown
822    - run: cargo build --target wasm32-wasi --no-default-features --features compile,cranelift,all-arch
823      env:
824        VERSION: ${{ github.sha }}
825
826    # common logic to cancel the entire run if this job fails
827    - run: gh run cancel ${{ github.run_id }}
828      if: failure() && github.event_name != 'pull_request'
829      env:
830        GH_TOKEN: ${{ github.token }}
831
832
833  bench:
834    needs: determine
835    if: needs.determine.outputs.run-full
836    name: Run benchmarks
837    runs-on: ubuntu-latest
838    steps:
839    - uses: actions/checkout@v4
840      with:
841        submodules: true
842    - uses: ./.github/actions/install-rust
843    - run: rustup target add wasm32-wasi
844    - run: cargo test --benches --release
845
846    # common logic to cancel the entire run if this job fails
847    - run: gh run cancel ${{ github.run_id }}
848      if: failure() && github.event_name != 'pull_request'
849      env:
850        GH_TOKEN: ${{ github.token }}
851
852  # Verify that cranelift's code generation is deterministic
853  meta_deterministic_check:
854    needs: determine
855    if: needs.determine.outputs.run-full
856    name: Meta deterministic check
857    runs-on: ubuntu-latest
858    steps:
859    - uses: actions/checkout@v4
860      with:
861        submodules: true
862    - uses: ./.github/actions/install-rust
863    - run: cd cranelift/codegen && cargo build --features all-arch
864    - run: ci/ensure_deterministic_build.sh
865
866    # common logic to cancel the entire run if this job fails
867    - run: gh run cancel ${{ github.run_id }}
868      if: failure() && github.event_name != 'pull_request'
869      env:
870        GH_TOKEN: ${{ github.token }}
871
872  verify-publish:
873    needs: determine
874    if: github.repository == 'bytecodealliance/wasmtime' && needs.determine.outputs.run-full
875    runs-on: ubuntu-latest
876    steps:
877    - uses: actions/checkout@v4
878      with:
879        submodules: true
880    - uses: ./.github/actions/install-rust
881    - run: |
882        cd ${{ runner.tool_cache }}
883        curl -L https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz | tar xzf -
884        echo "`pwd`/sccache-0.2.13-x86_64-unknown-linux-musl" >> $GITHUB_PATH
885        echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV
886    - run: rustc scripts/publish.rs
887    # Make sure the tree is publish-able as-is
888    - run: ./publish verify
889    # Make sure we can bump version numbers for the next release
890    - run: ./publish bump
891
892    # common logic to cancel the entire run if this job fails
893    - run: gh run cancel ${{ github.run_id }}
894      if: failure() && github.event_name != 'pull_request'
895      env:
896        GH_TOKEN: ${{ github.token }}
897
898  # Run a subset of tests under MIRI on CI to help check the `unsafe` code in
899  # Wasmtime to make sure it's at least not obviously incorrect for basic usage.
900  # Note that this doesn't run the full test suite since MIRI can't actually run
901  # WebAssembly itself at this time (aka it doesn't support a JIT). There are a
902  # number of annotations throughout the code which gates some tests on MIRI not
903  # being run.
904  #
905  # Note that `cargo nextest` is used here additionally to get parallel test
906  # execution by default to help cut down on the time in CI.
907  miri:
908    strategy:
909      matrix:
910        crate: ["wasmtime", "wasmtime-cli", "wasmtime-environ"]
911    needs: determine
912    if: needs.determine.outputs.run-full && github.repository == 'bytecodealliance/wasmtime'
913    name: Miri
914    runs-on: ubuntu-latest
915    env:
916      CARGO_NEXTEST_VERSION: 0.9.67
917    steps:
918    - uses: actions/checkout@v4
919      with:
920        submodules: true
921    - uses: ./.github/actions/install-rust
922      with:
923        toolchain: wasmtime-ci-pinned-nightly
924    - run: rustup component add rust-src miri
925    - uses: actions/cache@v4
926      with:
927        path: ${{ runner.tool_cache }}/cargo-nextest
928        key: cargo-nextest-bin-${{ env.CARGO_NEXTEST_VERSION }}
929    - run: echo "${{ runner.tool_cache }}/cargo-nextest/bin" >> $GITHUB_PATH
930    - run: cargo install --root ${{ runner.tool_cache }}/cargo-nextest --version ${{ env.CARGO_NEXTEST_VERSION }} cargo-nextest --locked
931    - run: |
932        cargo miri nextest run -j4 --no-fail-fast -p ${{ matrix.crate }}
933      env:
934        MIRIFLAGS: -Zmiri-strict-provenance
935
936    # common logic to cancel the entire run if this job fails
937    - run: gh run cancel ${{ github.run_id }}
938      if: failure() && github.event_name != 'pull_request'
939      env:
940        GH_TOKEN: ${{ github.token }}
941
942  # Perform release builds of `wasmtime` and `libwasmtime.so`. Builds a variety
943  # of platforms and architectures and then uploads the release artifacts to
944  # this workflow run's list of artifacts.
945  #
946  # Note that the full matrix is computed by `ci/build-build-matrix.js`.
947  build:
948    needs: determine
949    if: needs.determine.outputs.run-full
950    name: Release build for ${{ matrix.build }}
951    runs-on: ${{ matrix.os }}
952    strategy:
953      fail-fast: ${{ github.event_name != 'pull_request' }}
954      matrix: ${{ fromJson(needs.determine.outputs.build-matrix) }}
955    steps:
956    - uses: actions/checkout@v4
957      with:
958        submodules: true
959
960    - uses: ./.github/actions/install-rust
961      with:
962        toolchain: ${{ matrix.rust }}
963    - run: |
964        rustup component add rust-src
965        rustup target add ${{ matrix.target }}
966
967    # On one builder produce the source tarball since there's no need to produce
968    # it everywhere
969    - run: ./ci/build-src-tarball.sh
970      if: matrix.build == 'x86_64-linux'
971    - uses: ./.github/actions/binary-compatible-builds
972      with:
973        name: ${{ matrix.build }}
974
975    - uses: ./.github/actions/android-ndk
976      if: contains(matrix.target, 'android')
977      with:
978        target: ${{ matrix.target }}
979
980    - run: $CENTOS ./ci/build-release-artifacts.sh "${{ matrix.build }}" "${{ matrix.target }}"
981
982    # Assemble release artifacts appropriate for this platform, then upload them
983    # unconditionally to this workflow's files so we have a copy of them.
984    - run: ./ci/build-tarballs.sh "${{ matrix.build }}" "${{ matrix.target }}"
985
986    - uses: actions/upload-artifact@v4
987      with:
988        name: bins-${{ matrix.build }}
989        path: dist
990
991    # common logic to cancel the entire run if this job fails
992    - run: gh run cancel ${{ github.run_id }}
993      if: failure() && github.event_name != 'pull_request'
994      env:
995        GH_TOKEN: ${{ github.token }}
996
997  # This is a "join node" which depends on all prior workflows. The merge queue,
998  # for example, gates on this to ensure that everything has executed
999  # successfully.
1000  #
1001  # Note that this is required currently for odd reasons with github. Notably
1002  # the set of checks to enter the merge queue and leave the merge queue must
1003  # be the same which means that the "build" step for example shows as skipped
1004  # for PRs but expands to many different steps for merge-queue-based PRs. That
1005  # means that for that step there's no single name to gate on, so it's required
1006  # to have a "join" node here which joins everything.
1007  #
1008  # Note that this currently always runs to always report a status, even on
1009  # cancellation and even if dependency steps fail. Each dependency tries to
1010  # cancel the whole run if it fails, so if a test matrix entry fails, for
1011  # example, it cancels the build matrix entries too. This step then tries to
1012  # fail on cancellation to ensure that the dependency failures are propagated
1013  # correctly.
1014  ci-status:
1015    name: Record the result of testing and building steps
1016    runs-on: ubuntu-latest
1017    needs:
1018      - test
1019      - test_capi
1020      - test_debug_dwarf
1021      - test_fuzzing
1022      - test_wasi_nn
1023      - test_nightly
1024      - build
1025      - rustfmt
1026      - clangformat
1027      - cargo_deny
1028      - cargo_vet
1029      - doc
1030      - micro_checks
1031      - monolith_checks
1032      - checks_winarm64
1033      - bench
1034      - meta_deterministic_check
1035      - verify-publish
1036      - determine
1037      - miri
1038      - build-preview1-component-adapter
1039      - build-wasmtime-target-wasm32
1040      - test-min-platform-example
1041    if: always()
1042    steps:
1043    - name: Successful test and build
1044      if: ${{ !(contains(needs.*.result, 'failure')) }}
1045      run: exit 0
1046    - name: Failing test and build
1047      if: ${{ contains(needs.*.result, 'failure') }}
1048      run: exit 1
1049    - name: Report failure on cancellation
1050      if: ${{ contains(needs.*.result, 'cancelled') || cancelled() }}
1051      run: exit 1
1052
1053  # The purpose of this jobs is to watch for changes on the `release-*`
1054  # branches of this repository and look for the term
1055  # "automatically-tag-and-release-this-commit" within merged PRs/commits. Once
1056  # that term is found the current version of `Cargo.toml`, the `wasmtime-cli`
1057  # Cargo.toml, is created as a tag and the tag is pushed to the repo.
1058  # Currently the tag is created through the GitHub API with an access token to
1059  # ensure that CI is further triggered for the tag itself which performs the
1060  # full release process.
1061  #
1062  # Note that this depends on the `ci-status` step above which is the "join"
1063  # point of this workflow for when everything succeeds. the purpose of that is
1064  # so that the tag is only created after the aftifacts have been uploaded for
1065  # this workflow as the `publish-artifacts.yml` workflow will download these
1066  # artifacts and then publish them to the tag.
1067  push-tag:
1068    runs-on: ubuntu-latest
1069    needs: ci-status
1070    if: |
1071      always()
1072      && needs.ci-status.result == 'success'
1073      && github.event_name == 'push'
1074      && startsWith(github.ref, 'refs/heads/release-')
1075      && github.repository == 'bytecodealliance/wasmtime'
1076    steps:
1077    - uses: actions/checkout@v4
1078      with:
1079        submodules: true
1080        fetch-depth: 0
1081    - name: Test if tag is needed
1082      run: |
1083        git log ${{ github.event.before }}...${{ github.event.after }} | tee main.log
1084        version=$(grep '^version =' Cargo.toml | head -n 1 | sed 's/.*"\(.*\)"/\1/')
1085        echo "version: $version"
1086        echo "version=$version" >> $GITHUB_OUTPUT
1087        echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
1088        if grep -q "automatically-tag-and-release-this-commit" main.log; then
1089          echo push-tag
1090          echo "push_tag=yes" >> $GITHUB_OUTPUT
1091        else
1092          echo no-push-tag
1093          echo "push_tag=no" >> $GITHUB_OUTPUT
1094        fi
1095      id: tag
1096    - name: Push the tag
1097      run: |
1098        git_refs_url=$(jq .repository.git_refs_url $GITHUB_EVENT_PATH | tr -d '"' | sed 's/{\/sha}//g')
1099        curl -iX POST $git_refs_url \
1100          -H "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \
1101          -d @- << EOF
1102        {
1103          "ref": "refs/tags/v${{ steps.tag.outputs.version }}",
1104          "sha": "${{ steps.tag.outputs.sha }}"
1105        }
1106        EOF
1107      if: steps.tag.outputs.push_tag == 'yes'
1108