#!/usr/bin/env bash

#
# install-crashpad
#
# Copyright (C) 2022 by Posit Software, PBC
#
# Unless you have received this program directly from Posit Software pursuant
# to the terms of a commercial license agreement with Posit Software, then
# this program is licensed to you under the terms of version 3 of the
# GNU Affero General Public License. This program is distributed WITHOUT
# ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
# AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
#
#

set -e

source "$(dirname "${BASH_SOURCE[0]}")/../tools/rstudio-tools.sh"
section "Installing Crashpad"

# check for presence of flag to skip installation
# older platforms are unable to build crashpad and may request to skip it
if [ "$RSTUDIO_DISABLE_CRASHPAD" == "1" ]; then
   echo "Skipping crashpad install"
   exit 0
fi

if [ "$(arch)" == "aarch64" ]; then
   echo "Skipping crashpad install (not yet supported on aarch64)"
   exit 0
fi

# install dir
INSTALL_DIR=$(pwd)

# vars
DEPOT_TOOLS_DIR=$RSTUDIO_TOOLS_ROOT/depot_tools
LLVM_VERSION=4.0.0
LLVM_SOURCE_URL=${RSTUDIO_BUILDTOOLS}/llvm-$LLVM_VERSION.src.tar.xz
CLANG_SOURCE_URL=${RSTUDIO_BUILDTOOLS}/cfe-$LLVM_VERSION.src.tar.xz
CRASHPAD_PRE_BUILT_ROOT_URL=${RSTUDIO_BUILDTOOLS}/crashpad-bin
LLVM_DIR=$RSTUDIO_TOOLS_ROOT/llvm-$LLVM_VERSION
LLVM_BIN_DIR=$LLVM_DIR/build/bin
CRASHPAD_DIR=$RSTUDIO_TOOLS_ROOT/crashpad
CRASHPAD_BIN_DIR=$CRASHPAD_DIR/crashpad/out/Default

# flavor used for prebuilt crashpad binaries
FLAVOR="$1"
if is-macos; then
   FLAVOR=osx
fi

# check for presence of argument indicating to install pre-built binaries
# if present, we skip building crashpad altogether and just install from S3
if [ -n "${FLAVOR}" ]; then

   if [ -e "${CRASHPAD_BIN_DIR}/crashpad_handler" ]; then
      echo "Crashpad already installed at '${CRASHPAD_BIN_DIR}'"
      exit 0
   fi

   # re-run as root if necessary
   sudo-if-necessary-for "${RSTUDIO_TOOLS_ROOT}" "$@"

   echo "Installing pre-built Crashpad binaries for '${FLAVOR}'"
   if is-macos; then
      ARCHIVE_EXT=.zip
      ARCHIVE_FOLDER=osx
   else
      ARCHIVE_EXT=.tar.gz
      ARCHIVE_FOLDER=linux
   fi

   mkdir -p "$RSTUDIO_TOOLS_ROOT"
   cd "$RSTUDIO_TOOLS_ROOT"
   download "$CRASHPAD_PRE_BUILT_ROOT_URL/$ARCHIVE_FOLDER/crashpad-${FLAVOR}$ARCHIVE_EXT"
   tar xvf "crashpad-${FLAVOR}$ARCHIVE_EXT"
   if ! test -e "$CRASHPAD_BIN_DIR/crashpad_handler"; then
      echo "An error occurred installing pre-built Crashpad binaries - $CRASHPAD_BIN_DIR/crashpad_handler does not exist"
      exit 1
   else
      rm "./crashpad-${FLAVOR}$ARCHIVE_EXT"
      echo "Successfully installed pre-built Crashpad binaries to '${CRASHPAD_BIN_DIR}'"
      exit 0
   fi

fi

# re-run as root if necessary
sudo-if-necessary-for "${RSTUDIO_TOOLS_ROOT}" "$@"

# install google depot_tools if not already installed
# these tools are necessary to properly checkout and build crashpad
if ! test -e "$DEPOT_TOOLS_DIR"; then
   mkdir -p "$DEPOT_TOOLS_DIR"
   cd "$DEPOT_TOOLS_DIR"
   git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git .
else
   echo "depot_tools already installed in '$DEPOT_TOOLS_DIR'"
fi

# build clang if the system does not already have it or the system version is too low
# we do not do this on OSX because the system Clang should be sufficient
use_system_clang=true
if [[ "$OSTYPE" != "darwin"* ]]; then
   if ! command -v clang &> /dev/null; then
      use_system_clang=false
   else
      clang_version=$(clang --version | grep -oP "clang version \K[\w.]+")
      version_array=(${clang_version//./ })
      if ((${version_array[0]} < 4)); then
         use_system_clang=false
         echo "System clang version ${clang_version} is too low. Need > 4.0.0"
      fi
   fi

   if ! $use_system_clang && ! test -e $LLVM_BIN_DIR
   then
      echo "Insufficient system clang - building clang from source..."
      cd $RSTUDIO_TOOLS_ROOT

      if ! test -e $LLVM_DIR
      then
         download $LLVM_SOURCE_URL
         download $CLANG_SOURCE_URL

         tar xvf llvm-$LLVM_VERSION.src.tar.xz
         tar xvf cfe-$LLVM_VERSION.src.tar.xz
         mv llvm-$LLVM_VERSION.src llvm-$LLVM_VERSION
         mv cfe-$LLVM_VERSION.src llvm-$LLVM_VERSION/tools/clang
      fi

      cd $LLVM_DIR
      mkdir -p build && cd build
      cmake ../ -DLLVM_BUILD_TYPE=Release
      make
      echo "clang built successfully"
   else
      if ! $use_system_clang; then
         echo "clang already installed in $LLVM_BIN_DIR"
      else
         echo "Using system clang"
      fi
   fi
else
   echo "Using system clang"
fi

# install crashpad if we aren't already installed
if [ -e "${CRASHPAD_BIN_DIR}/crashpad_handler" ]; then
   echo "Crashpad already installed at '${CRASHPAD_BIN_DIR}'"
   exit 0
fi

mkdir -p $CRASHPAD_DIR
cd $CRASHPAD_DIR

# put depot tools on our path, otherwise they don't run properly
export PATH=$PATH:$DEPOT_TOOLS_DIR

# download crashpad using google tools
fetch crashpad || sync crashpad

# add the rstudio fork and switch to the rstudio branch which
# contains all of the patches we need to make it work for us
cd crashpad
git remote add rstudio https://github.com/rstudio/crashpad.git || true
git fetch rstudio
git checkout rstudio || true
git pull

if ! $use_system_clang; then
   # add custom built clang to path so it can be picked up by crashpad build
   export PATH=$LLVM_BIN_DIR:$PATH
fi

# build crashpad
$DEPOT_TOOLS_DIR/gn gen out/Default

if [[ "$OSTYPE" = "darwin"* ]]; then
   # on macos, we need to specify the minimum os target to support
   echo "mac_deployment_target=\"10.12\"" > out/Default/args.gn
   $DEPOT_TOOLS_DIR/gn gen out/Default
fi

$DEPOT_TOOLS_DIR/ninja -C out/Default

# fix up permissions (if run as root, bin dir will be unreadable by others)
chmod -R 755 out

