#!/bin/sh
## This script compiles the given list of files.
##

## Print the syntax out.
##
print_usage() {
  echo "Syntax: $0 {-o outfile} [options] files" >&2
  echo -e "Options:" >&2
  echo -e "-o, --outfile <name>    The executable or shared object file." >&2
  echo -e "-c, --cflags  <flags>   Flags passed to the compiler." >&2
  echo -e "-l, --lflags  <flags>   Flags passed to the linker." >&2
  echo -e "-s, --shared            Link as a shared library." >&2
}


## Compiles one single file using g++.
## Args: $1: The .c or .cc file to be compiled.
##       $2: The corresponding object file.
## Returns 1 if the file is unchanged, 2 on an error, otherwise 0.
##
compile_file() {
  FILE="$1"
  OBJECTFILE="$2"
  # Check whether the file needs to be compiled.
  check_up2date.sh $FILE $OBJECTFILE && return 1
  # Compile.
  echo Compiling: $FILE...
  set -x
  g++ -fPIC $CFLAGS -c $FILE -o $OBJECTFILE
  ERR="$?"
  set +x
  if [ "$ERR" -ne "0" ]; then
    echo Error during compilation of $FILE. >&2
    return 2
  fi
  return 0
}


## Compiles all the given files.
## Returns 1 if the files are all unchanged, 2 on an error, otherwise 0.
##
compile_files() {
  FILES="$@"
  CHANGED=0
  OBJECTFILES=''
  for FILE in $FILES; do
    OBJECTFILE="`echo $FILE | sed 's/\.[^\.]*$//'`.o"
    OBJECTFILES="$OBJECTFILES $OBJECTFILE"
    compile_file $FILE $OBJECTFILE
    ERR="$?"
    if [ "$ERR" -eq "2" ]; then    # An error occured.
      return 2
    fi
    if [ "$ERR" -eq "0" ]; then     # No error, but the file has been changed.
      CHANGED=1
    fi
  done
  if [ "$CHANGED" -eq "0" ]; then
    return 1
  fi
  return 0
}


###############################################################################
# Use getopt to determine the allowed options.
###############################################################################
#FIXME: How does getopt handle multiple arguments to one option?
#getopt -l "outfile:,cflags:,lflags:,shared" -- "o:c:l:s" $* 1>/dev/null
if [ $? != 0 ]; then
  print_usage
  exit 1
fi

SHARED=''

for i in "$@"; do
  case $i in
    "-o" | "--outfile")
      OUTFILE="$2"
      shift 2;;
    "-c" | "--cflags")
      CFLAGS="$2"
      shift 2;;
    "-l" | "--lflags")
      LFLAGS="$2"
      shift 2;;
    "-s" | "--shared")
      SHARED=1
      shift;;
    # End of options, only command arguments left.
    "--")
      shift
      break;;
  esac
done

#echo FILES: $@
#echo OUTFILE: $OUTFILE
#echo CFLAGS: $CFLAGS
#echo LFLAGS: $LFLAGS
#echo SHARED: $SHARED

compile_files $@
ERR="$?"
if [ "$ERR" -eq "2" ]; then     # An error occured.
  exit 1
fi
if [ "$ERR" -eq "0" ]; then     # No error, but the file has been changed.
  CHANGED=1
fi
if [ ! -e "$OUTFILE" ]; then    # Make sure that the outfile exists.
  CHANGED=1
fi
if [ "$CHANGED" -ne "1" ]; then # If nothing has been changed, we are done.
  echo Program unchanged, skipping linker.
  exit 0
fi

if [ "$SHARED" = "" ]; then
  # Link as an executable.
  set -x
  g++ $CFLAGS $LFLAGS -o $OUTFILE $OBJECTFILES
  ERR="$?"
  set +x
else
  # Link as a shared library.
  SHAREDNAME=`echo $OUTFILE | sed 's/\..*//'`
  set -x
  g++ -shared -Wl,-soname,$SHAREDNAME $CFLAGS $LFLAGS -o $OUTFILE $OBJECTFILES
  ERR="$?"
  set +x
fi

if [ "$ERR" -ne "0" ]; then     # Check the linker's return value.
  echo Linker returned an error. Aborting. >&2
  exit 1
fi

echo Built successfully.
