The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Module::Build::HiPi;

use 5.14.0;
use strict;
use warnings;
use Module::Build;
use Config;
use File::Copy;
use Cwd;
use File::Path;
our @ISA = qw( Module::Build );

our $VERSION ='0.50';

sub process_xs_files {
	my $self = shift;

	# Override Module::Build with a null implementation
	# We will be doing our own custom XS file handling
}

sub hipi_run_command {
	my ($self, $cmds) = @_;
	my $cmd = join( ' ', @$cmds );
    if ( !$self->verbose and $cmd =~ /(cc|gcc|g\+\+|cl).+-o\s+(\S+)/ ) {
		my $object_name = File::Basename::basename($2);
		$self->log_info("    CC -o $object_name\n");
    } elsif ( !$self->verbose and $cmd =~ /(configure|make)/i ) {
		$self->log_info("    SH $1\n")
    } else {
		$self->log_info("$cmd\n");
	}
	my $rc = system($cmd);
	die "Failed with exit code $rc\n$cmd\n"  if $rc != 0;
	die "Ctrl-C interrupted command\n$cmd\n" if $rc & 127;
}

sub ACTION_clean {
    my $self = shift;
    $self->SUPER::ACTION_clean;
    File::Path::remove_tree('BCM2835/buildlib') if -d 'BCM2835/buildlib';
    unlink('mylib/lib/libbcm2835Static.a');
}

sub ACTION_build {
    my $self = shift;
    $self->SUPER::ACTION_build;
    $self->hipi_build_bcm2835_library;
    $self->hipi_build_xs;
    $self->hipi_build_data;
    $self->log_info(qq(Build Complete\n));
}

# Build test action invokes build first
sub ACTION_test {
	my $self = shift;
	$self->depends_on('build');
	$self->SUPER::ACTION_test;
}

# Build install action invokes build first
sub ACTION_install {
	my $self = shift;
	$self->depends_on('build');
	$self->SUPER::ACTION_install;
}

sub hipi_build_wiring_library {
    my $self = shift;
    
    $self->log_info(qq(Building wiringPi library\n));
    mkdir( 'mylib/lib', 0777 ) if !-d 'mylib/lib';
    my $tgtlib = 'mylib/lib/libwiringPiStatic.a';
	my $tgtlibDev = 'mylib/lib/libwiringPiDevStatic.a';
    return if( $self->up_to_date( 'Build', $tgtlib ) && $self->up_to_date( 'Build', $tgtlibDev ) );
    
    my $gcc = $Config{cc};
    $gcc = 'gcc' if $gcc eq 'cc';
    my $gxx = $gcc;
    $gxx =~ s/^gcc/g\+\+/;
    my $gld = $Config{ld};
    
    my @cmd = (
		qq(make static CC=$gcc CXX=$gxx LD=$gld),
	);
	chdir('Wiring/src/wiringPi');
	$self->hipi_run_command( \@cmd );
	chdir('../../../');
	chdir('Wiring/src/devLib');
	$self->hipi_run_command( \@cmd );
    chdir('../../../');
    # copy the lib
    my $srclib = 'Wiring/src/wiringPi/libwiringPi.a';
	my $srclibDev = 'Wiring/src/devLib/libwiringPiDev.a';
    File::Copy::copy( $srclib, $tgtlib );
	File::Copy::copy( $srclibDev, $tgtlibDev );
}

sub hipi_build_bcm2835_library {
    my $self = shift;
    
    $self->log_info(qq(Building bcm2835 library\n));
    mkdir( 'mylib/lib', 0777 ) if !-d 'mylib/lib';
    my $tgtlib = 'mylib/lib/libbcm2835Static.a';
    
    return if $self->up_to_date( [ 'Build', 'BCM2835/src/src/bcm2835.c', 'BCM2835/src/src/bcm2835.h' ], [ $tgtlib ] );
     
    my $gcc = $Config{cc};
    $gcc = 'gcc' if $gcc eq 'cc';
    my $gxx = $gcc;
    $gxx =~ s/^gcc/g\+\+/;
    my $gld = $Config{ld};
    
    chdir('BCM2835');
    my $buildlibdir = Cwd::abs_path( getcwd() );
    die 'Failed to determine working directory' unless $buildlibdir && $buildlibdir =~ /\/BCM2835/;
    
    $buildlibdir .= '/buildlib';
    mkdir( $buildlibdir, 0777 );
        
    chdir('buildlib');
    my $quiet = ( $self->verbose ) ? '' : '--quiet ';
    my @cmd = (
		qq(sh ../src/configure $quiet--prefix=$buildlibdir CC=$gcc CXX=$gxx LD=$gld),
	);

	$self->hipi_run_command( \@cmd );
    @cmd = (
		qq(make $quiet),
	);
    $self->hipi_run_command( \@cmd );
    
    chdir('../../');
    
    # copy the lib
    my $srclib = 'BCM2835/buildlib/src/libbcm2835.a';
    File::Copy::copy( $srclib, $tgtlib );
}

