#! /bin/sh
##
## mkoctfile -- create a .oct file suitable for dynamic linking by
## Octave.
##
## Copyright (C) 1996-2012 John W. Eaton
##
## This file is part of Octave.
##
## Octave is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 3 of the License, or (at
## your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

# Exit immediately on any error.

set -e

: ${SED=%OCTAVE_CONF_SED%}

OCTAVE_VERSION=%OCTAVE_CONF_VERSION%
OCTAVE_PREFIX=%OCTAVE_CONF_PREFIX%

DEFAULT_BINDIR=%OCTAVE_BINDIR%
DEFAULT_INCLUDEDIR=%OCTAVE_CONF_INCLUDEDIR%
DEFAULT_LIBDIR=%OCTAVE_CONF_LIBDIR%
DEFAULT_OCTINCLUDEDIR=%OCTAVE_CONF_OCTINCLUDEDIR%
DEFAULT_OCTLIBDIR=%OCTAVE_CONF_OCTLIBDIR%

if [ -n "$OCTAVE_HOME" ]; then
  DEFAULT_BINDIR="`echo $DEFAULT_BINDIR | $SED "s,^$OCTAVE_PREFIX,$OCTAVE_HOME,"`"
  DEFAULT_INCLUDEDIR="`echo $DEFAULT_INCLUDEDIR | $SED "s,^$OCTAVE_PREFIX,$OCTAVE_HOME,"`"
  DEFAULT_LIBDIR="`echo $DEFAULT_LIBDIR | $SED "s,^$OCTAVE_PREFIX,$OCTAVE_HOME,"`"
  DEFAULT_OCTINCLUDEDIR="`echo $DEFAULT_OCTINCLUDEDIR | $SED "s,^$OCTAVE_PREFIX,$OCTAVE_HOME,"`"
  DEFAULT_OCTLIBDIR="`echo $DEFAULT_OCTLIBDIR | $SED "s,^$OCTAVE_PREFIX,$OCTAVE_HOME,"`"
fi

: ${BINDIR=$DEFAULT_BINDIR}
: ${INCLUDEDIR=$DEFAULT_INCLUDEDIR}
: ${LIBDIR=$DEFAULT_LIBDIR}
: ${OCTINCLUDEDIR=$DEFAULT_OCTINCLUDEDIR}
: ${OCTLIBDIR=$DEFAULT_OCTLIBDIR}

DEFAULT_INCFLAGS="-I$OCTINCLUDEDIR/.. -I$OCTINCLUDEDIR"
if [ "$INCLUDEDIR" != /usr/include ]; then
  DEFAULT_INCFLAGS="$DEFAULT_INCFLAGS -I$INCLUDEDIR"
fi

DEFAULT_LFLAGS="-L$OCTLIBDIR"
if [ "$LIBDIR" != /usr/lib ]; then
  DEFAULT_LFLAGS="$DEFAULT_LFLAGS -L$LIBDIR"
fi

# Default values for these variables are filled in when Octave is
# compiled.

: ${EXEEXT=%OCTAVE_CONF_EXEEXT%}

: ${CPPFLAGS=%OCTAVE_CONF_CPPFLAGS%}
: ${INCFLAGS=$DEFAULT_INCFLAGS}
: ${F77=%OCTAVE_CONF_F77%}
: ${FFLAGS=%OCTAVE_CONF_FFLAGS%}
: ${FPICFLAG=%OCTAVE_CONF_FPICFLAG%}
: ${CC=%OCTAVE_CONF_CC%}
: ${CFLAGS=%OCTAVE_CONF_CFLAGS%}
: ${CPICFLAG=%OCTAVE_CONF_CPICFLAG%}
: ${CXX=%OCTAVE_CONF_CXX%}
: ${CXXFLAGS=%OCTAVE_CONF_CXXFLAGS%}
: ${CXXPICFLAG=%OCTAVE_CONF_CXXPICFLAG%}
: ${XTRA_CFLAGS=%OCTAVE_CONF_XTRA_CFLAGS%}
: ${XTRA_CXXFLAGS=%OCTAVE_CONF_XTRA_CXXFLAGS%}

