#!/usr/bin/perl
#############################################################################
#
# MakeMake.pl -- makefiles creating utility
#
# (c) Vladi Belperchinov-Shabanski  (c) Ivaylo Baylov    1998
#     <cade@biscom.net>                 <ivo@datamax.bg>
#
# DISTRIBUTED `AS IS' WITHOUT ANY KIND OF WARRANTY OR ELSE.
# YOU MAY USE, MODIFY AND DISTRIBUTE THIS UTILITY AS LONG AS
# THE ORIGINAL CREDITS ARE KEPT INTACT!
# ( AND YOU CREDIT YOURSELF FOR THE APROPRIATE MODIFICATIONS ONLY )
#
# CREDITS ANS MODIFICATIONS:
#
# dec1998: cade@biscom.net, ivo@datamax.bg
#          first version -- though there were a number of utilities like
#                           this I still haven't found what I'm looking for...
#                           the closest approach is `tmake' of Troll Tech
#                           used for `Qt', but is far too complex...
#
# oct1999: cade@biscom.net
#          added multi-target feature
#
# aug2000: cade@biscom.net
#          general cleanup, target clean uses `rm -rf' instead of `rmdir'
#          added targets `rebuild' and `link' (does `relink' actually)
#          globbing replaced with the use of `ls'
#
# dec2000: cade@biscom.net
#          added modules (subdir targets) support:
#          $MODULES = "module1 module2 ...";
#          now target name is required and not set to `a.out' by default   
#
#############################################################################
#
# GENERAL USAGE AND TIPS:
# input file is `mm.conf' or/and `make.make' or given file as 1st arg
# output is printed to the stdout
#
# usage: makemake.pl > makefile
#
# ---sample-mm.conf-is:---
# # set compiler executable
# $CC      = "gcc";
# # set linker executable
# $LD      = "gcc";
# # set compiler flags executable
# $CCFLAGS = "-I../vslib -I/usr/include/ncurses -O2";
# # set linker flags executable
# $LDFLAGS = "-L../vslib -lvslib -lncurses";
# # set target name ( NOTE if name ends with `.a' target will be library! )
# $TARGET  = "vfu";
# # set source files
# $SRC     = "*.cpp";
# # set header files ( actually not used! )
# $HDR     = "*.h";
# # set modules (subdirectory targets)
# $MODULES = "vslib vfu ftparc";
# ---sample-ends-here---
#
# you can give more than one target (you have to mark targets with numbers
# instead of `x' below ):
#
# ---sample-mm.conf-is:---
# $CC[x]      = "gcc";
# $LD[x]      = "gcc";
# $CCFLAGS[x] = "-I../vslib -I/usr/include/ncurses -O2";
# $LDFLAGS[x] = "-L../vslib -lvslib -lncurses";
# $TARGET[x]  = "vfu";
# $SRC[x]     = "*.cpp";
# $HDR[x]     = "*.h";
# # note: modules are global -- you cannot have $MODULES[x]
# ---sample-ends-here---
#
# `x' is a number from 0 to 255
# WARNING: you cannot skip target numbers, you should use it continuously!
# All fields (without TARGET,SRC) can be skipped and in this case default ones will be
# accepted (those without `[x]')
#
# NOTE: first sample above is equal to second one if `x' is set to `0' (zero)
# NOTE: since mm.conf (make.make) is actually a perl script you can do whatever you
#       want in there!
#
# ...more still to come...
#
# FOR ANY PROBLEMS, REMARKS, NOTES -- CONTACT AUTHORS FREELY!
#
#############################################################################

if ( $ARGV[0] )
  {
  do $ARGV[0];
  }
