#!/bin/bash

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

. "$(dirname "$0")/common.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/toolchain_util.sh" || exit 1

# Script must run inside the chroot
assert_inside_chroot

assert_not_root_user

# Developer-visible flags.
DEFINE_string board "${DEFAULT_BOARD}" \
  "The name of the board to set up."
DEFINE_boolean default "${FLAGS_FALSE}" \
  "Set board to the default board in your chroot"
DEFINE_boolean force "${FLAGS_FALSE}" \
  "Force re-creating board root."
DEFINE_boolean usepkg "${FLAGS_TRUE}" \
  "Use binary packages when possible."
DEFINE_boolean usepkgonly "${FLAGS_FALSE}" \
  "Only use/download binary packages."
DEFINE_boolean getbinpkg "${FLAGS_TRUE}" \
  "Download binary packages from remote repository."
DEFINE_string getbinpkgver "" \
  "Use binary packages from a specific version."
DEFINE_string pkgdir "" \
  "Use binary packages from a custom directory instead of /build/[ARCH]/var/lib/portage/pkgs/."
DEFINE_string binhost "" \
  "Use binary packages from a specific location instead of $FLATCAR_DEV_BUILDS/... "
DEFINE_boolean toolchainpkgonly "${FLAGS_FALSE}" \
  "Use binary packages only for the board toolchain."
DEFINE_boolean skip_toolchain_update "${FLAGS_FALSE}" \
  "Don't update toolchain automatically."
DEFINE_boolean skip_chroot_upgrade "${FLAGS_FALSE}" \
  "Don't run the chroot upgrade automatically; use with care."
DEFINE_boolean regen_configs "${FLAGS_FALSE}" \
  "Regenerate all config files (useful for modifying profiles w/out rebuild)."
DEFINE_boolean regen_configs_only "${FLAGS_FALSE}" \
  "Regenerate all config files and nothing else, even if nothing else is installed."

FLAGS_HELP="usage: $(basename $0) [flags]

setup_board sets up the sysroot for a particular board. This script is called
automatically when you run build_packages, so there is typically no need to
call it directly, unless you want to blow away your board (using --force).
"
show_help_if_requested "$@"

# The following options are advanced options, only available to those willing
# to read the source code. They are not shown in help output, since they are
# not needed for the typical developer workflow.
DEFINE_string libc_version "[stable]" \
  "Version of libc to use."
DEFINE_boolean quiet $FLAGS_FALSE \
  "Don't print warnings when board already exists."
DEFINE_string variant "" \
  "Board variant."


# builds wrappers like equery-arm-generic.
# args:
#   $1:  command to wrap
#   rest:  extra arguments to pass to the command
_generate_wrapper() {
  local command="${1}"
  shift
  local extra_args="$@"


  local target="/usr/local/bin/${command}-${BOARD_VARIANT}"
  sudo_clobber "${target}" <<EOF
#!/bin/bash

export PORTAGE_CONFIGROOT="$BOARD_ROOT"
export SYSROOT="\${SYSROOT:-$BOARD_ROOT}"
export ROOT="$BOARD_ROOT"
exec sudo -E ${command} ${extra_args} "\$@"
EOF
  # Note: parent will process these.
  wrappers+=( "${target}" )
  upper=${command^^}
  eval ${upper/-/_}_WRAPPER="${target}" # ${foo^^} returns toupper($foo)
}

generate_all_wrappers() {
  local cmds=() wrappers=()
  local wrapper

  info "Generating wrapper scripts"

  for wrapper in 'emerge --root-deps' ebuild eclean equery portageq \
                 qcheck qfile qlist emaint glsa-check; do
    _generate_wrapper ${wrapper}
  done

  wrapper="/usr/local/bin/cros_workon-${BOARD_VARIANT}"
  sudo_clobber "${wrapper}" <<EOF
#!/bin/bash
exec "${SRC_ROOT}/scripts/cros_workon" --board ${BOARD_VARIANT} "\$@"
EOF
  wrappers+=( "${wrapper}" )

  wrapper="/usr/local/bin/gdb-${BOARD_VARIANT}"
  sudo_clobber "${wrapper}" <<EOF
#!/bin/bash
exec ${BOARD_CHOST}-gdb -iex 'set sysroot ${BOARD_ROOT}' "\$@"
EOF
  wrappers+=( "${wrapper}" )

  cmds+=(
    "chmod a+rx ${wrappers[*]}"
    "chown root:root ${wrappers[*]}"
  )

  sudo_multi "${cmds[@]}"
}