: ${DEPEND_FLAGS=%OCTAVE_CONF_DEPEND_FLAGS%}
: ${DEPEND_EXTRA_SED_PATTERN=%OCTAVE_CONF_DEPEND_EXTRA_SED_PATTERN%}

: ${DL_LD=%OCTAVE_CONF_DL_LD%}
: ${DL_LDFLAGS=%OCTAVE_CONF_MKOCTFILE_DL_LDFLAGS%}

: ${RDYNAMIC_FLAG=%OCTAVE_CONF_RDYNAMIC_FLAG%}
: ${LIBOCTAVE=-loctave}
: ${LIBOCTINTERP=-loctinterp}
: ${READLINE_LIBS=-lreadline}
: ${LIBCRUFT=-lcruft}
: ${LAPACK_LIBS=%OCTAVE_CONF_LAPACK_LIBS%}
: ${BLAS_LIBS=%OCTAVE_CONF_BLAS_LIBS%}
: ${FFTW3_LDFLAGS=%OCTAVE_CONF_FFTW3_LDFLAGS%}
: ${FFTW3_LIBS=%OCTAVE_CONF_FFTW3_LIBS%}
: ${FFTW3F_LDFLAGS=%OCTAVE_CONF_FFTW3F_LDFLAGS%}
: ${FFTW3F_LIBS=%OCTAVE_CONF_FFTW3F_LIBS%}
: ${LIBS=%OCTAVE_CONF_LIBS%}
: ${FLIBS=%OCTAVE_CONF_FLIBS%}
: ${OCTAVE_LINK_DEPS=%OCTAVE_CONF_OCTAVE_LINK_DEPS%}
: ${OCTAVE_LINK_OPTS=%OCTAVE_CONF_OCTAVE_LINK_OPTS%}
: ${OCT_LINK_DEPS=%OCTAVE_CONF_OCT_LINK_DEPS%}
: ${OCT_LINK_OPTS=%OCTAVE_CONF_OCT_LINK_OPTS%}
: ${LD_CXX=%OCTAVE_CONF_LD_CXX%}
: ${LDFLAGS=%OCTAVE_CONF_LDFLAGS%}
: ${LD_STATIC_FLAG=%OCTAVE_CONF_LD_STATIC_FLAG%}
: ${LFLAGS=$DEFAULT_LFLAGS}
: ${F77_INTEGER_8_FLAG=%OCTAVE_CONF_F77_INTEGER_8_FLAG%}

: ${ALL_FFLAGS="$FFLAGS $F77_INTEGER_8_FLAG"}

: ${ALL_CFLAGS="$INCFLAGS $XTRA_CFLAGS $CFLAGS"}

: ${ALL_CXXFLAGS="$INCFLAGS $XTRA_CXXFLAGS $CXXFLAGS"}

: ${ALL_LDFLAGS="$LD_STATIC_FLAG $CPICFLAG $LDFLAGS"}

: ${OCTAVE_LIBS="$LIBOCTINTERP $LIBOCTAVE $SPECIAL_MATH_LIB $LIBCRUFT"}

# Local variables.

FFTW_LIBS="$FFTW3_LDFLAGS $FFTW3_LIBS $FFTW3F_LDFLAGS $FFTW3F_LIBS"

usage_msg="usage: mkoctfile [options] file ..."

version_msg="mkoctfile, version $OCTAVE_VERSION"

cfiles=
ccfiles=
f77files=
objfiles=
libfiles=
octfiles=
octfile=
outputfile=
incflags=
defs=
ldflags=
dbg=:
pass_on_options=
strip=false
no_oct_file_strip_on_this_platform=%NO_OCT_FILE_STRIP%
link=true
link_stand_alone=false
output_ext=".oct"
depend=false
compile=true

