#!/bin/sh
#
#   makefile generator, by sandro@w3.org
#
#   Shell script to generate a makefile automatically based on my 
#   coding conventions.
#
#   I think the first version dates from about 1990.....
#
#

#
#  Stuff you probably want to override in each directory
#

program=a.out
library=
ldlibs=
local_libraries=
extra_includes=-I$HOME/src
cxxflags="-g -Wall -Wno-unused"
cxxflags_fast="-g -O3 -Wall -DNDEBUG"  # -Winline nice if possible!
  # NOT CARRIED THROUGHT YET.     MAY WANT gcov stuff, too
cxxflags_prof="-g -O3 -Wall -DNDEBUG -pg -DPROFILING"
cflags="-g -pg"

#
#  What files we use
#

mf=Makefile
dep=.dependencies
deplist=.depend_files
aux=./genmake.info
yacclines=.yacc_lines
lexlines=.lex_lines

#
#    Do local override
#

if [ "X$1" = X-new ]; then
  if [ -f $aux ] ; then
    echo Will not over-write $aux -- remove it first.
    exit 1
  fi
  cat << _END > $aux
#
#  Local information for genmake
#
program="$program"
library="$library"
ldlibs="$ldlibs"
local_libraries="$local_libraries"
extra_includes="$extra_includes"
cxxflags="$cxxflags"
cxxflags_fast="$cxxflags_fast"
cflags="$cflags"
mf="$mf"
_END
   echo
   echo $aux written.
   exit 0
fi

if [ -f $aux ] ; then
  . $aux
else 
  echo
  echo 'No local information -- using defaults.   "genmake -new" will make'
  echo "a local information template in $aux for you."
  echo
fi

#
#   Work with yacc
#
rm -f $yacclines
set allow_null_glob_expansion
for f in *.y ; do
  case $f in
  *.cpp.y)   
         b=`basename $f .cpp.y`
         o="yacc_out_$b"        
	 c=$o.cpp
         ;;
  *c.y)  b=`basename $f .c.y`
         o="yacc_out_$b"        
	 c=$o.c
         ;;
  *)     echo "Rename $f files should be .c.y or .cpp.y"
         exit 1
  esac
  make1="yacc -d -v -p ${b}_ $f"
  make2="mv -f y.output yacc_out_${b}_diagnostics"
  make3="mv -f y.tab.c $c"
  make4="mv -f y.tab.h $o.h"
  cat <<_END >> $yacclines

$c: $f
	$make1 && $make2 && $make3 && $make4

$o.h: $f
	$make1 && $make2 && $make3 && $make4

_END
  echo "Running yacc on $f so we can find dependencies"    
  $make1 && $make2 && $make3 && $make4
done

#
#   ... and lex
#
rm -f $lexlines
for f in *.l ; do
  case $f in
  *.cpp.l)   
         b=`basename $f .cpp.l`
         o="lex_out_$b"        
	 c=$o.cpp
         ;;
  *c.l)  b=`basename $f .c.l`
         o="lex_out_$b"        
	 c=$o.c
         ;;
  *)     echo "Rename $f files should be .c.l or .cpp.l"
         exit 1
  esac
  cat <<_END >> $lexlines

$c: $f
	\$(LEX) -t $f > \$@

_END
  echo "Running lex on $f so we can find dependencies"    
  lex -t $f > $c
done

#
#   The files we use for dependencies
#
#   .c .cc .cxx .h .l .y, with no leading or trailing blanks
#   and not counting files starting with _ or the lex/yacc
#   output files
#

headers=""       # list of source header files
bodies=""        # list of source body files
objs=""          # list of object files to build & link
objs_fast=""     # objs alternate, for fast version

for x in *; do
  case $x in
    y_tab*)     ;;
    y.tab*)     ;;
    *.c)   #echo "checking $x as C source code"
           bodies="$bodies $x"
	   base=`basename $x .c`
	   objs="$objs $base.o"
	   objs_fast="$objs $base.fast.o"
	   ;;
    *.cpp) #echo "checking $x as C++ source code"
	   bodies="$bodies $x"
	   base=`basename $x .cpp`
	   objs="$objs $base.o"
	   objs_fast="$objs $base.fast.o"
	   ;;
    *.h)   #echo "checking $x as C/C++ header file"
           headers="$headers $x"
	   ;;
    *)     # just ignore extra files
           ;;
  esac
done

srcs="$headers $bodies"
nhsrcs="$bodies"

#srcs=`echo XXX *.c *.cc *.cxx *.cpp *.h *.l *.y XXX | sed -e 's/\*\.[chxly]*//g' -e 's/ /  /g' -e 's/ _[^ ]* / /g' -e s/XXX//g -e 's/^ *//' -e 's/ *$//' -e 's/ *.tab.h //g' -e 's/ *.tab.c //g' -e 's/ *_tab.h //g' -e 's/ *_tab.c //g' -e 's/ lex.yy.c //g' `

