on:
  workflow_call:
    inputs:
      save-rust-cache:
        required: false
        type: string
        default: "true"

permissions: {}

env:
  CARGO_INCREMENTAL: 0
  CARGO_NET_RETRY: 10
  CARGO_TERM_COLOR: always
  RUSTUP_MAX_RETRIES: 10

jobs:
  build-binary-linux-libc:
    name: "linux libc"
    timeout-minutes: 10
    runs-on: github-ubuntu-24.04-x86_64-8
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Install mold"
        run: ./scripts/install-mold.sh

      - name: "Install Rust toolchain"
        run: rustup toolchain install --profile minimal

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        run: cargo build --profile no-debug-nightly -Z checksum-freshness

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-linux-libc-${{ github.sha }}
          path: |
            ./target/no-debug-nightly/uv
            ./target/no-debug-nightly/uvx
          retention-days: 1

  build-binary-linux-aarch64:
    name: "linux aarch64"
    timeout-minutes: 10
    runs-on: github-ubuntu-24.04-aarch64-4
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Install mold"
        run: ./scripts/install-mold.sh

      - name: "Install Rust toolchain"
        run: rustup toolchain install --profile minimal

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        run: cargo build --profile no-debug-nightly -Z checksum-freshness

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-linux-aarch64-${{ github.sha }}
          path: |
            ./target/no-debug-nightly/uv
            ./target/no-debug-nightly/uvx
          retention-days: 1

  build-binary-linux-armv7-gnueabihf:
    name: "linux armv7 gnueabihf"
    timeout-minutes: 15
    runs-on: github-ubuntu-24.04-x86_64-8
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Install mold"
        run: ./scripts/install-mold.sh

      - name: "Setup armv7"
        run: |
          sudo apt-get update
          sudo apt-get install gcc-arm-linux-gnueabihf

      - name: "Install Rust toolchain"
        run: |
          rustup toolchain install --profile minimal
          rustup target add armv7-unknown-linux-gnueabihf

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        env:
          CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --target armv7-unknown-linux-gnueabihf --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-linux-armv7-gnueabihf-${{ github.sha }}
          path: |
            ./target/armv7-unknown-linux-gnueabihf/no-debug-nightly/uv
            ./target/armv7-unknown-linux-gnueabihf/no-debug-nightly/uvx
          retention-days: 1

  build-binary-linux-musl:
    name: "linux musl"
    timeout-minutes: 10
    runs-on: github-ubuntu-24.04-x86_64-8
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Install mold"
        run: ./scripts/install-mold.sh

      - name: "Setup musl"
        run: sudo apt-get install musl-tools

      - name: "Install Rust toolchain"
        run: |
          rustup toolchain install --profile minimal
          rustup target add x86_64-unknown-linux-musl

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --target x86_64-unknown-linux-musl --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-linux-musl-${{ github.sha }}
          path: |
            ./target/x86_64-unknown-linux-musl/no-debug-nightly/uv
            ./target/x86_64-unknown-linux-musl/no-debug-nightly/uvx
          retention-days: 1

  build-binary-macos-aarch64:
    name: "macos aarch64"
    timeout-minutes: 10
    runs-on: namespace-profile-macos-15
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Install Rust toolchain"
        run: rustup toolchain install --profile minimal

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}
          cache-bin: false
      - name: "Build"
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-macos-aarch64-${{ github.sha }}
          path: |
            ./target/no-debug-nightly/uv
            ./target/no-debug-nightly/uvx
          retention-days: 1

  build-binary-macos-x86_64:
    name: "macos x86_64"
    timeout-minutes: 10
    runs-on: macos-latest-large # github-macos-14-x86_64-12
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Install Rust toolchain"
        run: rustup toolchain install --profile minimal

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}
          cache-bin: false
      - name: "Build"
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-macos-x86_64-${{ github.sha }}
          path: |
            ./target/no-debug-nightly/uv
            ./target/no-debug-nightly/uvx
          retention-days: 1

  build-binary-windows-x86_64:
    name: "windows x86_64"
    timeout-minutes: 10
    runs-on: windows-latest
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
      - name: Copy Git Repo to Dev Drive
        run: |
          Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse

      - name: "Install Rust toolchain"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: rustup toolchain install --profile minimal

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          workspaces: ${{ env.UV_WORKSPACE }}
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-windows-x86_64-${{ github.sha }}
          path: |
            ${{ env.UV_WORKSPACE }}/target/no-debug-nightly/uv.exe
            ${{ env.UV_WORKSPACE }}/target/no-debug-nightly/uvx.exe
          retention-days: 1

  build-binary-windows-aarch64:
    name: "windows aarch64"
    timeout-minutes: 10
    runs-on: windows-11-arm
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Setup Dev Drive
        run: ${{ github.workspace }}/.github/workflows/setup-dev-drive.ps1

      # actions/checkout does not let us clone into anywhere outside ${{ github.workspace }}, so we have to copy the clone...
      - name: Copy Git Repo to Dev Drive
        run: |
          Copy-Item -Path "${{ github.workspace }}" -Destination "$Env:UV_WORKSPACE" -Recurse

      - name: "Install Rust toolchain"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: rustup toolchain install --profile minimal

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          workspaces: ${{ env.UV_WORKSPACE }}
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        working-directory: ${{ env.UV_WORKSPACE }}
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-windows-aarch64-${{ github.sha }}
          path: |
            ${{ env.UV_WORKSPACE }}/target/no-debug-nightly/uv.exe
            ${{ env.UV_WORKSPACE }}/target/no-debug-nightly/uvx.exe
          retention-days: 1

  build-binary-msrv:
    name: "msrv"
    runs-on: github-ubuntu-24.04-x86_64-8
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: "Read MSRV from Cargo.toml"
        id: msrv
        run: |
          MSRV=$(grep -m1 'rust-version' Cargo.toml | sed 's/.*"\([^"]*\)".*/\1/')
          echo "value=$MSRV" >> "$GITHUB_OUTPUT"
      - name: "Install Rust toolchain"
        run: rustup default ${MSRV}
        env:
          MSRV: ${{ steps.msrv.outputs.value }}
      - name: "Install mold"
        run: ./scripts/install-mold.sh
      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          save-if: ${{ inputs.save-rust-cache == 'true' }}
      - run: cargo +${MSRV} build --profile no-debug
        env:
          MSRV: ${{ steps.msrv.outputs.value }}
      - run: ./target/no-debug/uv --version

  build-binary-android-aarch64:
    name: "android aarch64"
    timeout-minutes: 10
    runs-on: github-ubuntu-24.04-x86_64-8
    env:
      RUSTC_BOOTSTRAP: 1
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: "Setup Android NDK"
        run: |
          # Use ANDROID_NDK_ROOT if set, otherwise find the latest installed NDK
          if [ -z "${ANDROID_NDK_ROOT}" ]; then
            NDK_ROOT=$(ls -d "${ANDROID_HOME}/ndk/"* 2>/dev/null | sort -V | tail -n1)
          else
            NDK_ROOT="${ANDROID_NDK_ROOT}"
          fi
          TOOLCHAIN="${NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64"

          echo "CC_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
          echo "CXX_aarch64_linux_android=${TOOLCHAIN}/bin/aarch64-linux-android24-clang++" >> "$GITHUB_ENV"
          echo "AR_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ar" >> "$GITHUB_ENV"
          echo "RANLIB_aarch64_linux_android=${TOOLCHAIN}/bin/llvm-ranlib" >> "$GITHUB_ENV"
          echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=${TOOLCHAIN}/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
          echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_RANLIB=${TOOLCHAIN}/bin/llvm-ranlib" >> "$GITHUB_ENV"

          # NDK 23+ removed libgcc, provide a stub that redirects to libunwind
          LIBDIR=$(echo "${TOOLCHAIN}"/lib/clang/*/lib/linux/aarch64)
          if [ ! -f "${LIBDIR}/libgcc.a" ]; then
            echo 'INPUT(-lunwind)' | sudo tee "${LIBDIR}/libgcc.a" > /dev/null
          fi

      - name: "Install Rust toolchain"
        run: |
          rustup toolchain install --profile minimal
          rustup target add aarch64-linux-android

      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          prefix-key: v1-rust-workspace
          cache-workspace-crates: "true"
          save-if: ${{ inputs.save-rust-cache == 'true' }}

      - name: "Build"
        run: cargo build --profile no-debug-nightly -Z checksum-freshness --target aarch64-linux-android --bin uv --bin uvx

      - name: "Upload binary"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: uv-android-aarch64-${{ github.sha }}
          path: |
            ./target/aarch64-linux-android/no-debug-nightly/uv
            ./target/aarch64-linux-android/no-debug-nightly/uvx
          retention-days: 1

  build-binary-freebsd:
    name: "freebsd"
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          save-if: ${{ inputs.save-rust-cache == 'true' }}
      - name: "Cross build"
        run: |
          # Install cross from `freebsd-firecracker`
          wget -q -O cross https://github.com/acj/freebsd-firecracker/releases/download/v0.0.10/cross
          chmod +x cross
          mv cross /usr/local/bin/cross

          cross build --target x86_64-unknown-freebsd --profile no-debug

      - name: Test in Firecracker VM
        uses: acj/freebsd-firecracker-action@53cfec625fd81cfb34160e6d9ec19c9d9baa8adf # v0.10.3
        with:
          verbose: false
          checkout: false
          pre-run: |
            # The exclude `*` prevents examination of directories so we need to
            # include each parent directory of the binary
            include_path="$(mktemp)"
            cat <<EOF > $include_path
            target
            target/x86_64-unknown-freebsd
            target/x86_64-unknown-freebsd/no-debug
            target/x86_64-unknown-freebsd/no-debug/uv
            EOF

            rsync -r -e "ssh" \
              --relative \
              --copy-links \
              --include-from "$include_path" \
              --exclude "*" \
              . firecracker:
          run-in-vm: |
            mv target/x86_64-unknown-freebsd/no-debug/uv uv
            chmod +x uv
            ./uv --version
