#!/bin/sh
# opp_makemake
#
#  Creates a makefile for a given OMNeT++/OMNEST model.
#  Assumes that .ned, .msg, .cc and .h files are in one directory.
#  The name of the program defaults to the name of the directory ('myproject').
#
#  Original script (jar_mkmk) written by Jan Heijmans et al.
#
#  --VA
#

#
# process command line args
#
args="$*"

MAKEDEPEND="opp_makedep -Y --objdirtree"

userif=TKENV
outfile=`pwd | sed "s/\/$//;s/.*\///"`
recursive=n
type="exe"
includes=""
libpath=""
libs=""
linkdirs=""
xobjs=""
fragments=""
makefilein=n
doxyconf="doxy.cfg"
cfgfile=""
basedir=""
generatecfgfile=""
linkwithobjects=n
ignorened=n
force=n
tstamp=y
tstampdirs=""
exceptdirs=""


# save a temporary Perl script, for conversion of absolute paths to relative
abs2rel=/tmp/abs2rel.$$.$RANDOM
(umask 077 && cat >$abs2rel <<EOT

use Cwd;

\$abs = shift @ARGV;
\$base = shift @ARGV;
\$cur = shift @ARGV;
print abs2rel(\$abs, \$base, \$cur);

sub abs2rel(\$;\$;\$;)
{
    my(\$abs, \$base, \$cur) = @_;

    if (\$base eq '') {
        return \$abs;
    }
    if (\$cur eq '') {
        \$cur = cwd;
    }

    # some normalization
    \$abs  =~ s|/./|/|g;
    \$cur  =~ s|/./|/|g;
    \$base =~ s|/./|/|g;

    \$abs  =~ s|//+|/|g;
    \$cur  =~ s|//+|/|g;
    \$base =~ s|//+|/|g;

    \$cur  =~ s|/*\$|/|;
    \$base =~ s|/*\$|/|;

    if (!(\$abs =~ /^\$base/ && \$cur =~ /^\$base/)) {
        return \$abs;
    }

    while (1)
    {
       # keep cutting off common prefixes until we can
       \$abs =~ m|^(.*?/)|;
       my \$prefix = \$1;
       last if (\$prefix eq '');
       if (\$cur =~ /^\$prefix/) {
           \$abs =~ s/^\$prefix//;
           \$cur =~ s/^\$prefix//;
       } else {
           last;
       }
    }


    # assemble relative path: change every directory name in \$cur to "..",
    # then add \$abs to it.
    \$cur =~ s|[^/]+|..|g;
    my \$rel = \$cur.\$abs;

    return \$rel;
}

EOT
)

while [ ! $# = 0 ] ; do
    case "$1" in

        -h | --help)
        cat <<EOT
opp_makemake: generate OMNeT++/OMNEST Makefile based on contents of current
directory

opp_makemake [-h] [-f] [-m] [-o make-target] [-r] [-n] [-s] [-u userinterface]
        [-c configfile] [-p] [-w] [-x] [-Idir] [-Llibrary-path] [-llibrary]
        [-i makefile-fragment-file] [directories, library and object files]...
    -h, --help            This help text
    -f, --force           Force overwriting existing Makefile (or config file)
    -N, --ignore-ned      Leave out .ned files from the makefile. Useful when
                          you want to load all .ned files dynamically (see
                          preload-ned-files omnetpp.ini option).
    -m, --makefile-in     Create Makefile.in instead of Makefile
    -c cfgfile, --configfile cfgfile
                          Omit variable definitions from generated Makefile,
                          and let the Makefile include the given config file
                          instead (see --genconfig option).
    --genconfig cfgfile   Creates a config file for use with the -c option.
                          Does not create a Makefile, so all other options
                          except -f are ignored. Recommended file name is
                          omnetppconfig.
    -o filename, --outputfile filename
                          Name of simulation executable or library
    -r, --recurse         Call make recursively in all subdirectories. If you
                          need to maintain a specific order, declare dependen-
                          cies in the makefrag file.
    -X directory, -Xdirectory, --except directory
                          With -r (recurse) option: ignore the given directory
    -b directory, --basedir directory
                          Project base (root) directory; all absolute paths
                          (-I, -L, object file names, etc.) which point into
                          this directory will be converted to relative, to
                          ease compiling the project in a different directory.
    -n, --nolink          Produce object files but do not create executable or
                          library. Useful for models with parts in several dirs.
    -s, --make-so         Make shared library. Useful if you want to load the
                          model dynamically (Cmdenv/Tkenv -l so-file switch).
    -w, --withobjects     Link with all object files found in -I directories,
                          or include them if library is created. Ignored when
                          -n option is present. Dependencies between directo-
                          ries have be handled in high Makefiles (see -r
                          option).
    -x, --notstamp        Do not require a .tstamp file to be present in the
                          link directories and (if -w option is present)
                          -I directories after this option.
    -u name, --userinterface name
                          Use Cmdenv or Tkenv. Defaults to Tkenv.
    -Idir                 Additional NED and C++ include directory
    -Llibrary-path        Additional library path
    -llibrary             Additional library to link against
    -i filename, --includefragment filename
                          Append file to near end of Makefile. The file makefrag
                          is appended implicitly if no -i options are given.
    directory             Link with object files in that directory. Dependencies
                          between directories have to be added manually. See also
                          -w option.
    library or object     Link with that file

With the -n and -s options, -u, -p and -l have no effect. makefrag (and the
-i option) is useful when a source file (.ned, .msg or .cc) is to be generated
from other files, or when you want to add extra dependencies that opp_makemake
could not figure out.
EOT
        exit
        ;;

        -f | --force)
        force=y
        ;;

        -c | --configfile)
        shift
        cfgfile=`perl $abs2rel $1 $basedir`
        ;;

        --genconfig)
        shift
        generatecfgfile=`perl $abs2rel $1 $basedir`
        ;;

        -o | --outputfile)
        shift
        outfile=`perl $abs2rel $1 $basedir`
        ;;

        -N | --ignore-ned)
        ignorened=y
        ;;

        -r | --recurse)
        recursive=y
        ;;

        -X | --except)
        shift
        exceptdirs="$exceptdirs $1"
        ;;

        -X*)
        dir=`echo $1 | sed 's/^-X//'`
        exceptdirs="$exceptdirs $dir"
        ;;

        -b | --basedir)
        shift
        basedir=$1
        ;;

        -n | --nolink)
        type="o"
        ;;

        -s | --make-so)
        type="so"
        ;;

        -w | --withobjects)
        linkwithobjects=y
        ;;

        -x | --notstamp)
        tstamp=n
        ;;

        -m | --makefile-in)
        makefilein=y
        ;;

        -u | --userinterface)
        shift
        userif=`echo $1 | tr '[a-z]' '[A-Z]'`
        case "$userif" in
            CMDENV | TKENV)
            ;;
            *)
            echo "opp_makemake: -u: valid user interface names are Cmdenv or Tkenv" 1>&2
            exit 1
            ;;
        esac
        ;;

        -i | --includefragment)
        shift
        frag=`perl $abs2rel $1 $basedir`
        fragments="$fragments $frag"
        ;;

        -I*)
        dir=`echo $1 | sed 's/^-I//'`
        dir=`perl $abs2rel $dir $basedir`
        includes="$includes -I$dir"
        if [ $tstamp = "y" -a $dir != "." ]; then
            tstampdirs="$tstampdirs $dir"
        fi
        ;;

        -L*)
        dir=`echo $1 | sed 's/^-L//'`
        dir=`perl $abs2rel $dir $basedir`
        libpath="$libpath -L$dir"
        ;;

        -l*)
        libs="$libs $1"
        ;;

        *)
        if [ -d $1 ]; then
            dir=`perl $abs2rel $1 $basedir`
            linkdirs="$linkdirs $dir"
            if [ $tstamp = "y" ]; then
                tstampdirs="$tstampdirs $dir"
            fi
        else
            if [ ! -f $1 ]; then
                echo "opp_makemake: $1 is neither an existing file/dir nor a valid option" 1>&2
                exit 1
            fi
            file=`perl $abs2rel $1 $basedir`
            xobjs="$xobjs $file"
        fi
        ;;

    esac
    shift
done

rm $abs2rel

# handle generating a config file first
if [ "$generatecfgfile" != "" ]; then
    if [ -f "$generatecfgfile" ]; then
        if [ ! "$force" = "y" ]; then
            echo "opp_makemake: use -f to force overwriting existing $generatecfgfile" 1>&2
            exit 1
        fi
    fi
    cat >"$generatecfgfile" <<EOT
#
# OMNeT++ simulation model configuration file
# (included in makefiles generated with -c option)
#
# Generated by opp_makemake --genconfig $generatecfgfile
#

#
# Local configuration
#
NEDC=/home/andras/omnetpp-3.1/bin/nedtool
MSGC=opp_msgc
CXX=g++
CC=gcc
AR=ar cr
SHLIB_LD=g++ -shared -fPIC
MAKEDEPEND=opp_makedep -Y --objdirtree

CFLAGS=-O2 -DNDEBUG=1  -fPIC -DWITH_PARSIM -DWITH_NETBUILDER
NEDCFLAGS=-Wno-unused
LDFLAGS=
EXE_SUFFIX=

WITH_PARSIM=yes
WITH_NETBUILDER=yes

OMNETPP_INCL_DIR=/home/andras/omnetpp-3.1/include
OMNETPP_LIB_DIR=/home/andras/omnetpp-3.1/lib

TK_LIBS=-L/usr/X11R6/lib -lX11 -ltk8.4 -ltcl8.4
MPI_LIBS=
XML_LIBS=-lxml2
SYS_LIBS=-ldl -lstdc++ 
SYS_LIBS_PURE=-ldl -lsocket -lnsl -lm \$(shell \$(CXX) -print-file-name=libstdc++.a)

#
# Definitions for models
#

# User interface libs
CMDENV_LIBS=-lenvir -lcmdenv
TKENV_LIBS=-lenvir -ltkenv \$(TK_LIBS)

# Simulation kernel
KERNEL_LIBS=-lsim_std

ifeq (\$(WITH_NETBUILDER),yes)
KERNEL_LIBS += -lnedxml \$(XML_LIBS)
endif

ifeq (\$(WITH_PARSIM),yes)
KERNEL_LIBS += \$(MPI_LIBS)
endif

# Simulation kernel and user interface libraries
OMNETPP_LIBS=-L\$(OMNETPP_LIB_DIR) \$(USERIF_LIBS) \$(KERNEL_LIBS) \$(SYS_LIBS)

COPTS=\$(CFLAGS) \$(INCLUDE_PATH) -I\$(OMNETPP_INCL_DIR)
NEDCOPTS=\$(CFLAGS) \$(NEDCFLAGS) \$(INCLUDE_PATH) -I\$(OMNETPP_INCL_DIR)
EOT
    echo "$generatecfgfile created."
    exit
fi

# from here on, makefile creation
if [ $makefilein = "y" ]; then
    outputfile="Makefile.in"
else
    outputfile="Makefile"
fi

if [ -f $outputfile ]; then
    if [ ! "$force" = "y" ]; then
        echo "opp_makemake: use -f to force overwriting existing $outputfile" 1>&2
        exit 1
    fi
fi

subdirs=
if [ $recursive = "y" ]; then
    for i in *; do
        if [ -d "$i" -a "$i" != "CVS" -a "$i" != "backups" -a "$i" != "." -a "$i" != ".." ]; then
            if echo $exceptdirs | grep "$i" >/dev/null; then :; else
                subdirs="$subdirs $i"
            fi
        fi
    done
fi

extdirobjs=
if [ "$linkwithobjects" = "y" ]; then
    for i in $includes; do
        dir=`echo $i | sed 's/^-I//'`
        if [ "$dir" != "." ]; then
            extdirobjs="$extdirobjs $dir/*.o"
        fi
    done
fi

for i in $linkdirs; do
    extdirobjs="$extdirobjs $i/*.o"
done

extdirtstamps=
for i in $tstampdirs; do
    extdirtstamps="$extdirtstamps $i/.tstamp"
done

if [ "$ignorened" = "y" ]; then
    objs=`echo *.msg *.cc`
else
    objs=`echo *.ned *.msg *.cc`
fi
objs=`echo $objs | sed 's/\*[^ ]*//g; s/[^ ]*_n\.cc//g; s/[^ ]*_m\.cc//g; s/\.ned/_n.o/g; s/\.msg/_m.o/g; s/\.cc/.o/g'`

generatedheaders=`echo *.msg | sed 's/\*[^ ]*//g; s/\.msg/_m.h/g'`

#
# now the Makefile creation
#
(

suffix=''
if [ "$type" = "so" ]; then
    suffix='.so'
fi
if [ "$type" = "o" ]; then
    suffix=''
fi

case "$userif" in
    CMDENV)
    c_cmd=''; c_tk='# '; ;;
    TKENV)
    c_cmd='# '; c_tk=''; ;;
esac

wildcard_extdirobjs=
if [ "$extdirobjs" != "" ]; then
    wildcard_extdirobjs="\$(wildcard $extdirobjs)"
fi

cat <<EOT
#
#  Makefile for $outfile
#
#  ** This file was automatically generated by the command:
#  opp_makemake $args
#

# Name of target to be created (-o option)
TARGET = $outfile$suffix

# User interface (uncomment one) (-u option)
${c_cmd}USERIF_LIBS=\$(CMDENV_LIBS)
${c_tk}USERIF_LIBS=\$(TKENV_LIBS)

# .ned or .h include paths with -I
INCLUDE_PATH=$includes

# misc additional object and library files to link
EXTRA_OBJS=$xobjs

# object files from other directories to link with (wildcard needed to prevent "no such file *.o" errors)
EXT_DIR_OBJS=$wildcard_extdirobjs

# time stamps of other directories (used as dependency)
EXT_DIR_TSTAMPS=$extdirtstamps

# Additional libraries (-L option -l option)
LIBS=$libpath$libs

#------------------------------------------------------------------------------
EOT

if [ "$cfgfile" != "" ]; then

  echo "# Import generic settings from $cfgfile"
  echo "include $cfgfile"
  echo

elif [ $makefilein = "n" ]; then
# Makefile
cat <<EOT

# Generic definitions. To avoid redundancies across Makefiles, you can
# import them from a common file -- see opp_makemake -c flag.
NEDC=/home/andras/omnetpp-3.1/bin/nedtool
MSGC=opp_msgc
CXX=g++
CC=gcc
AR=ar cr
SHLIB_LD=g++ -shared -fPIC
MAKEDEPEND=opp_makedep -Y --objdirtree

CFLAGS=-O2 -DNDEBUG=1  -fPIC -DWITH_PARSIM -DWITH_NETBUILDER
NEDCFLAGS=-Wno-unused
LDFLAGS=
EXE_SUFFIX=

WITH_PARSIM=yes
WITH_NETBUILDER=yes

OMNETPP_INCL_DIR=/home/andras/omnetpp-3.1/include
OMNETPP_LIB_DIR=/home/andras/omnetpp-3.1/lib

TK_LIBS=-L/usr/X11R6/lib -lX11 -ltk8.4 -ltcl8.4
MPI_LIBS=
XML_LIBS=-lxml2
SYS_LIBS=-ldl -lstdc++ 
SYS_LIBS_PURE=-ldl -lsocket -lnsl -lm \$(shell \$(CXX) -print-file-name=libstdc++.a)
EOT

else

# Makefile.in
null=
cat <<EOT

NEDC=@${null}NEDC@
MSGC=@${null}MSGC@
CXX=@${null}CXX@
CC=@${null}CC@
AR=@${null}AR@
SHLIB_LD=@${null}SHLIB_LD@
MAKEDEPEND=@${null}MAKEDEPEND@

CFLAGS=@${null}CFLAGS@
NEDCFLAGS=@${null}NEDCFLAGS@
LDFLAGS=@${null}LDFLAGS@
EXE_SUFFIX=@${null}EXE_SUFFIX@

WITH_PARSIM=@${null}WITH_PARSIM@
WITH_NETBUILDER=@${null}WITH_NETBUILDER@

OMNETPP_INCL_DIR=@${null}OMNETPP_INCL_DIR@
OMNETPP_LIB_DIR=@${null}OMNETPP_LIB_DIR@

TK_LIBS=@${null}TK_LIBS@
MPI_LIBS=@${null}MPI_LIBS@
XML_LIBS=@${null}XML_LIBS@
SYS_LIBS=@${null}SYS_LIBS@
SYS_LIBS_PURE=-lm \$(shell \$(CXX) -print-file-name=libstdc++.a)
EOT

fi

if [ "$cfgfile" = "" ]; then
cat <<EOT

# User interface libs
CMDENV_LIBS=-lenvir -lcmdenv
TKENV_LIBS=-lenvir -ltkenv \$(TK_LIBS)

# Simulation kernel
KERNEL_LIBS=-lsim_std

ifeq (\$(WITH_NETBUILDER),yes)
KERNEL_LIBS += -lnedxml \$(XML_LIBS)
endif

ifeq (\$(WITH_PARSIM),yes)
KERNEL_LIBS += \$(MPI_LIBS)
endif

# Simulation kernel and user interface libraries
OMNETPP_LIBS=-L\$(OMNETPP_LIB_DIR) \$(USERIF_LIBS) \$(KERNEL_LIBS) \$(SYS_LIBS)

COPTS=\$(CFLAGS) \$(INCLUDE_PATH) -I\$(OMNETPP_INCL_DIR)
NEDCOPTS=\$(CFLAGS) \$(NEDCFLAGS) \$(INCLUDE_PATH) -I\$(OMNETPP_INCL_DIR)

EOT
fi

echo "#------------------------------------------------------------------------------"
echo

echo '# subdirectories to recurse into'
echo "SUBDIRS= $subdirs"
echo
echo '# object files in this directory'
echo "OBJS= $objs"
echo

echo '# header files generated (from msg files)'
echo "GENERATEDHEADERS= $generatedheaders"
echo

# include external Makefile fragments here, so that they can override
# the default target if they want
if [ ! "$fragments" = "" ]; then
    echo "#------------------------------------------------------------------------------"
    echo "# contents of file(s) $fragments:"
    cat $fragments
    echo
else
    if [ -f makefrag ]; then
        echo "#------------------------------------------------------------------------------"
        echo "# contents of file makefrag:"
        cat makefrag
        echo
    fi
fi

echo "#------------------------------------------------------------------------------"
echo

# note: if "subdirs" were always among the target dependencies, it would cause
# the executable to re-link every time (which is not good).
if [ $recursive = "y" ]; then
    subdirs_target='subdirs'
else
    subdirs_target=
fi

case "$type" in
exe)
    echo '$(TARGET): $(OBJS) $(EXTRA_OBJS) $(EXT_DIR_TSTAMPS)' $subdirs_target 'Makefile'
    echo '	$(CXX) $(LDFLAGS) $(OBJS) $(EXTRA_OBJS) $(EXT_DIR_OBJS) $(LIBS) $(OMNETPP_LIBS) -o $(TARGET)'
    echo '	echo>.tstamp'
    ;;
