# This file declares functions to install the uClibc C library
# Copyright 2007 Yann E. MORIN
# Licensed under the GPL v2. See COPYING in the root of this package

# This function builds and install the full C library
uClibc_ng_main()
{
    CT_DoStep INFO "Installing C library"
    CT_mkdir_pushd "${CT_BUILD_DIR}/build-libc"
    CT_IterateMultilibs uClibc_ng_backend_once multilib
    CT_Popd
    CT_EndStep
}

# Common backend for 1st and 2nd passes, once per multilib.
uClibc_ng_backend_once()
{
    local multi_dir multi_os_dir multi_root multi_flags multi_index multi_count
    local multilib_dir startfiles_dir
    local jflag=${CT_JOBSFLAGS}
    local -a make_args
    local extra_cflags f cfg_cflags cf
    local hdr_install_subdir

    for arg in "$@"; do
        eval "${arg// /\\ }"
    done

    CT_DoStep INFO "Building for multilib ${multi_index}/${multi_count}: '${multi_flags}'"

    multilib_dir="lib/${multi_os_dir}"
    startfiles_dir="${multi_root}/usr/${multilib_dir}"
    CT_SanitizeVarDir multilib_dir startfiles_dir

    # Construct make arguments:
    # - uClibc uses the CROSS environment variable as a prefix to the compiler
    #   tools to use.  Since it requires core compiler, thusly named compiler is
    #   already available.
    # - Note about CFLAGS: In uClibc, CFLAGS are generated by Rules.mak,
    #   depending  on the configuration of the library. That is, they are tailored
    #   to best fit the target. So it is useless and seems to be a bad thing to
    #   use LIBC_EXTRA_CFLAGS here.
    # - We do _not_ want to strip anything for now, in case we specifically
    #   asked for a debug toolchain, thus the STRIPTOOL= assignment.
    make_args=( CROSS_COMPILE="${CT_TARGET}-"                           \
                HOSTCC="${CT_BUILD}-gcc"                                \
                PREFIX="${multi_root}/"                                 \
                MULTILIB_DIR="${multilib_dir}"                          \
                STRIPTOOL=true                                          \
                ${CT_LIBC_UCLIBC_VERBOSITY}                             \
                )

    # Simply copy files until uClibc has the ability to build out-of-tree
    CT_DoLog EXTRA "Copying sources to build dir"
    CT_DoExecLog ALL cp -av "${CT_SRC_DIR}/uClibc-ng/." .

    # Force the date of the pregen locale data, as the
    # newer ones that are referenced are not available
    CT_DoLog EXTRA "Applying configuration"

    # Use the default config if the user did not provide one.
    if [ -z "${CT_LIBC_UCLIBC_CONFIG_FILE}" ]; then
        CT_LIBC_UCLIBC_CONFIG_FILE="${CT_LIB_DIR}/packages/uClibc-ng/config"
    fi

    manage_uClibc_config "${CT_LIBC_UCLIBC_CONFIG_FILE}" .config "${multi_flags}"
    CT_DoExecLog ALL make "${make_args[@]}" olddefconfig

    # Now filter the multilib flags. manage_uClibc_config did the opposite of
    # what Rules.mak in uClibc would do: by the multilib's CFLAGS, it determined
    # the applicable configuration options. We don't want to pass the same options
    # in the UCLIBC_EXTRA_CFLAGS again (on some targets, the options do not correctly
    # override each other). On the other hand, we do not want to lose the options
    # that are not reflected in the .config.
    extra_cflags="-pipe"
    { echo "include Rules.mak"; echo "show-cpu-flags:"; printf '\t@echo $(CPU_CFLAGS)\n'; } \
                > .show-cpu-cflags.mk
    cfg_cflags=$( make "${make_args[@]}" \
        --no-print-directory -f .show-cpu-cflags.mk show-cpu-flags )
    CT_DoExecLog ALL rm -f .show-cpu-cflags.mk
    CT_DoLog DEBUG "CPU_CFLAGS detected by uClibc: ${cfg_cflags[@]}"
    for f in ${multi_flags}; do
        for cf in ${cfg_cflags}; do
            if [ "${f}" = "${cf}" ]; then
                f=
                break
            fi
        done
        if [ -n "${f}" ]; then
            extra_cflags+=" ${f}"
        fi
    done
    CT_DoLog DEBUG "Filtered multilib CFLAGS: ${extra_cflags}"
    make_args+=( UCLIBC_EXTRA_CFLAGS="${extra_cflags}" )

    # uClibc does not have a way to select the installation subdirectory for headers,
    # it is always $(DEVEL_PREFIX)/include. Also, we're reinstalling the headers
    # at the final stage (see the note below), we may already have the subdirectory
    # in /usr/include.
    CT_DoArchUClibcHeaderDir hdr_install_subdir "${multi_flags}"
    if [ -n "${hdr_install_subdir}" ]; then
        CT_DoExecLog ALL cp -a "${multi_root}/usr/include" "${multi_root}/usr/include.saved"
    fi

    CT_DoLog EXTRA "Building C library"
    CT_DoExecLog ALL make "${make_args[@]}" pregen
    CT_DoExecLog ALL make ${jflag} "${make_args[@]}" all
    CT_DoLog EXTRA "Installing C library"
    CT_DoExecLog ALL make "${make_args[@]}" install install_utils

    # Now, if installing headers into a subdirectory, put everything in its place.
    # Remove the header subdirectory if it existed already.
    if [ -n "${hdr_install_subdir}" ]; then
        CT_DoExecLog ALL mv "${multi_root}/usr/include" "${multi_root}/usr/include.new"
        CT_DoExecLog ALL mv "${multi_root}/usr/include.saved" "${multi_root}/usr/include"
        CT_DoExecLog ALL rm -rf "${multi_root}/usr/include/${hdr_install_subdir}"
        CT_DoExecLog ALL mv "${multi_root}/usr/include.new" "${multi_root}/usr/include/${hdr_install_subdir}"
    fi

    CT_EndStep
}

