#!/bin/sh
#
# Copyright 2005-2006 Timo Hirvonen
#
# This file is licensed under the GPLv2.

# C compiler
# ----------
# CC          default gcc
# LD          default $CC
# LDFLAGS     common linker flags for CC
#
# C++ Compiler
# ------------
# CXX         default g++
# CXXLD       default $CXX
# CXXLDFLAGS  common linker flags for CXX
#
# Common for C and C++
# --------------------
# SOFLAGS     flags for compiling position independent code (-fPIC)
# LDSOFLAGS   flags for linking shared libraries
# LDDLFLAGS   flags for linking dynamically loadable modules

msg_checking()
{
	printf "checking $@... "
}

msg_result()
{
	echo "$@"
}

msg_error()
{
	echo "*** $@"
}

# @program: program to check
# @name:    name of variable where to store the full program name (optional)
#
# returns 0 on success and 1 on failure
check_program()
{
	argc check_program $# 1 2
	msg_checking "for program $1"
	__cp_file=`path_find "$1"`
	if test $? -eq 0
	then
		msg_result $__cp_file
		test $# -eq 2 && set_var $2 "$__cp_file"
		return 0
	else
		msg_result "no"
		return 1
	fi
}

cc_supports()
{
	$CC $CPPFLAGS $CFLAGS "$@" -S -o /dev/null -x c /dev/null 2> /dev/null
	return $?
}

cxx_supports()
{
	$CXX $CPPFLAGS $CXXFLAGS "$@" -S -o /dev/null -x c /dev/null 2> /dev/null
	return $?
}

# @flag: option flag(s) to check
#
# add @flag to EXTRA_CFLAGS if CC accepts it
# EXTRA_CFLAGS are added to CFLAGS in the end of configuration
check_cc_flag()
{
	argc check_cc_flag $# 1

	test -z "$CC" && die "check_cc_flag: CC not set"
	msg_checking "for CFLAGS $*"
	if cc_supports $*
	then
		EXTRA_CFLAGS="$EXTRA_CFLAGS $*"
		msg_result "yes"
		return 0
	else
		msg_result "no"
		return 1
	fi
}

# @flag: option flag(s) to check
#
# add @flag to EXTRA_CXXFLAGS if CXX accepts it
# EXTRA_CXXFLAGS are added to CXXFLAGS in the end of configuration
check_cxx_flag()
{
	argc check_cxx_flag $# 1

	test -z "$CXX" && die "check_cxx_flag: CXX not set"
	msg_checking "for CXXFLAGS $*"
	if cxx_supports $*
	then
		EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS $*"
		msg_result "yes"
		return 0
	else
		msg_result "no"
		return 1
	fi
}

cc_cxx_common()
{
	test "$cc_cxx_common_done" && return 0
	cc_cxx_common_done=yes

	var_default SOFLAGS "-fPIC"
	var_default LDSOFLAGS "-shared"
	var_default LDDLFLAGS "-shared"

	common_cf=
	common_lf=

	case `uname -s` in
	*BSD)
		common_cf="$common_cf -I/usr/local/include"
		common_lf="$common_lf -L/usr/local/lib"
		;;
	Darwin)
		# fink
		if test -d /sw/lib
		then
			common_cf="$common_cf -I/sw/include"
			common_lf="$common_lf -L/sw/lib"
		fi
		# darwinports
		if test -d /opt/local/lib
		then
			common_cf="$common_cf -I/opt/local/include"
			common_lf="$common_lf -L/opt/local/lib"
		fi
		LDSOFLAGS="-dynamic"
		case ${MACOSX_DEPLOYMENT_TARGET} in
		10.[012])
			LDDLFLAGS="-bundle -flat_namespace -undefined suppress"
			;;
		10.*)
			LDDLFLAGS="-bundle -undefined dynamic_lookup"
			;;
		*)
			LDDLFLAGS="-bundle -flat_namespace -undefined suppress"
			;;
		esac
		;;
	SunOS)
		common_cf="$common_cf -D__EXTENSIONS__ -I/usr/local/include"
		common_lf="$common_lf -R/usr/local/lib -L/usr/local/lib"
		;;
	esac
	makefile_vars SOFLAGS LDSOFLAGS LDDLFLAGS
}

# CC, LD, CFLAGS, LDFLAGS, SOFLAGS, LDSOFLAGS, LDDLFLAGS
check_cc()
{
	var_default CC ${CROSS}gcc
	var_default LD $CC
	var_default CFLAGS "-g -O2 -Wall"
	var_default LDFLAGS ""
	check_program $CC || return 1

	cc_cxx_common
	CFLAGS="$CFLAGS $common_cf"
	LDFLAGS="$LDFLAGS $common_lf"

	makefile_vars CC LD CFLAGS LDFLAGS
	__check_lang=c
	return 0
}