if [ $# -eq 0 ]; then
  echo $usage_msg 1>&2
  exit 1
fi

if [ $# -eq 1 ]; then
  case "$1" in
    -v | -version | --version)
      echo $version_msg 1>&2
      exit 0
    ;;
  esac
fi

while [ $# -gt 0 ]; do
  file=
  case "$1" in
    *.c)
      file=$1
      cfiles="$cfiles $file"
    ;;
    *.cc | *.C | *.cpp)
      file=$1
      ccfiles="$ccfiles $file"
    ;;
    *.f | *.F | *.f90 | *.F90)
      file=$1
      f77files="$f77files $file"
    ;;
    *.o)
      file=$1
      objfiles="$objfiles $file"
    ;;
    *.a)
      file=$1
      libfiles="$libfiles $file"
    ;;
    -d | -debug | --debug | -v | -verbose | --verbose)
      dbg=echo
    ;;
    -h | -\? | -help | --help)
      echo $usage_msg 1>&2
      cat << EOF

Options:

  -h, -?, --help          Print this message.

  -IDIR                   Add -IDIR to compile commands.

  -idirafter DIR          Add -idirafter DIR to compile commands.

  -DDEF                   Add -DDEF to compile commands.

  -lLIB                   Add -lLIB to link command.

  -LDIR                   Add -LDIR to link command.

  -RDIR                   Add -RDIR to link command.

  -g                      Enable debugging option for all compilers.

  -pthread                Add -pthread to link command.

  -W...                   Pass flags to the compiler such as -Wa,OPTION.

  -Wl,...                 Pass flags to the linker such as -Wl,-rpath=...

  -M, --depend            Generate dependency files (.d) for C and C++
                          source files.

  -c, --compile           Compile, but do not link.

  --link-stand-alone      Link a stand-alone executable file.

  -s, --strip             Strip output file.

  --mex                   Create a MEX file.
                          Set the default output extension to ".mex".

  -o FILE, --output FILE  Output file name.  Default extension is .oct
                          (or .mex if --mex is specified) unless linking
                          a stand-alone executable.

  -p VAR, --print VAR     Print configuration variable VAR.  Recognized
                          variables are:

                            ALL_CFLAGS                FFTW3F_LIBS
                            ALL_CXXFLAGS              FLIBS
                            ALL_FFLAGS                FPICFLAG
                            ALL_LDFLAGS               INCFLAGS
                            BLAS_LIBS                 LAPACK_LIBS
                            CC                        LDFLAGS
                            CFLAGS                    LD_CXX
                            CPICFLAG                  LD_STATIC_FLAG
                            CPPFLAGS                  LFLAGS
                            CXX                       LIBCRUFT
                            CXXFLAGS                  LIBOCTAVE
                            CXXPICFLAG                LIBOCTINTERP
                            DEPEND_EXTRA_SED_PATTERN  LIBS
                            DEPEND_FLAGS              OCTAVE_LIBS
                            DL_LD                     OCTAVE_LINK_DEPS
                            DL_LDFLAGS                OCT_LINK_DEPS
                            EXEEXT                    RDYNAMIC_FLAG
                            F77                       READLINE_LIBS
                            F77_INTEGER_8_FLAG        SED
                            FFLAGS                    XTRA_CFLAGS
                            FFTW3_LDFLAGS             XTRA_CXXFLAGS
                            FFTW3_LIBS
                            FFTW3F_LDFLAGS

  -v, --verbose           Echo commands as they are executed.

  FILE                    Compile or link FILE.  Recognized file types are:

                            .c    C source
                            .cc   C++ source
                            .C    C++ source
                            .cpp  C++ source
                            .f    Fortran source (fixed form)
                            .F    Fortran source (fixed form)
                            .f90  Fortran source (free form)
                            .F90  Fortran source (free form)
                            .o    object file
                            .a    library file

