#!/bin/bash
set -e

# The name and location of the br2-external tree, once validated.
declare BR2_NAME
declare BR2_EXT

main() {
    local OPT OPTARG
    local br2_ext ofile ofmt

    while getopts :hkmo: OPT; do
        case "${OPT}" in
        h)  help; exit 0;;
        o)  ofile="${OPTARG}";;
        k)  ofmt="kconfig";;
        m)  ofmt="mk";;
        :)  error "option '%s' expects a mandatory argument\n" "${OPTARG}";;
        \?) error "unknown option '%s'\n" "${OPTARG}";;
        esac
    done
    # Forget options; keep only positional args
    shift $((OPTIND-1))

    # Accept 0 or 1 br2-external tree.
    if [ ${#} -gt 1 ]; then
        error "only zero or one br2-external tree allowed.\n"
    fi

    br2_ext="${1}"

    case "${ofmt}" in
    mk|kconfig)
        ;;
    *)  error "no output format specified (-m/-k)\n";;
    esac
    if [ -z "${ofile}" ]; then
        error "no output file specified (-o)\n"
    fi

    exec >"${ofile}"

    do_validate "${br2_ext}"

    do_${ofmt}
}

# Validates the br2-external tree passed as argument. Makes it cannonical
# and store it in global variable BR2_EXT.
#
# Note: since this script is always first called from Makefile context
# to generate the Makefile fragment before it is called to generate the
# Kconfig snippet, we're sure that any error in do_validate will be
# interpreted in Makefile context. Going up to generating the Kconfig
# snippet means that there were no error.
#
do_validate() {
    local br2_ext="${1}"
    local br2_name n

    # No br2-external tree is valid
    if [ -z "${br2_ext}" ]; then
        return
    fi

    if [ ! -d "${br2_ext}" ]; then
        error "'%s': no such file or directory\n" "${br2_ext}"
    fi
    if [ ! -r "${br2_ext}" -o ! -x "${br2_ext}" ]; then
        error "'%s': permission denied\n" "${br2_ext}"
    fi
    if [ ! -f "${br2_ext}/external.desc" ]; then
        error "'%s': does not have a name (in 'external.desc')\n" "${br2_ext}"
    fi
    br2_name="$(sed -r -e '/^name: +(.*)$/!d; s//\1/' "${br2_ext}/external.desc")"
    if [ -z "${br2_name}" ]; then
        error "'%s/external.desc': does not define the name\n" "${br2_ext}"
    fi
    # Only ASCII chars in [A-Za-z0-9_] are permitted
    n="$(sed -r -e 's/[A-Za-z0-9_]//g' <<<"${br2_name}" )"
    if [ -n "${n}" ]; then
        # Escape '$' so that it gets printed
        error "'%s': name '%s' contains invalid chars: '%s'\n" \
            "${br2_ext}" "${br2_name//\$/\$\$}" "${n//\$/\$\$}"
    fi
    if [ ! -f "${br2_ext}/external.mk" ]; then
        error "'%s/external.mk': no such file or directory\n" "${br2_ext}"
    fi
    if [ ! -f "${br2_ext}/Config.in" ]; then
        error "'%s/Config.in': no such file or directory\n" "${br2_ext}"
    fi

    BR2_NAME="${br2_name}"
    BR2_EXT="$(cd "${br2_ext}"; pwd -P )"
}

# Generate the .mk snippet that defines makefile variables
# for the br2-external tree
do_mk() {
    printf '#\n# Automatically generated file; DO NOT EDIT.\n#\n'
    printf '\n'

    printf 'BR2_EXTERNAL ?= %s\n' "${BR2_EXT}"
    printf 'BR2_EXTERNAL_NAME = \n'
    printf 'BR2_EXTERNAL_MK =\n'
    printf '\n'

    if [ -z "${BR2_NAME}" ]; then
        printf '# No br2-external tree defined.\n'
        return
    fi

    printf 'BR2_EXTERNAL_NAME = %s\n' "${BR2_NAME}"
    printf 'BR2_EXTERNAL_MK = %s/external.mk\n' "${BR2_EXT}"
    printf 'BR2_EXTERNAL_%s_PATH = %s\n' "${BR2_NAME}" "${BR2_EXT}"
}

# Generate the kconfig snippet for the br2-external tree.
do_kconfig() {
    printf '#\n# Automatically generated file; DO NOT EDIT.\n#\n'
    printf '\n'

    if [ -z "${BR2_NAME}" ]; then
        printf '# No br2-external tree defined.\n'
        return
    fi

    printf 'menu "User-provided options"\n'
    printf '\n'
    printf 'comment "%s (in %s)"\n' "${BR2_NAME}" "${BR2_EXT}"
    printf '\n'
    printf 'config BR2_EXTERNAL_%s_PATH\n' "${BR2_NAME}"
    printf '\tstring\n'
    printf '\tdefault "%s"\n' "${BR2_EXT}"
    printf '\n'
    printf 'source "$BR2_EXTERNAL_%s_PATH/Config.in"\n' "${BR2_NAME}"
    printf '\n'
    printf "endmenu # User-provided options\n"
}

help() {
    cat <<-_EOF_
	Usage:
	    ${my_name} <-m|-k> -o FILE PATH

	With -m, ${my_name} generates the makefile fragment that defines
	variables related to the br2-external tree passed as positional
	argument.

	With -k, ${my_name} generates the kconfig snippet to include the
	configuration options specified in the br2-external tree passed
	as positional argument.

	Using -k and -m together is not possible. The last one wins.

	Options:
	    -m  Generate the makefile fragment.

	    -k  Generate the kconfig snippet.

	    -o FILE
	        FILE in which to generate the kconfig snippet or makefile
	        fragment.

	Returns:
	    0   If no error
	    !0  If any error
	_EOF_
}

error() { local fmt="${1}"; shift; printf "BR2_EXTERNAL_ERROR = ${fmt}" "${@}"; exit 1; }

my_name="${0##*/}"
main "${@}"