so)
    echo '$(TARGET): $(OBJS) $(EXTRA_OBJS) $(EXT_DIR_TSTAMPS)' $subdirs_target 'Makefile'
    echo '	$(SHLIB_LD) -o $(TARGET) $(OBJS) $(EXTRA_OBJS) $(EXT_DIR_OBJS)'
    echo '	echo>.tstamp'
    ;;
o)
    echo '$(TARGET): $(OBJS)' $subdirs_target 'Makefile .tstamp'
    echo '	@# do nothing'
    echo
    echo '.tstamp: $(OBJS)'
    echo '	echo>.tstamp'
    ;;
esac
echo

if [ "$objs" != "" ]; then
    echo '$(OBJS) : $(GENERATEDHEADERS)'
    echo
fi

echo
echo 'purify: $(OBJS) $(EXTRA_OBJS) $(EXT_DIR_TSTAMPS)' $subdirs_target 'Makefile'
echo '	purify $(CXX) $(LDFLAGS) $(OBJS) $(EXTRA_OBJS) $(EXT_DIR_OBJS) $(LIBS) -L$(OMNETPP_LIB_DIR) $(KERNEL_LIBS) $(USERIF_LIBS) $(SYS_LIBS_PURE) -o $(TARGET).pure'
echo

if [ "$extdirtstamps" != "" ]; then
    echo '$(EXT_DIR_TSTAMPS):'
    echo '	@echo'
    echo '	@echo Error: $@ does not exist.'
    echo '	@echo This means that at least the above dependency directory has not been built.'
    echo '	@echo Maybe you need to do a top-level make?'
    echo '	@echo'
    echo '	@exit 1'
    echo
