@rem = '--*-Perl-*-- @echo off if "%OS%" == "Windows_NT" goto WinNT "C:\Perl\bin\perl.exe" -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9 goto endofperl_ppminst :WinNT "C:\Perl\bin\perl.exe" -x -S %0 %* if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl_ppminst if %errorlevel% == 9009 echo You do not have Perl in your PATH. if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul goto endofperl_ppminst @rem '; #!perl #line 15 use strict; use FindBin; use lib "$FindBin::Bin/../lib"; use Data::Dumper; use PPM::Config; my $VERSION = '3.00'; my %INST; my %CONF; my %keys = ( ARCHITECTURE => 0, CPU => 0, OSVALUE => 0, OSVERSION => 0, PERLCORE => 0, root => 1, tempdir => 1, TARGET_TYPE => 0, VERSION => 0, ); my $ERR; #============================================================================ # Register a dummy object which implements the required interface. #============================================================================ my $i = bless { }, "Implementation"; PPM::InstallerClient::init($ENV{PPM_PORT}, $i); #============================================================================ # Command Implementors #============================================================================ package Implementation; @Implementation::ISA = qw(PPM::InstallerClient); use Config; use Fcntl qw(LOCK_SH LOCK_UN LOCK_EX); use PPM::InstallerClient; use PPM::PPD; use PPM::Search; use Data::Dumper; # There's a bug in ExtUtils::Install in perl 5.6.1. # Also exists in ActivePerl 522 (line 168) BEGIN { local $^W; require ExtUtils::Install; } # Query installed packages: returns a list of records about the results. sub query { my $inst = shift; my $query = shift; my $case = shift; load_pkgs(); my @ppds = map { $_->{ppd} } values %INST; my @matches = PPM::PPD::Search->new($query, $case)->search(@ppds); return map { $_->ppd } @matches; } sub properties { my $inst = shift; my $pkg = shift; if (pkg_installed($pkg) && load_pkg($pkg)) { return ($INST{$pkg}{ppd}->ppd, $INST{$pkg}{pkg}{INSTDATE}, $INST{$pkg}{pkg}{LOCATION}); } $ERR = "'$pkg' is not installed"; return (); } sub dependents { my $inst = shift; my $pkg = shift; if (pkg_installed($pkg) && load_pkg($pkg)) { return @{ $INST{$pkg}{pkg}{dependents} || [] }; } undef; } sub remove { my $inst = shift; my $pkg = shift; if (pkg_installed($pkg) && load_pkg($pkg)) { my $packlist = $INST{$pkg}{pkg}{INSTPACKLIST}; (my $altpacklist = $packlist) =~ s<\Q$CONF{ARCHITECTURE}\E[\\/]><>i; if (-f $packlist) { eval { ExtUtils::Install::uninstall($packlist, 1, 0); }; } elsif (-f $altpacklist) { eval { ExtUtils::Install::uninstall($altpacklist, 1, 0); }; } $ERR = "$@" and return 0 if $@; # Update html table of contents, if ActivePerl::DocTools is installed: if (eval { require ActivePerl::DocTools; 1 }) { ActivePerl::DocTools::WriteTOC(); } # Remove the package and references to it: my $ppd_ref = $INST{$pkg}{ppd}; for my $impl ($ppd_ref->implementations) { my $match = 1; for my $field (keys %$impl) { next if ref($field); my $value = $inst->config_get($field); next unless defined $value; $match &&= ($value eq $impl->{$field}); } if ($match == 1) { del_dependent($_->name, $ppd_ref->name) for $impl->prereqs; last; } } purge_pkg($pkg); } else { $ERR = "package '$pkg' not installed."; return 0; } return 1; } sub precious { return @{$CONF{precious}}; } sub bundled { return @{$CONF{bundled}}; } sub upgrade { my ($inst, $pkg, $ppmpath, $ppd, $repos) = @_; remove($inst, $pkg); install($inst, $pkg, $ppmpath, $ppd, $repos); } sub install { my ($inst, $pkg, $ppmpath, $ppd, $repos) = @_; use Cwd qw(getcwd); my $cwd = getcwd(); # Install: # 1. chdir to temp directory chdir $ppmpath or do { $ERR = "can't chdir to $ppmpath: $!"; return 0; }; chdir $pkg; # this is expected to fail! reloc_perl('.') if $Config{osname} ne 'MSWin32'; # 2. use ExtUtils::MakeMaker to install the blib my $inst_archlib = $Config{installsitearch}; my $inst_root = $Config{prefix}; my $packlist = MM->catfile("$inst_archlib/auto", split(/-/, $pkg), ".packlist"); # Copied from ExtUtils::Install my $INST_LIB = MM->catdir(MM->curdir, "blib", "lib"); my $INST_ARCHLIB = MM->catdir(MM->curdir, "blib", "arch"); my $INST_BIN = MM->catdir(MM->curdir, "blib", "bin"); my $INST_SCRIPT = MM->catdir(MM->curdir, "blib", "script"); my $INST_MAN1DIR = MM->catdir(MM->curdir, "blib", "man1"); my $INST_MAN3DIR = MM->catdir(MM->curdir, "blib", "man3"); my $INST_HTMLDIR = MM->catdir(MM->curdir, "blib", "html"); my $INST_HTMLHELPDIR = MM->catdir(MM->curdir, "blib", "htmlhelp"); my $inst_script = $Config{installscript}; my $inst_man1dir = $Config{installman1dir}; my $inst_man3dir = $Config{installman3dir}; my $inst_bin = $Config{installbin}; my $inst_htmldir = $Config{installhtmldir}; my $inst_htmlhelpdir = $Config{installhtmlhelpdir}; my $inst_lib = $Config{installsitelib}; # For some reason, some boxes don't have installhtml* in Config.pm: $inst_htmldir ||= "$inst_bin/../html"; $inst_htmlhelpdir ||= "$inst_bin/../html"; if ($CONF{root} && $CONF{root} !~ /^\Q$inst_root\E$/i) { my $root = $CONF{root}; $packlist =~ s/\Q$inst_root/$root\E/i; $inst_lib =~ s/\Q$inst_root/$root\E/i; $inst_archlib =~ s/\Q$inst_root/$root\E/i; $inst_bin =~ s/\Q$inst_root/$root\E/i; $inst_script =~ s/\Q$inst_root/$root\E/i; $inst_man1dir =~ s/\Q$inst_root/$root\E/i; $inst_man3dir =~ s/\Q$inst_root/$root\E/i; $inst_htmldir =~ s/\Q$inst_root/$root\E/i; $inst_htmlhelpdir =~ s/\Q$inst_root/$root\E/i; $inst_root = $root; } while (1) { eval { ExtUtils::Install::install({ "read" => $packlist, "write" => $packlist, $INST_LIB => $inst_lib, $INST_ARCHLIB => $inst_archlib, $INST_BIN => $inst_bin, $INST_SCRIPT => $inst_script, $INST_MAN1DIR => $inst_man1dir, $INST_MAN3DIR => $inst_man3dir, $INST_HTMLDIR => $inst_htmldir, $INST_HTMLHELPDIR => $inst_htmlhelpdir},0,0,0); }; # install might have croaked in another directory chdir $cwd; # Can't remove some DLLs, but we can rename them and try again. if ($@ && $@ =~ /Cannot forceunlink (\S+)/) { my $oldname = $1; $oldname =~ s/:$//; my $newname = $oldname . "." . time(); unless (rename($oldname, $newname)) { $ERR = "renaming $oldname to $newname: $!"; return 0; } } # Some other error elsif($@) { $ERR = "$@"; return 0; } else { last; } } # Update html table of contents, if ActivePerl::DocTools is installed: if (eval { require ActivePerl::DocTools; 1 }) { ActivePerl::DocTools::WriteTOC(); } # XXX: Run the install script if it exists # Add the package to the list of installed packages my $ppd_ref = PPM::PPD->new($ppd); $INST{$pkg} = { pkg => { INSTDATE => scalar localtime, LOCATION => $repos, INSTROOT => $CONF{root}, INSTPACKLIST => $packlist, }, ppd => $ppd_ref, }; save_pkg($pkg); # "Register" the package as dependent on each prerequisite: # Note: because the PPM::PPD package's find_impl() is designed to use a # PPM::Installer() object, we can't use it. Instead, we have to do the # work ourselves, here: for my $impl ($ppd_ref->implementations) { my $match = 1; for my $field (keys %$impl) { next if ref($field); my $value = $inst->config_get($field); next unless defined $value; $match &&= ($value eq $impl->{$field}); } if ($match == 1) { add_dependent($_->name, $pkg) for $impl->prereqs; last; } } return 1; } sub config_keys { map { [$_, $keys{$_}] } keys %keys; } sub _str { my $a = shift; return '' unless defined $a; $a; } sub config_info { map { [$_, _str($CONF{$_})] } keys %keys; } sub config_set { my $inst = shift; my ($key, $val) = @_; unless (defined $keys{$key}) { $ERR = "unknown config key '$key'"; return 0; } $CONF{$key} = $val; return 1; } sub config_get { my $inst = shift; my $key = shift; unless (defined $key and exists $keys{$key}) { $key = '' unless defined $key; $ERR = "unknown config key '$key'"; return undef; } _str($CONF{$key}); } sub error_str { defined $ERR ? $ERR : 'No error'; } #---------------------------------------------------------------------------- # Utilities #---------------------------------------------------------------------------- # This can deal with files as well as directories sub abspath { use Cwd qw(abs_path); my ($path, $file) = shift; if (-f $path) { my @p = split '/', $path; $path = join '/', @p[0..$#p-1]; # can't use -2 in a range $file = $p[-1]; } $path = abs_path($path); return ($path, $file, defined $file ? join '/', $path, $file : ()) if wantarray; return defined $file ? join '/', $path, $file : $path; } #============================================================================ # Relocate Perl (stolen from PPM::RelocPerl) #============================================================================ my $frompath_default; BEGIN { # We have to build up this variable, otherwise # PPM will mash it when it upgrades itself. $frompath_default = ('/tmp' . '/.ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZpErLZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZperl' ); } my ($topath, $frompath); sub wanted { if (-l) { return; # do nothing for symlinks } elsif (-B) { check_for_frompath($_, 1); # binary file edit } elsif (-e && -s && -f) { check_for_frompath($_, 0); # text file edit } } sub check_for_frompath { my ($file, $binmode) = @_; local(*F, $_); open(F, "<$file") or die "Can't open `$file': $!"; binmode F if $binmode; while () { if (/\Q$frompath\E/o) { close F; edit_it($file, $binmode); last; } } # implicit close of F; } sub edit_it { my ($file, $binmode) = @_; my $nullpad = length($frompath) - length($topath); $nullpad = "\0" x $nullpad; local $/; # Force the file to be writable my $mode = (stat($file))[2] & 07777; chmod $mode | 0222, $file; open(F, "+<$file") or die "Couldn't open $file: $!"; binmode(F) if $binmode; my $dat = ; if ($binmode) { $dat =~ s|\Q$frompath\E(.*?)\0|$topath$1$nullpad\0|gs; } else { $dat =~ s|\Q$frompath\E|$topath|gs; } seek(F, 0, 0) or die "Couldn't seek on $file: $!"; truncate(F, 0); print F $dat; close(F); # Restore the permissions chmod $mode, $file; } use File::Find; sub reloc_perl { my ($dir, $opt_topath, $opt_frompath) = @_; $topath = defined $opt_topath ? $opt_topath : $Config{'prefix'}; $frompath = defined $opt_frompath ? $opt_frompath : $frompath_default; find(\&wanted, $dir); } #============================================================================ # Settings and packages #============================================================================ my ($conf_dir, $conf, $conf_obj); BEGIN { # By putting an invalid package character in the directory, we're making # sure no real package could overwrite our settings, and vice versa. $conf_dir = join '/', $Config{sitelib}, 'ppm-conf'; $conf = join '/', $conf_dir, 'ppm.cfg'; } # Loads the configuration file and populates %CONF sub load_conf { $conf_obj = PPM::Config->new($conf); %CONF = $conf_obj->config; # Special values; set them here $CONF{ARCHITECTURE} = $Config{archname}; $CONF{PERLCORE} = $Config{version}; $CONF{TARGET_TYPE} = "perl"; $CONF{VERSION} = '3.00'; } # Saves %CONF to the configuration file sub save_conf { $conf_obj->merge(\%CONF); $conf_obj->save; } # Loads the given package into $INST{$pkg}. Returns true if the package could # be loaded, false otherwise. sub load_pkg { my $pkg = shift; return 1 if exists $INST{$pkg}; return 0 unless -f "$conf_dir/$pkg.ppd"; return 0 unless -f "$conf_dir/$pkg.pkg"; my $ppdref = PPM::PPD->new("$conf_dir/$pkg.ppd"); my $pkgfile = "$conf_dir/$pkg.pkg"; my $pkgref = PPM::Config->new($pkgfile); $INST{$pkg}{ppd} = $ppdref; $INST{$pkg}{pkg} = $pkgref->config; return 1; } # Saves the given package from $INST{$pkg}. sub save_pkg { my $pkg = shift; return 0 unless exists $INST{$pkg}; # The PPD file: my $ppdfile = "$conf_dir/$pkg.ppd"; if (-f $ppdfile) { unlink $ppdfile or die "$0: can't delete $ppdfile: $!"; } open PPD, "> $ppdfile" or die "$0: can't write $ppdfile: $!"; print PPD $INST{$pkg}{ppd}->ppd; close PPD or die "$0: can't close $ppdfile: $!"; # the PKG file: my $c = PPM::Config->new; $c->load($INST{$pkg}{pkg}); $c->save("$conf_dir/$pkg.pkg"); return 1; } sub add_dependent { my $package = shift; my $dependent = shift; return 0 unless load_pkg($package); push @{$INST{$package}{pkg}{dependents}}, $dependent; save_pkg($package); } sub del_dependent { my $package = shift; my $dependent = shift; return 0 unless load_pkg($package); @{$INST{$package}{pkg}{dependents}} = grep { $_ ne $dependent } @{$INST{$package}{pkg}{dependents}}; save_pkg($package); } sub purge_pkg { my $pkg = shift; # The PPD file: my $ppdfile = "$conf_dir/$pkg.ppd"; if (-f $ppdfile) { unlink $ppdfile or die "$0: can't delete $ppdfile: $!"; } # The %INST entry: delete $INST{$pkg}; # The PKG file: my $pkgfile = "$conf_dir/$pkg.pkg"; if (-f $pkgfile) { unlink $pkgfile or die "$0: can't delete $pkgfile: $!"; } return 1; } # Load all packages: only needed when doing an advanced query sub load_pkgs { my @pkgs = map { s/\.ppd$//; s!.*/([^/]+)$!$1!g; $_ } #! glob "$conf_dir/*.ppd"; load_pkg($_) for @pkgs; } sub pkg_installed { my $pkg = shift; return -f "$conf_dir/$pkg.ppd" && -f "$conf_dir/$pkg.pkg"; } BEGIN { load_conf(); } END { save_conf(); } __END__ :endofperl_ppminst