#
# $Id: makl_code,v 1.29 2007/10/11 11:19:51 tat Exp $
#

##\brief Compile a C file.
##
##  Compile a C file \e $1 with the supplied flags \e $2.
## 
##   \param $1 Pathname of the C file to be compiled 
##   \param $2 flags to be passed to the compiler
##   \return 0 on success, 1 on failure
##
makl_compile ()
{
    if [ ! "$1" ]; then
        makl_err 1 "makl_compile called with no arguments"
    fi

    c_file=$1
    shift
    flags=$*
    cwd=`pwd`
   
    cp ${c_file} ${makl_run_dir} 2>/dev/null
    cd ${makl_run_dir}

    ${CC} -o out `basename ${c_file}` ${flags} 2>/dev/null

    if [ $? -ne 0 ]; then
        cd ${cwd} 
        return 1
    fi 

    cd ${cwd}
    return 0
}

##\brief Write to a C file. 
##
##  Write to a C file. Data is read from standard input.
## 
##   \param $1 name of file to be written
##   \param $2 whether the code is a snippet (1) or entire C file (0)
##
makl_write_c ()
{
    # create a clean file
    [ -r $1 ] && rm -f $1

    if [ $2 = 1 ]; then
        ${ECHO} "int main() {" >> $1
    fi
    
    while read line; do
        ${ECHO} ${line} >> $1
    done
    
    if [ $2 = 1 ]; then
        {
        ${ECHO} "return 0;"
        ${ECHO} "}"
        } >> $1
    fi
    
    return 0
}

##\brief Compile C code.
##
##  Compile C code.
##
##   \param $1 whether the code is a snippet (1) or entire C file (0)
##   \param $2 flags to be passed to the compiler
##
makl_compile_code ()
{
    file=${makl_run_dir}/makl_code.c

    makl_write_c ${file} $1

    makl_compile ${file} $2
    [ $? = 0 ] || return 1

    return 0
}

##\brief Execute C code.
##
##  Execute C code.
##
##   \param $1 whether the code is a snippet (1) or entire C file (0)
##   \param $2 flags to be passed to the compiler
## 
makl_exec_code ()
{
    file=${makl_run_dir}/makl_code.c
    cwd=`pwd`
    
    makl_write_c ${file} $1
    makl_compile ${file} $2 
    [ $? = 0 ] || return 1

    # skip execution on cross-compilation
    [ -z `makl_get "__cross_compile__"` ] || return 0

    cd ${makl_run_dir} && eval ./out > /dev/null 2> /dev/null
    if [ $? -ne 0 ]; then 
        cd ${cwd}
        return 2
    fi

    cd ${cwd}
    return 0
}

##\brief Check existence of a function.
##
##  Define HAVE_$1 if function \e $1 is found.
##  \e $1 determines whether the feature is optional or required.
##
##   \param $1 0:optional/1:required
##   \param $2 function name
##   \param $3 flags to be passed to the compiler
##
makl_checkfunc ()
{
    [ -z `makl_get "__noconfig__"` ] || return

    makl_info "checking for function $2"

    ${ECHO} "$2();" | makl_compile_code 1 "$3"

    if [ $? = 0 ]; then
        makl_set_var "HAVE_"`makl_upper $2`
        return 0
    else
        [ $1 = 0 ] || makl_err 1 "failed check on required function $2!" 
        makl_warn "failed check on optional function $2"
        makl_unset_var "HAVE_"`makl_upper $2`
        return 1
    fi
}

##\brief Check existence of a header.
##
##  Define HAVE_$2 if header \e $3 is found.
##  \e $1 determines whether the feature is optional or required.
##
##  \param $1 0:optional,1:required
##  \param $2 id of header
##  \param $3 header file
##
makl_checkheader ()
{
    [ -z `makl_get "__noconfig__"` ] || return

    makl_info "checking for header $2"

    {
        ${ECHO} "#include $3"
        ${ECHO} "int main() { return 0; }"
    } | makl_compile_code 0

    if [ $? = 0 ]; then
        makl_set_var "HAVE_"`makl_upper $2`
        return 0
    else
        [ $1 = 0 ] || makl_err 1 "failed check on required header $2!"
        makl_unset_var "HAVE_"`makl_upper $2`
        makl_warn "failed check on optional header $2"
        return 1
    fi
}

##\brief Check existence of a type.
##
##  Define HAVE_$1 if type \e $2 is found. 
##  \e $1 determines whether the feature is optional or required.
##  Extra includes can be listed in \e $*.
## 
##  \param $1 0:optional,1:required
##  \param $2 data type
##  \param $* includes
##
makl_checktype ()
{
    [ -z `makl_get "__noconfig__"` ] || return

    makl_info "checking for type $2"

    opt=$1
    type=$2
    shift
    shift

    # substitute whitespace with underscores 
    def_type=`${ECHO} ${type} | sed 's/\ /_/g'`

    {
        for arg in $*; do
            ${ECHO} "#include ${arg}"
        done
        # on some systems (e.g. VxWorks) type checks are not picked up correctly
        # by the compiler, so force a check using sizeof() */
        ${ECHO} "
            int main() {
                ${type} x;
                return (sizeof(${type}) && 0);  
            }" 
    } | makl_compile_code 0 

    if [ $? = 0 ]; then
        makl_set_var "HAVE_"`makl_upper ${def_type}`
        return 0
    else
        [ ${opt} = 0 ] || makl_err 1 "failed check on required type ${type}!" 
        makl_unset_var "HAVE_"`makl_upper ${def_type}`
        makl_warn "failed check on optional type $2"
        return 1
    fi
}

##\brief Check existence of an extern variable.
##
##  Define HAVE_$1 if the extern variable \e $2 is found. 
##  \e $1 determines whether the feature is optional or required.
## 
##  \param $1 0:optional,1:required
##  \param $2 extern variables
##  \param $* optional compilation flags
##
makl_checkextern()
{
    [ -z `makl_get "__noconfig__"` ] || return

    makl_info "checking for extern variable $2"

    opt=$1
    var=$2
    shift
    shift

    {
        # this fails when the linker doesn't find the variable in any linked
        # libraries
        ${ECHO} "
            extern void* ${var};
            int main() 
            {
                return (int) & ${var};
            }" 
    } | makl_compile_code 0 $*

    if [ $? = 0 ]; then
        makl_set_var "HAVE_"`makl_upper ${var}`
        return 0
    else
        [ ${opt} = 0 ] || makl_err 1 "failed check on required extern variable ${var}!" 
        makl_unset_var "HAVE_"`makl_upper ${var}`
        makl_warn "failed check on optional extern variable $2"
        return 1
    fi
}