# Initialises the .config file to sensible values
# $1: original file
# $2: modified file
manage_uClibc_config()
{
    src="$1"
    dst="$2"
    flags="$3"

    # Start with fresh files
    CT_DoExecLog ALL cp "${src}" "${dst}"

    case "${CT_ARCH_ENDIAN}" in
        big|big,little)
            CT_KconfigDisableOption "ARCH_LITTLE_ENDIAN" "${dst}"
            CT_KconfigDisableOption "ARCH_WANTS_LITTLE_ENDIAN" "${dst}"
            CT_KconfigEnableOption "ARCH_BIG_ENDIAN" "${dst}"
            CT_KconfigEnableOption "ARCH_WANTS_BIG_ENDIAN" "${dst}"
            ;;
        little|little,big)
            CT_KconfigDisableOption "ARCH_BIG_ENDIAN" "${dst}"
            CT_KconfigDisableOption "ARCH_WANTS_BIG_ENDIAN" "${dst}"
            CT_KconfigEnableOption "ARCH_LITTLE_ENDIAN" "${dst}"
            CT_KconfigEnableOption "ARCH_WANTS_LITTLE_ENDIAN" "${dst}"
            ;;
    esac

    if [ "${CT_ARCH_USE_MMU}" = "y" ]; then
        CT_KconfigEnableOption "ARCH_USE_MMU" "${dst}"
    else
        CT_KconfigDisableOption "ARCH_USE_MMU" "${dst}"
        CT_KconfigDisableOption "UCLIBC_FORMAT_FDPIC" "${dst}"
        CT_KconfigDisableOption "UCLIBC_FORMAT_FLAT" "${dst}"
        CT_KconfigDisableOption "UCLIBC_FORMAT_SHARED_FLAT" "${dst}"
        case "${CT_ARCH_BINFMT_FLAT},${CT_ARCH_BINFMT_FDPIC},${CT_SHARED_LIBS}" in
            y,,y) CT_KconfigEnableOption "UCLIBC_FORMAT_SHARED_FLAT" "${dst}";;
            y,,) CT_KconfigEnableOption "UCLIBC_FORMAT_FLAT" "${dst}";;
            ,y,*) CT_KconfigEnableOption "UCLIBC_FORMAT_FDPIC" "${dst}";;
            *) CT_Abort "Unsupported binary format";;
        esac
    fi

    if [ "${CT_SHARED_LIBS}" = "y" ]; then
        CT_KconfigEnableOption "HAVE_SHARED" "${dst}"
    else
        CT_KconfigDisableOption "HAVE_SHARED" "${dst}"
    fi

    # Accomodate for old and new uClibc version, where the
    # way to select between hard/soft float has changed
    case "${CT_ARCH_FLOAT}" in
        hard|softfp)
            CT_KconfigEnableOption "UCLIBC_HAS_FPU" "${dst}"
            CT_KconfigEnableOption "UCLIBC_HAS_FLOATS" "${dst}"
            ;;
        soft)
            CT_KconfigDisableOption "UCLIBC_HAS_FPU" "${dst}"
            CT_KconfigEnableOption "UCLIBC_HAS_FLOATS" "${dst}"
            CT_KconfigEnableOption "DO_C99_MATH" "${dst}"
            ;;
    esac
    if [ "${CT_LIBC_UCLIBC_FENV}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_HAS_FENV" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_HAS_FENV" "${dst}"
    fi
    if [ "${CT_LIBC_UCLIBC_RPC}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_HAS_RPC" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_HAS_RPC" "${dst}"
    fi

    # We always want ctor/dtor
    CT_KconfigEnableOption "UCLIBC_CTOR_DTOR" "${dst}"

    # Change paths to work with crosstool-NG
    #
    # DEVEL_PREFIX is left as '/usr/' because it is post-pended to $PREFIX,
    # which is the correct value of ${PREFIX}/${TARGET}.
    CT_KconfigSetOption "DEVEL_PREFIX" "\"/usr/\"" "${dst}"
    CT_KconfigSetOption "RUNTIME_PREFIX" "\"/\"" "${dst}"
    CT_KconfigSetOption "KERNEL_HEADERS" "\"${CT_HEADERS_DIR}\"" "${dst}"

    # Locales support
    # Note that the two PREGEN_LOCALE and the XLOCALE lines may be missing
    # entirely if LOCALE is not set.  If LOCALE was already set, we'll
    # assume the user has already made all the appropriate generation
    # arrangements.  Note that having the uClibc Makefile download the
    # pregenerated locales is not compatible with crosstool.
    if [ -z "${CT_LIBC_UCLIBC_LOCALES}" ]; then
            CT_KconfigDisableOption "UCLIBC_HAS_LOCALE" "${dst}"
    else
            CT_KconfigEnableOption "UCLIBC_HAS_LOCALE" "${dst}"
            CT_KconfigDeleteOption "UCLIBC_PREGENERATED_LOCALE_DATA" "${dst}"
            CT_KconfigDeleteOption "UCLIBC_DOWNLOAD_PREGENERATED_LOCALE_DATA" "${dst}"
    fi

    # WCHAR support
    if [ "${CT_LIBC_UCLIBC_WCHAR}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_HAS_WCHAR" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_HAS_WCHAR" "${dst}"
    fi

    # IPv6 support
    if [ "${CT_LIBC_UCLIBC_IPV6}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_HAS_IPV6" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_HAS_IPV6" "${dst}"
    fi

    # Iconv support
    if [ "${CT_LIBC_UCLIBC_LIBICONV}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_HAS_LIBICONV" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_HAS_LIBICONV" "${dst}"
    fi

    # Force on options needed for C++ if we'll be making a C++ compiler.
    # I'm not sure locales are a requirement for doing C++... Are they?
    if [ "${CT_CC_LANG_CXX}" = "y" ]; then
        CT_KconfigEnableOption "DO_C99_MATH" "${dst}"
        CT_KconfigEnableOption "UCLIBC_HAS_GNU_GETOPT" "${dst}"
    fi

    # Stack Smash Protection (SSP)
    if [ "${CT_LIBC_UCLIBC_HAS_SSP}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_HAS_SSP" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_HAS_SSP" "${dst}"
    fi
    if [ "${CT_LIBC_UCLIBC_BUILD_SSP}" = "y" ]; then
        CT_KconfigEnableOption "UCLIBC_BUILD_SSP" "${dst}"
    else
        CT_KconfigDisableOption "UCLIBC_BUILD_SSP" "${dst}"
    fi

    # Push the threading model
    CT_KconfigDisableOption "UCLIBC_HAS_THREADS" "${dst}"
    CT_KconfigDisableOption "LINUXTHREADS_OLD" "${dst}"
    CT_KconfigDisableOption "LINUXTHREADS_NEW" "${dst}"
    CT_KconfigDisableOption "UCLIBC_HAS_THREADS_NATIVE" "${dst}"
    case "${CT_THREADS}" in
        none)
            ;;
        linuxthreads)
            CT_KconfigEnableOption "UCLIBC_HAS_THREADS" "${dst}"
            CT_KconfigEnableOption "UCLIBC_HAS_LINUXTHREADS" "${dst}"
            ;;
        nptl)
            CT_KconfigEnableOption "UCLIBC_HAS_THREADS" "${dst}"
            CT_KconfigEnableOption "UCLIBC_HAS_THREADS_NATIVE" "${dst}"
            ;;
        *)
            CT_Abort "Incorrect thread settings: CT_THREADS='${CT_THREADS}'"
            ;;
    esac

    # Always build the libpthread_db
    CT_KconfigEnableOption "PTHREADS_DEBUG_SUPPORT" "${dst}"

    # Force on debug options if asked for
    CT_KconfigDisableOption "DODEBUG" "${dst}"
    CT_KconfigDisableOption "DODEBUG_PT" "${dst}"
    CT_KconfigDisableOption "DOASSERTS" "${dst}"
    CT_KconfigDisableOption "SUPPORT_LD_DEBUG" "${dst}"
    CT_KconfigDisableOption "SUPPORT_LD_DEBUG_EARLY" "${dst}"
    CT_KconfigDisableOption "UCLIBC_MALLOC_DEBUGGING" "${dst}"
    case "${CT_LIBC_UCLIBC_DEBUG_LEVEL}" in
        0)
            ;;
        1)
            CT_KconfigEnableOption "DODEBUG" "${dst}"
            ;;
        2)
            CT_KconfigEnableOption "DODEBUG" "${dst}"
            CT_KconfigEnableOption "DOASSERTS" "${dst}"
            CT_KconfigEnableOption "SUPPORT_LD_DEBUG" "${dst}"
            CT_KconfigEnableOption "UCLIBC_MALLOC_DEBUGGING" "${dst}"
            ;;
        3)
            CT_KconfigEnableOption "DODEBUG" "${dst}"
            CT_KconfigEnableOption "DODEBUG_PT" "${dst}"
            CT_KconfigEnableOption "DOASSERTS" "${dst}"
            CT_KconfigEnableOption "SUPPORT_LD_DEBUG" "${dst}"
            CT_KconfigEnableOption "SUPPORT_LD_DEBUG_EARLY" "${dst}"
            CT_KconfigEnableOption "UCLIBC_MALLOC_DEBUGGING" "${dst}"
            ;;
    esac

    # Remove stripping: its the responsibility of the
    # firmware builder to strip or not.
    CT_KconfigDisableOption "DOSTRIP" "${dst}"

    # Now allow architecture to tweak as it wants
    CT_DoArchUClibcConfig "${dst}"
    CT_DoArchUClibcCflags "${dst}" "${flags}"

    # Preserve the config we created (before uclibc's `make olddefconfig`
    # overrides anything).
    CT_DoExecLog ALL cp "${dst}" "${dst}.created-by-ct-ng"
}

uClibc_ng_post_cc()
{
    # uClibc and GCC disagree where the dynamic linker lives. uClibc always
    # places it in the MULTILIB_DIR, while gcc does that for *some* variants
    # and expects it in /lib for the other. So, create a symlink from lib
    # to the actual location, but only if that will not override the actual
    # file in /lib. Thus, need to do this after all the variants are built.
    # Moreover, need to do this after the final compiler is built: on targets
    # that use elf2flt, the core compilers cannot find ld when running elf2flt.
    CT_MultilibFixupLDSO

    if [ -n "${CT_LIBC_UCLIBC_CONFIG_FILE}" ]; then
        CT_InstallConfigurationFile "${CT_LIBC_UCLIBC_CONFIG_FILE}" libc
    fi
}
