  name: Build

  on:
    create:
      tags:
    push:
      branches:
        - main
    pull_request:

  jobs:
    build:
      name: Native
      runs-on: ${{ matrix.os }}
      strategy:
        fail-fast: false
        matrix:
          os:
            - ubuntu-latest
            - ubuntu-24.04-arm
            - macos-latest
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Install deps (Linux)
          run: |
            sudo apt-get update
            sudo apt-get install libedit-dev xxd libffi-dev libssl-dev
          if: startsWith(matrix.os, 'ubuntu-')
        - name: Install deps (macOS)
          run: |
            brew update
            brew install vim libffi openssl make coreutils bash
            echo "$(brew --prefix)/opt/make/libexec/gnubin" >> $GITHUB_PATH
          if: matrix.os == 'macos-latest'
        - name: Fetch tags
          run: git fetch --tags --force origin
        - name: Build
          run: make release
        - name: Test
          run: make test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: ${{ format('tpl-{0}-{1}', runner.os, runner.arch) }}
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    windows:
      name: Windows
      runs-on: windows-latest
      defaults:
        run:
          shell: msys2 {0}
      steps:
        - uses: msys2/setup-msys2@v2
        - name: Install deps
          run: |
            pacman --noconfirm -S make mingw-w64-x86_64-gcc mingw-w64-x86_64-headers-git mingw-w64-x86_64-libffi mingw-w64-x86_64-dlfcn
            pacman --noconfirm -S mingw-w64-x86_64-openssl mingw-w64-x86_64-libwinpthread vim diffutils git
        - name: Configure line endings
          run: git config --global core.autocrlf input
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Fetch tags
          run: git fetch --tags --force origin
        - name: Build
          run: make WIN=1 release
        - name: Test
          run: make test
        - name: Make Windows-friendly
          run: |
            cp ${MINGW_PREFIX}/bin/{libcrypto-3-x64.dll,libssl-3-x64.dll,libffi-8.dll,libdl.dll,libwinpthread-1.dll} .
            echo -e "\n\nOpenSSL (libcrypto-3-x64.dll, libssl-3-x64.dll):\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/openssl/LICENSE >> ATTRIBUTION
            echo -e "\n\nlibffi: (libffi-8.dll)\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/libffi/LICENSE >> ATTRIBUTION
            echo -e "\n\ndlfcn: (libdl.dll)\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/dlfcn/LICENSE >> ATTRIBUTION
            echo -e "\n\nlibwinpthread:\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/libwinpthread/COPYING >> ATTRIBUTION
            echo -e "\n\nisocline:\n" >> ATTRIBUTION
            cat src/isocline/LICENSE >> ATTRIBUTION
            if [ ! -f "tpl.exe" ]; then
              mv tpl tpl.exe
            fi
            mv ATTRIBUTION ATTRIBUTION.txt
            mv LICENSE LICENSE.txt
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: ${{ format('tpl-{0}-{1}', runner.os, runner.arch) }}
            path: |
              tpl.exe
              LICENSE.txt
              ATTRIBUTION.txt
              README.md
              libcrypto-3-x64.dll
              libssl-3-x64.dll
              libffi-8.dll
              libdl.dll
              libwinpthread-1.dll

    windows-arm64:
      name: Windows ARM64
      runs-on: windows-11-arm
      defaults:
        run:
          shell: msys2 {0}
      steps:
        - uses: msys2/setup-msys2@v2
          with:
            msystem: CLANGARM64
            update: true
        - name: Install deps
          run: |
            pacman --noconfirm -S make file mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-headers-git
            pacman --noconfirm -S mingw-w64-clang-aarch64-libffi mingw-w64-clang-aarch64-dlfcn mingw-w64-clang-aarch64-openssl
            pacman --noconfirm -S mingw-w64-clang-aarch64-winpthreads mingw-w64-clang-aarch64-libwinpthread vim diffutils git
        - name: Configure line endings
          run: git config --global core.autocrlf input
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Fetch tags
          run: git fetch --tags --force origin
        - name: Build
          run: make WIN=1 CC=clang HOST_CC=clang AR=llvm-ar release
        - name: Verify architecture
          run: |
            llvm-readobj --file-headers tpl.exe | grep -q 'Machine: IMAGE_FILE_MACHINE_ARM64'
        - name: Test
          run: make test
        - name: Make Windows-friendly
          run: |
            cp ${MINGW_PREFIX}/bin/{libcrypto-3-arm64.dll,libssl-3-arm64.dll,libffi-8.dll,libdl.dll,libwinpthread-1.dll} .
            echo -e "\n\nOpenSSL (libcrypto-3-arm64.dll, libssl-3-arm64.dll):\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/openssl/LICENSE >> ATTRIBUTION
            echo -e "\n\nlibffi: (libffi-8.dll)\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/libffi/LICENSE >> ATTRIBUTION
            echo -e "\n\ndlfcn: (libdl.dll)\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/dlfcn/LICENSE >> ATTRIBUTION
            echo -e "\n\nlibwinpthread:\n" >> ATTRIBUTION
            cat ${MINGW_PREFIX}/share/licenses/winpthreads/COPYING >> ATTRIBUTION
            echo -e "\n\nisocline:\n" >> ATTRIBUTION
            cat src/isocline/LICENSE >> ATTRIBUTION
            mv ATTRIBUTION ATTRIBUTION.txt
            mv LICENSE LICENSE.txt
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-Windows-ARM64
            path: |
              tpl.exe
              LICENSE.txt
              ATTRIBUTION.txt
              README.md
              libcrypto-3-arm64.dll
              libssl-3-arm64.dll
              libffi-8.dll
              libdl.dll
              libwinpthread-1.dll

    linux32:
      name: Linux x86 (32-bit)
      runs-on: ubuntu-24.04
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Install 32-bit dependencies
          run: |
            sudo dpkg --add-architecture i386
            sudo apt-get update
            sudo apt-get install file gcc-multilib libedit-dev:i386 libffi-dev:i386 libssl-dev:i386 xxd
        - name: Fetch tags
          run: git fetch --tags --force origin
        - name: Build
          run: make OPT=-m32 release
        - name: Verify architecture
          run: file tpl | grep -q 'ELF 32-bit.*Intel 80386'
        - name: Test
          run: make test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-Linux-X86
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    s390x:
      name: Linux s390x (big-endian, experimental)
      runs-on: ubuntu-24.04
      continue-on-error: true
      timeout-minutes: 60
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Set up QEMU
          uses: docker/setup-qemu-action@v4
          with:
            platforms: s390x
        - name: Build and test under s390x
          run: |
            docker run --rm --platform linux/s390x \
              --volume "$GITHUB_WORKSPACE:/workspace" \
              --workdir /workspace \
              --env LANG=C.UTF-8 \
              --env LC_ALL=C.UTF-8 \
              ubuntu:24.04 \
              bash -euxo pipefail -c '
                export DEBIAN_FRONTEND=noninteractive
                apt-get update
                apt-get install -y build-essential file git libedit-dev libffi-dev libssl-dev xxd
                git config --global --add safe.directory /workspace
                make clean
                make release
                file tpl | grep -q "ELF 64-bit MSB.*IBM S/390"
                make test
              '
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-Linux-S390X
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    cosmo:
      name: Cosmopolitan
      runs-on: ubuntu-latest
      env:
        COSMOCC_VERSION: 4.0.2
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Cache cosmocc
          id: cache-cosmocc
          uses: actions/cache@v4
          with:
            path: ${{ runner.temp }}/cosmocc
            key: cosmocc-${{ runner.os }}-${{ env.COSMOCC_VERSION }}
        - name: Install cosmocc
          if: steps.cache-cosmocc.outputs.cache-hit != 'true'
          run: |
            mkdir -p "${RUNNER_TEMP}/cosmocc"
            curl -fsSL "https://cosmo.zip/pub/cosmocc/cosmocc-${COSMOCC_VERSION}.zip" -o "${RUNNER_TEMP}/cosmocc.zip"
            unzip -q "${RUNNER_TEMP}/cosmocc.zip" -d "${RUNNER_TEMP}/cosmocc"
        - name: Add cosmocc to PATH
          run: echo "${RUNNER_TEMP}/cosmocc/bin" >> "$GITHUB_PATH"
        - name: Build
          run: make cosmo
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-cosmopolitan
            path: |
              tpl
              tpl.aarch64.elf
              tpl.com.dbg
              LICENSE
              ATTRIBUTION
              README.md

    freebsd:
      name: FreeBSD
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Build and test in FreeBSD
          uses: vmactions/freebsd-vm@v1.5.3
          with:
            release: "14"
            cache-after-prepare: true
            prepare: pkg install -y bash git gmake libedit libffi
            run: |
              make release
              make test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-FreeBSD-X64
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    openbsd:
      name: OpenBSD
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Build and test in OpenBSD
          uses: vmactions/openbsd-vm@v1.4.6
          with:
            release: "7.9"
            cache-after-prepare: true
            prepare: |
              pkg_add bash coreutils gdiff git gmake libffi
              ln -sf /usr/local/bin/gtimeout /usr/local/bin/timeout
              ln -sf /usr/local/bin/gdiff /usr/local/bin/diff
            run: |
              export PATH="/usr/local/bin:$PATH"
              git config --global --add safe.directory "$GITHUB_WORKSPACE"
              gmake release
              gmake test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-OpenBSD-X64
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    netbsd-x64:
      name: NetBSD 11 X64 (experimental)
      runs-on: ubuntu-latest
      continue-on-error: true
      timeout-minutes: 90
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Build and test in NetBSD
          uses: vmactions/netbsd-vm@v1.4.6
          with:
            release: "11.0"
            arch: x86_64
            sync: scp
            usesh: true
            cache-after-prepare: true
            prepare: |
              /usr/sbin/pkg_add -u bash coreutils gmake
              ln -sf /usr/pkg/bin/gtimeout /usr/pkg/bin/timeout
            run: |
              export PATH="/usr/pkg/bin:$PATH"
              # Trealla documents a UTF-8 locale as a requirement, and this
              # VM starts in C. Without one, test0252 and test0556 differ:
              # writeq only quotes a non-ASCII atom when the C library calls
              # its characters alphabetic, which is locale-dependent. The
              # other BSD runners are already UTF-8, which is why only this
              # one shows it.
              if locale -a 2>/dev/null | grep -qx 'C.UTF-8'; then
                LC_ALL=C.UTF-8
              elif locale -a 2>/dev/null | grep -qx 'en_US.UTF-8'; then
                LC_ALL=en_US.UTF-8
              else
                LC_ALL=C
                echo "WARNING: no UTF-8 locale on this image; encoding tests will differ"
              fi
              export LC_ALL
              echo "using LC_ALL=$LC_ALL"
              version=$(printf '%s' "$GITHUB_SHA" | cut -c1-7)
              gmake "GIT_VERSION=\"$version\"" CC=gcc HOST_CC=gcc ISOCLINE=1 NOFFI=1 NOSSL=1 release
              ./tpl -q -g 'writeln(netbsd_x64_ok),halt'
              gmake test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-NetBSD-X64
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    solaris:
      name: Solaris 11.4 CBE (experimental)
      runs-on: ubuntu-latest
      continue-on-error: true
      timeout-minutes: 60
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Build and test in Solaris
          uses: vmactions/solaris-vm@v1.3.8
          with:
            release: "11.4-gcc-14"
            cache-after-prepare: true
            prepare: |
              rc=0
              pkg install --accept bash git gnu-coreutils gnu-diffutils gnu-make || rc=$?
              case "$rc" in 0|3|4) ;; *) exit "$rc" ;; esac
            run: |
              export PATH="/usr/gnu/bin:$PATH"
              git config --global --add safe.directory "$GITHUB_WORKSPACE"
              gmake CC=gcc HOST_CC=gcc ISOCLINE=1 NOFFI=1 NOSSL=1 release
              ./tpl -q -g 'writeln(solaris_ok),halt'
              gmake test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-Solaris-X64
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    openindiana:
      name: OpenIndiana Hipster 2026.04 (experimental)
      runs-on: ubuntu-latest
      continue-on-error: true
      timeout-minutes: 60
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Build and test in OpenIndiana
          uses: vmactions/openindiana-vm@v1.1.5
          with:
            release: "202604-build"
            cache-after-prepare: true
            prepare: |
              rc=0
              pkg install --accept build-essential git gnu-coreutils gnu-diffutils gnu-make || rc=$?
              case "$rc" in 0|3|4) ;; *) exit "$rc" ;; esac
            run: |
              export PATH="/usr/gnu/bin:$PATH"
              git config --global --add safe.directory "$GITHUB_WORKSPACE"
              gmake CC=gcc HOST_CC=gcc ISOCLINE=1 NOFFI=1 NOSSL=1 release
              ./tpl -q -g 'writeln(openindiana_ok),halt'
              gmake test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-OpenIndiana-X64
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    riscos:
      name: RISC OS ARM EABI (experimental)
      runs-on: ubuntu-24.04
      continue-on-error: true
      timeout-minutes: 30
      env:
        RISCOS_TOOLCHAIN_URL: https://github.com/netsurf-browser/toolchains/releases/download/gh-master-unstable/arm-riscos-gnueabi.tar.gz
        RISCOS_TOOLCHAIN_SHA256: 1b7aaa8b4a35e8de9e263d7bd36e52e6f960b360a4ffb0e5c0068de5a07fb102
        GCCSDK_INSTALL_CROSSBIN: /opt/netsurf/arm-riscos-gnueabi/cross/bin
        GCCSDK_INSTALL_ENV: /opt/netsurf/arm-riscos-gnueabi/env
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Install RISC OS cross-toolchain
          run: |
            curl --fail --location --retry 3 \
              --output "${RUNNER_TEMP}/arm-riscos-gnueabi.tar.gz" \
              "$RISCOS_TOOLCHAIN_URL"
            echo "$RISCOS_TOOLCHAIN_SHA256  ${RUNNER_TEMP}/arm-riscos-gnueabi.tar.gz" | sha256sum --check
            sudo tar -xzf "${RUNNER_TEMP}/arm-riscos-gnueabi.tar.gz" -C /
        - name: Cross-build for RISC OS
          run: |
            export PATH="$GCCSDK_INSTALL_CROSSBIN:$PATH"
            make CC=arm-riscos-gnueabi-gcc HOST_CC=cc \
              ISOCLINE=1 NOFFI=1 NOSSL=1 NOTHREADS=1 NOPIC=1 NOLIB=1 release
            cp tpl 'tpl,e1f'
            file 'tpl,e1f'
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-RISCOS-ARM-EABI
            path: |
              tpl,e1f
              LICENSE
              ATTRIBUTION
              README.md

    haiku:
      name: Haiku (experimental)
      runs-on: ubuntu-latest
      continue-on-error: true
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Build and smoke-test in Haiku
          uses: vmactions/haiku-vm@v1.1.3
          with:
            release: "r1beta5"
            usesh: true
            run: |
              make ISOCLINE=1 NOFFI=1 NOSSL=1 release
              ./tpl -q -g 'writeln(haiku_ok),halt'
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-Haiku-X64
            path: |
              tpl
              LICENSE
              ATTRIBUTION
              README.md

    # Roughly matches https://github.com/WebAssembly/wasi-sdk#install
    wasm:
      name: WebAssembly
      runs-on: ubuntu-latest
      env:
        WASI_VERSION: 19
        BINARYEN_VERSION: 109
      steps:
        - uses: actions/checkout@v4
          with:
            fetch-depth: 0
        - name: Fetch tags
          run: git fetch --tags --force origin
        - name: Set environment (1/3)
          run: |
            echo "WASI_VERSION_FULL=${WASI_VERSION}.0" >> $GITHUB_ENV
        - name: Set environment (2/3)
          run: |
            echo "WASI_SDK_PATH=`pwd`/wasi-sdk-${WASI_VERSION_FULL}" >> $GITHUB_ENV
        - name: Set environment (3/3)
          run: |
            echo "WASI_CC=${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot" >> $GITHUB_ENV
        - name: Add Homebrew to $PATH
          run: echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
        - name: Install WASI SDK
          run: |
            wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz
            tar xvf wasi-sdk-${WASI_VERSION_FULL}-linux.tar.gz
        - name: Install Binaryen
          run: |
            brew install binaryen
        - name: Setup Wasmtime
          uses: mwilliamson/setup-wasmtime-action@v2
          with:
            wasmtime-version: "12.0.1"
        - name: Build
          run: make clean wasm
        - name: Test
          run: |
            echo 'wasmtime run --max-wasm-stack 8388608 --dir . tpl.wasm -- $@' > tpl
            chmod +x tpl
            make test
        - name: Upload artifacts
          uses: actions/upload-artifact@v4
          with:
            name: tpl-wasm-wasi
            path: |
              tpl.wasm
              LICENSE
              ATTRIBUTION
              README.md

    release:
      if: startsWith(github.ref, 'refs/tags/')
      needs: [build, windows, windows-arm64, linux32, cosmo, freebsd, openbsd, wasm]
      name: Release binaries
      runs-on: ubuntu-latest
      steps:
        - name: Download artifacts
          uses: actions/download-artifact@v4
          with:
            path: artifacts
        - name: List files
          run: ls -R
        - name: Zip releases
          run: |
            cd artifacts
            chmod +x */tpl* */*.wasm
            for i in */; do
              dir=${i%/}
              lower=${dir,,}
              if [ "$dir" != "$lower" ]
              then
                mv "$dir" "$lower"
              fi
              zip -r "${lower}.zip" "$lower"
            done
        - name: Publish
          uses: softprops/action-gh-release@v1
          with:
            files: 'artifacts/*.zip'
