#!/bin/bash
#
# This helper script is supposed to be executed from a build directory
# and sets CMake options depending on  the name of the directory.  The
# build directory must be called
#
#     build.feature1-feature2-...
#
# Recognized features can be found below, in the various "case $config"
# statements.  Note that this mechanism is comparable to the
# _presets_ mechanism of recent CMake versions.

declare -A seen_options  # Requires bash 4 or later

build_type=RelWithDebInfo
generator=Ninja
export CC=
export CXX=
opts=
cmake=cmake
os=any

config=$(basename $(pwd))

# gcc-version
# clang-version
#
# Set the compiler

case $config in
  *gcc-*)
    version=$(echo $config | sed 's/.*gcc-\(\(mp-\)\{0,1\}[0-9]*\).*/\1/')
    export CC=gcc-$version
    export CXX=g++-$version
    seen_options["gcc-$version"]=1
    ;;
  *clang-[0-9]*)
    version=$(echo $config | sed 's/.*clang-\([0-9]*\).*/\1/')
    export CC=clang-$version
    export CXX=clang++-$version
    seen_options["clang-$version"]=1
    ;;
  *clang*)
    export CC=clang
    export CXX=clang++
    seen_options["clang"]=1
    ;;
esac

# wasm
# termux
#
# Build for specific targets

case $config in
    *wasm*)
        os=wasm
	WASM_HOME=${WASM_HOME-$HOME/wasm}
	source $WASM_HOME/emsdk/emsdk_env.sh
	TOOLCHAIN=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
	[ -f $TOOLCHAIN ] || echo "Could not find emscripten toolchain"
	cmake="emcmake cmake"
	build_type=Release
	opts+=" -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN"
	opts+=" -DCMAKE_FIND_ROOT_PATH=$WASM_HOME"
	opts+=" -DUSE_GMP=OFF"
	opts+=" -DINSTALL_DOCUMENTATION=OFF"
	opts+=" -DINSTALL_PROLOG_SRC=OFF"
	seen_options["wasm"]=1
	;;
    *termux*)
	opts+=" -DPOSIX_SHELL=${PREFIX}/bin/sh"
	opts+=" -DSWIPL_TMP_DIR=${PREFIX}/tmp"
	opts+=" -DSYSTEM_CACERT_FILENAME=${PREFIX}/etc/tls/cert.pem"
	seen_options["termux"]=1
	;;
    *homebrew*)
	# MacOS with dependencies from Homebrew
	opts+=" -DMACOSX_DEPENDENCIES_FROM=Homebrew"
        seen_options["homebrew"]=1
	;;
esac

# nogmp
# libbf
#
# Use LibBF rather than LibGMP for unbounded integers, rationals and random

case $config in
  *nogmp*|*libbf*)
    opts+=" -DUSE_GMP=OFF"
    seen_options["nogmp|libbf"]=1
    ;;
esac

# single
#
# Build the single threaded version

case $config in
  *single*)
    opts+=" -DMULTI_THREADED=OFF"
    seen_options["single"]=1
    ;;
esac

# engines
#
# Include engine support, also in single threaded versions.

case $config in
  *engines*)
    opts+=" -DENGINES=ON"
    seen_options["engines"]=1
    ;;
esac

# vmif
#
# Implement VMI instructions as functions.  Slower on GCC, about
# equal for Clang (and WASM) and much faster for MSVC

case $config in
  *novmif*)
    opts+=" -DVMI_FUNCTIONS=OFF"
    seen_options["novmif"]=1
    ;;
  *vmif*)
    opts+=" -DVMI_FUNCTIONS=ON"
    seen_options["vmif"]=1
    ;;
esac

# profile
#
# Use good settings for valgrind based profiling

case $config in
  *profile*)
    opts+=" -DVMI_FUNCTIONS=ON"
    CFLAGS+=" -fno-inline"
    seen_options["profile"]=1
    ;;
esac

# gcov
#
# Prepare for coverage analysis

case $config in
  *gcov*)
    opts+=" -DGCOV=ON -DINSTALL_TESTS=ON"
    CFLAGS+=" -fno-inline"
    seen_options["gcov"]=1
    ;;
esac

# c11
#
# Enable pedantic C11 checking for GCC.  Use for compliancy testing as
# it  forces a  less  efficient virtual  machine due  to  the lack  of
# support for empty structures in C11.

case $config in
  *c11*)
    CFLAGS+=" -pedantic -DPEDANTIC"
    seen_options["c11"]=1
    ;;
esac

# c23
#
# Enable C23 mode

case $config in
  *c23*)
    CFLAGS+=" -std=gnu23"
    seen_options["c23"]=1
    ;;
