#!/usr/bin/sh
#
# Souffle - A Datalog Compiler
# Copyright (c) 2013-14, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at:
# - https://opensource.org/licenses/UPL
# - <souffle root>/licenses/SOUFFLE-UPL.txt

#
# script that compiles a generated C++ program and executes it
#

# Show usage
usage() {
  printf "Name:
  souffle-compile - compile a C++ source file generated by souffle
Usage:
  souffle-compile [options] <FILE>.cpp
Options:
  -h           show usage
  -g           build in debug mode
  -l           additional shared libraries
  -L           library paths
  -t           build in test mode, implies '-gw' and compiles using '-Werror'
  -v           verbose output
  -w           enable warnings
  -s <value>   use SWIG interface to generate into <value> language\n"

  exit 1;
}

# Print a message to STDERR and exit. If the second argument (exit code)
# is provided and it is '0' then do nothing, otherwise print the error message
# along with the usage (if the third argument is defined)
error() {
  if [ -z "$2" ] || ! [ "$2" = 0 ]; then
    echo "souffle-compile error: $1" 1>&2
    if [ -n "$3" ]; then
      usage;
    fi
    exit 1;
  fi
}

# set by autoconf
CXX="$(printenv CXX || true)"
test -z "$CXX" && CXX="g++ -std=c++17"
CPPFLAGS="$(printenv CPPFLAGS || true) -std=c++17 "
CXXFLAGS="$(printenv CXXFLAGS || true)  -fwrapv  -DUSE_NCURSES  -O3 -DUSE_LIBZ  -DUSE_SQLITE  -fopenmp  -march=native"
LDFLAGS="$(printenv LDFLAGS || true) -Wl,-z,relro -Wl,--as-needed  -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld "
LIBS="$(printenv LIBS || true) -ldl -lpthread -lsqlite3 -lz -lncursesw"

# set by command flags
WARNINGS=""
SWIGLANG=""

# find header files of souffle
HEADER_DIRS=" -I$(dirname $0)/../include -I$(dirname $0)/include "

# Options processing via getopts builtin, it is very limiting but on OSX the
# default getopt is an old BSD getopt, so need this for portability
while getopts "hwtl:L:vgs:" opt; do
  case "$opt" in
    h|\?) # Show usage and exit
      usage;
    ;;
    g) # enable debug mode
      CXXFLAGS="$(echo $CXXFLAGS|sed 's/-O[0-9s]//g') -g -O0";
    ;;
    L) # enable shared library
      LDFLAGS="$LDFLAGS -L${OPTARG}";
    ;;
    l) # enable shared library
      LIBS="$LIBS -l${OPTARG}";
    ;;
    t) # enable testing mode (debug + warnings + `-Werror`)
      CXXFLAGS="$(echo $CXXFLAGS|sed 's/-O[0-9s]//g') -g -O0 -Wall -Wextra -Werror";
      WARNINGS="1"
    ;;
    w) # enable warnings
      CXXFLAGS="$(echo $CXXFLAGS) -Wall -Wextra"
      WARNINGS="1"
    ;;
    v) # Verbose output
      set -x
    ;;
    s) # Set swig language
      SWIGLANG="${OPTARG}";
    ;;
  esac
done

# Shift positional arguments
shift $(($OPTIND - 1))

# Show usage if no input is given
test -n "$1"
error "no input file" $? 1

# Check if the input file exists
test -f "$1"
error "cannot open source file: '$1'" $?

# Check if the input file has a valid extension
exe=`basename $1 .cpp`
test "$1" != "$exe"
error "source file is not a .cpp file: '$1'" $?

# Ensure binary is compiled to same directory as cpp file
cd "$(dirname $1)"
dir="$PWD"
cd "$OLDPWD"

# Make temp folder and copy relevant files there
if [ -n "$SWIGLANG" ]
then
  # set environment variables
  # directory where swig interface files are in souffle
  TMP_DIR="$(mktemp -d)"
  BASE_DIR="$(dirname $0)"

  if [ -d "$BASE_DIR/include/souffle/swig" ];
  then
    cp "$BASE_DIR/include/souffle/swig/SwigInterface.h" "$TMP_DIR"
    cp "$BASE_DIR/include/souffle/swig/SwigInterface.i" "$TMP_DIR"
  else
    cp "$BASE_DIR/../include/souffle/swig/SwigInterface.h" "$TMP_DIR"
    cp "$BASE_DIR/../include/souffle/swig/SwigInterface.i" "$TMP_DIR"
  fi

  cd "$TMP_DIR"
  swig -c++ -"$SWIGLANG" "SwigInterface.i"

  # Checks input language and compiles files with local python3 config or local java_home config
  if [ "$SWIGLANG" = "python" ]
  then
    $CXX -fPIC -c -D__EMBEDDED_SOUFFLE__ SwigInterface_wrap.cxx $dir/$exe.cpp $(python3-config --cflags) $HEADER_DIRS 2>&1
    $CXX -shared SwigInterface_wrap.o $exe.o $(python3-config --ldflags) -o _SwigInterface.so -lstdc++
  elif [ "$SWIGLANG" = "java" ]
  then
    JAVADIR="$(printenv JAVA_HOME || java -XshowSettings:properties -version 2>&1 | grep java.home | sed 's/^.*= //')"

    if [ -z "$JAVADIR" ]
    then
      error "JAVA_HOME environment variable has not been set"
    fi

    $CXX -fPIC -c -D__EMBEDDED_SOUFFLE__ SwigInterface_wrap.cxx $dir/$exe.cpp -I$JAVADIR -I$JAVADIR/include -I$JAVADIR/include/linux  -I$JAVADIR/include/darwin -I/usr/lib/x86_64-linux-gnu/ $HEADER_DIRS 2>&1
    $CXX -shared SwigInterface_wrap.o $exe.o -o libSwigInterface.so -lstdc++
  fi

  # move generated files to same directory as cpp file
  for file in `ls | grep -v "SwigInterface.i" | grep -v "SwigInterface.h"`
  do
    mv "$file" "$dir"
  done

  rm -rf "$TMP_DIR"
  exit 0
fi

# Compile
rm -f $dir/$exe
CCERR=$(mktemp)
# HACK: don't exit if the compile fails, we need to report the error
( $CXX $CXXFLAGS $CPPFLAGS -o$dir/$exe $1 $HEADER_DIRS $OMP_FLAG $LDFLAGS $LIBS 2> $CCERR ) || true

if test -f $dir/$exe
then
  if [ "$WARNINGS" = 1 ]
  then
     echo "$CXX $CXXFLAGS $CPPFLAGS -o$dir/$exe $1 $LIBS $HEADER_DIRS"
     cat $CCERR 1>&2
  fi
  rm $CCERR
else
  echo "compiler error: cannot compile source file $1" 1>&2
  echo "$CXX $CXXFLAGS $CPPFLAGS -o$dir/$exe $1 $LIBS $HEADER_DIRS"
  cat $CCERR 1>&2
  rm -f $CCERR
  exit 1
fi
exit 0