fi

# rules for recursive build
echo '.PHONY: subdirs $(SUBDIRS)'
echo
echo 'subdirs: $(SUBDIRS)'
echo

for i in $subdirs; do
     echo "$i:"
     echo "	cd $i && \$(MAKE)"
     echo
done

# rules for ned files
if [ "$ignorened" = "n" ]; then
    for i in *.ned; do
        if [ "$i" = "*.ned" ]; then break; fi;

        obj=`echo $i | sed 's/\.ned/_n.o/g'`
        c=`echo $i | sed 's/\.ned/_n.cc/g'`

        echo "$obj: $c"
        echo '	$(CXX) -c $(NEDCOPTS) '$c
        echo

        echo "$c: $i"
        echo '	$(NEDC) $(INCLUDE_PATH) '$i
        echo
    done
fi

# rules for msg files
for i in *.msg; do
        if [ "$i" = "*.msg" ]; then break; fi;

        obj=`echo $i | sed 's/\.msg/_m.o/g'`
        c=`echo $i | sed 's/\.msg/_m.cc/g'`
        h=`echo $i | sed 's/\.msg/_m.h/g'`

        echo "$obj: $c"
        echo '	$(CXX) -c $(NEDCOPTS) '$c
        echo

        echo "$c $h: $i"
        echo '	$(MSGC) $(INCLUDE_PATH) '$i
        echo