else
  {
  if ( !(( -e "mm.conf" ) || ( -e "make.make" )) )
    { die "makemake.pl: cannot find neither mm.conf nor make.make files\n" };

  do 'mm.conf';
  do 'make.make';
  }

  print "### MAKEMAKE STARTS HERE #########################################\n" .
        "#\n" .
        "# Created by makemake.pl on " . localtime(time()) . "\n" .
        "#\n" .
        "##################################################################\n";

  # put default values
  $CC = "gcc"                   unless $CC;
  $LD = "gcc"                   unless $LD;
  $AR = "ar rvs"                unless $AR;
  $HDR = "*.h *.hpp"            unless $HDR;
  $SRC = "*.c *.cpp *.cc *.cxx" unless $SRC;
  
  # $TARGET = "a.out"             unless $TARGET;
 
  # if no target defined in the TARGET array take defaults
 
  $TARGET[0] = $TARGET if $TARGET && $#TARGET == -1;

  print "\n### GLOBAL TARGETS ###############################################\n";

  print "\ndefault: all\n\n";
  print "\nre: rebuild\n\n";

  $_all = "all: ";
  $_clean = "clean: ";
  $_rebuild = "rebuild: ";
  $_link = "link: ";

  for( @TARGET )
    {
    $_all .= "$_ ";
    $_clean .= "clean-$_ ";
    $_rebuild .= "rebuild-$_ ";
    $_link .= "link-$_ ";
    }
  if ( $MODULES )
    {
    $_all .= "modules";
    $_clean .= "clean-modules ";
    $_rebuild .= "rebuild-modules ";
    $_link .= "link-modules ";
    }
  
  print "$_all\n\n$_clean\n\n$_rebuild\n\n$_link\n\n";

  $z = 0;
  while( $z <= $#TARGET )
    { make_target( $z++ ); } # output all targets...

  
  if ( $MODULES )
    {
    print "### MODULES #####################################################\n\n";
    make_module( "" );
    make_module( "clean" );
    make_module( "rebuild" );
    make_module( "link" );
    }
  
print "\n### END ##########################################################\n";


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

sub make_target
{
  my $n = shift;

  print "### TARGET: $TARGET[$n] #########################################\n\n";

  #=======================================================================
  # target init and setups

  # take local values just to be handy
  $_CC      = $CC[$n];
  $_LD      = $LD[$n];
  $_AR      = $AR[$n];
  $_CFLAGS  = $CFLAGS[$n];
  $_CCFLAGS = $CCFLAGS[$n];
  $_LDFLAGS = $LDFLAGS[$n];
  $_ARFLAGS = $ARFLAGS[$n];
  $_TARGET  = $TARGET[$n];
  $_SRC     = $SRC[$n];
  $_HDR     = $HDR[$n];

  $_OBJDIR = ".OBJ.$n.$_TARGET";

  # for all undefined values -- take default ones
  $_CC      = $CC      unless $_CC      ;
  $_LD      = $LD      unless $_LD      ;
  $_AR      = $AR      unless $_AR      ;
  $_CFLAGS  = $CFLAGS  unless $_CFLAGS  ;
  $_CCFLAGS = $CCFLAGS unless $_CCFLAGS ;
  $_LDFLAGS = $LDFLAGS unless $_LDFLAGS ;
  $_ARFLAGS = $ARFLAGS unless $_ARFLAGS ;
  $_TARGET  = $TARGET  unless $_TARGET  ;
  $_SRC     = $SRC     unless $_SRC     ;
  $_HDR     = $HDR     unless $_HDR     ;

  #=======================================================================

  # now print main target variables
  print "CC_$n      = $_CC\n";
  print "LD_$n      = $_LD\n";
  print "AR_$n      = $_AR\n";
  print "CFLAGS_$n  = $_CFLAGS\n";
  print "CCFLAGS_$n = $_CCFLAGS\n";
  print "LDFLAGS_$n = $_LDFLAGS\n";
  print "ARFLAGS_$n = $_ARFLAGS\n";
  print "TARGET_$n  = $_TARGET\n";
  print "\n";
  print "# IN.SRC_$n = $_SRC\n";
  print "# IN.HDR_$n = $_HDR\n";

  my @_OBJ;
  my @_SRC;

  # for( glob($_SRC) )
  # or
  for( split( /[\s\n]+/, `ls -1 $_SRC 2> /dev/null` ) )
    {
    push @_SRC,$_;
    /(.*)\.[^\.]+/;
    push @_OBJ,"$_OBJDIR/$1.o";
    }

  print "\n### SOURCES FOR TARGET $n: $_TARGET #################################\n\n";
  print "SRC_$n= \\\n";
  for( @_SRC )
    { print "     $_ \\\n"; }



  print "\n#### OBJECTS FOR TARGET $n: $_TARGET ################################\n\n";
  print "OBJ_$n= \\\n";
  for( @_OBJ )
    { print "     $_ \\\n"; }



  print "\n### TARGET DEFINITION FOR TARGET $n: $_TARGET #######################\n\n";

  print "$_OBJDIR: \n" .
        "\tmkdir -p $_OBJDIR\n\n";

  print "$_TARGET: $_OBJDIR \$(OBJ_$n)\n";
  if ($_TARGET =~ /\.a[ \t]*$/)
    {
    $target_link = "\t\$(AR_$n) \$(ARFLAGS_$n) \$(TARGET_$n) \$(OBJ_$n)\n\n";
    }
  else
    {
    $target_link = "\t\$(LD_$n) \$(OBJ_$n) \$(LDFLAGS_$n) -o \$(TARGET_$n)\n\n";
    }
  print $target_link;

  print "clean-$_TARGET: \n" .
        "\trm -f \$(TARGET_$n)\n" .
        "\trm -rf $_OBJDIR\n\n";

  print "rebuild-$_TARGET: clean-$_TARGET $_TARGET\n\n";
 
  print "link-$_TARGET: $_OBJDIR \$(OBJ_$n)\n" .
        "\trm -f $_TARGET\n" .
        $target_link;

  print "### TARGET OBJECTS FOR TARGET $n: $_TARGET ##########################\n\n";

  $c = 0;
  while( $c <= $#_OBJ )
    {
    $deps = file_deps( $_SRC[$c] );
    print "$_OBJ[$c]: $deps\n" .
          "\t\$(CC_$n) \$(CFLAGS_$n) \$(CCFLAGS_$n) -c $_SRC[$c] -o $_OBJ[$c]\n";
    $c++;
    }

  print "\n";
}

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

sub make_module
{
  my $target = shift;

  my @MODULES = split( /\s+/, $MODULES );
  my $modules_list = "";
  for( @MODULES )
    {
    $modules_list .= "\tmake -C $_ $target\n";
    }
  $target .= "-" if $target;
  print $target . "modules:\n$modules_list\n";  
}

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

sub file_deps
{
  my $fname = shift;
  $_ = `$CC -MM $fname 2> /dev/null`;
  s/^[^:]+://;
  s/[\n\r]$//;
  $_;
}

### EOF #######################################################################

