#!/bin/sh
# Based on the configure script from musl libc, MIT licensed; vis, ISC licensed

usage () {
cat <<EOF
Usage: $0 [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Configuration:
  --config-file=FILE      save config to FILE [config.mk]

Installation directories:
  --prefix=PREFIX         main installation prefix [\$PREFIX or /usr/local]
  --exec-prefix=EPREFIX   installation prefix for executable files [PREFIX]

Fine tuning of the installation directories:
  --bindir=DIR            user executables [EPREFIX/bin]
  --sharedir=DIR          share directories [PREFIX/share]
  --docdir=DIR            misc. documentation [PREFIX/share/doc]
  --mandir=DIR            man pages [PREFIX/share/man]

Optional features:
  --enable-small-lut      build with smaller lookup tables [no]
  --enable-debug-stderr   build with debug msgs in stderr [no]
  --enable-clock          build with timing support [no]
  --enable-pie            build with position independent executables [auto]
  --enable-pic            build with position independent shared libraries [auto]

Some influential environment variables:
  CC                      C compiler command [detected]
  AWK                     AWK tool command [detected]
  AR                      archive command [detected]
  RANLIB                  ranlib archive indexer command [detected]
  CFLAGS                  C compiler flags [-Os -pipe ...]
  LDFLAGS                 Linker flags

Use these variables to override the choices made by configure.

EOF
exit 0
}

# Helper functions

quote () {
tr '\n' ' ' <<EOF | grep '^[-[:alnum:]_=,./:]* $' >/dev/null 2>&1 && { echo "$1" ; return 0 ; }
$1
EOF
printf %s\\n "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#"
}
echo () { printf "%s\n" "$*" ; }
fail () { echo "$*" ; exit 1 ; }
fnmatch () { eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac" ; }
cmdexists () { type "$1" >/dev/null 2>&1 ; }
trycc () { test -z "$CC" && cmdexists "$1" && CC=$1 ; }
tryar () { test -z "$AR" && cmdexists "$1" && AR=$1 ; }
tryranlib () { test -z "$RANLIB" && cmdexists "$1" && RANLIB=$1 ; }
tryawk () { test -z "$AWK" && "$1" 'function works () {return 0} BEGIN{exit works()}' && AWK=$1 ; }
tryllvmprofdata () { test -z "$LLVM_PROFDATA" && cmdexists "$1" && LLVM_PROFDATA=$1 ; }

stripdir () {
while eval "fnmatch '*/' \"\${$1}\"" ; do eval "$1=\${$1%/}" ; done
}

trycchdr () {
printf "checking whether there is a header called %s... " "$2"
if find $CCSEARCHPATH -name "$2" | grep "/$2$" >/dev/null 2>&1 ; then
upper2=$(echo "$2" | tr a-z A-Z | tr . _)
eval "$1=\"\${$1} -DHAVE_\${upper2}\""
eval "$1=\${$1# }"
printf "yes\n"
return 0
else
printf "no\n"
return 1
fi
}

tryccfn () {
printf "checking whether compiler accepts %s from %s... " "$2" "$3"
printf "%s\n" $3 | sed 's/\(.*\)/#include <\1>/' > "$tmpc"
cat >> "$tmpc" <<EOF
int main() {$2;}
EOF
if $CC -o "$tmpo" "$tmpc" >/dev/null 2>&1 ; then
flag=$(echo "$2" | cut -d'(' -f1 | tr a-z A-Z)
eval "vars=\$$1"
if ! printf "%s\n" ${vars} | grep "\-DHAVE_${flag}\$" >/dev/null 2>&1; then
flag="-DHAVE_${flag}"
eval "$1=\"\${vars} \${flag}\""
eval "$1=\${$1# }"
fi
printf "yes\n"
return 0
else
printf "no\n"
return 1
fi
}

tryflag () {
printf "checking whether compiler accepts %s... " "$2"
echo "int main() {return 0;}" > "$tmpc"
if $CC $CFLAGS_TRY $2 -c -o "$tmpo" "$tmpc" >/dev/null 2>&1 ; then
printf "yes\n"
eval "$1=\"\${$1} \$2\""
eval "$1=\${$1# }"
return 0
else
printf "no\n"
return 1
fi
}

tryldflag () {
printf "checking whether linker accepts %s... " "$2"
echo "int main(){return 0;}" > "$tmpc"
if $CC $LDFLAGS_TRY "$2" -o "$tmpo" "$tmpc" >/dev/null 2>&1 ; then
printf "yes\n"
eval "$1=\"\${$1} \$2\""
eval "$1=\${$1# }"
return 0
else
printf "no\n"
return 1
fi
}

trysharedldflag () {
printf "checking whether linker accepts %s... " "$2"
echo "typedef int x;" > "$tmpc"
if $CC $LDFLAGS_TRY -shared "$2" -o "$tmpo" "$tmpc" >/dev/null 2>&1 ; then
printf "yes\n"
eval "$1=\"\${$1} \$2\""
eval "$1=\${$1# }"
return 0
else
printf "no\n"
return 1
fi
}

# Beginning of actual script

CFLAGS_AUTO=
CFLAGS_TRY=
LDFLAGS_AUTO=
LDFLAGS_TRY=
CONFIGFILE=config.mk
PREFIX=$PREFIX
if [ "$PREFIX" == "" ]; then
    PREFIX=/usr/local
fi
EXEC_PREFIX='$(PREFIX)'
BINDIR='$(EXEC_PREFIX)/bin'
SHAREDIR='$(PREFIX)/share'
DOCDIR='$(PREFIX)/share/doc'
MANDIR='$(PREFIX)/share/man'

help=yes
usesmalllut=no
usedebugstderr=no
useclock=no
usepie=auto
usepic=auto

for arg ; do
case "$arg" in
--help|-h) usage ;;
--host=*) host=${arg#*=} ;;
--config-file=*) CONFIGFILE=${arg#*=} ;;
--prefix=*) PREFIX=${arg#*=} ;;
--exec-prefix=*) EXEC_PREFIX=${arg#*=} ;;
--bindir=*) BINDIR=${arg#*=} ;;
--sharedir=*) SHAREDIR=${arg#*=} ;;
--docdir=*) DOCDIR=${arg#*=} ;;
--mandir=*) MANDIR=${arg#*=} ;;
--enable-small-lut|--enable-small-lut=yes) usesmalllut=yes ;;
--disable-small-lut|--enable-small-lut=no) usesmalllut=no ;;
--enable-debug-stderr|--enable-debug-stderr=yes) usedebugstderr=yes ;;
--disable-debug-stderr|--enable-debug-stderr=no) usedebugstderr=no ;;
--enable-clock|--enable-clock=yes) useclock=yes ;;
--disable-clock|--enable-clock=no) useclock=no ;;
--enable-pie|--enable-pie=yes) usepie=yes ;;
--enable-pie=auto) usepie=auto ;;
--disable-pie|--enable-pie=no) usepie=no ;;
--enable-pic|enable-pic=yes) usepic=yes ;;
--enable-pic=auto) usepic=auto ;;
--disable-pic|--enable-pic=no) usepic=no ;;
--enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;;
-* ) echo "$0: unknown option $arg" ;;
CC=*) CC=${arg#*=} ;;
CFLAGS=*) CFLAGS=${arg#*=} ;;
CPPFLAGS=*) CPPFLAGS=${arg#*=} ;;
LDFLAGS=*) LDFLAGS=${arg#*=} ;;
*=*) ;;
*) ;;
esac
done

