#!/bin/sh
#############################################################################
##
#W  gac.sh                      GAP compiler                 Martin Schoenert
##
#H  @(#)$Id: gac.in,v 4.22 2003/11/24 16:52:33 gap Exp $
##
#Y  Copyright (C)  1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##
##  gac [-d] [-c|-C] [-o <output>] {-f<option>} <input>...
##
##  'gac'  compiles the input files.   Input files  must be  GAP  source code
##  (suffix '.g' or '.gap'),  C source code (suffix '.c'),  or compiled  code
##  files (suffix '.o').
##
##  If '-d' is given then the code is compiled for dynamic loading
##  
##  If  neither '-c' nor '-C'  is given,  then 'gac'  compiles the code completely,
##  producing a  new kernel for static compilation or a dynamically loadable '.so'
##  file for dynamic compilation.
##
##  If '-c' is given,  then 'gac' only compiles the input files to 
##  '.o' object files, which must be further linked to make a static kernel or
##  dynamically loadable module
##
##  If '-C is given, then 'gac' only compiles the input files to C code, which 
##  will require compilation and linking to be usable.
##
##  If '-r' is given, then statically compiled files will be assumed to be given
##  by pathnames relative to the GAP root, and will be compiled for automatic loading
##  when files are sought relative to the GAP root.
##
##  The option '-o <output>' tells 'gac' to name the output file <output>.
##
##  The option  '-ffast-int-arith' tells  'gac' to emit  code for  arithmetic
##  operations, which works faster if both  operands are  small integers  and
##  slower otherwise.
##
##  The option '-ffast-plain-lists' tells 'gac' to emit code for list access,
##  which works faster if the list is a plain list and slower otherwise.
##
##  The option '-ffast-list-funcs' tells 'gac' to inline  the functions 'Add'
##  and 'Length' the code, which makes it a bit faster.
##
##  The option '-fno-check-types' tells 'gac' to  emit  no  error checks  for
##  list access, which makes it faster but may dump core if an error happens.
##
##  The option '-p <option>' tells 'gac' to pass the option  <option> to  the
##  C compiler.
##
##  The option '-P <option>' tells 'gac' to pass the option  <option> to  the
##  C linker.
##
gap_bin=`echo $0 | sed -e 's:/[^/]*$::'`
if [ "X${gap_bin}" = "X$0" ];  then
    gap_dir="../../";
    gap_compiler="./gap";
    gap_binary="./";
else
    gap_dir="${gap_bin}/../..";
    gap_compiler="${gap_bin}/gap";
    gap_binary="${gap_bin}";
fi
gap_options=""
gap_include="${gap_dir}/src"
stat_identifier="USER";

# These three should be filled in by the standard autoconf procedures 
c_compiler="gcc"
c_options=" ${CFLAGS}" 
c_linker="gcc"
c_link_options="${LDFLAGS}"
c_libs=""

# These three will need special care 
c_dyn_options="-fpic -Wall -O2 ${CFLAGS}"
c_dyn_linker="ld"
c_dyn_linking="-Bshareable -x"
c_dynlibs=""

# hardwire this for now. There is probably a nicer way 

OBJECTS="\
    c_meths1.o  c_type1.o   c_oper1.o   c_filt1.o    c_random.o  \
    ariths.o    blister.o   bool.o      calls.o      code.o      \
    compiler.o  costab.o    cyclotom.o  dt.o         dteval.o    \
    exprs.o     finfield.o  funcs.o     gap.o        gasman.o    \
    gvars.o     integer.o   intrprtr.o  listfunc.o   listoper.o  \
    lists.o     objcftl.o   objects.o   objfgelm.o   objpcgel.o  \
    objscoll.o  objccoll.o  opers.o     permutat.o   plist.o     \
    precord.o   range.o     rational.o  read.o       records.o   \
    saveload.o  scanner.o   sctable.o   set.o        stats.o     \
    streams.o   string.o    sysfiles.o  system.o     tietze.o    \
    vars.o      vecgf2.o    vecffe.o    vec8bit.o    vector.o    \
    weakptr.o   iostream.o  float.o "

#############################################################################
##
#F  gap_compile <output> <input> <module-name> <identifier>
##
gap_compile () {
    echo ${gap_compiler} ${gap_options} -C $1 $2 $3 $4
    ${gap_compiler} ${gap_options} -C "$1" "$2" "$3" "$4"
}


#############################################################################
##
#F  c_compile   <output> <input> <options>
##
c_compile () {
    echo ${c_compiler}  $3 -o $1 -I${gap_dir} \
         -I${gap_bin} -DCONFIG_H -c $2
    ${c_compiler} $3 -o $1 -I${gap_dir} \
         -I${gap_bin} -DCONFIG_H -c $2
}


#############################################################################
##
#F  c_link_dyn <output> <input>
##
c_link_dyn () {
    echo ${c_dyn_linker} ${c_dyn_linking} -o $1 $2
    ${c_dyn_linker} ${c_dyn_linking} -o $1 $2 ${c_link_options}
}


