#!/bin/sh
#
# Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.

usage() {
    cat << EOF
$(basename "$0") [options] <input files>

    -d          Build a debug version.
    -g          Disable HILTI-side optimizations of the generated code.
    -o <file>   Destination name for the compiled executable; default is "a.out".
    -t          Do not delete tmp files (useful for inspecting, and use with debugger)
    -v          Verbose output, display command lines executing.
    -S          Do not compile the "spicy-driver" host application into executable.

Input files may be anything that spicyc can compile to C++.

EOF
}

mktmp() {
    base=$(basename "$1")
    ext=$2
    cnt=0
    while : ; do
        tmp=${dest}/${base}.${cnt}.tmp.${ext}
        test -e "${tmp}" || break
        cnt=$((cnt + 1))
    done
    touch "${tmp}"
    echo "${tmp}"
}

mkcc() {
    tmp=$(mktmp "$1" cc)
    echo "${tmp}"
}

execute() {
    # Remove absolute path of executable
    test "${verbose}" = 1 && echo "$@" | sed 's#^[^ ]*/\([a-zA-Z+_-]\{1,\}\) #> \1 #'
    $@
}

cleanup() {
   test "${delete_tmps}" = 1 && rm -rf "${dest}"
}

### Main

hiltic_flags=""
spicy_config_flags=""
verbose=0
delete_tmps=1
compile_spicy_driver=1
out=a.out

while getopts "ghdo:Stv" opt; do
    case "$opt" in
        d)
           hiltic_flags="${hiltic_flags} -d"
           spicy_config_flags="--debug"
           ;;

        g)
           hiltic_flags="${hiltic_flags} -g"
           ;;

        o) out=$OPTARG;;
        S) compile_spicy_driver=0;;
        t) delete_tmps=0;;
        v) verbose=1;;

        h) usage; exit 0;;
        *) usage; exit 1;;
    esac
done

shift $((OPTIND - 1))

inputs=$*
test -z "${inputs}" && usage && exit 1
test -z "${out}" && usage && exit 1

base=$(cd "$(dirname "$0")/.." && pwd)

# On MSYS/Git Bash, paths like /c/... are not understood by native Windows
# programs. Convert them to Windows-compatible paths (C:/...) when needed.
native_path() {
    if which cygpath >/dev/null 2>&1; then
        cygpath -m "$1"
    else
        echo "$1"
    fi
}

if [ "${out}" != "${out#/}" ]; then
    # absolute path
    dest=${out}.$$.tmp.d
else
    # relative path
    dest=$(native_path "$(pwd)")/${out}.$$.tmp.d
fi

rm -rf "${dest}"
mkdir -p "${dest}"

trap cleanup EXIT

for i in $(which hilti-config 2>/dev/null) ${base}/bin/hilti-config ${base}/build/bin/hilti-config; do
   test -x "$i" && hilti_config=$i && break
done

for i in $(which spicy-config 2>/dev/null) ${base}/bin/spicy-config ${base}/build/bin/spicy-config; do
   test -x "$i" && spicy_config=$i && break
done

if [ -z "${hilti_config}" ]; then
    echo cannot find hilti-config
    exit 1
fi

if [ -z "${spicy_config}" ]; then
    echo cannot find spicy-config
    exit 1
fi

cxx="$("${spicy_config}" --cxx ${spicy_config_flags})"
cxxflags="$("${spicy_config}" --cxxflags ${spicy_config_flags})"
ldflags="$("${spicy_config}" --ldflags ${spicy_config_flags})"

spicy_lib_dir=$(for i in $(${spicy_config} --libdirs); do test -e "${i}/spicy_rt.hlt" && echo "${i}" && break; done) # extract the directory we want
spicy_driver=${spicy_lib_dir}/spicy-driver-host.cc

rt_modules=""

cc_inputs=""
if [ "${compile_spicy_driver}" = 1 ]; then
    rt_modules="${spicy_lib_dir}/filter.spicy"
    cc_inputs="${cc_inputs} ${spicy_driver}"
fi

spicy_inputs=""
for i in ${inputs}; do
    i=$(native_path "$i")
    case "${i}" in
        *.cc)
            cc_inputs="${cc_inputs} ${i}"
            ;;
        *)
            spicy_inputs="${spicy_inputs} ${i}"
            ;;
    esac
done


# Generate C++ output files. We pass `cc_inputs` here as well even though its
# only effect is to cause emitting of generated files.
execute "$("${spicy_config}" --spicyc)" ${hiltic_flags} -x "${dest}/" ${spicy_inputs} ${cc_inputs} ${rt_modules} || exit 1

cxxs="${cc_inputs} $(find "${dest}" -type f)"

# Compile all C++ code into executable
test "${verbose}" = 1 && echo "> ${cxx} [cxxflags] ${cxxs} [ldflags] -o ${out}"
"${cxx}" ${cxxflags} ${cxxs} ${ldflags} -o "${out}" || exit 1