EOF
      exit 0
    ;;
    -I*)
      incflags="$incflags $1"
    ;;
    -idirafter)
      shift
      if [ $# -gt 0 ]; then
        incflags="$incflags -idirafter $1"
      else
        echo "mkoctfile: include directory name missing" 1>&2
      fi
    ;;
    -D*)
      defs="$defs $1"
    ;;
    -[lLR]* | -Wl,*)
      ldflags="$ldflags $1"
    ;;
    -pthread)
      ldflags="$ldflags $1"
    ;;
    -M | -depend | --depend)
      depend=true
      compile=false
    ;;
    -o | -ouput | --output)
      shift
      if [ $# -gt 0 ]; then
        outputfile="$1"
      else
        echo "mkoctfile: output file name missing" 1>&2
      fi
    ;;
    -p | -print | --print)
      shift
      if [ $# -gt 0 ]; then
        eval echo \${$1}
        exit 0
      else
        echo "mkoctfile: --print requires argument" 1>&2
        exit 1
      fi
    ;;
    -s | -strip | --strip)
      if $no_oct_file_strip_on_this_platform; then
        echo "mkoctfile: stripping disabled on this platform" 1>&2
      else
        strip=true
      fi
    ;;
    -c | -compile | --compile)
      link=false
    ;;
    -g)
      ALL_CFLAGS="$ALL_CFLAGS -g"
      ALL_CXXFLAGS="$ALL_CXXFLAGS -g"
      ALL_FFLAGS="$ALL_FFLAGS -g"
    ;;
    -link-stand-alone | --link-stand-alone)
      link_stand_alone=true
    ;;
    -mex | --mex)
      incflags="$incflags -I."
      output_ext=".mex"
    ;;
    -W*)
      pass_on_options="$pass_on_options $1"
    ;;
    *)
      echo "mkoctfile: unrecognized argument $1" 1>&2
      exit 1
    ;;
  esac
  if [ -n "$file" ]; then
    if [ -z "$octfile" ]; then
      octfile="$file"
    fi
  fi
  shift
done

if $link_stand_alone; then
  if [ -n "$outputfile" ]; then
    output_option="-o $outputfile"
  fi
else
  if [ -n "$outputfile" ]; then
    octfile="$outputfile"
    case "$octfile" in
      *$output_ext)
      ;;
      *)
        octfile="$octfile$output_ext"
      ;;
    esac
  else
    octfile=`basename $octfile`
    octfile=`echo $octfile | $SED 's,\.[^.]*$,,'`$output_ext
  fi
fi

# Generate dependency files for C and C++ files.

if $depend; then
  if [ -n "$cfiles" ]; then
    for f in $cfiles; do
      b=`echo $f | $SED 's,\.c$,,'`
      d=$b.d
      cmd="rm -f $d"
      $dbg $cmd
      eval $cmd
      cmd="$CC $DEPEND_FLAGS $CPPFLAGS $ALL_CFLAGS $incflags $def $f | $SED $DEPEND_EXTRA_SED_PATTERN -e 's,^[^:]*/\(.*\.o\):,\1:,' -e 's,$b\.o,pic/& & $d,g' > $d-t && mv $d-t $d"
      $dbg $cmd
      eval $cmd
    done
  fi

  if [ -n "$ccfiles" ]; then
    for f in $ccfiles; do
      case $f in
        *.cc)
          b=`echo $f | $SED 's,\.cc$,,'`
        ;;
        *.C)
          b=`echo $f | $SED 's,\.C$,,'`
        ;;
        *.cpp)
          b=`echo $f | $SED 's,\.cpp$,,'`
        ;;
      esac
      d=$b.d
      cmd="rm -f $d"
      $dbg $cmd
      eval $cmd
      cmd="$CXX $DEPEND_FLAGS $CPPFLAGS $ALL_CXXFLAGS $incflags $defs $f | $SED $DEPEND_EXTRA_SED_PATTERN -e 's,^[^:]*/\(.*\.o\):,\1:,' -e 's,$b\.o,pic/& & $d,g' > $d-t && mv $d-t $d"
      $dbg $cmd
      eval $cmd
    done
  fi
  # If generating dependencies, that's all we do.
  exit 0
fi

# Compile Fortran, C, and C++ files.  Add the name of each object file
# that is produced to the overall list of object files.