for i in PREFIX EXEC_PREFIX BINDIR SHAREDIR DOCDIR MANDIR ; do
stripdir $i
done

#
# Get a temp filename we can use
#
i=0
set -C
while : ; do i=$(($i+1))
tmpc="./conf$$-$PPID-$i.c"
tmpo="./conf$$-$PPID-$i.o"
2>|/dev/null > "$tmpc" && break
test "$i" -gt 50 && fail "$0: cannot create temporary file $tmpc"
done
set +C
trap 'rm -f "$tmpc" "$tmpo"' EXIT QUIT TERM HUP
trap 'rm -f "$tmpc" "$tmpo" && echo && fail "$0: interrupted"' INT

#
# Find an AWK tool to use
#
printf "checking for AWK tool... "
for a in awk gawk mawk nawk; do tryawk "$a"; done
printf "%s\n" "$AWK"
test -n "$AWK" || fail "$0: cannot find an AWK tool"

#
# Find a C compiler to use
#
printf "checking for C compiler... "
for c in cc gcc clang; do trycc "$c"; done
printf "%s\n" "$CC"
test -n "$CC" || fail "$0: cannot find a C compiler"

printf "checking whether C compiler works... "
echo "typedef int x;" > "$tmpc"
if output=$($CC $CPPFLAGS $CFLAGS -c -o "$tmpo" "$tmpc" 2>&1) ; then
printf "yes\n"
else
fail "no; compiler output follows:\n%s\n" "$output"
fi

#
# Get compiler search paths
#
CCSEARCHPATH=$(echo | ${CC} -E -Wp,-v - 2>&1 | ${AWK} '/ \//{print substr($0,2);}')