# HOSTCC, HOSTLD, HOST_CFLAGS, HOST_LDFLAGS
check_host_cc()
{
	var_default HOSTCC gcc
	var_default HOSTLD $HOSTCC
	var_default HOST_CFLAGS "-g -O2 -Wall"
	var_default HOST_LDFLAGS ""
	check_program $HOSTCC || return 1
	makefile_vars HOSTCC HOSTLD HOST_CFLAGS HOST_LDFLAGS
	__check_lang=c
	return 0
}

# CXX, CXXLD, CXXFLAGS, CXXLDFLAGS, SOFLAGS, LDSOFLAGS, LDDLFLAGS
check_cxx()
{
	var_default CXX ${CROSS}g++
	var_default CXXLD $CXX
	var_default CXXFLAGS "-g -O2 -Wall"
	var_default CXXLDFLAGS ""
	check_program $CXX || return 1

	cc_cxx_common
	CXXFLAGS="$CXXFLAGS $common_cf"
	CXXLDFLAGS="$CXXLDFLAGS $common_lf"

	makefile_vars CXX CXXLD CXXFLAGS CXXLDFLAGS
	__check_lang=cxx
	return 0
}

# check if CC can generate dependencies (.dep-*.o files)
# always succeeds
check_cc_depgen()
{
	msg_checking "if CC can generate dependency information"
	if cc_supports -MMD -MP -MF /dev/null
	then
		EXTRA_CFLAGS="$EXTRA_CFLAGS -MMD -MP -MF .dep-\$(subst /,-,\$@)"
		msg_result yes
	else
		msg_result no
	fi
	return 0
}

# check if CXX can generate dependencies (.dep-*.o files)
# always succeeds
check_cxx_depgen()
{
	msg_checking "if CXX can generate dependency information"
	if cxx_supports -MMD -MP -MF /dev/null
	then
		EXTRA_CXXFLAGS="$EXTRA_CXXFLAGS -MMD -MP -MF .dep-\$(subst /,-,\$@)"
		msg_result yes
	else
		msg_result no
	fi
	return 0
}

# adds AR to config.mk
check_ar()
{
	var_default AR ${CROSS}ar
	var_default ARFLAGS "-cr"
	if check_program $AR
	then
		makefile_vars AR ARFLAGS
		return 0
	fi
	return 1
}

# adds AS to config.mk
check_as()
{
	var_default AS ${CROSS}gcc
	if check_program $AS
	then
		makefile_vars AS
		return 0
	fi
	return 1
}

check_pkgconfig()
{
	if test -z "$PKG_CONFIG"
	then
		if check_program pkg-config PKG_CONFIG
		then
			makefile_vars PKG_CONFIG
		else
			# don't check again
			PKG_CONFIG="no"
			return 1
		fi
	fi
	return 0
}

# check for library FOO and add FOO_CFLAGS and FOO_LIBS to config.mk
#
# @name:    variable prefix (e.g. CURSES -> CURSES_CFLAGS, CURSES_LIBS)
# @cflags:  CFLAGS for the lib
# @libs:    LIBS to check
#
# adds @name_CFLAGS and @name_LIBS to config.mk
# CFLAGS are not checked, they are assumed to be correct
check_library()
{
	argc check_library $# 3 3
	msg_checking "for ${1}_LIBS ($3)"
	if try_link $3
	then
		msg_result yes
		makefile_var ${1}_CFLAGS "$2"
		makefile_var ${1}_LIBS "$3"
		return 0
	else
		msg_result no
		return 1
	fi
}

# run pkg-config
#
# @prefix:  variable prefix (e.g. GLIB -> GLIB_CFLAGS, GLIB_LIBS)
# @modules: the argument for pkg-config
# @cflags:  CFLAGS to use if pkg-config failed (optional)
# @libs:    LIBS to use if pkg-config failed (optional)
#
# if pkg-config fails and @libs are given check_library is called
#
# example:
#   ---
#   check_glib()
#   {
#           pkg_config GLIB "glib-2.0 >= 2.2"
#           return $?
#   }
#
#   check check_cc
#   check check_glib
#   ---
#   GLIB_CFLAGS and GLIB_LIBS are automatically added to Makefile
pkg_config()
{
	argc pkg_config $# 2 4

	# optional
	__pc_cflags="$3"
	__pc_libs="$4"

	check_pkgconfig
	msg_checking "for ${1}_LIBS (pkg-config)"
	if test "$PKG_CONFIG" != "no" && $PKG_CONFIG --exists "$2" >/dev/null 2>&1
	then
		# pkg-config is installed and the .pc file exists
		__pc_libs="`$PKG_CONFIG --libs ""$2""`"
		msg_result "$__pc_libs"

		msg_checking "for ${1}_CFLAGS (pkg-config)"
		__pc_cflags="`$PKG_CONFIG --cflags ""$2""`"
		msg_result "$__pc_cflags"

		makefile_var ${1}_CFLAGS "$__pc_cflags"
		makefile_var ${1}_LIBS "$__pc_libs"
		return 0
	fi

	# no pkg-config or .pc file
	msg_result "no"

	if test -z "$__pc_libs"
	then
		if test "$PKG_CONFIG" = "no"
		then
			# pkg-config not installed and no libs to check were given
			msg_error "pkg-config required for $1"
		else
			# pkg-config is installed but the required .pc file wasn't found
			$PKG_CONFIG --errors-to-stdout --print-errors "$2" | sed 's:^:*** :'
		fi
		return 1
	fi

	check_library "$1" "$__pc_cflags" "$__pc_libs"
	return $?
}

