#! perl

use Config;
use File::Basename qw(&basename &dirname);
use File::Spec;
use Cwd;

# List explicitly here the variables you want Configure to
# generate.  Metaconfig only looks for shell variables, so you
# have to mention them as if they were shell variables, not
# %Config entries.  Thus you write
#  $startperl
# to ensure Configure will look for $Config{startperl}.
# Wanted:  $archlibexp

# This forces PL files to create target in same directory as PL file.
# This is so that make depend always knows where to find PL derivatives.
$origdir = cwd;
chdir dirname($0);
$file = basename($0, '.PL');
$file .= '.com' if $^O eq 'VMS';

open OUT,">$file" or die "Can't create $file: $!";

print "Extracting $file (with variable substitutions)\n";

# In this section, perl variables will be expanded during extraction.
# You can use $Config{...} to use Configure variables.

print OUT <<"!GROK!THIS!";
$Config{startperl}
    eval 'exec $Config{perlpath} -S \$0 \${1+"\$@"}'
    if \$running_under_some_shell;
--\$running_under_some_shell;
!GROK!THIS!

# In the following, perl variables are not expanded during extraction.

print OUT <<'!NO!SUBS!';

# Version 1.00, Reini Urban, 2013-02-11 09:52:10

use strict;
use warnings;
use 5.006_000;

use FileHandle;
use Config;
use Fcntl qw(:DEFAULT :flock);
use File::Temp qw(tempfile);
use File::Basename qw(basename dirname);
use File::Path qw(mkpath);
# use Cwd;
use Pod::Usage;
# Time::HiRes does not work with 5.6
# use Time::HiRes qw(gettimeofday tv_interval);
our $VERSION = 1.00;
$| = 1;
eval { require B::C::Flags; };

$SIG{INT} = sub { exit(); } if exists $SIG{INT}; # exit gracefully and clean up after ourselves.

# usage: vprint [level] msg args
sub vprint {
    my $level;
    if (@_ == 1) {
        $level = 1;
    } elsif ($_[0] =~ /^-?\d$/) {
        $level = shift;
    } else {
        # well, they forgot to use a number; means >0
        $level = 0;
    }
    my $msg = "@_";
    $msg .= "\n" unless substr($msg, -1) eq "\n";
    if (opt('v') > $level)
    {
	if (opt('log')) {
	    print $logfh "$0: $msg" ;
	} else {
	    print        "$0: $msg";
	}
    }
}

sub vsystem {
    if (opt('dryrun')) {
        print "@_\n";
    } else {
       system(@_);
    }
}

# parse most options thru to perlcc, just use -m|--module and -l:s|--local=path

sub parse_argv {
  $Options = {};
  if (grep /^-m$/, @ARGV) {
    $Options->{m}++;
    @ARGV = grep !/^-m$/, @ARGV;
  }
  if (my ($l) = grep /^-l(.*)$/, @ARGV) {
    if ($l) {
      $l =~ s/^=//;
      $Options->{l} = $l;
    } else {
      # check next ARGV for -
      $Options->{l} = '~/.perl5/pcc';
    }
    @ARGV = grep !/^-l(.*)$/, @ARGV;
  }
}

sub opt(*) {
    my $opt = shift;
    return exists($Options->{$opt}) && ($Options->{$opt} || 0);
}

# File spawning and error collecting
sub spawnit {
    my $command = shift;
    my (@error,@output,$errname,$errcode);
    if (opt('dryrun')) {
        print "$command\n";;
    }
    elsif ($Options->{spawn}) {
        (undef, $errname) = tempfile("pccXXXXX");
        {
	    my $pid = open (S_OUT, "$command 2>$errname |")
	      or _die("Couldn't spawn the compiler.\n");
            $errcode = $?;
            my $kid;
            do {
              $kid = waitpid($pid, 0);
            } while $kid > 0;
            @output = <S_OUT>;
        }
        open (S_ERROR, $errname) or _die("Couldn't read the error file.\n");
        @error = <S_ERROR>;
        close S_ERROR;
        close S_OUT;
        unlink $errname or _die("Can't unlink error file $errname\n");
    } else {
        @output = split /\n/, `$command`;
    }
    return (\@output, \@error, $errcode);
}

