eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
    & eval 'exec perl -S $0 $argv:q'
    if 0;

# Creates an ACE build tree in directory "build/<build name>" below the current
# directory, which must be an ACE "top level" directory (such as
# $ACE_ROOT).  The build tree directory structure mirrors that of the ACE
# top level directory structure, except that instead of containing any plain
# files, it contains only symlinks to the files in the ACE top level structure.
#
# This program has a similar purpose to "clone", but in addition to
# only creating symlinks (clone creates hard links, by default), this
# script:
# 1) uses relative rather than absolute symlinks,
# 2) tries not to put junk files into the build tree,
# 3) only creates a new tree in a build/ directory below the current,
#    top level ACE directory (it's a feature :-), but it does enforce
#    consistency).
#
# This program can be re-run on a build tree at any time in order to
# update it.  It will add symlinks for newly added files, and remove
# any that are no longer valid.
#
# If the <build name> starts with "build/", that part will be removed
# from it.
#
# The first three lines above let this script run without specifying the
# full path to perl, as long as it is in the user's PATH.
# Taken from perlrun man page.

use File::Find ();
use File::Basename;
use FileHandle;

print "You should consider using clone_build_tree.pl found with MPC\n";

$usage = "usage: $0 -? | [-a] [-d <directory mode>] [-v] <build name>\n";
$directory_mode = 0777;   #### Will be modified by umask, also.
$verbose = 0;

$source='.';
$absolute= 0;

$perl_version = $] + 0;
if ($perl_version >= 5) {
  #### Use an eval so that this script will compile with perl4.
  eval <<'PERL5_CWD'
  require Cwd;
  sub cwd {
    Cwd::getcwd ();
  }
PERL5_CWD
} else {
  sub cwd {
    local ($pwd);

    chop ($pwd = `pwd`);
    $pwd;
  }
}

my($starting_dir) = cwd ();
my(@nlinks)       = ();
my($build_re)     = undef;

sub cab_link {
  my($real) = shift;
  my($fake) = shift;
  my($uif)  = ($^O eq 'MSWin32' ? 'link' : 'symlink');

  print "$uif $real $fake\n" if $verbose;

  my($status) = 0;
  if ($^O eq 'MSWin32') {
    my($fixed) = $fake;
    $fixed =~ s/$build_re//;
    push(@nlinks, $fixed);

    chdir(dirname($fake));
    $status = link ($real, basename($fake));
    chdir($starting_dir);
  }
  else {
    $status = symlink ($real, $fake);
  }
  if (!$status) {
    warn "$0: $uif to $fake failed\n";
  }
}

####
#### Process command line args.
####
while ($#ARGV >= 0  &&  $ARGV[0] =~ /^-/) {
  if ($ARGV[0] eq '-v') {
    $verbose = 1;
  } elsif ($ARGV[0] eq '-d') {
    if ($ARGV[1] =~ /^\d+$/) {
      $directory_mode = eval ($ARGV[1]); shift;
    } else {
      warn "$0:  must provide argument for -d option\n";
      die $usage;
    }
  } elsif ($ARGV[0] eq '-a') {
    $source = &cwd ();
    $absolute = 1;
  } elsif ($ARGV[0] eq '-?') {
    print "$usage";
    exit;
  } else {
    warn "$0:  unknown option $ARGV[0]\n";
    die $usage;
  }
  shift;
}

die $usage unless $#ARGV == 0;
$build = $ARGV[0];
$build =~ s%^build[/\\]%%;        #### remove leading "build/", if any
$build = "build/$build";

## Set up the build regular expression use under MSWin32
if ($^O eq 'MSWin32') {
  ## Get the original build name
  $build_re = $build;

  ## Remove any trailing slashes
  $build_re =~ s/[\\\/]+$//;

  ## Add a single trailing slash
  $build_re .= '/';

  ## Escape any special characters
  $build_re =~ s/([\\\$\[\]\(\)\.])/\\$1/g;
}

####
#### Check that we're in an ACE "top level" directory.
####
(-d 'ace' && -d 'include')  ||
  die "$0:  must be in an ACE top level (ACE_ROOT) directory!\n";