#############################################################################
##
#F  c_link <output> <inputs_o>
##
c_link () {
    echo ${c_linker} $c_dynlibs  -o $1 $2 -lm ${c_libs}
    ${c_linker} $c_dynlibs -o $1 $2 ${c_link_options} -lm ${c_libs}
}

#############################################################################
##
#F process_o_file <basename> <filename> 
##
## Compile according to comp_mode and comp_howfar
## 
## This does everything except the final link phase in the static case
## in that case it adds the basename and object file path of $names and $objects
##

process_o_file () {
  name=$1
  o_file=$2
  if [ $comp_mode = comp_static ]; then
    # just remember for the linking stage later
    names="${names} ${name}"
    objects="${objects} $o_file"
  else
     if [ "X$output" != "X" ]; then
	so_file=$output
     else
	so_file=${name}.so
     fi
     c_link_dyn $so_file $o_file
  fi
}

#############################################################################
##
#F process_c_file <basename> <filename> 
##
## Compile according to comp_mode and comp_howfar
## 
## This does everything except the final link phase in the static case
## in that case it adds the basename and object file path of $names and $objects
##

process_c_file () {
    name=$1
    c_file=$2
    if [ $comp_howfar != "object" ]; then
       o_file=${gactmp}/$$_${name}.o
        temps_o="${temps_o} ${o_file}"
    elif [ "X$output" != "X" ]; then
       o_file=$output
    else 
       o_file=${name}.o
    fi
    if [ $comp_mode = "comp_static" ]; then
       c_compile_opts=$c_options
    else
       c_compile_opts=$c_dyn_options
    fi
    c_compile $o_file $c_file "$c_compile_opts"
    if [ $comp_howfar = "link" ]; then
      process_o_file $name $o_file
      if [ $comp_mode != "comp_static" ]; then
	echo rm -f $o_file
	rm -f $o_file
      fi
    fi 
}

#############################################################################
##
#F process_gap_file <filename> <ext>
##
## Compile according to comp_mode and comp_howfar
## 
## This does everything except the final link phase in the static case
## in that case it adds the basename and object file path of $names and $objects
##

process_gap_file () {
  name=`basename $1 $2`

  if [ $comp_howfar != "c_code" ]; then
    c_file=${gactmp}/$$_${name}.c
    temps_c="${temps_c} $gap_compile_to"
  elif [ "X$output" = "X" ]; then
     c_file=${name}.c
  else
     c_file=$output
  fi
  gap_compile_in=$input
  gap_compile_name=$input  
  if [ $comp_mode = "comp_static" ]; then
    gap_compile_id=Init_${name}
    if [ $comp_static_root_relative = "yes" ]; then
	gap_compile_in=${gap_dir}/$input;
        gap_compile_name=GAPROOT/$input;
    fi
  else
    gap_compile_id=Init_Dynamic
  fi
  gap_compile $c_file ${gap_compile_in} $gap_compile_id ${gap_compile_name}
  if [ $comp_howfar != "c_code" ]; then
    process_c_file $name $c_file
    echo rm -f $c_file
    rm -f $c_file
  fi
}

#############################################################################
##
#F  clean_up
##
clean_up () {
    echo rm -f ${temps_c} ${temps_o}
    rm -f ${temps_c} ${temps_o}
}
trap "clean_up" 2 3


#############################################################################
##
##  parse the arguments
##
if [ $# = 0 ]; then
    echo "usage: $0 [-d|-r] [-c|-C] [-o <output>] [-l <gap_binary>] <input>..."
    exit 1
fi

comp_mode="comp_static"
comp_howfar="link"
comp_static_root_relative="no"
output=""
inputs=""

while [ $# -gt 0 ]; do
    case $1 in

    -c|--compile)         comp_howfar="object";;

    -d|--dynamic)         comp_mode="comp_dyna";;

    -C|--create-c)	  comp_howfar="c_code";;

    -o|--output)          shift; output="$1";;
  
    -r)                   comp_static_root_relative="yes";;

    -ffast-int-arith)     if [ "X${gap_options}" = "X" ]; then
                              gap_options="-U FAST_INT_ARITH"
                          else
                              gap_options="${gap_options},FAST_INT_ARITH"
                          fi;;

    -ffast-plain-lists)   if [ "X${gap_options}" = "X" ]; then
                              gap_options="-U FAST_PLAIN_LISTS"
                          else
                              gap_options="${gap_options},FAST_PLAIN_LISTS"
                          fi;;

    -ffast-list-funcs)   # echo "$0: option $1 is not currently allowed, ignoring it";;

			    
			   if [ "X${gap_options}" = "X" ]; then
                              gap_options="-U FAST_LIST_FUNCS"
                          else
                              gap_options="${gap_options},FAST_LIST_FUNCS"
                          fi;;

    -fno-check-types)     if [ "X${gap_options}" = "X" ]; then
                              gap_options="-U NO_CHECK_TYPES"
                          else
                              gap_options="${gap_options},NO_CHECK_TYPES"
                          fi;;

    -fno-check-list-elms) if [ "X${gap_options}" = "X" ]; then
                              gap_options="-U NO_CHECK_LIST_ELMS"
                          else
                              gap_options="${gap_options},NO_CHECK_LIST_ELMS"
                          fi;;

    -f*)                  echo "$0: no such option '$1'"
                          exit 1;;

    -k|--gap-compiler)    shift; gap_compiler="$1";;

    -i|--gap-include)     shift; gap_include="$1";;

    -l|--gap-binary)      shift; gap_binary="$1";;

    -p)                   shift; c_options="${c_options} $1";;

    -P)                   shift; c_link_options="${c_link_options} $1";;

    *.g|*.gap|*.gd|*.gi|*.c|*.o)
                          inputs="${inputs} $1";;

    *)                    echo "$0: cannot handle this argument '$1'"
                          exit 1;;

    esac
    shift
