#!/usr/bin/env perl

# by Matt Simerson & Phil Nadeau
# circa 2008, but based in installer in Mail::Toaster dating back to the 20th century

use strict;
use warnings;

use English qw( -no_match_vars );

my $deps = {
    'apps' => [
#        { app => 'perl', info => { } },
#        { app => 'make', info => { } },
#        { app => 'gcc',  info => { } },
#        { app -> 'unzip', info => { } },
    ],
    'modules' => [
        { module => 'Test::More'        , info => { } },
        { module => 'Getopt::Long'      , info => { version => 2.37 } },
        { module => 'Config::Tiny'      , info => { } },
        { module => 'Params::Validate'  , info => { } },
        { module => 'Apache::ConfigFile', info => { } },
        { module => 'JSON::XS'          , info => { } },
        { module => 'Proc::ProcessTable', info => { } },
        { module => 'LWP::UserAgent'    , info => { } },
        { module => 'URI'               , info => { } },
    ],
};

$EUID == 0 or die( "You will have better luck if you run me as root.\n");

# this causes problems when CPAN is not configured.
#$ENV{PERL_MM_USE_DEFAULT} = 1;       # supress CPAN prompts

$ENV{FTP_PASSIVE} = 1;        # for FTP behind NAT/firewalls

my $apps = $deps->{apps};
foreach ( @$apps ) {
    print "checking $_->{app}\n";
    install_package( $_->{app} => $_->{info} );
}

my $modules = $deps->{modules};
my @failed  = ();
foreach ( @$modules ) {
    my $module = $_->{module} or die 'missing module name';
    my $info   = $_->{info};
    my $version = $info->{version} || '';
    print "checking for $module $version\n";
    eval "use $module $version";
    if ($EVAL_ERROR) {
        next if $info->{ships_with} && $info->{ships_with} eq 'perl';
        install_module( $module, $info );
        eval "use $module $version";
        if ($EVAL_ERROR) {
            push @failed, $module;
        }
    }
}

if ( scalar @failed > 0 ) {
    print "The following modules failed installation:\n";
    print join( "\n", @failed );
    print "\n";
}

exit;

sub install_module {
    my ( $module, $info) = @_;

    if ( lc($OSNAME) eq 'darwin' ) {
        my $dport = '/opt/local/bin/port';
        return warn "Darwin ports is not installed!" if ! -x $dport;

        my $port = "p5-$module";
        $port =~ s/::/-/g;
        system "sudo $dport install $port";
    }

    if ( lc($OSNAME) eq 'freebsd' ) {

        my $portname = "p5-$module";
        $portname =~ s/::/-/g;

        if (`/usr/sbin/pkg_info | /usr/bin/grep $portname`) {
            print "$module is installed.\n";
            return 1;
        }

        print "installing $module";

        my $portdir = </usr/ports/*/$portname>;

        if ( $portdir && -d $portdir && chdir $portdir ) {
            print " from ports ($portdir)\n";
            system "make clean && make install clean";
        }
    }
    if ( lc($OSNAME) eq 'linux' ) {        
        my $rpm = $info->{rpm};
        if ( $rpm ) {
            my $portname = "perl-$rpm";
            $portname =~ s/::/-/g;
            my $yum = '/usr/bin/yum';
            system "$yum -y install $portname" if -x $yum;
        }
    };

    print " from CPAN...";
    require CPAN;

    # some Linux distros break CPAN by auto/preconfiguring it with no URL mirrors.
    # this works around that annoying little habit
    no warnings;
    $CPAN::Config = get_cpan_config();
    use warnings;

    CPAN::Shell->install($module);
}

sub get_cpan_config {

    my $ftp = `which ftp`; chomp $ftp;
    my $gzip = `which gzip`; chomp $gzip;
    my $unzip = `which unzip`; chomp $unzip;
    my $tar  = `which tar`; chomp $tar;
    my $make = `which make`; chomp $make;
    my $wget = `which wget`; chomp $wget;

    return 
{
  'build_cache' => q[10],
  'build_dir' => qq[$ENV{HOME}/.cpan/build],
  'cache_metadata' => q[1],
  'cpan_home' => qq[$ENV{HOME}/.cpan],
  'ftp' => $ftp,
  'ftp_proxy' => q[],
  'getcwd' => q[cwd],
  'gpg' => q[],
  'gzip' => $gzip,
  'histfile' => qq[$ENV{HOME}/.cpan/histfile],
  'histsize' => q[100],
  'http_proxy' => q[],
  'inactivity_timeout' => q[5],
  'index_expire' => q[1],
  'inhibit_startup_message' => q[1],
  'keep_source_where' => qq[$ENV{HOME}/.cpan/sources],
  'lynx' => q[],
  'make' => $make,
  'make_arg' => q[],
  'make_install_arg' => q[],
  'makepl_arg' => q[],
  'ncftp' => q[],
  'ncftpget' => q[],
  'no_proxy' => q[],
  'pager' => q[less],  'prerequisites_policy' => q[follow],  'scan_cache' => q[atstart],  'shell' => q[/bin/csh],
  'tar' => $tar,
  'term_is_latin' => q[1],
  'unzip' => $unzip,
  'urllist' => [ 'http://www.perl.com/CPAN/', 'ftp://cpan.cs.utah.edu/pub/CPAN/', 'ftp://mirrors.kernel.org/pub/CPAN', 'ftp://osl.uoregon.edu/CPAN/', 'http://cpan.yahoo.com/' ],  
  'wget' => $wget,
};          

}           

sub install_package {
    my ($app, $info) = @_;

    if ( lc($OSNAME) eq 'freebsd' ) {

        my $portname = $info->{port}
            or return warn "skipping install of $app b/c port dir not set.";

        if (`/usr/sbin/pkg_info | /usr/bin/grep $app`) {
            print "$app is installed.\n";
            return 1;
        }

        print "installing $app\n";
        my $portdir = </usr/ports/*/$portname>;

        if ( ! -d $portdir || ! chdir $portdir ) {
            print "oops, couldn't find port $app at '$portname'\n";
            return;
        }

        system "make install clean"
            or return warn "'make install clean' failed for port $app";
        return 1;
    };

    if ( lc($OSNAME) eq 'linux' ) {
        my $rpm = $info->{rpm} or return warn "skipping install of $app b/c rpm not set";
        my $yum = '/usr/bin/yum';
        return warn "couldn't find yum, skipping install." if ! -x $yum;
        return system "$yum install $rpm";
    };
}

