#! /usr/local/bin/perl

# Changes Makefile.in to work correctly with moc files. When called
# without parameters, automoc works recursively on all Makefile.in in
# and below the current subdirectory. When called with parameters,
# only these Makefile.in are changed.
# The Makefile.in should have an entry with the following syntax:
#
# {program}_METASOURCES = USE_AUTOMOC [{suffix}]
#
# Moc files will have a 'cpp' suffix unless overriden by the optional
# {suffix} parameter.
# Be careful not to use a suffix that isn't used by at least one
# of the main source files in {program}_SOURCES.
#
# 1998-07-23  Harri Porten  <porten@tu-harburg.de>
#       * small bugfixes for MOCSOURCES
# 1998-11-08  Alex Zepeda  <garbanzo@hooked.net>
#	* tweak it so it will run from a "common" directory.
# 1998-11-11  Harri Porten <porten@tu-harburg.de> & David Faure <faure@kde.org>
#       * bug fix for invocation with arguments
# 1998-11-16  Harri Porten <porten@kde.org>
#       * take care of "ar" object files, too.
#         (as proposed by Martin Vogt <mvogt@rhrk.uni-kl.de>)
# 1999-01-13  Harri Porten <porten@kde.org>
#       * implemented {suffix} parameter
# 1999-01-16  Stephan Kulow <coolo@kde.org>
#       * implemented support for included MOC files and command line 
#         parameters

use Cwd;
use File::Find;
use File::Basename;

# TRUE if in verbose mode.
$verbose = 0;

$direct_arguments = 0;

@arglist = @ARGV;

@input_files = ();

$errorflag = 0;

$printname = '';

$lastdirname = '';

@mocnames = ();

$callpath = "$0";

@header_suffixes = ("h", "H", "hh", "hxx");

$cxx_suffix = "";

while (@arglist) {
  if ($arglist[0] eq "--version")
    {
      print "\n\n";
      print "*********************************************\n";
      print "automoc - Software that makes even more sense\n";
      print "*********************************************\n\n";
      print "Welcome to the wonderful world of automoc!\n";
      print "This is really free software, unencumbered by the GPL.\n";
      print "You can do anything you like with it except sueing me.\n";
      print "Copyright 1998 Kalle Dalheimer <kalle\@kde.org>\n";
      print "Concept, design and unnecessary questions about Perl by Matthias Ettrich <ettrich\@kde.org>\n\n";
      print "Making it useful by Stephan Kulow <coolo\@kde.org> and\n";
      print "Harri Porten <porten\@kde.org>";

      exit 0;
    } 
  elsif ($arglist[0] eq '--verbose' || $arglist[0] eq '-v')
    {
      $verbose = 1;
    }
  elsif ($arglist[0] =~ /^-p(.+)$/ || $arglist[0] =~ /^--path=(.+)$/)
    {
      $callpath = "$1/" . basename($0);
    }
  elsif ($arglist[0] =~ /^--help$/ || $arglist[0] =~ /^-h$/)
    {
      print "Usage automoc [OPTION] ... [Makefile.in]...\n";
      print "\n";
      print "Patches Makefile.in generated from automake\n";
      print "\n";
      print "  -v, --verbose      verbosely list files processed\n";
      print "  -h, --help         print this help, then exit\n";
      print "  --version          print version number, then exit\n";
      print "  -p, --path=        use the path to automoc if the path\n";
      print "                         called from is not the one to be used\n";
      
      exit 0;
    }
  else 
    {
      push (@input_files, $arglist[0]);
      $direct_arguments = 1;
    }

  shift @arglist;
}

if ($direct_arguments == 0)
  {
    find( \&add_makefile, cwd() );
  }

$curdir = cwd();
while( @input_files )
  {
    $filename = $input_files[0];
    $errorflag = 0;
    undef @headersourcedirs;
    @headersourcedirs = ('$(srcdir)');
    process_makefile( $filename );
    chdir ( $curdir);
    shift @input_files;
  }

sub add_makefile()
  {
	if( $_ ne "Makefile.in" )
	  {
		return;
	  }
	else
	  {
	    push (@input_files, $File::Find::name );
	  }
  }