generate_binhost_list() {
  local t
  [[ "${FLAGS_toolchainpkgonly}" -eq "${FLAGS_TRUE}" ]] && t="-t"
  FLAGS_getbinpkgver="${FLAGS_getbinpkgver/current/${FLATCAR_VERSION_ID}}"
  FLAGS_getbinpkgver="${FLAGS_getbinpkgver/latest/${FLATCAR_VERSION_ID}}"
  FLAGS_getbinpkgver="${FLAGS_getbinpkgver/sdk/${FLATCAR_SDK_VERSION}}"

  get_board_binhost $t "${BOARD}" ${FLAGS_getbinpkgver}
}

# Parse command line flags
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# Only now can we die on error.  shflags functions leak non-zero error codes,
# so will die prematurely if 'switch_to_strict_mode' is specified before now.
switch_to_strict_mode

if [ -z "$FLAGS_board" ] ; then
  error "--board required."
  exit 1
fi

if [[ "${FLAGS_usepkgonly}" -eq "${FLAGS_TRUE}" ]]; then
  for flag in usepkg getbinpkg; do
    fvar="FLAGS_${flag}"
    if [[ "${!fvar}" -ne "${FLAGS_TRUE}" ]]; then
      die_notrace "--usepkgonly is incompatible with --no${flag}"
    fi
  done
fi

get_board_and_variant $FLAGS_board $FLAGS_variant

# Locations we will need
COREOS_OVERLAY="${REPO_ROOT}/src/third_party/coreos-overlay"
COREOS_CONFIG="${COREOS_OVERLAY}/coreos/config"
BOARD_ROOT="/build/${BOARD_VARIANT}"
BOARD_ETC="${BOARD_ROOT}/etc"
BOARD_ARCH=$(get_board_arch "$BOARD")
BOARD_CHOST=$(get_board_chost ${BOARD})
PORTAGE_PROFILE=$(get_board_profile "$BOARD")
BOARD_BINHOST="$FLAGS_binhost $(generate_binhost_list)"
BOARD_PKGDIR="${FLAGS_pkgdir:-${BOARD_ROOT}/var/lib/portage/pkgs}"

if [[ ${FLAGS_regen_configs_only} -eq ${FLAGS_TRUE} ]]; then
  FLAGS_regen_configs=${FLAGS_TRUE}
  FLAGS_skip_chroot_upgrade=${FLAGS_TRUE}
elif [[ -d "${BOARD_ROOT}" ]]; then
  if [[ ${FLAGS_force} -eq ${FLAGS_TRUE} ]]; then
    info "--force set.  Re-creating ${BOARD_ROOT}..."
    # Removal takes long. Make it asynchronous.
    TEMP_DIR=`mktemp -d`
    sudo mv "${BOARD_ROOT}" "${TEMP_DIR}"
    sudo rm -rf "${TEMP_DIR}" &
  elif [[ ${FLAGS_regen_configs} -eq ${FLAGS_FALSE} ]]; then
    if [[ ${FLAGS_quiet} -eq ${FLAGS_FALSE} ]]; then
      warn "Board output directory '$BOARD_ROOT' already exists."
      warn "Not setting up board root. "
      warn "Use --force to clobber the board root and start again."
    fi
    exit 0
  fi
else
  # Missing board root and --regen_configs_only wasn't used.
  FLAGS_regen_configs=${FLAGS_FALSE}
fi

