#! /usr/bin/env perl
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/NPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation.  Portions created by Netscape are
# Copyright (C) 1999 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): 
#

# make-makefiles - Quickly create Makefiles for subdirectories.
#      Also, creates any needed subdirectories.
#
# usage: make-makefiles [ -d <depth> ] [ <subdir> | <subdir>/Makefile ] ...

# Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com).

#$debug = 1;

# Determine various tree path variables
#
($depth, @makefiles) = parse_arguments(@ARGV);

$object_fullpath = `pwd`;
chdir $depth;
$object_root = `pwd`;
chomp $object_fullpath;
chomp $object_root;

# $source_subdir is the path from the object root to where
#    'make-makefile' was called. For example, if make-makefile was
#    called from "mozilla/gfx/src", then $source_subdir would be
#    "gfx/src/".
$source_subdir = "$object_fullpath/";
$source_subdir =~ s|^$object_root/||;

# Prefix makefiles with $source_subdir so that paths
# will be relative to the top of the object tree.
#
for $makefile (@makefiles) {
  $makefile = "$source_subdir$makefile";
}

create_directories(@makefiles);

# Find the path to the source directory based on how 'make-makefile'
#  was invoked. The path is either relative to the object directory
#  or an absolute path.
$given_srcdir = find_srcdir($0, $depth);

if ($debug) {
  warn "object_fullpath = $object_fullpath\n";
  warn "object_root     = $object_root\n";
  warn "source_subdir   = $source_subdir\n";
  warn "makefiles       = @makefiles\n";
  warn "given_srcdir    = $given_srcdir\n";
}

@unhandled = update_makefiles($given_srcdir, @makefiles);

run_config_status(@unhandled);

# end of Main
############################################################

sub dirname {
 return $_[0] =~ /(.*)\/.*/ ? "$1" : '.';
}

# find_depth: Pull the value of DEPTH out of a Makefile (or Makefile.in)
sub find_depth {
  my $depth = '';
  open(MAKEFILE, "<$_[0]") || die "Unable to open $_[0]: $!\n";
  while (<MAKEFILE>) {
    next unless /^DEPTH\s*=\s*(\..*)/;
    $depth = $1;
    last;
  }
  close MAKEFILE;
  return $depth;
}

sub parse_arguments {
  my @args = @_;
  my $depth = '';
  my @makefiles = ();

  if ($args[0] eq '-d') {
    $depth = $args[1];
    shift @args; 
    shift @args; 
  } else {
    # Use $(DEPTH) in the Makefile or Makefile.in to determine the depth
    if (-e "Makefile.in") {
      $depth = find_depth("Makefile.in");
    } elsif (-e "Makefile") {
      $depth = find_depth("Makefile");
    } elsif (-e "../Makefile") {
      $depth = "../".find_depth("../Makefile");
      $depth =~ s/\/\.$//;
    } else {
      warn "Unable to determine depth (e.g. ../..) to root of objdir tree.\n";
      die  "No Makefile(.in) present. Try running with '-d <depth>'\n";
    }
  } 

  # Build the list of makefiles to generate
  #
  @makefiles = ();
  my $makefile;
  foreach $makefile (@args) {
    $makefile =~ s/\.in$//;
    $makefile =~ s/\/$//;
    $makefile =~ /Makefile$/ or $makefile .= "/Makefile";
    push @makefiles, "$makefile";
  }
  @makefiles = "Makefile" unless @args;

  return ($depth, @makefiles);
}


# Create all the directories at once.
#   This can be much faster than calling mkdir() for each one.
sub create_directories {
  my @makefiles = @_;
  my @dirs = ();
  my $ac_file;
  foreach $ac_file (@makefiles) {
    push @dirs, dirname($ac_file);
  }
  # Call mkdir with the directories sorted by subdir count (how many /'s)
  system "mkdir -p ". join(' ', @dirs) if @dirs;
}