sub process_automoc_makefile
  {
    	# find the name of the program
	$search_use_automoc[0] =~ /^([^=]*)_METASOURCES/; 
	$programname = $1;

        $cxx_suffix = "cpp";

        # check whether a suffix is given
	if( $search_use_automoc[0] =~ /USE_AUTOMOC\s+([^\s]+)/ )
          {
              $cxx_suffix = $1;
          }

	$header_list = "";
	foreach $suffix (@header_suffixes) {
	  $header_list .= " *.$suffix";
	}
       	@mocable_files = `grep -l Q_OBJECT $header_list 2> /dev/null`;

	foreach $mocable_file (@mocable_files) {
	  chomp $mocable_file;
	  print STDERR "looking for header file $mocable_file\n" if $verbose;
	  $mocable_file =~ s/\.[^\.]*$//;      # remove the suffix (.h etc)
	  push @mocnames, $mocable_file;
	  push @mocsources, "$mocable_file\.moc\.$cxx_suffix";
	}

	$objectline = $programname . "_OBJECTS";

	LINE: while( <FILEIN> )
	  {
                if( /^#/ ) {
		     print FILEOUT $_;
                     next LINE;
                }
			
		if( /(.*_METASOURCES\s*=\s*)(USE_AUTOMOC)/ )
		  {
		    print FILEOUT "$1 ";
		    foreach $mocsource (@mocsources) {
		      print FILEOUT $mocsource . " ";
		    }
		    print FILEOUT "\n";
		  }
		elsif( /^$objectline/ ) # look for programs and libraries
		  {
			chomp $_;
                        $line = $_;
			$morelines = /\\$/ ? 1 : 0;
                        $line =~ s/\\$//; 

			print FILEOUT "$line ";
			foreach $mocname (@mocnames) {
			  if ( $objectline !~ /_la_OBJECTS$/ ) {
			    print FILEOUT $mocname . ".moc.o ";
			  }
			  else  {
			    print FILEOUT $mocname . ".moc.lo ";
			  }
			}
                        if($morelines) {
                          print FILEOUT "\\";
                        }
			print FILEOUT "\n";
		  }
		else {
		  process_generell_rules($_);
		}
	      }
	
	print FILEOUT "\n\n";
	foreach $file (@mocnames) {
	  foreach $header_suffix (@header_suffixes) {
	    if (-f $file . ".$header_suffix") {
	        print FILEOUT "$file.moc.$cxx_suffix: \$(srcdir)/$file.$header_suffix\n";
	        print FILEOUT "\t\$(MOC) \$(srcdir)/$file.$header_suffix -o $file.moc.$cxx_suffix\n\n";
	        last;
        }
	  }
	}

  }

sub process_generell_rules
  {
    if ( m+cd \$\(top_srcdir\) \&\& \$\(AUTOMAKE\)+ )
      {
	print FILEOUT $_;
	print FILEOUT "\tcd \$(top_srcdir) && perl $callpath $printname\n";
        return;
      }

    if (/^distclean:/) 
      {
	s/^distclean: //;
	print FILEOUT "distclean: distclean-metasources ";
      }

    if (/^[^=]*META_INCLUDES.*=(.*)/)
      {
	chomp $1;
	@list = split(' ', $1);
	for $dir (@list) {
	  $realdir = $dir;
	  $realdir =~ s#\$\(srcdir\)#.#;
	    if (! -d $realdir) {
	      print STDERR "$printname: warning $dir can't be found. Must be a relative path to \$(srcdir)\n";
	    } else {
	      if ($dir !~ /\$\(srcdir\)/) 
		{
		  $dir = "\$(srcdir)/" . $dir; 
		  print STDERR "addded $dir\n";
		}
	      push @headersourcedirs, $dir;
	    }
	}
      }
    
    print FILEOUT $_;
    
  }

