#!/bin/bash -e

if [ "$(uname)" = Darwin ] ; then
    binutils='binutils-gdb'
else
    binutils='binutils-2.31'
fi


order=(
    make-4.2.1

    autoconf-2.69
    automake-1.15.1
    m4-1.4.18

    $binutils

    gettext-0.19.8
    gmp-6.1.2
    mpfr-4.0.1
    mpc-1.1.0

    diffutils-3.6
    patch-2.7
    texinfo-6.5
    help2man-1.47.8

    libunistring-0.9.10
    bdwgc
    #guile-2.2.4
    guile-2.0.14
    autogen-5.18.7

    flex

    # # gcc-5.5.0
    # # gcc-6.5.0
    gcc-8.2.0
)

files=(
    ftp://ftp.gnu.org/gnu/make/make-4.2.1.tar.bz2
    ftp://ftp.gnu.org/gnu/make/make-4.2.1.tar.bz2.sig

    ftp://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz
    ftp://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz.sig

    ftp://ftp.gnu.org/gnu/libunistring/libunistring-0.9.10.tar.xz
    ftp://ftp.gnu.org/gnu/libunistring/libunistring-0.9.10.tar.xz.sig

    # for --with-isl (Graphite loop optimization)
    # ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2

    # exact version:
    ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.xz
    ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.xz.sig

    # exact version:
    ftp://ftp.gnu.org/gnu/automake/automake-1.15.1.tar.xz
    ftp://ftp.gnu.org/gnu/automake/automake-1.15.1.tar.xz.sig

    ftp://ftp.gnu.org/gnu/autogen/autogen-5.18.7.tar.xz
    ftp://ftp.gnu.org/gnu/autogen/autogen-5.18.7.tar.xz.sig

    ftp://ftp.gnu.org/gnu/libiconv/libiconv-1.15.tar.gz
    ftp://ftp.gnu.org/gnu/libiconv/libiconv-1.15.tar.gz.sig

    ftp://ftp.gnu.org/gnu/guile/guile-2.0.14.tar.xz
    ftp://ftp.gnu.org/gnu/guile/guile-2.0.14.tar.xz.sig

    # ftp://ftp.gnu.org/gnu/guile/guile-2.2.4.tar.xz
    # ftp://ftp.gnu.org/gnu/guile/guile-2.2.4.tar.xz.sig

    ftp://ftp.gnu.org/gnu/gettext/gettext-0.19.8.tar.xz
    ftp://ftp.gnu.org/gnu/gettext/gettext-0.19.8.tar.xz.sig

    ftp://ftp.gnu.org/gnu/m4/m4-1.4.18.tar.xz
    ftp://ftp.gnu.org/gnu/m4/m4-1.4.18.tar.xz.sig

    ftp://ftp.gnu.org/gnu/binutils/binutils-2.31.tar.xz
    ftp://ftp.gnu.org/gnu/binutils/binutils-2.31.tar.xz.sig

    ftp://ftp.gnu.org/gnu/diffutils/diffutils-3.6.tar.xz
    ftp://ftp.gnu.org/gnu/diffutils/diffutils-3.6.tar.xz.sig

    ftp://ftp.gnu.org/gnu/texinfo/texinfo-6.5.tar.xz
    ftp://ftp.gnu.org/gnu/texinfo/texinfo-6.5.tar.xz.sig

    ftp://ftp.gnu.org/gnu/help2man/help2man-1.47.8.tar.xz
    ftp://ftp.gnu.org/gnu/help2man/help2man-1.47.8.tar.xz.sig

    ftp://ftp.gnu.org/gnu/patch/patch-2.7.tar.xz
    ftp://ftp.gnu.org/gnu/patch/patch-2.7.tar.xz.sig

    ftp://ftp.gnu.org/gnu/gcc/gcc-5.5.0/gcc-5.5.0.tar.xz
    ftp://ftp.gnu.org/gnu/gcc/gcc-5.5.0/gcc-5.5.0.tar.xz.sig

    ftp://ftp.gnu.org/gnu/gcc/gcc-6.5.0/gcc-6.5.0.tar.xz
    ftp://ftp.gnu.org/gnu/gcc/gcc-6.5.0/gcc-6.5.0.tar.xz.sig

    ftp://ftp.gnu.org/gnu/gcc/gcc-8.2.0/gcc-8.2.0.tar.xz
    ftp://ftp.gnu.org/gnu/gcc/gcc-8.2.0/gcc-8.2.0.tar.xz.sig

    https://www.hboehm.info/gc/gc_source/gc-7.6.8.tar.gz
    https://www.hboehm.info/gc/gc_source/libatomic_ops-7.6.6.tar.gz

#   https://www.mpfr.org/mpfr-current/mpfr-4.0.1.tar.xz
    https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz

)