# Find the top of the source directory
# (Assuming that the executable is in $top_srcdir/build/autoconf)
sub find_srcdir {
  my ($program_name, $depth) = @_;

  my $ac_given_srcdir = $program_name;
  $ac_given_srcdir =~ s|/?build/autoconf/.*$||;

  if ($debug) {
    print "ac_given_srcdir = $ac_given_srcdir\n";
    print "depth           = $depth\n";
  }
  if ($ac_given_srcdir =~ /^\./ and $depth ne '.') {
    my $quoted_depth = quotemeta($depth);
    $ac_given_srcdir =~ s|^$quoted_depth/?||;
  }
  if ($debug) {
    print "ac_given_srcdir = $ac_given_srcdir\n";
  }
  $ac_given_srcdir = '.' if $ac_given_srcdir eq '';
  return $ac_given_srcdir;
}

# Output the makefiles.
#
sub update_makefiles {
  my ($ac_given_srcdir, @makefiles) = @_;
  my @unhandled=();

  my $ac_file;
  foreach $ac_file (@makefiles) {
    my $ac_file_in    = "$ac_given_srcdir/${ac_file}.in";
    my $ac_dir        = dirname($ac_file);
    my $ac_dots       = '';
    my $ac_dir_suffix = '';
    my $srcdir        = '.';
    my $top_srcdir    = '.';

    # Determine $srcdir and $top_srcdir
    #
    if ($ac_dir ne '.') {
      $ac_dir_suffix = "/$ac_dir";
      $ac_dir_suffix =~ s%^/\./%/%;
      $ac_dots = $ac_dir_suffix;
      $ac_dots =~ s%/[^/]*%../%g;
    }
    if ($ac_given_srcdir eq '.') {
      if ($ac_dots ne '') {
        $top_srcdir = $ac_dots;
        $top_srcdir =~ s%/$%%;
      }
    } elsif ($ac_given_srcdir =~ m%^/%) {
      $srcdir     = "$ac_given_srcdir$ac_dir_suffix";
      $top_srcdir = "$ac_given_srcdir";
    } else {
      $srcdir     = "$ac_dots$ac_given_srcdir$ac_dir_suffix";
      $top_srcdir = "$ac_dots$ac_given_srcdir";
    }

    if ($debug) {
      print "ac_dir     = $ac_dir\n";
      print "ac_file    = $ac_file\n";
      print "ac_file_in = $ac_file_in\n";
      print "srcdir     = $srcdir\n";
      print "top_srcdir = $top_srcdir\n";
      print "cwd        = " . `pwd` . "\n";
    }

    # Copy the file and make substitutions.
    #    @srcdir@     -> value of $srcdir
    #    @top_srcdir@ -> value of $top_srcdir
    #
    if (-e $ac_file) {
      next if -M _ < -M $ac_file_in;  # Next if Makefile is up-to-date.
      warn "updating $ac_file\n";
    } else {
      warn "creating $ac_file\n";
    }

    open INFILE, "<$ac_file_in" or do {
      warn "$0: Cannot read $ac_file_in: No such file or directory\n";
      next;
    };
    open OUTFILE, ">$ac_file" or do {
      warn "$0: Unable to create $ac_file\n";
      next;
    };

    while (<INFILE>) {
      if (/\@[_a-zA-Z]*\@.*\@[_a-zA-Z]*\@/) {
        #warn "Two defines on a line:$ac_file:$.:$_";
        push @unhandled, $ac_file;
        last;
      }

      s/\@srcdir\@/$srcdir/;
      s/\@top_srcdir\@/$top_srcdir/;

      if (/\@[_a-zA-Z]*\@/) {
        #warn "Unknown variable:$ac_file:$.:$_";
        push @unhandled, $ac_file;
        last;
      }
      print OUTFILE;
    }
    close INFILE;
    close OUTFILE;
  }
  return @unhandled;
}

sub run_config_status {
  my @unhandled = @_;

  # Run config.status with any unhandled files.
  #
  if (@unhandled) {
    $ENV{CONFIG_FILES}= join ' ', @unhandled;
    system "./config.status";
  }
}
