#!/bin/bash
#
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# TODO(mmoss) This currently only works with official builds, since non-official
# builds don't add the "${BUILDDIR}/installer/" files needed for packaging.

set -e
if [ "$VERBOSE" ]; then
  set -x
fi
set -u

# Create the Debian changelog file needed by dpkg-gencontrol. This just adds a
# placeholder change, indicating it is the result of an automatic build.
# TODO(mmoss) Release packages should create something meaningful for a
# changelog, but simply grabbing the actual 'svn log' is way too verbose. Do we
# have any type of "significant/visible changes" log that we could use for this?
gen_changelog() {
  rm -f "${DEB_CHANGELOG}"
  process_template "${SCRIPTDIR}/changelog.template" "${DEB_CHANGELOG}"
  debchange -a --nomultimaint -m --changelog "${DEB_CHANGELOG}" \
    "Release Notes: ${RELEASENOTES}"
  GZLOG="${STAGEDIR}/usr/share/doc/${PACKAGE}-${CHANNEL}/changelog.gz"
  mkdir -p "$(dirname "${GZLOG}")"
  gzip -9 -c "${DEB_CHANGELOG}" > "${GZLOG}"
  chmod 644 "${GZLOG}"
}

# Create the Debian control file needed by dpkg-deb.
gen_control() {
  dpkg-gencontrol -v"${VERSIONFULL}" -c"${DEB_CONTROL}" -l"${DEB_CHANGELOG}" \
  -f"${DEB_FILES}" -p"${PACKAGE}-${CHANNEL}" -P"${STAGEDIR}" \
  -O > "${STAGEDIR}/DEBIAN/control"
  rm -f "${DEB_CONTROL}"
}

# Setup the installation directory hierachy in the package staging area.
prep_staging_debian() {
  prep_staging_common
  install -m 755 -d "${STAGEDIR}/DEBIAN" \
    "${STAGEDIR}/etc/cron.daily" \
    "${STAGEDIR}/usr/share/menu" \
    "${STAGEDIR}/usr/share/doc/${PACKAGE}"
}

# Put the package contents in the staging area.
stage_install_debian() {
  # Always use a different name for /usr/bin symlink depending on channel.
  # First, to avoid file collisions. Second, to make it possible to
  # use update-alternatives for /usr/bin/google-chrome.
  local USR_BIN_SYMLINK_NAME="${PACKAGE}-${CHANNEL}"

  if [ "$CHANNEL" != "stable" ]; then
    # This would ideally be compiled into the app, but that's a bit too
    # intrusive of a change for these limited use channels, so we'll just hack
    # it into the wrapper script. The user can still override since it seems to
    # work to specify --user-data-dir multiple times on the command line, with
    # the last occurrence winning.
    local SXS_USER_DATA_DIR="\${XDG_CONFIG_HOME:-\${HOME}/.config}/${PACKAGE}-${CHANNEL}"
    local DEFAULT_FLAGS="--user-data-dir=\"${SXS_USER_DATA_DIR}\""

    # Avoid file collisions between channels.
    local INSTALLDIR="${INSTALLDIR}-${CHANNEL}"

    local PACKAGE="${PACKAGE}-${CHANNEL}"

    # Make it possible to distinguish between menu entries
    # for different channels.
    local MENUNAME="${MENUNAME} (${CHANNEL})"
  fi
  prep_staging_debian
  stage_install_common
  echo "Staging Debian install files in '${STAGEDIR}'..."
  install -m 755 -d "${STAGEDIR}/${INSTALLDIR}/cron"
  process_template "${BUILDDIR}/installer/common/repo.cron" \
      "${STAGEDIR}/${INSTALLDIR}/cron/${PACKAGE}"
  chmod 755 "${STAGEDIR}/${INSTALLDIR}/cron/${PACKAGE}"
  pushd "${STAGEDIR}/etc/cron.daily/"
  ln -snf "${INSTALLDIR}/cron/${PACKAGE}" "${PACKAGE}"
  popd
  process_template "${BUILDDIR}/installer/debian/debian.menu" \
    "${STAGEDIR}/usr/share/menu/${PACKAGE}.menu"
  chmod 644 "${STAGEDIR}/usr/share/menu/${PACKAGE}.menu"
  process_template "${BUILDDIR}/installer/debian/postinst" \
    "${STAGEDIR}/DEBIAN/postinst"
  chmod 755 "${STAGEDIR}/DEBIAN/postinst"
  process_template "${BUILDDIR}/installer/debian/prerm" \
    "${STAGEDIR}/DEBIAN/prerm"
  chmod 755 "${STAGEDIR}/DEBIAN/prerm"
  process_template "${BUILDDIR}/installer/debian/postrm" \
    "${STAGEDIR}/DEBIAN/postrm"
  chmod 755 "${STAGEDIR}/DEBIAN/postrm"
}