# Before we can run any tools, we need to update chroot
UPDATE_ARGS="--toolchain_boards=${BOARD}"
if [ "${FLAGS_usepkg}" -eq "${FLAGS_TRUE}" ]; then
  UPDATE_ARGS+=" --usepkg"
  if [[ "${FLAGS_usepkgonly}" -eq "${FLAGS_TRUE}" ]]; then
    UPDATE_ARGS+=" --usepkgonly"
  else
    UPDATE_ARGS+=" --nousepkgonly"
  fi
  if [[ "${FLAGS_getbinpkg}" -eq "${FLAGS_TRUE}" ]]; then
    UPDATE_ARGS+=" --getbinpkg "
  else
    UPDATE_ARGS+=" --nogetbinpkg "
  fi
  if [[ -n "${FLAGS_binhost}" ]]; then
    UPDATE_ARGS+=" --binhost=${FLAGS_binhost} "
  fi
else
  UPDATE_ARGS+=" --nousepkg"
fi
if [ "${FLAGS_skip_toolchain_update}" -eq "${FLAGS_TRUE}" ]; then
  UPDATE_ARGS+=" --skip_toolchain_update"
fi
if [ "${FLAGS_skip_chroot_upgrade}" -eq "${FLAGS_FALSE}" ] ; then
  "${SRC_ROOT}/scripts"/update_chroot ${UPDATE_ARGS}
fi

# Migrate board roots that were created before the package location
# was standardized to /var/lib/portage/pkgs, build_image will fail if we
# simply forget about the old location and start writing to the new.
# Keep /packages as a compatibility symlink until everyone is updated.
if [[ ! -L "${BOARD_ROOT}/packages" ]]; then
  if [[ ! -d "${BOARD_ROOT}/var/lib/portage/pkgs" ]]; then
    if [[ -d "${BOARD_ROOT}/packages" ]]; then
      warn "Moving board package directory to ${BOARD_ROOT}/var/lib/portage/pkgs"
      sudo mkdir -p "${BOARD_ROOT}/var/lib/portage"
      sudo mv "${BOARD_ROOT}/packages" "${BOARD_ROOT}/var/lib/portage/pkgs"
    else
      sudo mkdir -p "${BOARD_ROOT}/var/lib/portage/pkgs"
    fi
  fi
  sudo ln -sfT "var/lib/portage/pkgs" "${BOARD_ROOT}/packages"
fi

