name: CI

on:
  push:
    branches: ["*"]
    tags: [v0.*]
  pull_request:
  merge_group:

env:
  CARGO_INCREMENTAL: false
  CARGO_TERM_COLOR: always
  RUST_LOG: info
  RUST_BACKTRACE: full
  # This is the MSRV used by `wgpu` itself and all surrounding infrastructure.
  REPO_MSRV: "1.70"
  # This is the MSRV used by the `wgpu-core`, `wgpu-hal`, and `wgpu-types` crates,
  # to ensure that they can be used with firefox.
  CORE_MSRV: "1.65"
  PKG_CONFIG_ALLOW_CROSS: 1 # allow android to work
  RUSTFLAGS: --cfg=web_sys_unstable_apis -D warnings
  RUSTDOCFLAGS: -Dwarnings
  CACHE_SUFFIX: c # cache busting

# We distinguish the following kinds of builds:
# - native: build for the same target as we compile on
# - web: build for the Web
# - em: build for the Emscripten

# For build time and size optimization we disable debug
# entirely on clippy jobs and reduce it to line-numbers
# only for ones where we run tests.
#
# Additionally, we disable incremental builds entirely
# as our caching system doesn't actually cache our crates.
# It adds overhead to the build and another point of failure.

jobs:
  check:
    strategy:
      fail-fast: false
      matrix:
        include:
          # Windows
          - name: Windows x86_64
            os: windows-2022
            target: x86_64-pc-windows-msvc
            kind: native

          # MacOS
          - name: MacOS x86_64
            os: macos-12
            target: x86_64-apple-darwin
            kind: native

          - name: MacOS aarch64
            os: macos-12
            target: aarch64-apple-darwin
            kind: native

          # IOS
          - name: IOS aarch64
            os: macos-12
            target: aarch64-apple-ios
            kind: native

          # Linux
          - name: Linux x86_64
            os: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
            kind: native

          - name: Linux aarch64
            os: ubuntu-22.04
            target: aarch64-unknown-linux-gnu
            kind: native

          # Android
          - name: Android aarch64
            os: ubuntu-22.04
            target: aarch64-linux-android
            kind: native

          # WebGPU/WebGL
          - name: WebAssembly
            os: ubuntu-22.04
            target: wasm32-unknown-unknown
            kind: web

          - name: Emscripten
            os: ubuntu-22.04
            target: wasm32-unknown-emscripten
            kind: em

    name: Clippy ${{ matrix.name }}
    runs-on: ${{ matrix.os }}

    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Repo MSRV toolchain
        run: |
          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy --target ${{ matrix.target }}
          rustup override set ${{ env.REPO_MSRV }}
          cargo -V

      - name: disable debug
        shell: bash
        run: |
          mkdir -p .cargo
          echo """
          [profile.dev]
          debug = false" >> .cargo/config.toml

      - name: caching
        uses: Swatinem/rust-cache@v2
        with:
          key: clippy-${{ matrix.target }}-${{ matrix.kind }}-${{ env.CACHE_SUFFIX }}

      - name: (linux aarch64) install aarch64-linux-gnu g++
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          set -e

          sudo apt-get update -y -qq

          sudo apt-get install g++-aarch64-linux-gnu

      - name: (android) add android apk to path
        if: matrix.target == 'aarch64-linux-android'
        run: |
          # clang++ will be detected correctly by CC from path
          echo "$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH

          # the android sdk doesn't use the conventional name for ar, so explicitly set it.
          echo "AR_aarch64_linux_android=llvm-ar" >> "$GITHUB_ENV"

      - name: check web
        if: matrix.kind == 'web'
        shell: bash
        run: |
          set -e

          # build for WebGPU
          cargo clippy --target ${{ matrix.target }} --tests --features glsl,spirv,fragile-send-sync-non-atomic-wasm
          cargo clippy --target ${{ matrix.target }} --tests --features glsl,spirv
          cargo doc --target ${{ matrix.target }} --no-deps --features glsl,spirv

          # all features
          cargo clippy --target ${{ matrix.target }} --tests --all-features
          cargo doc --target ${{ matrix.target }} --no-deps --all-features

      - name: check em
        if: matrix.kind == 'em'
        shell: bash
        run: |
          set -e

          # build for Emscripten
          cargo clippy --target ${{ matrix.target }} -p wgpu -p wgpu-hal --no-default-features

          # Don't check samples since we use winit in our samples which has dropped support for Emscripten.

          # all features
          cargo clippy --target ${{ matrix.target }} -p wgpu-hal --all-features
          cargo clippy --target ${{ matrix.target }} -p wgpu --all-features

      - name: check native
        if: matrix.kind == 'native'
        shell: bash
        run: |
          set -e

          # check with no features
          cargo clippy --target ${{ matrix.target }} --no-default-features

          # Check with all features.
          cargo clippy --target ${{ matrix.target }} --tests --all-features

          # build docs
          cargo doc --target ${{ matrix.target }} --all-features --no-deps

  # We run minimal checks on the MSRV of the core crates, ensuring that
  # its dependency tree does not cause issues for firefox.
  #
  # We don't test all platforms, just ones with different dependency stacks.
  check-core-msrv:
    strategy:
      fail-fast: false
      matrix:
        include:
          # Windows
          - name: Windows x86_64
            os: windows-2022
            target: x86_64-pc-windows-msvc

          # MacOS
          - name: MacOS x86_64
            os: macos-12
            target: x86_64-apple-darwin

          # Linux
          - name: Linux x86_64
            os: ubuntu-22.04
            target: x86_64-unknown-linux-gnu

    name: MSRV Check ${{ matrix.name }}
    runs-on: ${{ matrix.os }}

    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Core MSRV toolchain
        run: |
          rustup toolchain install ${{ env.CORE_MSRV }} --no-self-update --profile=minimal --component clippy --target ${{ matrix.target }}
          rustup override set ${{ env.CORE_MSRV }}
          cargo -V

      - name: disable debug
        shell: bash
        run: |
          mkdir -p .cargo
          echo """
          [profile.dev]
          debug = false" >> .cargo/config.toml

      - name: caching
        uses: Swatinem/rust-cache@v2
        with:
          key: msrv-check-${{ matrix.target }}-${{ env.CACHE_SUFFIX }}

      - name: check native
        shell: bash
        run: |
          set -e

          # check wgpu-core with all features. This will also get wgpu-hal and wgpu-types.
          cargo check --target ${{ matrix.target }} --all-features -p wgpu-core

  naga-minimal-versions:
    name: MSRV naga Minimal Versions
    runs-on: ubuntu-22.04

    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Core MSRV toolchain
        run: |
          rustup toolchain install ${{ env.CORE_MSRV }} --no-self-update --profile=minimal --component clippy
          rustup override set ${{ env.CORE_MSRV }}
          cargo -V

      - name: Install Nightly toolchain
        run: |
          rustup toolchain install nightly --no-self-update --profile=minimal --component clippy
          cargo +nightly -V

      - name: Install cargo-hack
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-hack

      - name: disable debug
        shell: bash
        run: |
          mkdir -p .cargo
          echo """
          [profile.dev]
          debug = false" >> .cargo/config.toml

      - name: Set Minimal Versions
        shell: bash
        run: |
          set -e

          cargo +nightly hack generate-lockfile --remove-dev-deps -Z minimal-versions -p naga -p naga-cli

      - name: Clippy
        shell: bash
        run: |
          set -e

          cargo clippy --all-features -p naga -p naga-cli

  wasm-test:
    name: Test WebAssembly
    runs-on: ubuntu-latest
    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Repo MSRV toolchain
        run: |
          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy --target wasm32-unknown-unknown
          rustup override set ${{ env.REPO_MSRV }}
          cargo -V

      - name: Install wasm-pack
        uses: taiki-e/install-action@v2
        with:
          tool: wasm-pack

      - name: execute tests
        run: |
          cd wgpu
          wasm-pack test --headless --chrome --features webgl --workspace

  gpu-test:
    strategy:
      fail-fast: false
      matrix:
        include:
          # Windows
          - name: Windows x86_64
            os: windows-2022

          # Mac
          - name: Mac aarch64
            os: [self-hosted, macOS]

          # Linux
          - name: Linux x86_64
            os: ubuntu-22.04

    name: Test ${{ matrix.name }}
    runs-on: ${{ matrix.os }}

    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Repo MSRV toolchain
        run: |
          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal -c llvm-tools
          cargo -V

      - name: Install cargo-nextest and cargo-llvm-cov
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-nextest,cargo-llvm-cov

        # Cache step must go before warp and mesa install on windows as they write into the
        # target directory, and rust-cache will overwrite the entirety of the target directory.
      - name: caching
        uses: Swatinem/rust-cache@v2
        if: matrix.os[0] != 'self-hosted'
        with:
          key: test-${{ matrix.os }}-${{ env.CACHE_SUFFIX }}
          workspaces: |
            . -> target
            xtask -> xtask/target

      - name: (windows) install warp
        if: matrix.os == 'windows-2022'
        shell: bash
        run: |
          set -e

          curl.exe -L https://www.nuget.org/api/v2/package/Microsoft.Direct3D.WARP/1.0.7.1 -o warp.zip
          7z.exe e warp.zip -owarp build/native/amd64/d3d10warp.dll

          mkdir -p target/llvm-cov-target/debug/deps

          cp -v warp/d3d10warp.dll target/llvm-cov-target/debug/
          cp -v warp/d3d10warp.dll target/llvm-cov-target/debug/deps

      - name: (windows) install mesa
        if: matrix.os == 'windows-2022'
        shell: bash
        run: |
          set -e

          curl.exe -L https://github.com/pal1000/mesa-dist-win/releases/download/23.2.1/mesa3d-23.2.1-release-msvc.7z -o mesa.7z
          7z.exe e mesa.7z -omesa x64/{opengl32.dll,libgallium_wgl.dll,libglapi.dll,vulkan_lvp.dll,lvp_icd.x86_64.json}

          cp -v mesa/* target/llvm-cov-target/debug/
          cp -v mesa/* target/llvm-cov-target/debug/deps

          echo "VK_DRIVER_FILES=$PWD/mesa/lvp_icd.x86_64.json" >> "$GITHUB_ENV"
          echo "GALLIUM_DRIVER=llvmpipe" >> "$GITHUB_ENV"

      - name: (linux) install llvmpipe, lavapipe, vulkan sdk
        if: matrix.os == 'ubuntu-22.04'
        shell: bash
        run: |
          set -e

          sudo apt-get update -y -qq

          # vulkan sdk
          wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
          sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list

          sudo add-apt-repository ppa:kisak/kisak-mesa

          sudo apt-get update
          sudo apt install -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk mesa-vulkan-drivers

      - name: disable debug
        shell: bash
        run: |
          mkdir -p .cargo
          echo """
          [profile.dev]
          debug = 1" >> .cargo/config.toml

      - name: run wgpu-info
        shell: bash
        run: |
          echo "$PATH"
          
          export RUST_LOG=trace

          # This needs to match the command in xtask/tests.rs
          cargo llvm-cov --no-cfg-coverage --no-report run --bin wgpu-info

      - name: run tests
        shell: bash
        run: |
          set -e

          cargo xtask test --llvm-cov

      - name: check naga snapshots
        run: git diff --exit-code -- naga/tests/out

      - uses: actions/upload-artifact@v3
        if: always() # We want artifacts even if the tests fail.
        with:
          name: comparison-images
          path: |
            **/*-actual.png
            **/*-difference.png

      - name: generate coverage report
        shell: bash
        run: |
          set -e

          cargo llvm-cov report --lcov --output-path lcov.info

      - name: upload coverage report to codecov
        uses: codecov/codecov-action@v3
        with:
          files: lcov.info

  doctest:
    name: Doctest
    runs-on: ubuntu-latest

    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Repo MSRV toolchain
        run: |
          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component rustfmt
          rustup override set ${{ env.REPO_MSRV }}
          cargo -V

      - name: disable debug
        shell: bash
        run: |
          mkdir -p .cargo
          echo """
          [profile.dev]
          debug = 1" >> .cargo/config.toml

      - name: caching
        uses: Swatinem/rust-cache@v2
        with:
          key: doctests-${{ env.CACHE_SUFFIX }}

      - name: run doctests
        shell: bash
        run: |
          set -e

          cargo test --doc

  fmt:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install Repo MSRV toolchain
        run: |
          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component rustfmt
          rustup override set ${{ env.REPO_MSRV }}
          cargo -V

      - name: run rustfmt
        run: |
          cargo fmt -- --check
          cargo fmt --manifest-path xtask/Cargo.toml -- --check

  check-cts-runner:
    name: Clippy cts_runner
    runs-on: ubuntu-latest
    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Install MSRV toolchain
        run: |
          rustup toolchain install ${{ env.REPO_MSRV }} --no-self-update --profile=minimal --component clippy
          rustup override set ${{ env.REPO_MSRV }}
          cargo -V

      - name: disable debug
        shell: bash
        run: |
          mkdir -p .cargo
          echo """
          [profile.dev]
          debug = 1" >> .cargo/config.toml

      - name: caching
        uses: Swatinem/rust-cache@v2
        with:
          key: cts-runner-${{ env.CACHE_SUFFIX }}

      - name: build Deno
        run: |
          cargo clippy --manifest-path cts_runner/Cargo.toml

  # Separate job so that new advisories don't block CI.
  #
  # This job is not required to pass for PRs to be merged.
  cargo-deny-check-advisories:
    name: "cargo-deny advisories"
    runs-on: ubuntu-latest
    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Run `cargo deny check`
        uses: EmbarkStudios/cargo-deny-action@v1
        with:
          command: check advisories
          arguments: --all-features --workspace
          rust-version: ${{ env.REPO_MSRV }}

  cargo-deny-check-rest:
    name: "cargo-deny"
    runs-on: ubuntu-latest
    steps:
      - name: checkout repo
        uses: actions/checkout@v4

      - name: Run `cargo deny check`
        uses: EmbarkStudios/cargo-deny-action@v1
        with:
          command: check bans licenses sources
          arguments: --all-features --workspace
          rust-version: ${{ env.REPO_MSRV }}