# a version of only c sources, used for gcc -M  (makedepend)
#nhsrcs=`echo XXX *.c *.cc *.cxx *.cpp XXX | sed -e 's/\*\.[chxly]*//g' -e 's/ /  /g' -e 's/ _[^ ]* / /g' -e s/XXX//g -e 's/^ *//' -e 's/ *$//' -e 's/ *.tab.h //g' -e 's/ *.tab.c //g' -e 's/ *_tab.h //g' -e 's/ *_tab.c //g' -e 's/ lex.yy.c //g' `

#
#   The object files we try to build and link
#
#   .c .cc .cxx .cpp (not starting with _) all turned into .o 
#   also .y and .l  (remove dups, in case the .y had a .c or something)

# objs=`echo XXX *.c *.cc *.cxx *.cpp XXX | sed -e 's/\*\.[chx]*//g' -e 's/ /  /g' -e 's/ _[^ ]* / /g' -e 's/\.cc* /.o /g' -e 's/\.cxx /.o /g' -e 's/\.cpp /.o /g' -e 's/\.[ly] /.o /g' -e s/XXX//g | tr ' ' \\\\n | sort | uniq | tr \\\\n ' ' `

# objs_fast=`echo $objs XXX | sed -e 's/.o /.fast.o /g' -e s/XXX//g` 

#if [ -f *.l ] ; then
#  objs=`echo $objs lex.yy.o`
#fi
#if [ -f *.y ] ; then
#  objs=`echo $objs y.tab.o`
#fi


#
#   Backup the makefile
#

if [ -f $mf ]; then 
  mv $mf $mf.bak
fi

#
#    Have we lost a file?
#

if [ -f $deplist ]; then
  oldsrcs=`cat $deplist`
else
  oldsrcs=
fi

if [ "X${srcs}X" != "X${oldsrcs}X" ]; then
  # why doesn't this work at all...??? 
  echo Forcing remake because of directory content change
  #echo "X${srcs}X" '!=' "X${oldsrcs}X"
  rm -f $dep
  echo $srcs > $deplist
fi

#
#    Is our dependency list bad?
#

# was:
#@g++ -MM $extra_includes -x c++ \$(SRCS) |  sed -e "s/.cxx.o :/.o:/"  > $dep

cat <<_END > $mf
SRCS=$srcs
NHSRCS=$nhsrcs
all:: $dep
$dep: \$(SRCS)
	@echo Some possible source has changed: remaking dependencies
	@g++ -MM $extra_includes \$(NHSRCS) > $dep
	@cp $dep $dep-tmp-copy
	@sed 's/\.o: /.fast.o: /' < $dep-tmp-copy >> $dep
_END

if make -f $mf ; then 
  true
else
  #rm -f $mf
  #rm -f $dep
  echo genmake failed.
  exit 1
fi


#
#    Set up to be either library or program makefile
#
if [ "X$program" = X ] ; then
  progline="#"
else 
  progline=""
fi
if [ "X$library" = X ] ; then
  libline="#"
else 
  libline=""
fi


#
#    Time to write the makefile....
#

################################################################
################################################################

cat <<_END > $mf
#
#  This makefile was generated by genmake for `whoami`@`hostname`
#  on `date`.   You probably shouldn't edit it, but
#  it is organized so you can, if you don't have the corresponding
#  genmake script.
#

# These are automatic defaults.   Change them if you want.  You may
# need to add/remove comment characters if you make these (non)null. 
PROGRAM = $program
LIBRARY = $library

# some -l parameters, if you need any, on the program
LDLIBS = $ldlibs

# some .o and .a files to include in the program or archive, but
# which aren't handled normally by this makefile
LOCAL_LIBRARIES = $local_libraries

EXTRA_INCLUDES = $extra_includes

CXXFLAGS = $cxxflags \$(EXTRA_INCLUDES)
CXXFLAGS_FAST = $cxxflags_fast \$(EXTRA_INCLUDES)
CFLAGS  += $cflags \$(EXTRA_INCLUDES)

SRCS=$srcs
OBJS=$objs
OBJS_FAST=$objs_fast

TARGET_ARCH=
SUFFIXES=.out .a .ln .o .c .cc .p .f .F .r .s .S .mod .sym .def .h .in\
         fo .dvi .tex .texinfo .cweb .web .sh .elc .el .cxx .cpp .fast.o
.SUFFIXES: \$(SUFFIXES)
CXX = g++
CC = gcc
RM = rm -f
# LINK.cxx = \$(CXX) \$(CXXFLAGS) \$(CPPFLAGS) \$(LDFLAGS) \$(TARGET_ARCH)
# COMPILE.cxx = \$(CXX) \$(CXXFLAGS) \$(CPPFLAGS) \$(TARGET_ARCH) -c
# STANDARD: COMPILE.cc = \$(CXX) \$(CXXFLAGS) \$(CPPFLAGS) \$(TARGET_ARCH) -c
COMPILE_FAST.cc = \$(CXX) \$(CXXFLAGS_FAST) \$(CPPFLAGS) \$(TARGET_ARCH) -c
COMPILE.c = \$(CC) \$(CXXFLAGS) \$(CPPFLAGS) \$(TARGET_ARCH) -c
YACC = yacc
YFLAGS = -d
LEX = lex
TESTS = test*