sub hipi_build_xs {
    my $self = shift;
    
    $self->log_info(qq(Building XS Files\n));
    
    my @modules = (
        { name => 'Utils', version => $VERSION, autopath => 'HiPi/Utils', libs => '' },
        { name => 'Exec', version => $VERSION, autopath => 'HiPi/Utils/Exec', libs => '-lz' },
        { name => 'I2C',  version => $VERSION, autopath => 'HiPi/Device/I2C', libs => '' },
        { name => 'SPI',  version => $VERSION, autopath => 'HiPi/Device/SPI', libs => '' },
        { name => 'BCM2835', version => $VERSION, autopath => 'HiPi/BCM2835', libs => '-Lmylib/lib -lbcm2835Static' },
        { name => 'Wiring',  version => $VERSION, autopath => 'HiPi/Wiring',  libs => '-lwiringPi -lwiringPiDev' },
    );
    
    #----------------------------------------------
    # determine typemap
    #----------------------------------------------
    
    my $perltypemap;
	
	for my $incpath (@INC) {
		my $perlcheckfile = qq($incpath/ExtUtils/typemap);
		if ( !$perltypemap && -f $perlcheckfile ) {
			$perltypemap = $perlcheckfile;
			$perltypemap =~ s/\\/\//g;
		}
		last if $perltypemap;
	}

	die 'Unable to determine Perl typemap' if !defined($perltypemap);
    
    for my $mod ( @modules ) {
        my $xsfile   = qq($mod->{name}.xs);
        my $cfile    = qq($mod->{name}.c);
        my $ofile    = qq($mod->{name}.o);
        my $autopath = 'blib/arch/auto/' . $mod->{autopath};
        my $bsfile   = qq($autopath/$mod->{name}.bs);
        my $dllfile  = qq($autopath/$mod->{name}.) . $Config{dlext};
        
        File::Path::make_path( $autopath, { mode => 0755 } );
        
        # make bootscript file
        if ( open my $fh, '>', $bsfile ) {
            close $fh;
        }
        
        # Build Object File
        
        unless ( $self->up_to_date( $xsfile, $cfile ) ) {
            
            for ( qw( o def c xsc obj ) ) {
                my $fname = qq($mod->{name}.$_);
                unlink( $fname ) if -f $fname;
            }
            require ExtUtils::ParseXS;
            ExtUtils::ParseXS::process_file(
                filename    => $xsfile,
                output      => $cfile,
                prototypes  => 0,
                linenumbers => 0,
                typemap     => [
                    $perltypemap,
                    'typemap',
                ],
            );
            
            my @cmd = (
                $Config{cc},
                '-c -o',
                $ofile,
                $Config{ccflags},
                $Config{optimize},
                '-DVERSION=\"' . $mod->{version} . '\" -DXS_VERSION=\"' . $mod->{version} . '\"',
                $Config{cccdlflags},
                '-Imylib/include',
                '-I' . $Config{archlibexp} . '/CORE',
                $cfile,
            );
            $self->hipi_run_command( \@cmd );  
        }
        
        # Link Object
        unless( $self->up_to_date( $cfile, $dllfile ) ) {
            
            my $libdirs = $Config{libpth};
            $libdirs =~ s/\s+/ -L/g;
            
            unlink( $dllfile );
            my @cmd = (
                $Config{ld},
                qq(-L$libdirs),
                $Config{lddlflags},
                $ofile,
                '-o ' . $dllfile,
                $mod->{libs}
            );
            $self->hipi_run_command( \@cmd );
        }
    }
}

sub hipi_build_data {
    my $self = shift;
    require File::Copy::Recursive;
    File::Copy::Recursive::dircopy('mylib/auto/share','blib/lib/auto/share'); 
}

1;