name: CI

on:
  - push
  - pull_request

defaults:
  run:
    shell: bash

permissions: {}

env:
  # Disabling incr comp reduces cache size and incr comp doesn't save as much
  # on CI anyway.
  CARGO_BUILD_INCREMENTAL: false
  # Rust's CI denies warnings. Deny them here too to ensure subtree syncs don't
  # fail because of warnings.
  RUSTFLAGS: "-Dwarnings"

jobs:
  rustfmt:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
    - uses: actions/checkout@v4

    - name: Avoid installing rustc-dev
      run: |
        sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain
        rustfmt -v

    - name: Rustfmt
      run: |
        cargo fmt --check
        rustfmt --check --edition 2024 build_system/main.rs
        rustfmt --check --edition 2024 example/*
        rustfmt --check --edition 2024 scripts/*.rs


  test:
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60

    env:
      CG_CLIF_EXPENSIVE_CHECKS: 1

    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            env:
              TARGET_TRIPLE: x86_64-unknown-linux-gnu
          - os: ubuntu-24.04-arm
            env:
              TARGET_TRIPLE: aarch64-unknown-linux-gnu
          - os: macos-13
            env:
              TARGET_TRIPLE: x86_64-apple-darwin
          - os: macos-latest
            env:
              TARGET_TRIPLE: aarch64-apple-darwin
          - os: ubuntu-latest
            env:
              TARGET_TRIPLE: s390x-unknown-linux-gnu
            apt_deps: gcc-s390x-linux-gnu qemu-user
          - os: ubuntu-latest
            env:
              TARGET_TRIPLE: riscv64gc-unknown-linux-gnu
            apt_deps: gcc-riscv64-linux-gnu qemu-user
          - os: windows-latest
            env:
              TARGET_TRIPLE: x86_64-pc-windows-msvc
          - os: windows-latest
            env:
              TARGET_TRIPLE: x86_64-pc-windows-gnu

    steps:
    - uses: actions/checkout@v4

    - name: CPU features
      if: matrix.os == 'ubuntu-latest'
      run: cat /proc/cpuinfo

    - name: Cache cargo target dir
      uses: actions/cache@v4
      with:
        path: build/cg_clif
        key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}

    - name: Set MinGW as the default toolchain
      if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
      run: rustup set default-host x86_64-pc-windows-gnu

    - name: Install toolchain and emulator
      if: matrix.apt_deps != null
      run: |
        sudo apt-get update
        sudo apt-get install -y ${{ matrix.apt_deps }}

    - name: Prepare dependencies
      run: ./y.sh prepare

    - name: Build
      run: ./y.sh build --sysroot none

    - name: Test
      env:
        TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
      run: ./y.sh test

    - name: Install LLVM standard library
      run: rustup target add ${{ matrix.env.TARGET_TRIPLE }}

    # This is roughly config rust-lang/rust uses for testing
    - name: Test with LLVM sysroot
      env:
        TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
      run: ./y.sh test --sysroot llvm --no-unstable-features


  # This job doesn't use cg_clif in any way. It checks that all cg_clif tests work with cg_llvm too.
  test_llvm:
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
    - uses: actions/checkout@v4

    - name: CPU features
      run: cat /proc/cpuinfo

    - name: Prepare dependencies
      run: ./y.sh prepare

    - name: Disable JIT tests
      run: |
        sed -i 's/jit./#jit./' config.txt

    - name: Test
      env:
        TARGET_TRIPLE: x86_64-unknown-linux-gnu
      run: ./y.sh test --use-backend llvm

  bench:
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
    - uses: actions/checkout@v4

    - name: CPU features
      run: cat /proc/cpuinfo

    - name: Cache cargo target dir
      uses: actions/cache@v4
      with:
        path: build/cg_clif
        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}

    - name: Install hyperfine
      run: |
        sudo apt update
        sudo apt install -y hyperfine

    - name: Build
      run: ./y.sh build --sysroot none

    - name: Benchmark
      run: ./y.sh bench


  dist:
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60

    strategy:
      fail-fast: false
      matrix:
        include:
          # Intentionally using an older ubuntu version to lower the glibc requirements of the distributed cg_clif
          - os: ubuntu-22.04
            env:
              TARGET_TRIPLE: x86_64-unknown-linux-gnu
          - os: ubuntu-24.04-arm
            env:
              TARGET_TRIPLE: aarch64-unknown-linux-gnu
          - os: macos-13
            env:
              TARGET_TRIPLE: x86_64-apple-darwin
          - os: macos-latest
            env:
              TARGET_TRIPLE: aarch64-apple-darwin
          - os: windows-latest
            env:
              TARGET_TRIPLE: x86_64-pc-windows-msvc
          - os: windows-latest
            env:
              TARGET_TRIPLE: x86_64-pc-windows-gnu

    steps:
    - uses: actions/checkout@v4

    - name: Cache cargo target dir
      uses: actions/cache@v4
      with:
        path: build/cg_clif
        key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-dist-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}

    - name: Set MinGW as the default toolchain
      if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
      run: rustup set default-host x86_64-pc-windows-gnu

    - name: Build backend
      run: ./y.sh build --sysroot none

    - name: Build sysroot
      run: ./y.sh build

    - name: Package prebuilt cg_clif
      run: tar cvfJ cg_clif.tar.xz dist

    - name: Upload prebuilt cg_clif
      uses: actions/upload-artifact@v4
      with:
        name: cg_clif-${{ matrix.env.TARGET_TRIPLE }}
        path: cg_clif.tar.xz

  release:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    if: ${{ github.ref == 'refs/heads/master' }}
    needs: [rustfmt, test, bench, dist]

    permissions:
      contents: write # for creating the dev tag and release

    concurrency:
      group: release-dev
      cancel-in-progress: true

    steps:
      - uses: actions/checkout@v4

      - name: Download all built artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts/

      - run: |
          ls -R artifacts/
          mkdir release/
          pushd artifacts/
          for dir in *; do
            mv $dir/cg_clif.tar.xz ../release/$dir.tar.xz
            rmdir $dir/ # verify $dir is empty
          done
          popd
          rmdir artifacts/ # verify all artifacts are represented in release/
          ls -R release/

      - name: Publish release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh release delete --cleanup-tag -y dev || true
          gh release create --target $GITHUB_SHA --prerelease dev release/*