# Actually generate the package file.
do_package() {
  echo "Packaging ${ARCHITECTURE}..."
  PREDEPENDS="$COMMON_PREDEPS"
  DEPENDS="${COMMON_DEPS}"
  REPLACES=""
  CONFLICTS=""
  PROVIDES="www-browser"
  gen_changelog
  process_template "${SCRIPTDIR}/control.template" "${DEB_CONTROL}"
  export DEB_HOST_ARCH="${ARCHITECTURE}"
  if [ -f "${DEB_CONTROL}" ]; then
    gen_control
  fi
  fakeroot dpkg-deb -Zlzma -b "${STAGEDIR}" .
}

# Remove temporary files and unwanted packaging output.
cleanup() {
  echo "Cleaning..."
  rm -rf "${STAGEDIR}"
  rm -rf "${TMPFILEDIR}"
}

usage() {
  echo "usage: $(basename $0) [-c channel] [-a target_arch] [-o 'dir'] "
  echo "                      [-b 'dir']"
  echo "-c channel the package channel (trunk, asan, unstable, beta, stable)"
  echo "-a arch    package architecture (ia32 or x64)"
  echo "-o dir     package output directory [${OUTPUTDIR}]"
  echo "-b dir     build input directory    [${BUILDDIR}]"
  echo "-h         this help message"
}

# Check that the channel name is one of the allowable ones.
verify_channel() {
  case $CHANNEL in
    stable )
      CHANNEL=stable
      RELEASENOTES="http://googlechromereleases.blogspot.com/search/label/Stable%20updates"
      ;;
    unstable|dev|alpha )
      CHANNEL=unstable
      RELEASENOTES="http://googlechromereleases.blogspot.com/search/label/Dev%20updates"
      ;;
    testing|beta )
      CHANNEL=beta
      RELEASENOTES="http://googlechromereleases.blogspot.com/search/label/Beta%20updates"
      ;;
    trunk|asan )
      # Setting this to empty will prevent it from updating any existing configs
      # from release packages.
      REPOCONFIG=""
      RELEASENOTES="http://googlechromereleases.blogspot.com/"
      ;;
    * )
      echo
      echo "ERROR: '$CHANNEL' is not a valid channel type."
      echo
      exit 1
      ;;
  esac
}

process_opts() {
  while getopts ":o:b:c:a:h" OPTNAME
  do
    case $OPTNAME in
      o )
        OUTPUTDIR=$(readlink -f "${OPTARG}")
        mkdir -p "${OUTPUTDIR}"
        ;;
      b )
        BUILDDIR=$(readlink -f "${OPTARG}")
        ;;
      c )
        CHANNEL="$OPTARG"
        ;;
      a )
        TARGETARCH="$OPTARG"
        ;;
      h )
        usage
        exit 0
        ;;
      \: )
        echo "'-$OPTARG' needs an argument."
        usage
        exit 1
        ;;
      * )
        echo "invalid command-line option: $OPTARG"
        usage
        exit 1
        ;;
    esac
  done
}

#=========
# MAIN
#=========

SCRIPTDIR=$(readlink -f "$(dirname "$0")")
OUTPUTDIR="${PWD}"
STAGEDIR=$(mktemp -d -t deb.build.XXXXXX) || exit 1
TMPFILEDIR=$(mktemp -d -t deb.tmp.XXXXXX) || exit 1
DEB_CHANGELOG="${TMPFILEDIR}/changelog"
DEB_FILES="${TMPFILEDIR}/files"
DEB_CONTROL="${TMPFILEDIR}/control"
CHANNEL="trunk"
# Default target architecture to same as build host.
if [ "$(uname -m)" = "x86_64" ]; then
  TARGETARCH="x64"
else
  TARGETARCH="ia32"
fi

# call cleanup() on exit
trap cleanup 0
process_opts "$@"
if [ ! "$BUILDDIR" ]; then
  BUILDDIR=$(readlink -f "${SCRIPTDIR}/../../../../../out/Release")
fi

source ${BUILDDIR}/installer/common/installer.include

get_version_info
VERSIONFULL="${VERSION}-${PACKAGE_RELEASE}"

if [ "$CHROMIUM_BUILD" = "_google_chrome" ]; then
  source "${BUILDDIR}/installer/common/google-chrome.info"