####
#### Create build directories, if needed.
####
-d 'build'  ||  mkdir ('build', $directory_mode);
-d "$build"  ||  mkdir ("$build", $directory_mode);

####
#### Get all ACE plain file and directory names.
####
@files = ();

sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    /^CVS\z/s &&
    ($File::Find::prune = 1)
    ||
    /^build\z/s &&
    ($File::Find::prune = 1)
    ||
    /^\..*obj\z/s &&
    ($File::Find::prune = 1)
    ||
    /^Templates\.DB\z/s &&
    ($File::Find::prune = 1)
    ||
    /^Debug\z/s &&
    ($File::Find::prune = 1)
    ||
    /^Release\z/s &&
    ($File::Find::prune = 1)
    ||
    /^Static_Debug\z/s &&
    ($File::Find::prune = 1)
    ||
    /^Static_Release\z/s &&
    ($File::Find::prune = 1)
    ||
    (
        ($nlink || (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) &&
        ! -l $_ &&
        ! /^core\z/s &&
        ! /^.*\.state\z/s &&
        ! /^.*\.so\z/s &&
        ! /^.*\.[oa]\z/s &&
        ! /^.*\.dll\z/s &&
        ! /^.*\.lib\z/s &&
        ! /^.*\.obj\z/s &&
        ! /^.*~\z/s &&
        ! /^\.\z/s &&
        ! /^\.#.*\z/s &&
        ! /^.*\.log\z/s
    ) &&
    push(@files, $File::Find::name);
}

File::Find::find({wanted => \&wanted}, '.');

####
#### Create directories and symlinks to files.
####
foreach $file (@files) {
  $file =~ s%^./%%g;  #### excise leading ./ directory component

  if (-d $file) {
     unless (-d "$build/$file") {
       print "mkdir $build/$file, $directory_mode\n" if $verbose;
       mkdir ("$build/$file", $directory_mode);
     }
  } else {
    unless (-e "$build/$file") {
      if (!$absolute) {
        $up = '../..';
        while ($file =~ m%/%g) {
          $up .= '/..';
        }

        cab_link("$up/$file", "$build/$file");
      } else {
        $path = $source . '/' . $file;
        cab_link("$path", "$build/$file");
      }
    }
  }
}

####
#### Find all the symlinks in the build directory, and remove ones
#### that are no longer actually linked to a file.
####

if ($^O eq 'MSWin32') {
  my($lfh) = new FileHandle();
  my($txt) = "$build/create_ace_build.links";
  if (open($lfh, "$txt")) {
    while(<$lfh>) {
      my($line) = $_;
      $line =~ s/\s+$//;
      if (-e $line) {
        push(@nlinks, $line);
      }
      else {
        print "Removing $build/$line \n" if $verbose;
        unlink("$build/$line") || warn "$0: unlink of $build/$line failed\n";
      }
    }
    close($lfh);
  }

  ## Rewrite the link file.
  unlink($txt);
  if (open($lfh, ">$txt")) {
    foreach my $file (@nlinks) {
      print $lfh "$file\n";
    }
    close($lfh);
  }
}
else {
  @lfiles = ();

  sub lcheck {
    ## There's no way to know if we have hard linked back to a now
    ## non-existent file.  So, just do the normal -l on the file
    ## which will cause no files to be pushed on Windows.
    if (-l $_) {
      push(@lfiles, $File::Find::name);
    }
  }

  File::Find::find({wanted => \&lcheck}, $build);

  foreach (@lfiles) {
    local @s = stat $_;
    if ($#s == -1) {
      print "Removing $_ \n" if $verbose;
      unlink $_  ||  warn "$0: unlink of $_ failed\n";
    }
  }
}

####
#### Done: print message.
####
print "\nCompleted creation of $build/.\n";
my($msg) = '';
if (! -e "$build/ace/config.h") {
  $msg .= "$build/ace/config.h";
}

if ($^O ne 'MSWin32' &&
    ! -e "$build/include/makeinclude/platform_macros.GNU") {
  if ($msg ne '') {
    $msg .= " and\n";
  }
  $msg .= "$build/include/makeinclude/platform_macros.GNU";
}

if ($msg ne '') {
  print "Be sure to setup $msg.\n";
}

#### EOF
