package inc::Installer::CPAN; use strict; use CPAN (); use Cwd; use Config (); use File::Path (); use ExtUtils::MakeMaker (); use inc::Installer::OutPaths; use inc::Installer::BuildTools; use base qw( inc::Installer inc::Installer::Logger inc::Installer::OptionsProviderI inc::Installer::EnvObservableI inc::Installer::EUMMArgProviderI ); { my $SINGLETON; sub new { my ( $class, $prefix ) = @_; if ( $SINGLETON ) { $SINGLETON->CPANPrefix( $prefix ) if $prefix; } else { my $outpaths = inc::Installer::OutPaths->new; $prefix = $outpaths->Prefix if not $prefix; $prefix = '' if not $prefix; my $self = { 'cpan_build_dir' => "$prefix/src/cpan/build", 'cpan_local_mirror' => "$prefix/src/cpan/mirror", 'cpan_config_dir' => "$ENV{HOME}/.cpan", 'keep_source_where' => "$prefix/src/cpan/sources", 'cpan_prefix' => $prefix, 'config_cpan' => undef, }; bless $self, $class; $SINGLETON = $self; } return $SINGLETON; } } sub Check { my ( $self, $key ) = @_; my $path = $self->{$key}; my $required = $self->{'config_cpan'} ? 1 : 0; if ( $key eq 'config_cpan' ) { my $msg = $self->{'config_cpan'} ? "Going to configure CPAN" : "Using pre-configured CPAN"; return [ $msg, 0 ]; } if ( not -d $path ) { undef( $@ ); eval { File::Path::mkpath( [ $path ], 0, 0777 ); }; if ( ! $@ and -d $path ) { return [ $path, $required ] } else { $self->warning( $path, $@ ); return [ 0, $required ]; } } else { return [ $path, $required ]; } } sub Keys { my $self = shift; return sort { $a cmp $b } keys %$self; } sub CPANPrefix { my ( $self, $new_prefix ) = @_; if ( $new_prefix ) { my $current_prefix = $self->{'cpan_prefix'}; $new_prefix = $self->FixSlashes( $new_prefix ); for my $path ( qw(cpan_build_dir keep_source_where cpan_prefix) ) { $self->{$path} =~ s/^\Q$current_prefix\E/$new_prefix/; } } return $self->{'cpan_prefix'}; } sub CPANHome { my ( $self, $home ) = @_; return $self->CPANPrefix( $home ); } sub CPANConfig { my $self = shift; $self->{'cpan_config_dir'} = shift if @_; return $self->{'cpan_config_dir'}; } sub CPANBuild { my $self = shift; $self->{'cpan_build_dir'} = shift if @_; return $self->{'cpan_build_dir'}; } sub CPANLocalMirror { my $self = shift; $self->{'cpan_local_mirror'} = shift if @_; return $self->{'cpan_local_mirror'}; } sub CPANSrc { my $self = shift; $self->{'keep_source_where'} = shift if @_; return $self->{'keep_source_where'}; } sub CPANURLs { my $self = shift; return $self->CPANLocalMirror(); } sub ConfigureCPAN { my $self = shift; $self->debug("Configuring CPAN..."); my $bin = inc::Installer::BuildTools->new; my $paths = inc::Installer::OutPaths->new; my $time = localtime; my $HOME = $self->CPANPrefix(); my $MAKE = $^O eq 'MSWin32' ? $bin->LocateBin( 'nmake' ) : $bin->LocateBin( 'make' ); my @argv = @Installer::OptionsGetter::ARGV_COPY; my $URLs; my $cwd = getcwd; my $perl5lib = $paths->MakePERL5LIB; my $configdir = $paths->Configdir; my $sep = $Config::Config{'path_sep'}; $URLs .= "'$_'," for $self->CPANURLs(); my $BACKUP_MSG; my $CPAN_CONFIG_FILE = $self->CPANConfig() . '/CPAN/MyConfig.pm'; if ( -e $CPAN_CONFIG_FILE ) { my $oldfile = $CPAN_CONFIG_FILE; my $newfile = $oldfile . '.bak' . time; rename $oldfile, $newfile; $BACKUP_MSG = "Previous CPAN config saved as: $newfile"; } mkdir $self->CPANConfig() if ! -d $self->CPANConfig(); mkdir $self->CPANConfig() . '/CPAN' if ! -d $self->CPANConfig() . '/CPAN'; open my $configfh, '>', $CPAN_CONFIG_FILE or die $!; print $configfh "# This file was automatically generated by $0 with args: # '@argv' # on $time. Do not edit by hand! All changes will # be lost next time $0 is run. # The purpose of this file is to provide CPAN.pm with a # configuration that: # * is not interactive (no scary choices) # * uses a local (tiny) CPAN mirror at: # file://" . $self->CPANURLs() . " # * sets up prefixes for non-root installs # $BACKUP_MSG \$ENV{'PERL5LIB'} = '$cwd$sep$configdir$sep$perl5lib'; \$ENV{'PERL5OPT'} = '-MCipres::Config'; \$CPAN::Config = { 'gzip' => '" . $bin->LocateBin( 'gzip' ) . "', 'tar' => '" . $bin->LocateBin( 'tar' ) . "', 'unzip' => '" . $bin->LocateBin( 'unzip' ) . "', 'make' => '$MAKE', 'pager' => '" . $bin->LocateBin( 'more' ) . "', 'ftp' => '" . $bin->LocateBin( 'ftp' ) . "', 'ncftp' => '" . $bin->LocateBin( 'ncftp' ) . "', 'build_cache' => 0, 'build_dir' => '" . $self->CPANBuild() . "', 'cache_metadata' => 0, 'cpan_home' => '" . $self->CPANHome() . "', 'dontload_hash' => { }, 'ftp_proxy' => '', 'http_proxy' => '', 'inactivity_timeout' => 1, 'index_expire' => 1, 'inhibit_startup_message' => 1, 'keep_source_where' => '" . $self->CPANSrc() . "', 'make_arg' => '', 'make_install_arg' => '', 'makepl_arg' => 'INSTALLARCHLIB=$HOME/lib INSTALLVENDORLIB=$HOME/lib INSTALLSITELIB=$HOME/lib INSTALLPRIVLIB=$HOME/lib INSTALLDIRS=site', 'mbuildpl_arg' => ' --install_base $HOME ', 'mbuild_arg' => '', 'mbuild_install_arg' => '', 'mbuild_install_build_command' => '', 'no_proxy' => '', 'prerequisites_policy' => 'follow', 'scan_cache' => 'never', # using firewall friendly local repository 'urllist' => [ $URLs ], }; 1; "; close $configfh; $self->debug("ok"); } sub InstallPrereqs { my ( $self, $prereq ) = @_; $self->ConfigureCPAN(); $self->debug("Installing prerequisites"); my $outpaths = inc::Installer::OutPaths->new; $outpaths->ToGlobalConfig; if ( defined $prereq ) { my $name = $prereq->StandardName(); my $response = $self->prompt("Auto-build prerequisite '$name'?", 'y'); if ( $response =~ qr/n/i ) { $self->warning("Skipping prerequisite '$name'"); } else { $self->info("Going to install prerequisite '$name'"); CPAN::Shell->force( 'install', $prereq->CPANID ); $self->info("Done installing prerequisite '$name'"); } } } sub Options { my $self = shift; my %options; $options{'config-cpan'} = \$self->{'config_cpan'}; for my $opt ( keys %$self ) { next if $opt eq 'configure_cpan'; my $gnu_opt = $opt; $gnu_opt =~ s/_/-/g; $options{"$gnu_opt=s"} = \$self->{$opt}; } return %options; } sub OptionsHelp { my $self = shift; my $current = $self->{'config_cpan'} ? 'enabled' : 'disabled'; my $help = <<"!GROK!THIS!"; CPAN options: --config-cpan Current setting: $current When you enable this option, I will attempt to configure CPAN for you. If you have set up CPAN before, and it works, you can skip this. You can influence my CPAN configuration with the options below: !GROK!THIS! for my $opt ( sort { $a cmp $b } keys %$self ) { next if $opt eq 'config_cpan'; my $gnu_opt = $opt; $gnu_opt =~ s/_/-/g; my $path = $self->FixSlashes( $self->{$opt} ); $path = $self->FixSlashes( $self->{$opt} ) if $opt !~ qr/^(?:cpan_prefix|cpan_config_dir)$/; $help .= " --$gnu_opt=[PATH]\n Current setting: $path\n\n"; } return $help; } sub GetEUMMArgs { return; } 1;