if [ -n "$f77files" ]; then
  for f in $f77files; do
    case $f in
      *.f)
        b=`echo $f | $SED 's,.*/,,; s,\.f$,,'`
      ;;
      *.F)
        b=`echo $f | $SED 's,.*/,,; s,\.F$,,'`
      ;;
      *.f90)
        b=`echo $f | $SED 's,.*/,,; s,\.f90$,,'`
      ;;
      *.F90)
        b=`echo $f | $SED 's,.*/,,; s,\.F90$,,'`
      ;;
    esac
    if [ -n "$F77" ]; then
      if [ -n "$outputfile" ]; then
        if $link; then
          o=$b.o
        else
          o=$outputfile
        fi
      else
        o=$b.o
      fi
      objfiles="$objfiles $o"
      cmd="$F77 -c $FPICFLAG $ALL_FFLAGS $incflags $defs $pass_on_options $f -o $o"
      $dbg $cmd
      eval $cmd
    else
      echo "mkoctfile: no way to compile Fortran file $f" 1>&2
    fi
  done
fi

if [ -n "$cfiles" ]; then
  for f in $cfiles; do
    if [ -n  "$CC" ]; then
      b=`echo $f | $SED 's,.*/,,; s,\.c$,,'`
      if [ -n "$outputfile" ]; then
        if $link; then
          o=$b.o
        else
          o=$outputfile
        fi
      else
        o=$b.o
      fi
      objfiles="$objfiles $o"
      cmd="$CC -c $CPPFLAGS $CPICFLAG $ALL_CFLAGS $pass_on_options $incflags $defs $f -o $o"
      $dbg $cmd
      eval $cmd
    else
      echo "mkoctfile: no way to compile C++ file $f" 1>&2
    fi
  done
fi

if [ -n "$ccfiles" ]; then
  for f in $ccfiles; do
    if [ -n "$CXX" ]; then
      case $f in
        *.cc)
          b=`echo $f | $SED 's,.*/,,; s,\.cc$,,'`
        ;;
        *.C)
          b=`echo $f | $SED 's,.*/,,; s,\.C$,,'`
        ;;
        *.cpp)
          b=`echo $f | $SED 's,.*/,,; s,\.cpp$,,'`
        ;;
      esac
      if [ -n "$outputfile" ]; then
        if $link; then
          o=$b.o
        else
          o=$outputfile
        fi
      else
        o=$b.o
      fi
      objfiles="$objfiles $o"
      cmd="$CXX -c $CPPFLAGS $CXXPICFLAG $ALL_CXXFLAGS $pass_on_options $incflags $defs $f -o $o"
      $dbg $cmd
      eval $cmd
    else
      echo "mkoctfile: no way to compile C++ file $f" 1>&2
    fi
  done
fi

## Uncomment the following group of lines if you get `Text file busy'
## errors from ld.  This may happen if the .oct file is currently
## running while you are trying to recompile it.  We try moving first,
## since on some systems (HP-UX, maybe others) it is possible to
## rename running programs but not remove them.

## if [ -f "$octfile" ]; then
##   cmd="mv $octfile $octfile.bak"
##   $dbg $cmd
##   eval $cmd
##   cmd="rm -f $octfile.bak"
##   $dbg $cmd
##   eval $cmd
## fi

# Link all the object files.

if $link && [ -n "$objfiles" ]; then
  if $link_stand_alone; then
    if [ -n "$LD_CXX" ]; then
      cmd="$LD_CXX $CPPFLAGS $ALL_CXXFLAGS $RDYNAMIC_FLAG $ALL_LDFLAGS $pass_on_options $output_option $objfiles $libfiles $ldflags $LFLAGS -loctinterp -loctave -lcruft $OCTAVE_LINK_OPTS $OCTAVE_LINK_DEPS"
      $dbg $cmd
      eval $cmd
    else
      echo "mkoctfile: no way to link stand-alone executable file" 1>&2
      exit 1
    fi
  else
    cmd="$DL_LD $DL_LDFLAGS $pass_on_options -o $octfile $objfiles $libfiles $ldflags $LFLAGS -loctinterp -loctave -lcruft $OCT_LINK_OPTS $OCT_LINK_DEPS"
    $dbg $cmd
    eval $cmd
  fi

# Maybe strip it.

  if $strip; then
    cmd="strip $octfile"
    $dbg $cmd
    eval $cmd
  fi
fi

exit 0