done

if [ "X${inputs}" = "X" ]; then
    echo "$0: no input files given"
    exit 1
fi




#############################################################################
##
#F  make_compstat 
##

make_compstat () {
    # make 'compstat.c' and compile it
    temps_c="${temps_c} ${gactmp}/$$compstat.c"
    echo     "/* made by 'gac', can be thrown away */"   >  ${gactmp}/$$compstat.c
    echo     "#include \"src/compiled.h\" "                   >> ${gactmp}/$$compstat.c

    echo     "#ifndef AVOID_PRECOMPILED"                         >> ${gactmp}/$$compstat.c
    echo     "extern StructInitInfo * Init__methsel1 ( void );"  >> ${gactmp}/$$compstat.c
    echo     "extern StructInitInfo * Init__type1 ( void );"     >> ${gactmp}/$$compstat.c
    echo     "extern StructInitInfo * Init__filter1 ( void );"   >> ${gactmp}/$$compstat.c
    echo     "extern StructInitInfo * Init__oper1( void );"      >> ${gactmp}/$$compstat.c
    echo     "extern StructInitInfo * Init__random( void );"     >> ${gactmp}/$$compstat.c
    echo     "#endif"                                            >> ${gactmp}/$$compstat.c

    for name in ${names}; do
        echo "extern StructInitInfo * Init__${name} ( void );" \
						         >> ${gactmp}/$$compstat.c
    done

    echo     "InitInfoFunc CompInitFuncs [] = {"         >> ${gactmp}/$$compstat.c
    echo     "#ifndef AVOID_PRECOMPILED"                 >> ${gactmp}/$$compstat.c
    echo     "    Init__methsel1,"                       >> ${gactmp}/$$compstat.c
    echo     "    Init__type1,"                          >> ${gactmp}/$$compstat.c
    echo     "    Init__oper1,"                          >> ${gactmp}/$$compstat.c
    echo     "    Init__filter1,"                        >> ${gactmp}/$$compstat.c
    echo     "    Init__random,"                         >> ${gactmp}/$$compstat.c
    echo     "#endif"                                    >> ${gactmp}/$$compstat.c

    for name in ${names}; do
        echo "    Init__${name},"                        >> ${gactmp}/$$compstat.c
    done
    echo "    0"                                         >> ${gactmp}/$$compstat.c
    echo "};"                                            >> ${gactmp}/$$compstat.c
    temps_o="${gactmp}/$$compstat.o ${temps_o}"
    c_compile ${gactmp}/$$compstat.o ${gactmp}/$$compstat.c
    echo rm -f ${gactmp}/$$compstat.c
    rm -f ${gactmp}/$$compstat.c
    objects="${gactmp}/$$compstat.o ${objects}"
}

make_tmpdir () {
    basetmp=${TMPDIR:-/tmp}; #honor the TMPDIR environment variable.
    gactmp="$basetmp/gac$$";
    mkdir "$gactmp" || exit 1;
}

#On systems with mktemp(1) this function is better:
#make_tmpdir ()
#{
#    gactmp=`mktemp -d -t "gacXXXXXXX"`;
#}

#############################################################################
##
##  main loop
##

#Make temporary directory
make_tmpdir;

# loop over the input files
for input in ${inputs}; do
  case $input in

        *.g) process_gap_file $input .g;;
        *.gap) process_gap_file $input .gap;;
        *.gd) process_gap_file $input .gd;;
        *.gi) process_gap_file $input .gi;;

        *.c) # compile '.c' source files
            name=`basename ${input} .c`
            process_c_file $name $input;;

        *.o) # look over '.o' source files
            name=`basename ${input} .o`
	    process_o_file $name $input;;
        esac
    done


#  static link phase
if [ $comp_mode = "comp_static" -a $comp_howfar = "link" ]; then
    make_compstat

    # link everything together
    if [ "X${output}" = "X" ]; then output="gacout"; fi
    for object in $OBJECTS;  do
        objects="${gap_binary}/${object} ${objects}"
    done
    c_link ${output} "${objects}"
    echo rm -f ${temps_o}
    rm -f ${temps_o}
fi

#Remove temporary directory.
#We may assume it is empty at this stage.
rmdir "${gactmp}"