# old name
pkg_check_modules()
{
	pkg_config "$@"
}

# run *-config
#
# @prefix:  variable prefix (e.g. ARTS -> ARTS_CFLAGS, ARTS_LIBS)
# @program: the -config program
#
# example:
#   ---
#   check_arts()
#   {
#           app_config ARTS artsc-config
#           return $?
#   }
#
#   check check_cc
#   check check_arts
#   ---
#   ARTS_CFLAGS and ARTS_LIBS are automatically added to config.mk
app_config()
{
	argc app_config $# 2 2
	check_program $2 || return 1

	msg_checking "for ${1}_CFLAGS"
	__ac_cflags="`$2 --cflags`"
	msg_result "$__ac_cflags"

	msg_checking "for ${1}_LIBS"
	__ac_libs="`$2 --libs`"
	msg_result "$__ac_libs"

	makefile_var ${1}_CFLAGS "$__ac_cflags"
	makefile_var ${1}_LIBS "$__ac_libs"
	return 0
}

# @contents:  file contents to compile
# @cflags:    extra cflags (optional)
try_compile()
{
	argc try_compile $# 1
	case $__check_lang in
	c)
		__src=`tmp_file prog.c`
		__obj=`tmp_file prog.o`
		echo "$1" > $__src || exit 1
		shift
		__cmd="$CC -c $CPPFLAGS $CFLAGS $@ $__src -o $__obj"
		$CC -c $CPPFLAGS $CFLAGS "$@" $__src -o $__obj 2>/dev/null
		;;
	cxx)
		__src=`tmp_file prog.cc`
		__obj=`tmp_file prog.o`
		echo "$1" > $__src || exit 1
		shift
		__cmd="$CXX -c $CPPFLAGS $CXXFLAGS $@ $__src -o $__obj"
		$CXX -c $CPPFLAGS $CXXFLAGS "$@" $__src -o $__obj 2>/dev/null
		;;
	esac
	return $?
}

# @contents:  file contents to compile and link
# @flags:     extra flags (optional)
try_compile_link()
{
	argc try_compile $# 1
	case $__check_lang in
	c)
		__src=`tmp_file prog.c`
		__exe=`tmp_file prog`
		echo "$1" > $__src || exit 1
		shift
		__cmd="$CC $__src -o $__exe $CPPFLAGS $CFLAGS $LDFLAGS $@"
		$CC $__src -o $__exe $CPPFLAGS $CFLAGS $LDFLAGS "$@" 2>/dev/null
		;;
	cxx)
		__src=`tmp_file prog.cc`
		__exe=`tmp_file prog`
		echo "$1" > $__src || exit 1
		shift
		__cmd="$CXX $__src -o $__exe $CPPFLAGS $CXXFLAGS $CXXLDFLAGS $@"
		$CXX $__src -o $__exe $CPPFLAGS $CXXFLAGS $CXXLDFLAGS "$@" 2>/dev/null
		;;
	esac
	return $?
}

# optionally used after try_compile or try_compile_link
__compile_failed()
{
	warn
	warn "Failed to compile simple program:"
	warn "---"
	cat $__src >&2
	warn "---"
	warn "Command: $__cmd"
	case $__check_lang in
	c)
		warn "Make sure your CC and CFLAGS are sane."
		;;
	cxx)
		warn "Make sure your CXX and CXXFLAGS are sane."
		;;
	esac
	exit 1
}

# tries to link against a lib
#
# @function:  some function
# @flags:     extra flags (optional)
check_function()
{
	argc check_function $# 1
	__func="$1"
	shift
	msg_checking "for function $__func"
	if try_compile_link "extern int $__func(); int (*ptr)() = &$__func; int main() { return 0; }" "$@"
	then
		msg_result yes
		return 0
	fi

	msg_result no
	return 1
}

# tries to link against a lib
#
# @ldadd:  something like -L/usr/X11R6/lib -lX11
try_link()
{
	try_compile_link "int main(int argc, char *argv[]) { return 0; }" "$@"
	return $?
}

