The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Hardware::Vhdl::Automake::CompileTool;
use Hardware::Vhdl::Automake::DesignUnit;
use File::Spec::Functions;
use Carp;

use strict;
use warnings;

=head1 NAME

Hardware::Vhdl::Automake::CompileTool - Base class for compilation tool controller

=cut

my $null = File::Spec::Functions::devnull();

sub new { # class or object method, returns a new project object
	my $class=shift;
    $class = ref $class || $class;
	my $self={
        toolid => $class,
        status_callback => undef,
    };
	bless $self, $class;
    $self->init(@_);
    $self;
}

sub toolid { $_[0]->{toolid} }

sub init {
}

sub set_status_callback {
    my $self = shift;
    $self->{status_callback} = shift;
}

sub report_status {
    my $self = shift;
    &{$self->{status_callback}}(@_) if defined $self->{status_callback};
}

sub compile_start {
    my $self = shift;
    $self->report_status({type => 'compile_start', text => 'Starting compilation'});
}

sub compile {
    my $self = shift;
    my $dunit = shift;
    $self->report_status({type => 'compile', text => 'Compiling a design unit'});
    #...
    $dunit->set_compile_info($self->{toolid}, { xxx => 123 });
}

sub compile_finish {
    my $self = shift;
    $self->report_status({type => 'compile_finish', text => 'Finishing compilation'});
}

sub compile_abort {
    my $self = shift;
    $self->report_status({type => 'compile_abort', text => 'Aborting compilation'});
}

sub sys_capture {
    # taken from Shell.pm by Larry Wall, Jenda@Krynicky.cz, Dave Cottle <d.cottle@csc.canterbury.ac.nz> and Casey West <casey@geeknest.com>.
    my $self = shift;
    my $cmd = shift;
    my @cmd_args;
    if (ref $cmd eq 'ARRAY') {
        @cmd_args = @$cmd;
        $cmd = shift @cmd_args;
    }
    my $raw = 0;
    my $capture_stderr = 1;
    #print join(' ',"\n# Executing command:",$cmd, @cmd_args)."\n";
    if ( @cmd_args < 1 ) {
        $capture_stderr == 1      ? qx/$cmd 2>\&1/
          : $capture_stderr == -1 ? qx/$cmd 2>$null/
          : qx/$cmd/;
    } elsif ( $^O eq 'os2' ) {
        local ( *SAVEOUT, *READ, *WRITE );

        open SAVEOUT, '>&STDOUT' or die;
        pipe READ, WRITE or die;
        open STDOUT, '>&WRITE' or die;
        close WRITE;

        my $pid = system( 1, $cmd, @cmd_args );
        die "Can't execute $cmd: $!\n" if $pid < 0;

        open STDOUT, '>&SAVEOUT' or die;
        close SAVEOUT;

        if (wantarray) {
            my @ret = <READ>;
            close READ;
            waitpid $pid, 0;
            @ret;
        } else {
            local ($/) = undef;
            my $ret = <READ>;
            close READ;
            waitpid $pid, 0;
            $ret;
        }
    } else {
        my $a;
        my @arr = @cmd_args;
        unless ($raw) {
            if ( $^O eq 'MSWin32' ) {

                # XXX this special-casing should not be needed
                # if we do quoting right on Windows. :-(
                #
                # First, escape all quotes.  Cover the case where we
                # want to pass along a quote preceded by a backslash
                # (i.e., C<"param \""" end">).
                # Ugly, yup?  You know, windoze.
                # Enclose in quotes only the parameters that need it:
                #   try this: c:> dir "/w"
                #   and this: c:> dir /w
                for (@arr) {
                    s/"/\\"/g;
                    s/\\\\"/\\\\"""/g;
                    $_ = qq["$_"] if /\s/;
                }
                #print "Win32 command: ", join(' ', $cmd, @arr), "\n";
            } else {
                for (@arr) {
                    s/(['\\])/\\$1/g;
                    $_ = $_;
                }
            }
        }
        push @arr, '2>&1'  if $capture_stderr == 1;
        push @arr, '2>$null' if $capture_stderr == -1;
        open( SUBPROC, join( ' ', $cmd, @arr, '|' ) )
          or die "Can't exec $cmd: $!\n";
        if (wantarray) {
            my @ret = <SUBPROC>;
            close SUBPROC;    # XXX Oughta use a destructor.
            @ret;
        } else {
            local ($/) = undef;
            my $ret = <SUBPROC>;
            close SUBPROC;
            $ret;
        }
    }
}

1;