#!/usr/bin/env bash

#
# make-package (linux)
#
# 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

PKG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${PKG_DIR}/../../dependencies/tools/rstudio-tools.sh"

# ensure required environment variables
if ! check_env_vars RSTUDIO_NODE_VERSION; then
	exit 1
fi

# remember cwd for later restoration
PREV_WD=$(pwd)

# raise the limit on number of open files
ulimit -n 2048

HELP_TARGETS='One of "Electron" or "Server"'

function help() {
    cat <<EOF
usage: make-package target package [clean]

Build RStudio (Electron or Server) and redistributable package for Linux.

Specify version by setting environment variables. The default is 99.9.9.

Examples
  RSTUDIO_VERSION_MAJOR=2022 RSTUDIO_VERSION_MINOR=7 RSTUDIO_VERSION_PATCH=1 RSTUDIO_VERSION_SUFFIX=-daily+321 ./make-package Server DEB

Positional Arguments
  target -- build target
    $HELP_TARGETS

  package
    One of "DEB" or "RPM"

Options
  clean
    Perform a clean build; default is incremental build.
EOF
exit 1
}

NPM_INSTALLED=0
install-npm-packages() {
   if [ "${NPM_INSTALLED}" = "0" ]; then
      MAKEFLAGS="" ${NPM} ci
      NPM_INSTALLED=1
   fi
}

# For a full package build the package.json file gets modified with the 
# desired build version and product name, and the build-info.ts source 
# file gets modified with details on the build (date, git-commit, etc).
# We try to put these back to their original state at the end of the 
# package build.
PACKAGE_VERSION_SET=0
set-version () {
   if [ "$RSTUDIO_TARGET" = "Electron" ]; then
      pushd ${ELECTRON_SOURCE_DIR}
      echo "ensure node-gyp installed for node ${RSTUDIO_NODE_VERSION}"
      ${NPX} node-gyp install ${RSTUDIO_NODE_VERSION}
      
      install-npm-packages

      # Set package.json info
      save-original-file package.json
      ${NPX} json -I -f package.json -e "this.version=\"$1\""
      ${NPX} json -I -f package.json -e "this.productName=\"$2\""

      # Keep a backup of build-info.ts so we can restore it
      save-original-file src/main/build-info.ts

      PACKAGE_VERSION_SET=1
      popd
   fi
}

restore-package-version () {
   if [ "${PACKAGE_VERSION_SET}" = "1" ]; then
      pushd ${ELECTRON_SOURCE_DIR}
      restore-original-file package.json
      restore-original-file src/main/build-info.ts
      PACKAGE_VERSION_SET=0
      popd
   fi
}

# ensure package.json restored on exit
on-exit () {
   restore-package-version
}

trap on-exit EXIT

PACKAGE_DIR=`pwd`
RSTUDIO_TARGET=$1
PACKAGE_TARGET=$2
CLEAN=$3

# The Qt-based "Desktop" target is defunct; re-route to "Electron".
if [ "$RSTUDIO_TARGET" = "Desktop" ]; then
   echo "Note: the 'Desktop' target is defunct; building 'Electron' instead."
   RSTUDIO_TARGET=Electron
fi

# This script populates BUILD_DIR, CMAKE_BUILD_TYPE, and RSTUDIO_VERSION_FULL,
# as well as filling in RSTUDIO_VERSION_MAJOR/MINOR/PATCH with defaults if not
# specified.
source "${PKG_DIR}/scripts/vars.sh"

if [ -f "${PKG_DIR}/scripts/overlay.sh" ]; then
  source "${PKG_DIR}/scripts/overlay.sh"
fi

if [ "$RSTUDIO_TARGET" != "Electron" ] && [ "$RSTUDIO_TARGET" != "Server" ]
then
   help
   exit 1
fi

if [ "$PACKAGE_TARGET" != "DEB" ] && [ "$PACKAGE_TARGET" != "RPM" ]
then
   help
   exit 1
fi

# figure out machine architecture
case "${PACKAGE_TARGET}" in
DEB) _ARCH=$(dpkg --print-architecture) ;;
RPM) _ARCH=$(rpm --eval '%_arch')
esac

# set up GWT module
if [ "$RSTUDIO_TARGET" = "Desktop" ] || [ "$RSTUDIO_TARGET" == "Electron" ]; then
   GWT_MAIN_MODULE=RStudioDesktop
else
   GWT_MAIN_MODULE=RStudioServer
fi
export GWT_MAIN_MODULE

# clean if requested
if [ "$CLEAN" == "clean" ]
then
   # remove existing build dir
   rm -rf $BUILD_DIR

   # clean out ant build if in source tree
   if [ -d "../../src/gwt" ]; then
      cd ../../src/gwt
      ant clean
   fi
   cd $PACKAGE_DIR
fi

if [ "$RSTUDIO_TARGET" = "Desktop" ] || [ "$RSTUDIO_TARGET" == "Electron" ]
then
  INSTALL_DIR=rstudio
else
  INSTALL_DIR=rstudio-server
fi

ELECTRON_SOURCE_DIR="${PKG_DIR}/../../src/node/desktop"

# find node and npm
find-program NODE node \
   "${PKG_DIR}/../../dependencies/common/node/${RSTUDIO_NODE_VERSION}/bin" \
   "/opt/rstudio-tools/dependencies/common/node/${RSTUDIO_NODE_VERSION}/bin"