#
# Check if it is clang, and the llvm tools instead
# TODO: PROFDATA triggers specific pgo behaviour
#       in the Makefile for clang or gcc...
#       This isn't the most elegant solution right
#       now...
#
compiler=$(${CC} -v 2>&1 | ${AWK} '/ +version +/{for(i=1;i<=NF;i++){if($i=="version"){printf("%s\n",(last=="LLVM")?"clang":last);exit 0;}last=$i;}}')
if test "$compiler" = "clang"; then
arlist="$CC-llvm-ar $host-llvm-ar $CC-ar $host-ar llvm-ar ar"
ranliblist="$CC-llvm-ranlib $host-llvm-ranlib $CC-ranlib $host-ranlib llvm-ranlib ranlib"
PROFDATA=zsv.profdata
#printf "checking for llvm-profdata... "
#tryllvmprofdata llvm-profdata
#printf "%s\n" "$LLVM_PROFDATA"
#test -n "$LLVM_PROFDATA" || fail "$0: cannot find llvm-profdata"
else
arlist="$CC-ar $host-$compiler-ar $host-ar $compiler-ar ar"
ranliblist="$CC-ranlib $host-$compiler-ranlib $host-ranlib $compiler-ranlib $compiler-ranlib ranlib"
PROFDATA=zsv.profile
fi

#
# Find ar and ranlib to use
#
printf "checking for ar... "
for a in $arlist; do tryar "$a"; done
printf "%s\n" "$AR"
test -n "$AR" || fail "$0: cannot find ar"

printf "checking for ranlib... "
for r in $ranliblist ; do tryranlib "$r"; done
printf "%s\n" "$RANLIB"
test -n "$RANLIB" || fail "$0: cannot find ranlib"

#
# Detect the host system
#
printf 'checking host system type... '
test -n "$host" || host=$($CC -dumpmachine 2>/dev/null) || fail "could not determine host"
printf '%s\n' "$host"

#
# Figure out options to force errors on unknown flags.
#
tryflag   CFLAGS_TRY  -Werror=unknown-warning-option
tryflag   CFLAGS_TRY  -Werror=unused-command-line-argument
tryflag   CFLAGS_TRY  -Werror=ignored-optimization-argument
tryldflag LDFLAGS_TRY -Werror=unknown-warning-option
tryldflag LDFLAGS_TRY -Werror=unused-command-line-argument
tryldflag LDFLAGS_TRY -Werror=ignored-optimization-argument

CFLAGS_STD="-std=gnu11 -D_POSIX_C_SOURCE=200809L -U_XOPEN_SOURCE -D_XOPEN_SOURCE=700"
CFLAGS_OPT="-DNDEBUG"
MINGW=0
case "$host" in
*-*freebsd*) CFLAGS_STD="$CFLAGS_STD -D_BSD_SOURCE -D__BSD_VISIBLE=1" ;;
*-*netbsd*) CFLAGS_STD="$CFLAGS_STD -D_NETBSD_SOURCE" ;;
*-*bsd*) CFLAGS_STD="$CFLAGS_STD -D_BSD_SOURCE" ;;
*-*darwin*) CFLAGS_STD="$CFLAGS_STD -D_DARWIN_C_SOURCE" ;;
*-*mingw32|*-*msys*|*-windows-gnu)
	CFLAGS_STD="$CFLAGS_STD -D__USE_MINGW_ANSI_STDIO"
	#LDFLAGS_AUTO="-Wl,--export-all-symbols"
	MINGW=1
	usepie=no
	usepic=no
	;;
esac

if test "$usepie" = "auto" ; then
usepie=yes
fi

if test "$usepic" = "auto" ; then
usepic=yes
fi

if test "$usepie" = "yes" ; then
tryflag CFLAGS_PIE -fpie || tryflag CFLAGS_PIE -fPIE
fi

if test "$usepic" = "yes" ; then
tryflag CFLAGS_PIC -fpic || tryflag CFLAGS_PIC -fPIC
fi

test "$usepie" = "yes" && tryldflag LDFLAGS_PIE -pie

if test "$usepic" = "yes" ; then
trysharedldflag LDFLAGS_PIC -fpic || trysharedldflag LDFLAGS_PIC -fPIC
fi

test "$usepie" = "no" && tryflag CFLAGS_PIE -fno-pie
test "$usepic" = "no" && tryflag CFLAGS_PIC -fno-pic
test "$usepie" = "no" && tryldflag LDFLAGS_PIE -no-pie
test "$usepic" = "no" && trysharedldflag LDFLAGS_PIC -fno-pic