esac

# lsp
#
# Generate compile_commands.json to support clangd

case $config in
  *lsp*)
    opts+=" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
    seen_options["lsp"]=1
    ;;
esac

# nogui
#
# Disable building the GUI

case $config in
  *nogui*)
    opts+=" -DSWIPL_PACKAGES_X=OFF"
    seen_options["nogui"]=1
    ;;
esac

# test
#
# Install the  tests along  with the  executable.  Allows  running the
# tests in the installed version using test_installation/0.

case $config in
  *test*)
    opts+=" -DINSTALL_TESTS=ON"
    seen_options["test"]=1
    ;;
esac

# PDF docs
#
# Build the PDF documentation

case $config in
  *pdf*)
    opts+=" -DBUILD_PDF_DOCUMENTATION=ON"
    seen_options["pdf"]=1
    ;;
esac

# qlf
#
# Compile entire library to qlf

case $config in
  *qlf*)
    opts+=" -DINSTALL_QLF=ON"
    seen_options["qlf"]=1
    ;;
esac

# nosrc
#
# Do NOT install the library .pl files (only the .qlf files)

case $config in
  *nosrc*)
    opts+=" -DINSTALL_PROLOG_SRC=OFF"
    seen_options["nosrc"]=1
    ;;
esac

# native tuning
#
# Optimize for local CPU

case $config in
  *native*)
    CFLAGS+=" -mtune=native -march=native"
    seen_options["native"]=1
    ;;
esac

# malloc
#
# Use  malloc  instead  of  tcmalloc.  Currently  required  for  using
# valgrind.  Do not use on  Linux builds aiming at 24x7 multi-threaded
# services: ptmalloc seems poor at reusing memory in these workloads.

case $config in
  *malloc*)
    opts+=" -DUSE_TCMALLOC=OFF"
    seen_options["malloc"]=1
    ;;
esac

# pgo
# debug
# asan
#
# Select the  build type.  PGO  provides best performance.   Use debug
# for C level debugging and asan for leak and memory issue detection.

case $config in
  *pgo*)
    build_type=PGO
    seen_options["pgo"]=1
    ;;
  *debug*)
    build_type=Debug
    seen_options["debug"]=1
    ;;
  *asan*)
    build_type=Sanitize
    seen_options["asan"]=1
    ;;
  *ubsan*)
    build_type=Sanitize
    opts+=" -DSANITIZE=undefined"
    seen_options["ubsan"]=1
    ;;
esac

function confirm ()
{ while true; do
    echo -n "$1 "
    read answer
    case "$answer" in
          y*)   return 0
                ;;
          n*)   return 1
                ;;
          *)
                echo "Please answer yes or no"
                ;;
    esac
  done
}

export CFLAGS
export LDFLAGS

using=
[ -z "$CC" ]      || using+=" "CC=$CC
[ -z "$CXX" ]     || using+=" "CXX=$CXX
[ -z "$CFLAGS" ]  || using+=" "CFLAGS='"'$CFLAGS'"'
[ -z "$LDFLAGS" ] || using+=" "LDFLAGS='"'$LDFLAGS'"'

cat << EOF
# Configure using
#
#    $using cmake -DCMAKE_BUILD_TYPE=$build_type -G $generator $opts ..
#
EOF

if ! confirm "Run Cmake? "; then
  exit 1
fi

cat > configure << EOF
# Created by ../scripts/configure for $config at $(date)

EOF
echo "# Options:" >>configure
for seen_key in ${!seen_options[@]}
do
    echo "#  " ${seen_key} >>configure
done | sort
echo "# (end options)" >>configure

cat >> configure << EOF

$using $cmake -DCMAKE_BUILD_TYPE=$build_type -G $generator $opts ..
EOF
chmod +x configure

# Create direnv file.  We need this to avoid resetting variables
# when re-running cmake
[ -z "$CC" ]      || echo "export CC=$CC"                  > .envrc
[ -z "$CXX" ]     || echo "export CXX=$CXX"               >> .envrc
[ -z "$CFLAGS" ]  || echo "export CFLAGS="'"'$CFLAGS'"'   >> .envrc
[ -z "$LDFLAGS" ] || echo "export LDFLAGS="'"'$LDFLAGS'"' >> .envrc
case $config in
    *wasm*)
        echo "export WASM_HOME="'"'$WASM_HOME'"'         >> .envrc
	echo "export EMSDK_QUIET=1"			 >> .envrc
	echo "source "'$'"WASM_HOME/emsdk/emsdk_env.sh"	 >> .envrc
esac
./configure