gits=(
    https://github.com/westes/flex.git
    git://sourceware.org/git/binutils-gdb.git
)


function sgr () {
    # SELECT GRAPHIC RENDITION
    echo -n "[$(semicolon='';res='';for arg in "$@";do res="${res}${semicolon}${arg}";semicolon=';';done;echo -n "${res}")m"
}

normal="$(sgr 0)"
bold="$(sgr 1)"
underline="$(sgr 4)"
blink="$(sgr 5)"
invert="$(sgr 7)"
no_bold="$(sgr 22)"
no_underline="$(sgr 24)"
no_blink="$(sgr 25)"
no_invert="$(sgr 27)"
black="$(sgr 30)"
red="$(sgr 31)"
green="$(sgr 32)"
yellow="$(sgr 33)"
blue="$(sgr 34)"
magenta="$(sgr 35)"
cyan="$(sgr 36)"
white="$(sgr 37)"
black_back="$(sgr 40)"
red_back="$(sgr 41)"
green_back="$(sgr 42)"
yellow_back="$(sgr 43)"
blue_back="$(sgr 44)"
magenta_back="$(sgr 45)"
cyan_back="$(sgr 46)"
white_back="$(sgr 47)"

function info(){
    printf '%s#  ' "$yellow"
    # shellcheck disable=SC2059
    printf "$@"
    printf '%s\n' "$normal"
}

function success(){
    printf '%s#  ' "$green"
    # shellcheck disable=SC2059
    printf "$@"
    printf '%s\n' "$normal"
}

function failure(){
    printf '%s#  ' "$red"
    # shellcheck disable=SC2059
    printf "$@"
    printf '%s\n' "$normal"
}

function trim_spaces(){
    local string="$1"
    # ${a## } or ${a%% } doesn't work! (in bash 4.3).
    string="$(echo "$string" | sed -e 's/^ *//' -e 's/ *$//')"
    echo -n "${string}"
}

function trim_colon(){
    local string="$1"
    string="$(echo "$string" | sed -e 's/^:*//' -e 's/:*$//')"
    echo -n "${string}"
}

function get_all(){
    cd  "$prefix/src"
    mkdir -p "$prefix/src/tarballs"
    for file in "${files[@]}" ; do
        # TODO: get the certificates CA Chains!
        (cd "$prefix/src/tarballs" && wget --no-check-certificate --timestamping  "$file")
    done
    for git in "${gits[@]}" ; do
        dir=$(basename "$git" .git)
        if [ -d "$dir" ] ; then
            (cd "$dir" && git pull)
        else
            git clone "$git"
        fi
    done
}

function unpack(){
    local tarball="$1"
    local extension="$2"
    local taropt=''
    case "$extension" in
    (xz)  taropt=J ;;
    (gz)  taropt=z ;;
    (bz2)  taropt=j ;;
    (*) failure 'Bad extension %s' "$extension"
        exit 1 ;;
    esac
    tar ${taropt}xf "$tarball"
}


function name_to_var(){
    local name="$1"
    name="${name//-/_}"
    name="${name//./_}"
    echo "${name}"
}

function name_CFLAGS(){
    local name; name=$(name_to_var "$1")_CFLAGS
    echo "${!name}"
}