if test $MINGW -eq 0 ; then
LDFLAGS_STD="-lc"
tryflag CFLAGS_AUTO -fstack-protector-all
case "$CFLAGS_AUTO" in
*-fstack-protector*) CFLAGS_AUTO="-D_FORTIFY_SOURCE=2"; ;;
esac
fi

tryflag CFLAGS -pipe

# Try flags to optimize speed
tryflag CFLAGS -ffunction-sections
tryflag CFLAGS -fdata-sections
tryflag CFLAGS_OPT -O3
tryflag CFLAGS_OPT -march=native
tryflag CFLAGS_AVX2  -mavx2
tryflag CFLAGS_CLMUL -mvpclmulqdq
tryflag CFLAGS_OPT -flto
tryflag CFLAGS_OPT -fwhole-program
tryflag CFLAGS_OPT -fvisibility=hidden
tryldflag LDFLAGS_AUTO -Wl,--gc-sections
tryldflag LDFLAGS_OPT -flto
tryldflag LDFLAGS_OPT -fwhole-program
tryldflag LDFLAGS_OPT -O3
tryldflag LDFLAGS_OPT -march=native

# Try hardening flags
if test "$usepie" = "yes" ; then
case "$LDFLAGS_PIE" in
*pie*)
	tryldflag LDFLAGS_PIE -Wl,-z,now
	tryldflag LDFLAGS_PIE -Wl,-z,relro
	;;
esac
fi
if test "$usepic" = "yes" ; then
case "$LDFLAGS_PIC" in
*pic*)
	tryldflag LDFLAGS_PIC -Wl,-z,now
	tryldflag LDFLAGS_PIC -Wl,-z,relro
	;;
esac
fi

# Check function availability
tryccfn CFLAGS_AUTO "_alloca(4)" "malloc.h"
trycchdr CFLAGS_AUTO "alloca.h"
tryccfn CFLAGS_AUTO "alloca(4)" "stdlib.h"
tryccfn CFLAGS_AUTO "alloca(4)" "alloca.h"

# Optional features
if test "$usesmalllut" = "yes" ; then
	USE_SMALL_LUT=1
else
	USE_SMALL_LUT=0
fi

if test "$usedebugstderr" = "yes" ; then
	USE_DEBUG_STDERR=1
else
	USE_DEBUG_STDERR=0
fi

if test "$useclock" = "yes" ; then
	USE_CLOCK=1
else
	USE_CLOCK=0
fi

printf "creating $CONFIGFILE... "

cmdline=$(quote "$0")
for i ; do cmdline="$cmdline $(quote "$i")" ; done

exec 3>&1 1> $CONFIGFILE

cat << EOF
# This version of $CONFIGFILE was generated by:
# $cmdline
# Any changes made here will be lost if configure is re-run
PREFIX = $PREFIX
EXEC_PREFIX = $EXEC_PREFIX
BINDIR = $BINDIR
DOCPREFIX = $DOCDIR
MANPREFIX = $MANDIR
SHAREPREFIX = $SHAREDIR
CC = $CC
AWK = $AWK
AR = $AR
RANLIB = $RANLIB
LLVM_PROFDATA = $LLVM_PROFDATA
PROFDATA = $PROFDATA
CFLAGS = $CFLAGS
LDFLAGS = $LDFLAGS
CFLAGS_STD = $CFLAGS_STD
LDFLAGS_STD = $LDFLAGS_STD
CFLAGS_OPT = $CFLAGS_OPT
LDFLAGS_OPT = $LDFLAGS_OPT
CFLAGS_AUTO = $CFLAGS_AUTO
LDFLAGS_AUTO = $LDFLAGS_AUTO
CFLAGS_AVX2 = $CFLAGS_AVX2
CFLAGS_CLMUL = $CFLAGS_CLMUL
CFLAGS_DEBUG = -U_FORTIFY_SOURCE -UNDEBUG -O0 -g3 -ggdb -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter
LDFLAGS_DEBUG = -U_FORTIFY_SOURCE -UNDEBUG -O0 -g3 -ggdb
CFLAGS_PIC = $CFLAGS_PIC
LDFLAGS_PIC = $LDFLAGS_PIC
CFLAGS_PIE = $CFLAGS_PIE
LDFLAGS_PIE = $LDFLAGS_PIE
USE_SMALL_LUT = $USE_SMALL_LUT
USE_DEBUG_STDERR = $USE_DEBUG_STDERR
USE_CLOCK = $USE_CLOCK
EOF
exec 1>&3 3>&-

printf "done\n"

exit 0