info "Configuring portage in ${BOARD_ROOT}"
sudo mkdir -p "${BOARD_ETC}/portage/"{profile,repos.conf}
sudo cp /etc/portage/repos.conf/* "${BOARD_ETC}"/portage/repos.conf/
sudo ROOT="${BOARD_ROOT}" eselect profile set --force "${PORTAGE_PROFILE}"

# Cleanup/migrate from older make.conf files
sudo rm -f "${BOARD_ETC}/make.conf" "${BOARD_ETC}/make.conf.common"
if [[ -f "${BOARD_ETC}/make.conf.user" ]]; then
    sudo mv "${BOARD_ETC}/make.conf.user" \
        "${BOARD_ETC}/portage/make.conf.user"
else
    sudo touch "${BOARD_ETC}/portage/make.conf.user"
fi

sudo_clobber "${BOARD_ETC}/portage/make.conf" <<EOF
# Created by setup_board

# Settings derived from the host environment
CBUILD="$(portageq envvar CHOST)"
HOSTCC="$(portageq envvar CHOST)-gcc"
PORTDIR="$(portageq envvar PORTDIR)"
PORTDIR_OVERLAY="$(portageq envvar PORTDIR_OVERLAY)"
DISTDIR="$(portageq envvar DISTDIR)"
MAKEOPTS="$(portageq envvar MAKEOPTS)"
PORTAGE_USERNAME="$(portageq envvar PORTAGE_USERNAME)"

# Board specific settings
CHOST="${BOARD_CHOST}"
ROOT="${BOARD_ROOT}/"
PKGDIR="${BOARD_PKGDIR}"
PORT_LOGDIR="${BOARD_ROOT}/var/log/portage"
PORTAGE_TMPDIR="${BOARD_ROOT}/var/tmp"
PORTAGE_BINHOST="${BOARD_BINHOST}"

# Generally there isn't any need to add packages to @world by default.
# You can use --select to override this.
EMERGE_DEFAULT_OPTS="--oneshot"

# Allow the user to override or define additional settings.
source "${BOARD_ETC}/portage/make.conf.user"
EOF

# required when using --regen_configs_only
sudo mkdir -p --mode=01777 "${BOARD_ROOT}"{/tmp,/var/tmp}

# make it easy to find debug symbols
sudo mkdir -p /usr/lib/debug/build
sudo ln -sfT ${BOARD_ROOT}/usr/lib/debug /usr/lib/debug/${BOARD_ROOT}

# remove bogus pkg-config wrapper
sudo rm -f "${BOARD_ROOT}/build/bin/${BOARD_CHOST}-pkg-config"

generate_all_wrappers

# Unclear why this is required but it doesn't happen automatically
info "Performing package updates..."
${EMAINT_WRAPPER} --fix movebin
${EMAINT_WRAPPER} --fix moveinst
${EMAINT_WRAPPER} --fix world

if [[ ${FLAGS_regen_configs} -eq ${FLAGS_FALSE} ]]; then
  EMERGE_FLAGS="--select --quiet --root-deps=rdeps"
  EMERGE_FLAGS+=" --jobs=${NUM_JOBS}"
  EMERGE_TOOLCHAIN_FLAGS="${EMERGE_FLAGS}"

  if [[ "${FLAGS_usepkg}" -eq "${FLAGS_TRUE}"  && \
        "${FLAGS_getbinpkg}" -eq "${FLAGS_TRUE}" ]]
  then
    if [[ "${FLAGS_usepkgonly}" -eq "${FLAGS_TRUE}" ]]; then
      EMERGE_FLAGS+=" --usepkgonly --rebuilt-binaries n"
    else
      EMERGE_FLAGS+=" --usepkg"
    fi
    EMERGE_FLAGS+=" --getbinpkg"
  fi

  info "Installing baselayout"
  "${EMERGE_WRAPPER}" ${EMERGE_FLAGS} --nodeps sys-apps/baselayout

  if [[ "${FLAGS_usepkg}" -ne "${FLAGS_TRUE}" ||
        "${FLAGS_getbinpkg}" -ne "${FLAGS_TRUE}" ]]
  then
    # When binary packages are disabled we need to make sure the cross
    # sysroot includes any build dependencies for the toolchain.
    info "Installing toolchain build dependencies"
    install_cross_libs "${BOARD_CHOST}" ${EMERGE_FLAGS} --buildpkg=n

    info "Building toolchain dependencies"
    "${EMERGE_WRAPPER}" --buildpkg --buildpkgonly --onlydeps -e \
        --root="/usr/${BOARD_CHOST}" --sysroot="/usr/${BOARD_CHOST}" \
        ${EMERGE_TOOLCHAIN_FLAGS} "${TOOLCHAIN_PKGS[@]}"
    info "Building toolchain"
    "${EMERGE_WRAPPER}" --buildpkg --buildpkgonly \
        --root="/usr/${BOARD_CHOST}" --sysroot="/usr/${BOARD_CHOST}" \
	${EMERGE_TOOLCHAIN_FLAGS} "${TOOLCHAIN_PKGS[@]}"
  fi

  info "Installing toolchain"
  "${EMERGE_WRAPPER}" \
      --usepkgonly --getbinpkg --rebuilt-binaries n \
      ${EMERGE_TOOLCHAIN_FLAGS} "${TOOLCHAIN_PKGS[@]}"
fi

if [[ ${FLAGS_regen_configs_only} -eq ${FLAGS_FALSE} ]]; then
  # Setup BOARD_ROOT for QEMU user emulation.
  setup_qemu_static "${BOARD_ROOT}"
fi

if [ $FLAGS_default -eq $FLAGS_TRUE ] ; then
  echo $BOARD_VARIANT > "$GCLIENT_ROOT/src/scripts/.default_board"
fi

command_completed
info "The SYSROOT is: ${BOARD_ROOT}"

# NOTE: Printing the working-on ebuilds does not only serve the informative
# purpose. It also causes the ${BOARD_ROOT}/etc/portage/package.* files to be
# regenerated.
WORKING_ON=$("${SRC_ROOT}/scripts/cros_workon" --board=${FLAGS_board} list)
if [ -n "${WORKING_ON}" ]; then
  info
  info "Currently working on the following ebuilds for this board:"
  info "${WORKING_ON}"
fi