function name_CXXFLAGS(){
    local name; name=$(name_to_var "$1")_CXXFLAGS
    echo "${!name}"
}

function name_LDFLAGS(){
    local name; name=$(name_to_var "$1")_LDFLAGS
    echo "${!name}"
}

function name_configure(){
    local name; name=$(name_to_var "$1")_configure
    echo "${!name}"
}

function define_alterations(){
    case "$(gcc -dumpversion)" in
    [789]*)
        # shellcheck disable=SC2034
        make_4_2_1_CFLAGS='-D__alloca=alloca -D__stat=stat'
        ;;
    *)
        true
        ;;
    esac
    # shellcheck disable=SC2034
    gcc_5_5_0_configure="--disable-multilib --with-mpc=${prefix}"
    gcc_6_5_0_configure="--disable-multilib --with-mpc=${prefix}"
    gcc_8_2_0_configure="--disable-multilib --with-mpc=${prefix}"
}

function runcmd(){
    printf "##  %s\n" "$*"
    "$@"
}

function compile_it(){
    local name="$1"
    local log
    local status
    CFLAGS=$(trim_spaces "${CFLAGS:-} $(name_CFLAGS "$name")")           ; export CFLAGS
    CXXFLAGS=$(trim_spaces "${CXXFLAGS:-} $(name_CXXFLAGS "$name")")     ; export CXXFLAGS
    LDFLAGS=$(trim_spaces "${LDFLAGS:-}  $(name_LDFLAGS "$name")")       ; export LDFLAGS
    log="$(pwd)/${name}.log"
    rm -f "$log"
    { date ; env | sort ; } > "$log"
    cd "${name}" && \
        case "${name}" in
        (gcc*)
            #
            # gcc uses a build directory, configure is ../configure !
            #
            # shellcheck disable=SC2046
            mkdir -p build && cd build \
            && info 'Configuring %s' "$name" \
            && runcmd ../configure --prefix="$prefix" $(name_configure "$name") >> "$log" 2>&1 \
            ;;
        (gc-*|gettext-*)
            #
            # Those components have both autogen.sh and configure, but we must skip autogen.sh.
            #
            info 'Configuring %s' "$name" \
            && runcmd ./configure --prefix="$prefix" $(name_configure "$name") >> "$log" 2>&1 \
            ;;
        (*)
            # shellcheck disable=SC2046
            if [ -x autogen.sh ] ; then
                info 'Autogening %s' "$name" \
                && runcmd ./autogen.sh >> "$log" 2>&1 ;
            fi \
            && info 'Configuring %s' "$name" \
            && runcmd ./configure --prefix="$prefix" $(name_configure "$name") >> "$log" 2>&1 \
            ;;
        esac \
            && info 'Compiling %s' "$name" \
            && runcmd make >> "$log" 2>&1 \
            && info 'Installing %s' "$name" \
            && runcmd make install >> "$log" 2>&1

    status=$?
    if [ $status -eq 0 ] ; then
        success 'Status %d' "$status"
    else
        failure 'Status %d' "$status"
    fi
}

function compile_bdwgc(){
    local name=gc-7.6.8
    # autoreconf -vif
    # automake --add-missing
    ln -sf  ../libatomic_ops-7.6.6 "${name}"/libatomic_ops \
        && compile_it "${name}"
}

function compile_module(){
    local name="$1"
    (
        if [ "x$(type -t "compile_${name}")" = xfunction ] ; then
            "compile_${name}"
        else
            compile_it "${name}"
        fi
    )
}

