#!/usr/bin/env bash

set -eo pipefail

cd "$(dirname "$0")/../"

mkdir -p target

# Keep the target folder from getting too big by deleting any builds older than
# 7 days.
if [ -d target/ ]; then
  find \
    target/ \
    -maxdepth 1 \
    -not -path target/ \
    -type d \
    -mtime +7 \
    -exec rm -vrf {} \;
fi

# See comments about generated code in main.go.
go generate

build_sha="$(../bin/current-content-sha client)"

if [[ -d "target/$build_sha" ]]; then
  echo "Existing build: $PWD/target/$build_sha"
  exit 0
fi

# Refuse to build unless the tests are all passing.
# shellcheck disable=2046
go test $(go list ./...)

# I first tried using an associative array for this, but those things behave
# strangely in the face of concurrency. Falling back to using files instead.
builds_dir="$(mktemp -d)"

function build() {
  local os="$1"
  local arch="$2"

  local ext=""
  case "$os" in
    windows)
      ext=".exe"
      ;;
    js)
      ext=".wasm"
      ;;
    *)
      ext=""
      ;;
  esac

  local output_filename="target/$build_sha/$os-$arch/alda$ext"

  echo "Building $output_filename..."

  if [[ "$os" != "js" ]]; then
    # Install the standard packages locally to speed up subsequent builds.
    GOOS="$os" GOARCH="$arch" go install || \
      {
        echo "$os-$arch build: ERROR"
        echo "1" > "$builds_dir/$os-$arch"
        return
      }
  fi

  local input_filename="main.go"
  if [[ "$os" == "js" ]]; then
    input_filename="wasm/main.go"
  fi

  # Build.
  #
  # Notes:
  # * The trimpath flags make it so that paths are stripped in the binary, which
  #   we need because otherwise, the full path to the executable (at build time!
  #   like, it contains "/home/circleci"!) ends up in the log output.
  #   Reference: https://github.com/rs/zerolog/issues/133#issuecomment-464404901
  CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" \
    go build \
    -tags netgo \
    -ldflags '-w -extldflags "-static"' \
    -gcflags="all=-trimpath=$PWD" \
    -asmflags="all=-trimpath=$PWD" \
    -o "$output_filename" \
    "$input_filename" || \
    {
      echo "$os-$arch build: ERROR"
      echo "1" > "$builds_dir/$os-$arch"
      return
    }

    echo "$os-$arch build: OK"
    echo "0" > "$builds_dir/$os-$arch"
}

for os in windows darwin linux js; do
  for arch in 386 amd64 arm64 wasm; do
    # Support for darwin/386 was removed in Go 1.15.
    if [[ "$os" == darwin ]] && [[ "$arch" == 386 ]]; then
      continue
    fi

    # darwin/arm64 is the only arm64 we're building for now.
    #
    # TODO: Expand this to include Linux ARM 6-8 (see arm-build branch)
    # Ref: https://github.com/alda-lang/alda/discussions/456
    if [[ "$arch" == arm64 ]] && [[ "$os" != darwin ]]; then
      continue
    fi

    # js/wasm is the only possible combination involving one of those
    if [[ ("$os" == js && "$arch" != wasm) || \
          ("$arch" == wasm && "$os" != js) ]]; then
      continue
    fi

    # Add an empty file in the builds directory right away, so that we can wait
    # on a result in the main thread.
    touch "$builds_dir/$os-$arch"

    build $os $arch &
  done
done

declare -A build_results

for file in "$builds_dir"/*; do
  while true; do
    if [[ -s "$file" ]]; then
      build_results["$(basename "$file")"]="$(cat "$file")"
      break
    fi

    sleep 0.5
  done
done

for build_result in "${!build_results[@]}"; do
  exit_code="${build_results[$build_result]}"

  if [[ "$exit_code" -ne 0 ]]; then
    exit "$exit_code"
  fi
done

echo "All builds successful."