################################################################

all:: \$(PROGRAM) \$(LIBRARY)
library:: \$(LIBRARY)
program:: \$(PROGRAM)
fast:: \$(PROGRAM).fast

################################################################
${progline}\$(PROGRAM): \$(OBJS) \$(LOCAL_LIBRARIES) 
${progline}	\$(CXX) -o \$@ \$(OBJS) \$(LDOPTIONS) \$(LOCAL_LIBRARIES) \$(LDLIBS)  \$(EXTRA_LOAD_FLAGS)

${progline}\$(PROGRAM).fast: \$(OBJS_FAST) \$(LOCAL_LIBRARIES) 
${progline}	\$(CXX) -o \$@ \$(OBJS_FAST) \$(LDOPTIONS) \$(LOCAL_LIBRARIES) \$(LDLIBS)  \$(EXTRA_LOAD_FLAGS)


#
# The approach assumes that the module with main just will never bother
# to be linked in, because main will already be defined.  You could set up
# your modules in ways where this wont work...
#
${libline}\$(LIBRARY): \$(OBJS)
${libline}	\$(RM) \$@
${libline}	ar cr \$@ \$(OBJS)
${libline}	ranlib \$@


clean::
	\$(RM) *~ core print.ps Makefile.bak y.output y.tab.h 
	\$(RM) \$(PROGRAM) \$(OBJS)
	\$(RM) \$(PROGRAM).fast \$(OBJS_FAST)
	# total hack: we shouldn't be hard coding these!
	\$(RM) _generated_*.cc yacc_out* lex_out*

run: \$(PROGRAM)
	\$(PROGRAM)

_END

cat > /dev/null << _END
print.ps: \$(SRCS)
	sh -c '> print.ps; for x in \$(SRCS) Makefile ; do pstext --title "$x" --scale 0.80 --font Courier-Bold >> print.ps < \$\$x; done'

wc::
	wc \$(SRCS)

doc:: docs
docs::
	sh make_doc.sh
   
pubdocs:: docs
	sh make_pubdoc.sh

.unit-tests-passed: \$(PROGRAM)
	./\$(PROGRAM) --unit-test '' | grep FAILED || touch \$@

tests:: test
test:: .unit-tests-passed


y.tab.h: parser.o

parser.o: parser.y
	\$(YACC) -d -v \$< 
	mv -f y.tab.c _generated_parser.c
	gcc -c -g _generated_parser.c -o \$@

lexer.o: lexer.l y.tab.h
	\$(LEX) -t lexer.l > _generated_lexer.c
	gcc -g -c _generated_lexer.c -o lexer.o

parser.fast.o: parser.y
	\$(YACC) -d -v \$< 
	mv -f y.tab.c _generated_parser.fast.c
	g++ \$(CXXFLAGS_FAST) -c _generated_parser.fast.c -o \$@

lexer.fast.o: lexer.l y.tab.h
	\$(LEX) -t lexer.l > _generated_lexer.fast.c
	gcc \$(CXXFLAGS_FAST) -c _generated_lexer.fast.c -o lexer.fast.o




# Something (g++ linking?) needs this!
#VIRTUAL_ROOT=/ 

# Sun Make wants this, I think
#
#.cxx:
#	\$(LINK.cxx) -o \$@@ $< \$(LDLIBS)
#.cxx.o:
#	\$(COMPILE.cxx) \$(OUTPUT_OPTION) $<
#


# GNU Make wants this, I think

#  [ commented out until I see it wants it back again... ]
#%.cxx:
#
#%: %.cxx
#	\$(LINK.cxx) $^ \$(LOADLIBES) \$(LDLIBS) -o \$@@
#%.o: %.cxx
#	\$(COMPILE.cxx) $< \$(OUTPUT_OPTION)
#
#

#%.lex.c: %.l
#	\$(LEX) $< -o \$@@
#%.tab.c: %.y
#	\$(YACC) -b % \$<
#%.tab.h: %.y
#	\$(YACC) -b % -d \$<

#%.cc: %.y
#	\$(YACC) -d -v \$< 
#	mv -f y.tab.c \$@

# done explicitely below

#
#%.cc: %.l
#	\$(LEX) -o\$@ \$<

%.fast.o: %.cc
	\$(COMPILE_FAST.cc) \$< \$(OUTPUT_OPTION)



_END

cat $yacclines >> $mf
cat $lexlines >> $mf
rm -f $yacclines $lexlines

echo '' >> $mf
echo "# The rest is dependencies, but we don't use makedepend." >> $mf
echo '' >> $mf
cat $dep >> $mf
sed -e '/\.o: /s//fast.o: /' < $dep >> $mf

exit 0