else
  source "${BUILDDIR}/installer/common/chromium-browser.info"
fi
eval $(sed -e "s/^\([^=]\+\)=\(.*\)$/export \1='\2'/" \
  "${BUILDDIR}/installer/theme/BRANDING")

REPOCONFIG="deb http://dl.google.com/linux/chrome/deb/ stable main"
verify_channel

# Some Debian packaging tools want these set.
export DEBFULLNAME="${MAINTNAME}"
export DEBEMAIL="${MAINTMAIL}"

# We'd like to eliminate more of these deps by relying on the 'lsb' package, but
# that brings in tons of unnecessary stuff, like an mta and rpm. Until that full
# 'lsb' package is installed by default on DEB distros, we'll have to stick with
# the LSB sub-packages, to avoid pulling in all that stuff that's not installed
# by default.

# Need a dummy debian/control file for dpkg-shlibdeps.
DUMMY_STAGING_DIR="${TMPFILEDIR}/dummy_staging"
mkdir "$DUMMY_STAGING_DIR"
cd "$DUMMY_STAGING_DIR"
mkdir debian
touch debian/control

# Generate the dependencies,
# TODO(mmoss): This is a workaround for a problem where dpkg-shlibdeps was
# resolving deps using some of our build output shlibs (i.e.
# out/Release/lib.target/libfreetype.so.6), and was then failing with:
#   dpkg-shlibdeps: error: no dependency information found for ...
# It's not clear if we ever want to look in LD_LIBRARY_PATH to resolve deps,
# but it seems that we don't currently, so this is the most expediant fix.
SAVE_LDLP=${LD_LIBRARY_PATH:-}
unset LD_LIBRARY_PATH
DPKG_SHLIB_DEPS=$(dpkg-shlibdeps -O "$BUILDDIR/chrome" 2> /dev/null | \
  sed 's/^shlibs:Depends=//')
if [ -n "$SAVE_LDLP" ]; then
  LD_LIBRARY_PATH=$SAVE_LDLP
fi

# Format it nicely and save it for comparison.
# The grep -v is for a duplicate libc6 dep caused by Lucid glibc silliness.
echo "$DPKG_SHLIB_DEPS" | sed 's/, /\n/g' | \
  grep -v '^libc6 (>= 2.3.6-6~)$' > actual

# Compare the expected dependency list to the generate list.
BAD_DIFF=0
diff "$SCRIPTDIR/expected_deps" actual || BAD_DIFF=1
if [ $BAD_DIFF -ne 0 ] && [ -z "${IGNORE_DEPS_CHANGES:-}" ]; then
  echo
  echo "ERROR: Shared library dependencies changed!"
  echo "If this is intentional, please update:"
  echo "chrome/installer/linux/debian/expected_deps"
  echo
  exit $BAD_DIFF
fi
rm -rf "$DUMMY_STAGING_DIR"

# Additional dependencies not in the dpkg-shlibdeps output.
# Pull a more recent version of NSS than required by runtime linking, for
# security and stability updates in NSS.
ADDITION_DEPS="ca-certificates, libappindicator1, libcurl3, \
  libnss3 (>= 3.14.3), lsb-base (>=3.2), xdg-utils (>= 1.0.2), wget"

# Fix-up libnspr dependency due to renaming in Ubuntu (the old package still
# exists, but it was moved to "universe" repository, which isn't installed by
# default).
DPKG_SHLIB_DEPS=$(sed \
    's/\(libnspr4-0d ([^)]*)\), /\1 | libnspr4 (>= 4.9.5-0ubuntu0), /g' \
    <<< $DPKG_SHLIB_DEPS)

# Fix-up libudev dependency because Ubuntu 13.04 has libudev1 instead of
# libudev0.
DPKG_SHLIB_DEPS=$(sed 's/\(libudev0 ([^)]*)\), /\1 | libudev1 (>= 198), /g' \
                  <<< $DPKG_SHLIB_DEPS)

COMMON_DEPS="${DPKG_SHLIB_DEPS}, ${ADDITION_DEPS}"
COMMON_PREDEPS="dpkg (>= 1.14.0)"


# Make everything happen in the OUTPUTDIR.
cd "${OUTPUTDIR}"

case "$TARGETARCH" in
  ia32 )
    export ARCHITECTURE="i386"
    stage_install_debian
    ;;
  x64 )
    export ARCHITECTURE="amd64"
    stage_install_debian
    ;;
  * )
    echo
    echo "ERROR: Don't know how to build DEBs for '$TARGETARCH'."
    echo
    exit 1
    ;;
esac

do_package