sub process_moc_makefile() 
  {
    local ($saw_bk) = 0;

    LINE: while( <FILEIN> )
      {
        if ( /^#/ ) {
		print FILEOUT $_;
		next LINE;
	}
 
	$textline = $_;
	
	if ($textline =~ /^[^=]*METASOURCES.*/ || $saw_bk)
	  {
	    $saw_bk = /\\$/;
	    $line=$textline;
	    $line =~ s/\\$//;
	    $line =~ s/.*METASOURCES.*=[ ]*//;
	    @list = split(' ', $line);
	    foreach $mocname (@list) {
	      if ($mocname !~ /\.moc$/) {
		print STDERR "error: filename $lastdirname/$mocname doesn't end with .moc\n";
                $errorflag = 1;
	      } else {
		$mocname =~ s/\.moc$//;
		push(@mocnames, $mocname);
	      }
	    }

	  } else {
            $saw_bk = 0;
          }
	
	process_generell_rules($textline);
      }

    if (@mocnames) {
      print FILEOUT "\n\n";
    }

    foreach $mocfile (@mocnames) {
      
      print STDERR "Looking for moc file $mocfile.moc\n" if $verbose;
      
      $found = 0;
      
      $header_suffix = "";

      foreach $dir (@headersourcedirs) {
	
	$realdir = $dir;
	$realdir =~ s/\$\(srcdir\)/./;
	
	foreach $suffix (@header_suffixes) {

	  if (-f $realdir . '/' . $mocfile . ".$suffix") 
	    {
	      $header_suffix = $suffix;
	      $sourcedir = $dir;
	      $found = 1;
	      last;
	    }
	}
      }
      
      if ($found == 0) {
	print STDERR "error: no header file found for mocfile $lastdirname/$mocfile.moc\n";
        $errorflag = 1;
      }

      @sourcenames=`grep -l "^#include[ ]*.$mocfile\.moc." *.cpp *.cc *.cxx *.C 2> /dev/null`;

      if (@sourcenames == 1) {
	$sourcename = $sourcenames[0];
	$sourcename =~ s/\n$//;
      } else {
        if (@sourcenames == 0)
        {
            print STDERR "error: no source file found for mocfile $lastdirname/$mocfile.moc\n";
        } else {
            print STDERR "error: Multiple source files found for mocfile $lastdirname/$mocfile.moc\n";
            print STDERR "\t",join ("\t", @sourcenames),"\n";
        }
	$errorflag = 1;
    
      }
      
      print FILEOUT "\$(srcdir)/$sourcename: $mocfile.moc\n";
      print FILEOUT "$mocfile.moc: $sourcedir/$mocfile.$header_suffix\n";
      print FILEOUT "\t\$(MOC) $sourcedir/$mocfile.$header_suffix -o $mocfile.moc\n";
      print FILEOUT "\n";

    }
   
  }

sub process_makefile
  {
    
    $printname = $filename;
    $printname =~ s#^$curdir/##;
    $lastdirname = dirname($filename);
    $lastdirname =~ s#^$curdir/##;

    print STDERR "Processing " .$printname . "\n" if $verbose;
    
    chdir( dirname( $filename ) );
    
    $filename = basename($filename);
    
    $search = `grep '[^=#]*METASOURCES.*=' $filename`;
    return if ($search eq '');
    
    $search = `grep "DO_NOT_USE_AUTOMOC" $filename`;
    return if ($search);
    
    $newfilename = $filename . ".tmp";

    open( FILEIN, "$filename" ) or die "Could not open $filename: $!\n";
    open( FILEOUT, ">$newfilename" ) or die "Could not open $filename: $!\n"; 
    
    # search for USE_AUTOMOC
    @search_use_automoc = `grep "[^#]*USE_AUTOMOC" $filename`;
    
    if( @search_use_automoc == 0 ) {
      process_moc_makefile();
      $cxx_suffix="";
    } else {
      process_automoc_makefile();
      $cxx_suffix="." . $cxx_suffix;
    }
    
    print FILEOUT "distclean-metasources:\n";
    if (@mocnames) 
      {
	print FILEOUT "\t-rm -f ";
	foreach $mocfile (@mocnames) 
	  {
	    print FILEOUT $mocfile . ".moc$cxx_suffix ";
	  }
      }
    print FILEOUT "\n";

    if ($errorflag == 0) {
      print FILEOUT "\n# DO_NOT_USE_AUTOMOC\n";
      rename $newfilename, $filename;
    } else {
      system("rm $newfilename");
      exit 1;   # cause the make to stop. (added 28/01/99 jbb)
                # (I've a bad feeling about this Stephan!)
    }

    undef @mocsources;
    undef @mocnames;
    
    
  }