find-program NPM npm \
   "${PKG_DIR}/../../dependencies/common/node/${RSTUDIO_NODE_VERSION}/bin" \
   "/opt/rstudio-tools/dependencies/common/node/${RSTUDIO_NODE_VERSION}/bin"
find-program NPX npx \
   "${PKG_DIR}/../../dependencies/common/node/${RSTUDIO_NODE_VERSION}/bin" \
   "/opt/rstudio-tools/dependencies/common/node/${RSTUDIO_NODE_VERSION}/bin"

# put node on the path
NODE_PATH=$(dirname "${NODE}")
PATH="${NODE_PATH}:${PATH}"

# build RStudio version suffix
RSTUDIO_VERSION_ARRAY=(
   "${RSTUDIO_VERSION_MAJOR-99}"
   "${RSTUDIO_VERSION_MINOR-9}"
   "${RSTUDIO_VERSION_PATCH-9}"
)

RSTUDIO_VERSION_FULL=$(IFS="."; echo "${RSTUDIO_VERSION_ARRAY[*]}")"${RSTUDIO_VERSION_SUFFIX}"

# put version into package.json
set-version ${RSTUDIO_VERSION_FULL} rstudio

set +x

if [ -z "${CMAKE_GENERATOR}" ]; then
   if has-program ninja || has-program ninja-build; then
      CMAKE_GENERATOR="Ninja"
   else
      CMAKE_GENERATOR="Unix Makefiles"
   fi
fi

: ${CMAKE_INSTALL_PREFIX="/usr/lib/${INSTALL_DIR}"}

echo "Building and packing with CMake Generator: $CMAKE_GENERATOR"

PKG_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo $PKG_DIR

GWT_BUILD="yes"
mkdir -p $BUILD_DIR/gwt
cd $BUILD_DIR
if test -z "$NO_REBUILD"
then
   rm -f CMakeCache.txt
   rm -rf $BUILD_DIR/_CPack_Packages
else
   GWT_BUILD="no"
fi

# set JAVA_HOME -- this may be required for some tools which use Java;
# e.g. we use 'ant' to build RStudio's front-end, but 'ant' might use
# an alternate JDK version by default.
if [ -z "${JAVA_HOME}" ]; then

   JVMS=(
      "/usr/lib/jvm/java-17-openjdk-${_ARCH}"
      "/usr/lib/jvm/java-17-openjdk"
   )

   for JVM in "${JVMS[@]}"; do
      if [ -e "${JVM}" ]; then
         JAVA_HOME="${JVM}"
         export JAVA_HOME
         break
      fi
   done

fi

# show build environment when building in CI
if [ -n "${JENKINS_URL}" ]; then

   echo "Build environment:"
   printenv | sort
   echo

   echo "Java version:"
   java -XshowSettings:properties -version || true
   echo "JAVA_HOME: ${JAVA_HOME:-(unset)}"
   echo

   echo "R version:"
   R -s -e 'sessionInfo()' || true
   R -s -e 'renv::diagnostics()' || true
   echo

fi

# if SCCACHE_ENABLED environment variable is set, use sccache
if [ -n "$SCCACHE_ENABLED" ]; then
    echo "Using sccache"
   
    if [ -n "$AWS_ACCESS_KEY_ID" ] || [ $(aws sts get-caller-identity --query "Account" --profile ${AWS_PROFILE:-sso}) -eq 14 ]; then
        echo "AWS credentials valid, using S3 build cache"
        export SCCACHE_BUCKET="rstudio-build-cache"
    else
        echo "No valid AWS SSO session, using only local build cache"
        export SCCACHE_DIR=$(pwd)/object_file_cache
        mkdir -p $SCCACHE_DIR
    fi
fi

# NOTE:  The PRODUCT, OS, and ARCH environment variables are
# normally set as part of the matrix builds on Jenkins; they
# will typically be undefined in local / development builds.
cmake                                                    \
   -G"${CMAKE_GENERATOR}"                                \
   -DRSTUDIO_BUILDINFO_PRODUCT="${PRODUCT}"              \
   -DRSTUDIO_BUILDINFO_OS="${OS}"                        \
   -DRSTUDIO_BUILDINFO_ARCH="${ARCH}"                    \
   -DRSTUDIO_BUILDINFO_VERSION="${RSTUDIO_VERSION_FULL}" \
   -DRSTUDIO_TARGET="${RSTUDIO_TARGET}"                  \
   -DRSTUDIO_PACKAGE_BUILD=1                             \
   -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"              \
   -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}"      \
   -DGWT_BIN_DIR="${BUILD_DIR}/gwt/bin"                  \
   -DGWT_WWW_DIR="${BUILD_DIR}/gwt/www"                  \
   -DGWT_EXTRAS_DIR="${BUILD_DIR}/gwt/extras"            \
   -DGWT_BUILD="${GWT_BUILD}"                            \
   -DSCCACHE_ENABLED=${SCCACHE_ENABLED}                  \
   "${PKG_DIR}/../.."

cmake --build . --target all -- ${MAKEFLAGS}

if [ -n "$SCCACHE_ENABLED" ]; then
   sccache --show-stats
fi

if [ "${RSTUDIO_BUILD_PACKAGE:-1}" = "1" ]; then
   cpack --verbose --debug -G "$PACKAGE_TARGET"
fi

cd "$PREV_WD"