done

# rules for normal (not generated) C++ files
for i in `echo *.cc | sed 's/[^ ]*_n\.cc//g; s/[^ ]*_m\.cc//g'`
do
        if [ "$i" = "*.cc" ]; then break; fi;

        obj=`echo $i | sed 's/\.cc/.o/g'`
        echo "$obj: $i"
        echo '	$(CXX) -c $(COPTS) '$i
        echo
done

# prevent accumulating of "-f" options during subsequent remakes
# also kill "-m" options
args1=`echo " $args " | sed 's/	/ /g;s/ -f / /g;s/ -m / /g'`

# rule for clean and other recursive targets
# also rule hints for documentation targets
cat <<EOT

#doc: neddoc doxy

#neddoc:
#	opp_neddoc -a

#doxy: $doxyconf
#	doxygen $doxyconf

generateheaders: \$(GENERATEDHEADERS)
	for i in \$(SUBDIRS); do (cd \$\$i && \$(MAKE) generateheaders) || exit 1; done

clean:
EOT
if [ $type = "exe" ]; then
    echo "	rm -f \$(TARGET)\$(EXE_SUFFIX)"
fi
cat <<EOT
	rm -f *.o *_n.cc *_n.h *_m.cc *_m.h .tstamp
	rm -f *.vec *.sca
	for i in \$(SUBDIRS); do (cd \$\$i && \$(MAKE) clean); done

depend:
	\$(MAKEDEPEND) \$(INCLUDE_PATH) -- *.cc
	# \$(MAKEDEPEND) \$(INCLUDE_PATH) -fMakefile.in -- *.cc
	for i in \$(SUBDIRS); do (cd \$\$i && \$(MAKE) depend) || exit 1; done

makefiles:
	# recreate Makefile
	opp_makemake -f $args1
	for i in \$(SUBDIRS); do (cd \$\$i && \$(MAKE) makefiles) || exit 1; done

makefile-ins:
	# recreate Makefile.in
	opp_makemake -f -m $args1
	for i in \$(SUBDIRS); do (cd \$\$i && \$(MAKE) makefile-ins) || exit 1; done

# "re-makemake" and "re-makemake-m" are deprecated, historic names of the above two targets
re-makemake: makefiles
re-makemake-m: makefile-ins


# DO NOT DELETE THIS LINE -- make depend depends on it.

EOT

) > $outputfile

echo "$outputfile created, adding dependencies..."

if ! $MAKEDEPEND $includes -f$outputfile -- *.cc; then
    echo "Error: command '$MAKEDEPEND $includes -f$outputfile -- *.cc' returned nonzero exit code"
else
    echo "Done."
fi


