#!/bin/bash

# Script to build numpy from source. This numpy version is NOT the same as one
# from the official PyPI repo.
#
# We take care not to link with libopenblas due to the reason documented in the
# description of:
#     https://git.zooxlabs.com/zooxco/driving/pull/21676
# with more context at:
#     https://github.com/tensorflow/tensorflow/issues/6968#issuecomment-279892449.
#
# To generate a new version of numpy:
# 1. Run this script (adjusting the python binary and numpy version arguments as appropriate):
#       $ bash scripts/build/build_numpy.sh python3.6 1.15.4
#
# After building a new version of numpy, we must make sure that all of the
# places that might reference numpy are updated in sync. If any of the listed
# files do not mention numpy, do not add it; only update it if it's already
# present.
#
# Files for Python 2.7:
#
# WORKSPACE
#     third_party_archive(name = "numpy_repo_py27", ...)
#     with info from $ fgrep -A 1 numpy build/wheels_inventory.yaml
#
# build/requirements2.7.txt
# scripts/ansible/roles/python_deps/files/requirements2.7.txt
#     numpy==1.15.4.post2
#
# Files for Python 3.6:
#
# WORKSPACE
#     third_party_archive(name = "numpy_repo_py36", ...)
#     with info from $ fgrep -A 1 numpy build/wheels_inventory3.yaml
#
# build/requirements3.4.txt
# scripts/ansible/roles/python_deps/files/requirements3.4.txt
# scripts/ansible/roles/python_deps/files/requirements3.6.txt
#     numpy==1.15.4.post2
#
# Note that the post2 part being the same between both python 2.7 and 3.6 is a
# coincidence.
#
# Note that as of 2019-09-20, ansible requirements3.4.txt and requirements3.6.txt
# do not have numpy listed, but that will probably change at some point as more
# code gets migrated to python 3. Always check.


set -euo pipefail
set -x
shopt -s failglob

usage() {
  cat <<EOF >&2
Usage:
  $0 [--debug] <python_bin> <version>

Specifying --debug will preserve the temp directory created during the build process.
EOF
}

if ! which twine &> /dev/null; then
  echo "Could not find twine. Please install it first: sudo pip install twine" >&2
  exit 1
fi

libopenblas="$(ldconfig -p | grep libopenblas || true)"
if [[ -n ${libopenblas} ]]; then
  echo "Error: libopenblas found:" >&2
  echo "${libopenblas}" >&2
  echo "See the description of https://git.zooxlabs.com/zooxco/driving/pull/21676 for background." >&2
  exit 1
fi

debug=0

args=()
for arg in "$@"; do
  case ${arg} in
    "--debug")
      debug=1
      ;;
    "--help" | "-h")
      usage
      ;;
    *)
      args+=("${arg}")
      ;;
  esac
done

if [[ ${#args[@]} -ne 2 ]]; then
  usage
  exit 1
fi

python_binary=${args[0]}
orig_version=${args[1]}

build_dir=$(mktemp --directory -t numpy_build_XXXXXX)

cleanup() {
  if [[ ${debug} -eq 0 && -n "${build_dir}" ]]; then
    rm -r "${build_dir}"
  else
    echo "Preserving the build directory ${build_dir}" >&2
  fi
}

trap cleanup EXIT

$python_binary -m pip wheel -w "${build_dir}" numpy=="${orig_version}" --no-binary numpy
cd "${build_dir}"
old_wheel_name=$(echo numpy-${orig_version}-*.whl)
mkdir "${build_dir}/unzipped"
cd "${build_dir}/unzipped"
unzip -qq "${build_dir}/${old_wheel_name}"

# find a ${orig_version}.post${post_index} that doesn't exist yet
post_index=1
while true; do
  if ! "$python_binary" -m pip wheel -w "${build_dir}" numpy=="${orig_version}.post${post_index}" &> /dev/null; then
    break
  fi
  post_index=$((post_index + 1))
done

post_version="${orig_version}.post${post_index}"
echo "Will build version ${post_version}." >&2

mv "${build_dir}/unzipped/numpy-${orig_version}.dist-info" "${build_dir}/unzipped/numpy-${post_version}.dist-info"

if [[ -e "${build_dir}/unzipped/numpy-${orig_version}.data" ]]; then
  mv "${build_dir}/unzipped/numpy-${orig_version}.data" "${build_dir}/unzipped/numpy-${post_version}.data"
fi

new_wheel_name="${old_wheel_name/${orig_version}/${post_version}}"
zip --quiet --recurse-paths "${build_dir}/${new_wheel_name}" .

twine upload "${build_dir}/${new_wheel_name}" --repository-url https://pypi.zooxlabs.com/pypi --username zoox --password zoox

echo "Successfully built numpy==${post_version}"