# compile and run
#
# @code:  simple program code to run
run_code()
{
	if test $CROSS
	then
		msg_error "cannot run code when cross compiling"
		exit 1
	fi
	try_compile_link "$1" || __compile_failed
	./$__exe
	return $?
}

# check if the architecture is big-endian
# parts are from autoconf 2.67
#
# defines WORDS_BIGENDIAN=y/n
check_endianness()
{
	msg_checking "byte order"
	WORDS_BIGENDIAN=n
	# See if sys/param.h defines the BYTE_ORDER macro.
	if try_compile_link "
#include <sys/types.h>
#include <sys/param.h>
int main() {
#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
		&& defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
		&& LITTLE_ENDIAN)
	bogus endian macros
#endif
	return 0;
}"
	then
		# It does; now see whether it defined to BIG_ENDIAN or not.
		if try_compile_link "
#include <sys/types.h>
#include <sys/param.h>
int main() {
#if BYTE_ORDER != BIG_ENDIAN
	not big endian
#endif
	return 0;
}"
		then
			WORDS_BIGENDIAN=y
		fi
	# See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
	elif try_compile_link "
#include <limits.h>
int main() {
#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
	bogus endian macros
#endif
	return 0;
}"
	then
		# It does; now see whether it defined to _BIG_ENDIAN or not.
		if try_compile_link "
#include <limits.h>
int main() {
#ifndef _BIG_ENDIAN
	not big endian
#endif
	return 0;
}"
		then
			WORDS_BIGENDIAN=y
		fi
	elif run_code "
int main(int argc, char *argv[])
{
	unsigned int i = 1;
	return *(char *)&i;
}"
	then
		WORDS_BIGENDIAN=y
	fi
	if test "$WORDS_BIGENDIAN" = y
	then
		msg_result "big-endian"
	else
		msg_result "little-endian"
	fi
	return 0
}

# check if @header can be included
#
# @header
# @cflags   -I/some/path (optional)
check_header()
{
	argc check_header $# 1
	__header="$1"
	shift
	msg_checking "for header <$__header>"
	if try_compile "#include <$__header>" "$@"
	then
		msg_result yes
		return 0
	fi
	msg_result no
	return 1
}

# check X11 libs
#
# adds X11_LIBS (and empty X11_CFLAGS) to config.mk
check_x11()
{
	for __libs in "-lX11" "-L/usr/X11R6/lib -lX11"
	do
		check_library X11 "" "$__libs" && return 0
	done
	return 1
}

# check posix threads
#
# adds PTHREAD_CFLAGS and PTHREAD_LIBS to config.mk
check_pthread()
{
	for __libs in "$PTHREAD_LIBS" -lpthread -lc_r -lkse
	do
		test -z "$__libs" && continue
		check_library PTHREAD "-D_REENTRANT" "$__libs" && return 0
	done
	echo "using -pthread gcc option"
	makefile_var PTHREAD_CFLAGS "-pthread -D_THREAD_SAFE"
	makefile_var PTHREAD_LIBS "-pthread"
	return 0
}

# check dynamic linking loader
#
# adds DL_LIBS to config.mk
check_dl()
{
	for DL_LIBS in "-ldl -Wl,--export-dynamic" "-ldl -rdynamic" "-Wl,--export-dynamic" "-rdynamic" "-ldl"
	do
		check_library DL "" "$DL_LIBS" && return 0
	done
	echo "assuming -ldl is not needed"
	DL_LIBS=
	makefile_vars DL_LIBS DL_CFLAGS
	return 0
}

# check for iconv
#
# adds ICONV_CFLAGS and ICONV_LIBS to config.mk
check_iconv()
{
	HAVE_ICONV=n
	if check_library ICONV "" "-liconv"
	then
		echo "taking iconv from libiconv"
	else
		echo "assuming libc contains iconv"
		makefile_var ICONV_CFLAGS ""
		makefile_var ICONV_LIBS ""
	fi
	msg_checking "for working iconv"
	if try_compile_link '
#include <stdio.h>
#include <string.h>
#include <iconv.h>
int main(int argc, char *argv[]) {
	char buf[128], *out = buf, *in = argv[1];
	size_t outleft = 127, inleft = strlen(in);
	iconv_t cd = iconv_open("UTF-8", "ISO-8859-1");
	iconv(cd, &in, &inleft, &out, &outleft);
	*out = 0;
	printf("%s", buf);
	iconv_close(cd);
	return 0;
}' $ICONV_CFLAGS $ICONV_LIBS
	then
		msg_result "yes"
		HAVE_ICONV=y
	else
		msg_result "no"
		msg_error "Your system doesn't have iconv!"
		msg_error "This means that no charset conversion can be done, so all"
		msg_error "your tracks need to be encoded in your system charset!"
	fi

	return 0
}
