#!/bin/bash
#
# sdk-foreach executes given command on each build target and device
#
# Copyright (C) 2016 Jolla Ltd.
# Contact: Martin Kampas <martin.kampas@jolla.com>
# All rights reserved.
#
# You may use this file under the terms of BSD license as follows:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#   * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the Jolla Ltd nor the
#     names of its contributors may be used to endorse or promote products
#     derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

set -o nounset

SELF=$(basename "$0")
DEVICES_XML=/etc/mersdk/share/devices.xml
TOOLINGS_DIR=/srv/mer/toolings

if tty --silent <&1; then
    COLOR_info=32
    COLOR_question=32
    COLOR_warn=31
    message() {
        local color=
        eval color=\${COLOR_$1}
        shift
        printf '\033[%sm%s %s\033[00m\n' $color "$SELF:" "$*"
    }
else
    message() {
        shift
        echo "$SELF: $*"
    }
fi

maybe_ask() {
    [[ $OPT_NONINTERACTIVE ]] && return 0

    local answer=
    while true; do
        read -p "$(message question "$* (y/n) ")" answer
        case $answer in
            [yY]*)
                return 0
                ;;
            [nN]*)
                return 1
                ;;
            *)
                message warn "Please answer yes or no"
                ;;
        esac
    done
}

fatal() {
    message warn "FATAL: $*"
}

usage() {
    cat <<EOF
sdk-foreach executes given command as root user in every (selected) tooling,
target, device and optionally also locally inside the SDK itself.

Usage:
    $SELF [OPTION]... [--] COMMAND [ARG]...

Options:
    -h | --help             : show this help
    -m <mode>               : use given scratchbox2 mapping mode with targets
                              (default '$OPT_SB2_MODE')
    -y | --non-interactive  : do not ask questions; execute on each build
                              target and device without confirmation
    -l                      : execute also locally

EOF
}

execute_locally() {
    sudo "$@"
}

list_toolings() {
    sdk-manage --tooling --list
}

execute_in_tooling() {
    local tooling=$1
    shift
    sudo "$TOOLINGS_DIR/$tooling/mer-tooling-chroot" "$@"
}

list_targets() {
    sdk-manage --target --list
}

execute_in_target() {
    local target=$1
    shift
    sb2 -t "$target" -m "$OPT_SB2_MODE" -R "$@"
}

list_devices() {
    [[ $INSIDE_VBOX ]] || return 0
    xmllint --xpath '//device/@name' "$DEVICES_XML" |sed 's/ name=/ /g' |tr '"' "'"
}

execute_on_device() {
    local device=$1
    shift
    mb2 -d "$device" ssh sudo "$@"
}

OPT_NONINTERACTIVE=
OPT_LOCAL=
OPT_SB2_MODE=sdk-install

# handle commandline options
while [[ ${1:-} ]]; do
    case $1 in
        -h | --help )
            usage
            exit
            ;;
        -y | --non-interactive )
            OPT_NONINTERACTIVE=1
            ;;
        -l )
            OPT_LOCAL=1
            ;;
        -m )
            OPT_SB2_MODE=${2:-}
            if ! [[ $OPT_SB2_MODE ]]; then
                fatal "$1: Argument expected"
                usage >&2
                exit 1
            fi
            shift
            ;;
        -- )
            break
            ;;
        -??* )
            arg=$1
            shift
            set -- "${arg}" -"${arg:1:1}" -"${arg:2}" "${@}"
            ;;
        -* )
            fatal "unknown option '$1'"
            exit 1
            ;;
        * )
            break
            ;;
    esac

    shift
done

if [[ $EUID -eq 0 ]]; then
    fatal "$SELF must not be run as root."
    exit 1
fi

INSIDE_CHROOT=$([[ -e /etc/mer-sdk-chroot ]] && echo 1)
INSIDE_VBOX=$([[ -e /etc/mer-sdk-vbox ]] && echo 1)

# exactly one must be true
if [[ $INSIDE_CHROOT$INSIDE_VBOX != 1 ]]; then
    echo >&2 "Internal error: Failed to determine type of SDK installation"
    exit 1
fi

if [[ $OPT_LOCAL ]]; then
    message info "Executing locally..."
    execute_locally "$@"
fi

for tooling in $(list_toolings); do
    maybe_ask "Execute in '$tooling' tooling?" || continue
    message info "Executing in '$tooling' tooling..."
    execute_in_tooling "$tooling" "$@"
done

for target in $(list_targets); do
    maybe_ask "Execute in '$target' build target?" || continue
    message info "Executing in '$target' build target..."
    execute_in_target "$target" "$@"
done

eval devices=($(list_devices))
for device in ${devices[@]:+"${devices[@]}"}; do
    maybe_ask "Execute on '$device' device?" || continue
    message info "Executing on '$device' device..."
    execute_on_device "$device" "$@"
done

# For Emacs:
# Local Variables:
# indent-tabs-mode:nil
# tab-width:4
# mode: sh
# End:
# For VIM:
# vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
