#!/bin/bash

########################################
# Authors:     Wojciech Frohmberg      #
#               Michal Kierzynka       #   
# e-mails: wfrohmberg@cs.put.poznan.pl #
#          mkierzynka@cs.put.poznan.pl #
########################################



############
# SETTINGS #
############

CUDA_LIBRARY=`echo $LD_LIBRARY_PATH | tr ':' '\n' | grep cuda | egrep 'lib64[/]?$' | head -1`
LIB=`echo $CUDA_LIBRARY | egrep -o 'lib64[/]?$'`
INC_PATH=${CUDA_LIBRARY:0:$(( ${#CUDA_LIBRARY} - ${#LIB} ))}

CUDA_LIBRARY=-L$CUDA_LIBRARY
CUDA_INCLUDE=-I${INC_PATH}include

# What should be the result of above:
#CUDA_LIBRARY=-L/usr/local/cuda/4.1/cuda/lib64/
#CUDA_INCLUDE=-I/usr/local/cuda/4.1/cuda/include/

EXEC_NAME=gdna
CC=g++
NVCC=nvcc

CC_FLAGS='-lstdc++ $(CUDA_INC) $(CUDA_LIB) -lcudart'
NVCC_FLAGS='-lstdc++ -arch=sm_20'

######################
#      CLEANING      #
######################

CLEANING=no
DEFAULTS=yes

for ARG in $*
do
  if [[ $ARG == "--clean" ]]
  then
    rm -rf tmp
    rm -f ${EXEC_NAME}.sh
    rm -f Makefile
    rm -f main_cu.cu
    exit 0
  fi
  if [[ $ARG == "--default-no" ]]
  then
    DEFAULTS=no    
  fi
done


##########################
# CHECKING FOR LIBS ETC. #
##########################


if [[ $CUDA_LIBRARY == "-L" ]]
then
   echo "NO CUDA FOUND!!!"
   exit 1
fi


args=$@

for arg in ${args[@]}
do
   if [[ $arg == "--mpi" ]]
   then
      CC=mpiCC
      CC_FLAGS="${CC_FLAGS} -D MPI_ASM_ALIGN=1"
      NVCC_FLAGS="${NVCC_FLAGS} -D MPI_ASM_ALIGN=1"
   fi
done

$CC 1>/dev/null 2>/dev/null
if [[ $? -eq 127 ]]
then
   echo "NO $CC FOUND!!!"
   exit 1
fi
$NVCC 1>/dev/null 2>/dev/null
if [[ $? -eq 127 ]]
then
   echo "NO $NVCC FOUND!!!"
   exit 1
fi



##########################
# CREATING HELPING FILES #
##########################

echo CREATING HELPING FILES

mkdir tmp

find . -type f -name "*.cpp"            >  tmp/CPPs_prev.txt
find . -type f -name "*.cuh"            >  tmp/CUHs_prev.txt
find . -type f -name "*.y"              >  tmp/Ys_prev.txt
find . -type f -name "*.l"              >  tmp/Ls_prev.txt

sed 's/^\.\///g' tmp/CPPs_prev.txt      >  tmp/CPPs.txt
sed 's/^\.\///g' tmp/CUHs_prev.txt      >  tmp/CUHs.txt
sed 's/^\.\///g' tmp/Ys_prev.txt        >  tmp/Ys.txt
sed 's/^\.\///g' tmp/Ls_prev.txt        >  tmp/Ls.txt


###############################
# RESOLVING EXEC DEPENDENCIES #
###############################

echo RESOLVING EXEC DEPENDENCIES

sed 's/\.cpp$/.o/g' tmp/CPPs.txt        >  tmp/exec.dep
echo main_cu.o                          >> tmp/exec.dep
sed 's/\.y$/_y.o/g' tmp/Ys.txt          >> tmp/exec.dep
sed 's/\.l$/_l.o/g' tmp/Ls.txt          >> tmp/exec.dep
#cat tmp/dep1.txt | tr '\n' ' '          >> tmp/exec.dep


#####################
# CREATING .CU FILE #
#####################

echo CREATING .CU FILE

sed 's/^/#include "/g' tmp/CUHs.txt     >  tmp/main_cu.cu
sed 's/$/"/g' tmp/main_cu.cu            >  main_cu.cu
echo ""                                 >> main_cu.cu


####################################
# RESOLVING MAIN_CU.O DEPENDENCIES #
####################################

echo RESOLVING MAIN_CU.O DEPENDENCIES

nvcc -M main_cu.cu | egrep ^\ *[^\ /]   >  tmp/dep1.txt
cat tmp/dep1.txt | tr '\n\\' '  '       >  tmp/dep2.txt
cat tmp/dep2.txt | tr -s ' '            >  tmp/dep3.txt
egrep [^:\ ][^:]+$ -o tmp/dep3.txt      >  tmp/dep4.txt
cat tmp/dep4.txt | tr ' ' '\n'          >  tmp/main_cu_o.dep


###############################
# RESOLVING CPPs DEPENDENCIES #
###############################

echo RESOLVING CPPs DEPENDENCIES

while read CPP
do
    DEP=`echo $CPP | sed 's/\.cpp$/_o.dep/g'`
    g++ -MG -MM $CPP                    >  tmp/dep1.txt
    cat tmp/dep1.txt | tr '\n\\' '  '   >  tmp/dep2.txt
    cat tmp/dep2.txt | tr -s ' '        >  tmp/dep3.txt
    egrep [^:\ ][^:]+$ -o tmp/dep3.txt  >  tmp/dep4.txt
    cat tmp/dep4.txt | tr ' ' '\n'      >  tmp/$DEP
done < tmp/CPPs.txt


###############################
# RESOLVING YACC DEPENDENCIES #
###############################

echo RESOLVING YACC DEPENDENCIES

while read Y
do
    DEP=`echo $Y | sed 's/\.y$/_y_o.dep/g'`
    CPP=`echo $Y | sed 's/\.y$/_y.cc/g'`
    cp $Y $CPP
    g++ -MG -MM $CPP                    >  tmp/dep1.txt
    cat tmp/dep1.txt | tr '\n\\' '  '   >  tmp/dep2.txt
    cat tmp/dep2.txt | tr -s ' '        >  tmp/dep3.txt
    egrep [^:\ ][^:]+$ -o tmp/dep3.txt  >  tmp/$DEP

#    egrep [^:\ ][^:]+$ -o tmp/dep3.txt  >  tmp/dep4.txt
#    tr ' ' '\n' tmp/dep4.txt            >  tmp/dep5.txt
#    tail --lines=+2 tmp/dep5.txt        >  tmp/dep6.txt
#    tr '\n' ' ' tmp/dep6.txt            >  tmp/$DEP
    rm $CPP
done < tmp/Ys.txt


###############################
# RESOLVING FLEX DEPENDENCIES #
###############################

echo RESOLVING FLEX DEPENDENCIES

while read L
do
    DEP=`echo $L | sed 's/\.l$/_l_o.dep/g'`
    CPP=`echo $L | sed 's/\.l$/_l.cc/g'`
    cp $L $CPP
    g++ -MG -MM $CPP                    >  tmp/dep1.txt
    cat tmp/dep1.txt | tr '\n\\' '  '   >  tmp/dep2.txt
    cat tmp/dep2.txt | tr -s ' '        >  tmp/dep3.txt
    egrep [^:\ ][^:]+$ -o tmp/dep3.txt  >  tmp/$DEP
    rm $CPP
done < tmp/Ls.txt


############################
# CREATING BASH COMPLETION #
#         SCRIPT           #
############################

echo CREATING BASH COMPLETION SCRIPT

cat > $EXEC_NAME.sh <<!

_$EXEC_NAME()
{
	local cur prev opts commands
	COMPREPLY=()
	cur="\${COMP_WORDS[COMP_CWORD]}"
	$EXEC_NAME --complete \${COMP_CWORD} \${COMP_WORDS[@]:1} > commands
	result=""
	while read command 
	do
		result="\$result "\`eval \$command\`
	done < commands
	rm -f commands
	COMPREPLY=( \$(compgen -W "\$result" -- \$cur) )
}

complete -o filenames -F _$EXEC_NAME $EXEC_NAME

!




###########################
#    HELPING  FUNCTION    #
#     $1  --  SUBJECT     #
# $2 -- DEPENDENCIES FILE #
###########################

function dependencies {
    printf "$1"                         >> Makefile
    COUNTER=$((${#1} + `echo $1 | grep \t -o | wc -l` * 7))
    while read D
    do
       if [[ $(($COUNTER + ${#D} + 1 )) -ge 70 ]]
       then
          echo " \\"                    >> Makefile
          printf "\t$D"                 >> Makefile
          COUNTER=$((${#D} + 8))
       else
          printf " $D"                  >> Makefile
          COUNTER=$((${#D} + $COUNTER))
       fi 
    done < $2
}

#############################
# CPP DEPENDENCIES FUNCTION #
#      $1 -- cpp file       #
#############################

function cpp_dependencies {
    DEP=`echo $1 | sed 's/\.[^\.]*$/_o.dep/g'`
    O=`echo $1 | sed 's/\.[^\.]*$/.o/g'`
    dependencies "$O:" tmp/$DEP
    echo ""                             >> Makefile
    printf "\t\$(CC) \$(CC_FL) -c $1\n" >> Makefile
}

#####################
# CREATING MAKEFILE #
#####################

echo CREATING MAKEFILE

cat > Makefile <<!
#######################
# DECLARING VARIABLES #
#######################

CUDA_LIB=$CUDA_LIBRARY
CUDA_INC=$CUDA_INCLUDE
EN=$EXEC_NAME
CC=$CC
NVCC=$NVCC
CC_FL=$CC_FLAGS
NVCC_FL=$NVCC_FLAGS


###############
# MAIN TARGET #
###############

all: \$(EN)


###############
# EXEC TARGET #
###############

!

dependencies "\$(EN):" tmp/exec.dep
echo ""                                 >> Makefile
dependencies "\t\$(CC) \$(CC_FL) -o \$(EN)" tmp/exec.dep
echo ""                                 >> Makefile

cat >> Makefile <<!


################
# CPPs TARGETs #
################

!

while read CPP
do
   cpp_dependencies $CPP
done < tmp/CPPs.txt

cat >> Makefile <<!


################
# FLEX TARGETs #
################

!

while read L
do
   CPP=`echo $L | sed 's/\.l/_l.cc/g'`
   PREFIX=`egrep [_a-zA-Z0-9]+wrap -o $L | head -1 | sed 's/wrap$//g'`
   cpp_dependencies $CPP
   printf "$CPP: $L\n"                  >> Makefile
   printf "\tflex -o$CPP -P$PREFIX "    >> Makefile
   printf "$L\n"                        >> Makefile
done < tmp/Ls.txt

cat >> Makefile <<!


################
# YACC TARGETs #
################

!

while read Y
do
   CPP=`echo $Y | sed 's/\.y$/_y.cc/g'`
   HH=`echo $Y | sed 's/\.y$/_y.hh/g'`
   H=`echo $Y | sed 's/\.y$/_y.h/g'`
   PREFIX=`egrep [_a-zA-Z0-9]+parse -o $Y | head -1 | sed 's/parse$//g'`
   cpp_dependencies $CPP
   printf "$CPP $HH: $Y\n"              >> Makefile
   printf "\tbison -o$CPP -p$PREFIX"    >> Makefile
   printf " -d $Y\n"                    >> Makefile
   printf "\tbash -c 'if [[ -f $H ]];"  >> Makefile
   printf " then mv $H $HH; fi'\n"      >> Makefile
done < tmp/Ys.txt


cat >> Makefile <<!


##############
# GPU TARGET #
##############

!

dependencies "main_cu.o:" tmp/main_cu_o.dep
echo ""                                 >> Makefile
printf "\t\$(NVCC) \$(NVCC_FL) -c "     >> Makefile
echo "main_cu.cu"                       >> Makefile

dependencies "ptx:" tmp/main_cu_o.dep
echo ""                                 >> Makefile
printf "\t\$(NVCC) \$(NVCC_FL) -c "         >> Makefile
echo "--ptxas-options=-v main_cu.cu"    >> Makefile

cat >> Makefile <<!


################
# CLEAN TARGET #
################

!

printf "clean: \n"                      >> Makefile
printf "\trm -f *.o\n"                  >> Makefile
printf "\trm -f *_y.cc\n"               >> Makefile
printf "\trm -f *_y.hh\n"               >> Makefile
printf "\trm -f *_l.cc\n"               >> Makefile
printf "\trm -f \$(EN)\n"               >> Makefile


cat >> Makefile <<!


##################
# INSTALL TARGET #
##################

!
if [[ $DEFAULTS == "no" ]]
then
  printf "enter binaries path (/usr/bin): "
  read BIN

  if [[ $BIN == "" ]]
  then
    BIN=/usr/bin
  fi


  while [[ $COMPL -ne 1 ]] && [[ $COMPL -ne 2 ]]
  do
     printf "enter completion path: \n"
     printf "1 -- /etc/bash_completion.d/ (for all users)\n"
     printf "2 -- ~/.bash_completion.d/ (for actual user)\n"
     printf ">"
     read COMPL
  done


  if [[ $COMPL -eq 1 ]]
  then
     COMPL_PATH=/etc/bash_completion.d/
  fi

  if [[ $COMPL -eq 2 ]]
  then
     COMPL_PATH=\~/.bash_completion.d/
  fi
else
  BIN=/usr/bin
  COMPL_PATH=/etc/bash_completion.d/
fi

printf "install: \n"                    >> Makefile
printf "\tif [[ ! -d $BIN ]]; then mkdir $BIN; fi\n" >> Makefile
printf "\tcp \$(EN) $BIN\n"             >> Makefile
printf "\tif [[ ! -d $COMPL_PATH ]]; then mkdir $COMPL_PATH; fi\n" >> Makefile
printf "\tmv \$(EN).sh "                >> Makefile
printf "$COMPL_PATH\$(EN)\n"                 >> Makefile