sub version {
    require B::C::Flags;
    no warnings 'once';
    my $BC_VERSION = $B::C::Flags::VERSION . $B::C::REVISION;
    return "buildcc $VERSION, B-C-${BC_VERSION} built for $Config{perlpath} $Config{archname}\n";
}

sub helpme {
    print version(),"\n";
    if (opt('v')) {
	pod2usage( -verbose => opt('v') );
    } else {
	pod2usage( -verbose => 0 );
    }
}

sub relativize {
    my ($args) = @_;

    return() if ($args =~ m"^[/\\]");
    return("./$args");
}

sub _die {
    my @args = ("$0: ", @_);
    $logfh->print(@args) if opt('log');
    print STDERR @args;
    exit(); # should die eventually. However, needed so that a 'make compile'
            # can compile all the way through to the end for standard dist.
}

sub _usage_and_die {
    _die(<<EOU);
Usage:
$0 [-o executable] [-h] [-m] -l [path] source.pl
    buildcc -o hello hello.pl  # pass thru perlcc
    buildcc -m app.pl          # detects dependencies for app.pl, write them to app.mak,
                               # and compile all into shared modules and app
    buildcc -l -m app.pl       # use local ~/.perl5/pcc/ path
    buildcc -l=~/pcc -m app.pl # use local ~/pcc/ path
EOU
}

sub run {
    my (@commands) = @_;

    my $t0 = [gettimeofday] if opt('time');
    print interruptrun(@commands) if (!opt('log'));
    $logfh->print(interruptrun(@commands)) if (opt('log'));
    my $elapsed = tv_interval ( $t0 ) if opt('time');
    vprint -1, "r time: $elapsed" if opt('time');
}

sub interruptrun {
    my (@commands) = @_;

    my $command = join('', @commands);
    local(*FD);
    my $pid = open(FD, "$command |");
    my $text;

    local($SIG{HUP}, $SIG{INT}) if exists $SIG{HUP};
    $SIG{HUP} = $SIG{INT} = sub { kill 9, $pid; exit } if exists $SIG{HUP};

    my $needalarm =
          ($ENV{PERLCC_TIMEOUT} &&
	   exists $SIG{ALRM} &&
	  $Config{'osname'} ne 'MSWin32' &&
	  $command =~ m"(^|\s)perlcc\s");

    eval {
         local($SIG{ALRM}) = sub { die "INFINITE LOOP"; } if exists $SIG{ALRM};
         alarm($ENV{PERLCC_TIMEOUT}) if $needalarm;
	 $text = join('', <FD>);
	 alarm(0) if $needalarm;
    };

    if ($@) {
        eval { kill 'HUP', $pid };
        vprint 0, "SYSTEM TIMEOUT (infinite loop?)\n";
    }

    close(FD);
    return($text);
}

sub is_win32() { $^O =~ m/^MSWin/ }
sub is_msvc() { is_win32 && $Config{cc} =~ m/^cl/i }

__END__

=head1 NAME

buildcc - build an executable with shared modules from a perl script

=head1 SYNOPSIS

    buildcc -o hello hello.pl  # pass thru perlcc
    buildcc -m app.pl          # detects dependencies for app.pl, write them to app.mak,
                               # and compile all into shared modules and app
    buildcc -l -m app.pl       # use local ~/.perl5/pcc/ path
    buildcc -l=~/pcc -m app.pl # use local ~/pcc/ path

=head1 DESCRIPTION

F<buildcc> is a C<perlcc -m> frontend to detect and maintain perlcc compiled perl 
modules as compiled shared libraries.
It creates a F<.mak> file for the compiled script with all dependencies.

C<-l> uses a local path for all compiled shared modules. Otherwise it checks if 
F< 'sitearch'/pcc/> is writable and puts/searches the modules there if so.

All other options are passed thru to perlcc verbatim.

=head1 OPTIONS

=over 4

=item -m

Create a .mak for the module depencencies, and create the target.

=item -l [path]

Use the given local path as prefix for the created shared modules.

=back

=cut

# Local Variables:
#   mode: cperl
#   cperl-indent-level: 4
#   fill-column: 100
# End:
# vim: expandtab shiftwidth=4:
!NO!SUBS!

close OUT or die "Can't close $file: $!";
chmod 0755, $file or die "Can't reset permissions for $file: $!\n";
exec("$Config{'eunicefix'} $file") if $Config{'eunicefix'} ne ':';
chdir $origdir;