function compile_all(){
    local tarball
    local extension
    local name
    # unpack
    for tarball in tarballs/*.tar.{xz,gz,bz2} ; do
        extension="${tarball/*.}"
        name=$(basename "$tarball" ".tar.$extension")
        info 'Extracting %s' "$name"
        rm -rf "$name"
        unpack "$tarball" "$extension"
    done
    define_alterations
    # compile first time
    for module in "${order[@]}" ; do
        compile_module "$module"
        hash -r
    done
    # compile again, with the new compiler!
    for module in "${order[@]}" ; do
        compile_module "$module"
        hash -r
    done
}

function save_env(){
    for var ; do
        echo "${var}=${!var}"
        echo export ${var}
    done > "${prefix}/environment"
    export BASH_ENV="${prefix}/environment"
    export ENV="${prefix}/environment"
}

function get_and_compile(){
    # prefix="$HOME/gcc"
    prefix='/usr/local/gcc'
    unset BASH_ENV
    unset ENV

    if [ "$(uname)" = Darwin ] ; then
        export PATH="/opt/local/bin:${PATH}"
        export CFLAGS=$(trim_spaces "-I/opt/local/include ${CFLAGS:-}")
        export CXXFLAGS=$(trim_spaces "-I/opt/local/include ${CXXFLAGS:-}")
        export LDFLAGS=$(trim_spaces "-L/opt/local/lib ${LDFLAGS:-}")
        export LD_LIBRARY_PATH=$(trim_colon "/opt/local/lib:${LD_LIBRARY_PATH:-}")
        export DYLD_LIBRARY_PATH=$(trim_colon "/opt/local/lib:${DYLD_LIBRARY_PATH:-}")
    fi
    export PATH=$(trim_colon "${prefix}/bin:${PATH}")
    export CFLAGS=$(trim_spaces "-I${prefix}/include ${CFLAGS:-}")
    export CXXFLAGS=$(trim_spaces "-I${prefix}/include ${CXXFLAGS:-}")
    export LDFLAGS=$(trim_spaces "-L${prefix}/lib64 -L${prefix}/lib ${LDFLAGS:-}")
    export PKG_CONFIG_PATH=$(trim_colon "${prefix}/lib/pkgconfig:${PKG_CONFIG_PATH:-}")
    export LD_LIBRARY_PATH=$(trim_colon "${prefix}/lib64:${prefix}/lib:${LD_LIBRARY_PATH:-}")
    export DYLD_LIBRARY_PATH=$(trim_colon "${prefix}/lib64:${prefix}/lib:${DYLD_LIBRARY_PATH:-}")

    hash -r

    save_env PATH CFLAGS CXXFLAGS LDFLAGS PKG_CONFIG_PATH

    get_all
    compile_all

    echo export PATH="'${prefix}/bin:$PATH'"
    echo export CFLAGS="'-I${prefix}/include ${CFLAGS:-}'"
    echo export CXXFLAGS="'-I${prefix}/include ${CXXFLAGS:-}'"
    echo export LDFLAGS="'-L${prefix}/lib64 -L${prefix}/lib ${LDFLAGS:-}'"
    echo export PKG_CONFIG_PATH="'${prefix}/lib/pkgconfig:${PKG_CONFIG_PATH}'"
}

function with_clean_environment(){
    local program="$1"
    local arg="$2"
    # We need to restrict the environment used while compiling.
    # We keep PATH and PKG_CONFIG_PATH, for the bootstrap compiler and tools.
    env -i \
        EDITOR="${EDITOR}" \
        VISUAL="${VISUAL}" \
        HOME="${HOME}" \
        INPUTRC="${INPUTRC}" \
        LANG="${LANG}" \
        LC_CTYPE="${LC_CTYPE}" \
        LOGNAME="${LOGNAME}" \
        MANPATH="${MANPATH}" \
        PAGER="${PAGER}" \
        PATH="${PATH}" \
        PKG_CONFIG_PATH="${PKG_CONFIG_PATH}" \
        SHELL="${SHELL}" \
        TERM="${TERM}" \
        TZ="${TZ}" \
        USER="${USER}" \
        "${program}" \
        "${arg}"
}

function main(){
    if [ $# -eq 0 ] ; then
        with_clean_environment "$0" '--%run'
    else
        for arg ; do
            case "$arg" in
            (--%run)
                get_and_compile
                ;;
            (*)
                printf 'Invalid argument: %s\n' 1>&2
                exit 64
            esac
        done
    fi
    exit 0
}

main "$@"


# ./configure --prefix="$prefix" --enable-multilib
# make
ViewGit