#!/bin/bash -e

function show_synopsis() {
  cat <<USAGE
Usage: $0 [-p FILE] BAZEL_PKG_NAME DEB_PKG_NAME ...

Create a tarball from one or more deb packages to be used as a
third-party archive in Bazel.  All of the listed packges must be
available using apt.

Options:
  -p FILE  Run a patching script prior to preparing the tarball.  This
           script will run from inside the top-level directory to be
           packaged.

Example: $0 yaml-cpp libyaml-cpp-dev libyaml-cpp0.5
USAGE
}

patch_script=

while getopts "hp:" opt; do
  case "$opt" in
    h )
      show_synopsis
      exit 0
      ;;
    p )
      patch_script="$(realpath $OPTARG)"
      ;;
    * )
      show_synopsis
      exit 2
      ;;
  esac
done
shift $((OPTIND -1))

package_name="$1"
if [ -z "$package_name" ]; then
    show_synopsis
    exit 2
fi
mangled_package_name="$(echo $1 | tr -- - _)"
shift

set -uo pipefail

# This converts absolute symlinks to symlinks relative to the current working
# directory.
function absolute_to_relative {
  local link="$1"
  local to_root=""
  local root_dir
  local intermediate_dir

  if readlink "$link" | grep --quiet --extended-regexp '^/'; then
    root_dir="$(readlink --canonicalize "$(pwd)")"
    intermediate_dir="$(readlink --canonicalize "$(dirname "$link")")"
    while [ "$intermediate_dir" != "$root_dir" ]; do
      if [ "$intermediate_dir" == "/" ]; then
        echo Intermediate directory is invalid while searching for root! >&2
        exit 1
      fi
      to_root="../$to_root"
      intermediate_dir="$(dirname "$intermediate_dir")"
    done
    # Remove trailing '/' so we don't have '//' anywhere in the path.
    to_root="${to_root%/}"

    ln --symbolic --force "$to_root$(readlink "$link")" "$link"
  fi
}
export -f absolute_to_relative

(
  temp_dir="$(mktemp --directory --tmpdir "$package_name".XXXXXXXXXX)"
  cd "$temp_dir"

  # Create directories to store the debs and their extracted content,
  # respectively.
  mkdir debs
  mkdir package

  (
    cd debs

    # Download all specified packages.
    apt-get download "$@"
  )

  for package in debs/*.deb; do
    dpkg --extract "$package" ./package
  done


  # Apply any patching scripts supplied by the caller and then fix
  # absolute symlinks.
  (
    cd package
    if [ -n "$patch_script" ]; then
      cp -p "$patch_script" patch_script
      if ! ./patch_script; then
        echo "error: patch script failed" >&2
        exit 1
      fi
    fi
    find . -type l | xargs --max-args=1 bash -c 'absolute_to_relative "$@"' _
  )

  if find package -xtype l | grep --silent --extended '.*'; then
    cat <<WARNING
WARNING: Broken symlink detected!

The following symlinks might be problematic:

$(ls -l $(find package -xtype l))

Fix the relevant ones and rebuild the tarball manually.


WARNING
  fi

  # Add README for posterity.
  cat <<README > package/README
Package created with \`scripts/build/create-tar-from-deb.sh\` on
$(date) with the following debs:

$(ls -l debs/)

If a patch script was applied it was included in the package with the
file name \`\`patch_script''.
README

  (
    # Create the tarball from the directory so the paths start with the actual
    # content rather than 'package/'.
    cd package
    tar -czf ../"$package_name".tar.gz ./
    # Create a blank WORKSPACE file for testing purposes.  This is not
    # included in the tarball.
    touch WORKSPACE
  )

  # Use awk to remove the filename that sha256sum likes printing out so much.
  sha_256="$(sha256sum "$package_name".tar.gz | awk '{print $1}')"

  cat <<WKS_LOADER > "$temp_dir/workspace.bzl"
# Created with \`scripts/build/create-tar-from-deb.sh\` on
# $(date).  See README in the tarball for its list of packages.

load("//build:third_party.bzl", "third_party_archive")

def workspace():
    third_party_archive(
        name = "$mangled_package_name",
        file_name = "$package_name.tar.gz",
        sha256 = "$sha_256",
        build_file = "//third_party/external/$mangled_package_name:workspace.bazel",
    )
WKS_LOADER

  cat <<MESSAGE
Tarball created!

Temp dir   : $temp_dir
Tarball    : $temp_dir/$package_name.tar.gz
Test dir   : $temp_dir/package
Loader file: $temp_dir/workspace.bzl
sha256     : $sha_256

Test this repository by performing the following steps:

  - Run \`\`mkdir third_party/external/$mangled_package_name''
  - Run \`\`touch third_party/external/$mangled_package_name/BUILD''
  - Add the following lines to the appropriate locations in third_party/workspaces.bzl:
      load("//third_party/external/$mangled_package_name:workspace.bzl", ${mangled_package_name}_workspace = "workspace")
      ${mangled_package_name}_workspace()
  - Write third_party/external/$mangled_package_name/workspace.bazel with build file
    content appropriate for this package
  - Run \`\`ln -s \$(realpath third_party/external/$mangled_package_name/workspace.bazel) $temp_dir/package/BUILD''
  - Test bazel builds using --override_repository=$mangled_package_name=$temp_dir/package

When this works as expected, upload the tarball to S3 like so:

    scripts/build/upload-to-third-party.sh $temp_dir/$package_name.tar.gz

...and then run bazel again without --override_repository to ensure
things still work.
MESSAGE
)
