1b8851fccSafresh1package ExtUtils::Packlist; 2b8851fccSafresh1use strict; 3eac174f2Safresh1 4b8851fccSafresh1use Carp qw(); 5b8851fccSafresh1use Config; 6eac174f2Safresh1our $Relocations; 7*e0680481Safresh1our $VERSION = '2.22'; 8b8851fccSafresh1$VERSION = eval $VERSION; 9b8851fccSafresh1 10b8851fccSafresh1# Used for generating filehandle globs. IO::File might not be available! 11b8851fccSafresh1my $fhname = "FH1"; 12b8851fccSafresh1 13b8851fccSafresh1=begin _undocumented 14b8851fccSafresh1 15b8851fccSafresh1=over 16b8851fccSafresh1 17b8851fccSafresh1=item mkfh() 18b8851fccSafresh1 19b8851fccSafresh1Make a filehandle. Same kind of idea as Symbol::gensym(). 20b8851fccSafresh1 21b8851fccSafresh1=cut 22b8851fccSafresh1 23b8851fccSafresh1sub mkfh() 24b8851fccSafresh1{ 25b8851fccSafresh1no strict; 26b8851fccSafresh1local $^W; 27b8851fccSafresh1my $fh = \*{$fhname++}; 28b8851fccSafresh1use strict; 29b8851fccSafresh1return($fh); 30b8851fccSafresh1} 31b8851fccSafresh1 32b8851fccSafresh1=item __find_relocations 33b8851fccSafresh1 34b8851fccSafresh1Works out what absolute paths in the configuration have been located at run 35b8851fccSafresh1time relative to $^X, and generates a regexp that matches them 36b8851fccSafresh1 37b8851fccSafresh1=back 38b8851fccSafresh1 39b8851fccSafresh1=end _undocumented 40b8851fccSafresh1 41b8851fccSafresh1=cut 42b8851fccSafresh1 43b8851fccSafresh1sub __find_relocations 44b8851fccSafresh1{ 45b8851fccSafresh1 my %paths; 46b8851fccSafresh1 while (my ($raw_key, $raw_val) = each %Config) { 47b8851fccSafresh1 my $exp_key = $raw_key . "exp"; 48b8851fccSafresh1 next unless exists $Config{$exp_key}; 49b8851fccSafresh1 next unless $raw_val =~ m!\.\.\./!; 50b8851fccSafresh1 $paths{$Config{$exp_key}}++; 51b8851fccSafresh1 } 52b8851fccSafresh1 # Longest prefixes go first in the alternatives 53b8851fccSafresh1 my $alternations = join "|", map {quotemeta $_} 54b8851fccSafresh1 sort {length $b <=> length $a} keys %paths; 55b8851fccSafresh1 qr/^($alternations)/o; 56b8851fccSafresh1} 57b8851fccSafresh1 58b8851fccSafresh1sub new($$) 59b8851fccSafresh1{ 60b8851fccSafresh1my ($class, $packfile) = @_; 61b8851fccSafresh1$class = ref($class) || $class; 62b8851fccSafresh1my %self; 63b8851fccSafresh1tie(%self, $class, $packfile); 64b8851fccSafresh1return(bless(\%self, $class)); 65b8851fccSafresh1} 66b8851fccSafresh1 67b8851fccSafresh1sub TIEHASH 68b8851fccSafresh1{ 69b8851fccSafresh1my ($class, $packfile) = @_; 70b8851fccSafresh1my $self = { packfile => $packfile }; 71b8851fccSafresh1bless($self, $class); 72b8851fccSafresh1$self->read($packfile) if (defined($packfile) && -f $packfile); 73b8851fccSafresh1return($self); 74b8851fccSafresh1} 75b8851fccSafresh1 76b8851fccSafresh1sub STORE 77b8851fccSafresh1{ 78b8851fccSafresh1$_[0]->{data}->{$_[1]} = $_[2]; 79b8851fccSafresh1} 80b8851fccSafresh1 81b8851fccSafresh1sub FETCH 82b8851fccSafresh1{ 83b8851fccSafresh1return($_[0]->{data}->{$_[1]}); 84b8851fccSafresh1} 85b8851fccSafresh1 86b8851fccSafresh1sub FIRSTKEY 87b8851fccSafresh1{ 88b8851fccSafresh1my $reset = scalar(keys(%{$_[0]->{data}})); 89b8851fccSafresh1return(each(%{$_[0]->{data}})); 90b8851fccSafresh1} 91b8851fccSafresh1 92b8851fccSafresh1sub NEXTKEY 93b8851fccSafresh1{ 94b8851fccSafresh1return(each(%{$_[0]->{data}})); 95b8851fccSafresh1} 96b8851fccSafresh1 97b8851fccSafresh1sub EXISTS 98b8851fccSafresh1{ 99b8851fccSafresh1return(exists($_[0]->{data}->{$_[1]})); 100b8851fccSafresh1} 101b8851fccSafresh1 102b8851fccSafresh1sub DELETE 103b8851fccSafresh1{ 104b8851fccSafresh1return(delete($_[0]->{data}->{$_[1]})); 105b8851fccSafresh1} 106b8851fccSafresh1 107b8851fccSafresh1sub CLEAR 108b8851fccSafresh1{ 109b8851fccSafresh1%{$_[0]->{data}} = (); 110b8851fccSafresh1} 111b8851fccSafresh1 112b8851fccSafresh1sub DESTROY 113b8851fccSafresh1{ 114b8851fccSafresh1} 115b8851fccSafresh1 116b8851fccSafresh1sub read($;$) 117b8851fccSafresh1{ 118b8851fccSafresh1my ($self, $packfile) = @_; 119b8851fccSafresh1$self = tied(%$self) || $self; 120b8851fccSafresh1 121b8851fccSafresh1if (defined($packfile)) { $self->{packfile} = $packfile; } 122b8851fccSafresh1else { $packfile = $self->{packfile}; } 123b8851fccSafresh1Carp::croak("No packlist filename specified") if (! defined($packfile)); 124b8851fccSafresh1my $fh = mkfh(); 125b8851fccSafresh1open($fh, "<$packfile") || Carp::croak("Can't open file $packfile: $!"); 126b8851fccSafresh1$self->{data} = {}; 127b8851fccSafresh1my ($line); 128b8851fccSafresh1while (defined($line = <$fh>)) 129b8851fccSafresh1 { 130b8851fccSafresh1 chomp $line; 131b8851fccSafresh1 my ($key, $data) = $line; 132b8851fccSafresh1 if ($key =~ /^(.*?)( \w+=.*)$/) 133b8851fccSafresh1 { 134b8851fccSafresh1 $key = $1; 135b8851fccSafresh1 $data = { map { split('=', $_) } split(' ', $2)}; 136b8851fccSafresh1 137b8851fccSafresh1 if ($Config{userelocatableinc} && $data->{relocate_as}) 138b8851fccSafresh1 { 139b8851fccSafresh1 require File::Spec; 140b8851fccSafresh1 require Cwd; 141b8851fccSafresh1 my ($vol, $dir) = File::Spec->splitpath($packfile); 142b8851fccSafresh1 my $newpath = File::Spec->catpath($vol, $dir, $data->{relocate_as}); 143b8851fccSafresh1 $key = Cwd::realpath($newpath); 144b8851fccSafresh1 } 145b8851fccSafresh1 } 146b8851fccSafresh1 $key =~ s!/\./!/!g; # Some .packlists have spurious '/./' bits in the paths 147b8851fccSafresh1 $self->{data}->{$key} = $data; 148b8851fccSafresh1 } 149b8851fccSafresh1close($fh); 150b8851fccSafresh1} 151b8851fccSafresh1 152b8851fccSafresh1sub write($;$) 153b8851fccSafresh1{ 154b8851fccSafresh1my ($self, $packfile) = @_; 155b8851fccSafresh1$self = tied(%$self) || $self; 156b8851fccSafresh1if (defined($packfile)) { $self->{packfile} = $packfile; } 157b8851fccSafresh1else { $packfile = $self->{packfile}; } 158b8851fccSafresh1Carp::croak("No packlist filename specified") if (! defined($packfile)); 159b8851fccSafresh1my $fh = mkfh(); 160b8851fccSafresh1open($fh, ">$packfile") || Carp::croak("Can't open file $packfile: $!"); 161b8851fccSafresh1foreach my $key (sort(keys(%{$self->{data}}))) 162b8851fccSafresh1 { 163b8851fccSafresh1 my $data = $self->{data}->{$key}; 164b8851fccSafresh1 if ($Config{userelocatableinc}) { 165b8851fccSafresh1 $Relocations ||= __find_relocations(); 166b8851fccSafresh1 if ($packfile =~ $Relocations) { 167b8851fccSafresh1 # We are writing into a subdirectory of a run-time relocated 168b8851fccSafresh1 # path. Figure out if the this file is also within a subdir. 169b8851fccSafresh1 my $prefix = $1; 170b8851fccSafresh1 if (File::Spec->no_upwards(File::Spec->abs2rel($key, $prefix))) 171b8851fccSafresh1 { 172b8851fccSafresh1 # The relocated path is within the found prefix 173b8851fccSafresh1 my $packfile_prefix; 174b8851fccSafresh1 (undef, $packfile_prefix) 175b8851fccSafresh1 = File::Spec->splitpath($packfile); 176b8851fccSafresh1 177b8851fccSafresh1 my $relocate_as 178b8851fccSafresh1 = File::Spec->abs2rel($key, $packfile_prefix); 179b8851fccSafresh1 180b8851fccSafresh1 if (!ref $data) { 181b8851fccSafresh1 $data = {}; 182b8851fccSafresh1 } 183b8851fccSafresh1 $data->{relocate_as} = $relocate_as; 184b8851fccSafresh1 } 185b8851fccSafresh1 } 186b8851fccSafresh1 } 187b8851fccSafresh1 print $fh ("$key"); 188b8851fccSafresh1 if (ref($data)) 189b8851fccSafresh1 { 190b8851fccSafresh1 foreach my $k (sort(keys(%$data))) 191b8851fccSafresh1 { 192b8851fccSafresh1 print $fh (" $k=$data->{$k}"); 193b8851fccSafresh1 } 194b8851fccSafresh1 } 195b8851fccSafresh1 print $fh ("\n"); 196b8851fccSafresh1 } 197b8851fccSafresh1close($fh); 198b8851fccSafresh1} 199b8851fccSafresh1 200b8851fccSafresh1sub validate($;$) 201b8851fccSafresh1{ 202b8851fccSafresh1my ($self, $remove) = @_; 203b8851fccSafresh1$self = tied(%$self) || $self; 204b8851fccSafresh1my @missing; 205b8851fccSafresh1foreach my $key (sort(keys(%{$self->{data}}))) 206b8851fccSafresh1 { 207b8851fccSafresh1 if (! -e $key) 208b8851fccSafresh1 { 209b8851fccSafresh1 push(@missing, $key); 210b8851fccSafresh1 delete($self->{data}{$key}) if ($remove); 211b8851fccSafresh1 } 212b8851fccSafresh1 } 213b8851fccSafresh1return(@missing); 214b8851fccSafresh1} 215b8851fccSafresh1 216b8851fccSafresh1sub packlist_file($) 217b8851fccSafresh1{ 218b8851fccSafresh1my ($self) = @_; 219b8851fccSafresh1$self = tied(%$self) || $self; 220b8851fccSafresh1return($self->{packfile}); 221b8851fccSafresh1} 222b8851fccSafresh1 223b8851fccSafresh11; 224b8851fccSafresh1 225b8851fccSafresh1__END__ 226b8851fccSafresh1 227b8851fccSafresh1=head1 NAME 228b8851fccSafresh1 229b8851fccSafresh1ExtUtils::Packlist - manage .packlist files 230b8851fccSafresh1 231b8851fccSafresh1=head1 SYNOPSIS 232b8851fccSafresh1 233b8851fccSafresh1 use ExtUtils::Packlist; 234b8851fccSafresh1 my ($pl) = ExtUtils::Packlist->new('.packlist'); 235b8851fccSafresh1 $pl->read('/an/old/.packlist'); 236b8851fccSafresh1 my @missing_files = $pl->validate(); 237b8851fccSafresh1 $pl->write('/a/new/.packlist'); 238b8851fccSafresh1 239b8851fccSafresh1 $pl->{'/some/file/name'}++; 240b8851fccSafresh1 or 241b8851fccSafresh1 $pl->{'/some/other/file/name'} = { type => 'file', 242b8851fccSafresh1 from => '/some/file' }; 243b8851fccSafresh1 244b8851fccSafresh1=head1 DESCRIPTION 245b8851fccSafresh1 246b8851fccSafresh1ExtUtils::Packlist provides a standard way to manage .packlist files. 247b8851fccSafresh1Functions are provided to read and write .packlist files. The original 248b8851fccSafresh1.packlist format is a simple list of absolute pathnames, one per line. In 249b8851fccSafresh1addition, this package supports an extended format, where as well as a filename 250b8851fccSafresh1each line may contain a list of attributes in the form of a space separated 251b8851fccSafresh1list of key=value pairs. This is used by the installperl script to 252b8851fccSafresh1differentiate between files and links, for example. 253b8851fccSafresh1 254b8851fccSafresh1=head1 USAGE 255b8851fccSafresh1 256b8851fccSafresh1The hash reference returned by the new() function can be used to examine and 257b8851fccSafresh1modify the contents of the .packlist. Items may be added/deleted from the 258b8851fccSafresh1.packlist by modifying the hash. If the value associated with a hash key is a 259b8851fccSafresh1scalar, the entry written to the .packlist by any subsequent write() will be a 260b8851fccSafresh1simple filename. If the value is a hash, the entry written will be the 261b8851fccSafresh1filename followed by the key=value pairs from the hash. Reading back the 262b8851fccSafresh1.packlist will recreate the original entries. 263b8851fccSafresh1 264b8851fccSafresh1=head1 FUNCTIONS 265b8851fccSafresh1 266b8851fccSafresh1=over 4 267b8851fccSafresh1 268b8851fccSafresh1=item new() 269b8851fccSafresh1 270b8851fccSafresh1This takes an optional parameter, the name of a .packlist. If the file exists, 271b8851fccSafresh1it will be opened and the contents of the file will be read. The new() method 272b8851fccSafresh1returns a reference to a hash. This hash holds an entry for each line in the 273b8851fccSafresh1.packlist. In the case of old-style .packlists, the value associated with each 274b8851fccSafresh1key is undef. In the case of new-style .packlists, the value associated with 275b8851fccSafresh1each key is a hash containing the key=value pairs following the filename in the 276b8851fccSafresh1.packlist. 277b8851fccSafresh1 278b8851fccSafresh1=item read() 279b8851fccSafresh1 280b8851fccSafresh1This takes an optional parameter, the name of the .packlist to be read. If 281b8851fccSafresh1no file is specified, the .packlist specified to new() will be read. If the 282b8851fccSafresh1.packlist does not exist, Carp::croak will be called. 283b8851fccSafresh1 284b8851fccSafresh1=item write() 285b8851fccSafresh1 286b8851fccSafresh1This takes an optional parameter, the name of the .packlist to be written. If 287b8851fccSafresh1no file is specified, the .packlist specified to new() will be overwritten. 288b8851fccSafresh1 289b8851fccSafresh1=item validate() 290b8851fccSafresh1 291b8851fccSafresh1This checks that every file listed in the .packlist actually exists. If an 292b8851fccSafresh1argument which evaluates to true is given, any missing files will be removed 293b8851fccSafresh1from the internal hash. The return value is a list of the missing files, which 294b8851fccSafresh1will be empty if they all exist. 295b8851fccSafresh1 296b8851fccSafresh1=item packlist_file() 297b8851fccSafresh1 298b8851fccSafresh1This returns the name of the associated .packlist file 299b8851fccSafresh1 300b8851fccSafresh1=back 301b8851fccSafresh1 302b8851fccSafresh1=head1 EXAMPLE 303b8851fccSafresh1 304b8851fccSafresh1Here's C<modrm>, a little utility to cleanly remove an installed module. 305b8851fccSafresh1 306b8851fccSafresh1 #!/usr/local/bin/perl -w 307b8851fccSafresh1 308b8851fccSafresh1 use strict; 309b8851fccSafresh1 use IO::Dir; 310b8851fccSafresh1 use ExtUtils::Packlist; 311b8851fccSafresh1 use ExtUtils::Installed; 312b8851fccSafresh1 313b8851fccSafresh1 sub emptydir($) { 314b8851fccSafresh1 my ($dir) = @_; 315b8851fccSafresh1 my $dh = IO::Dir->new($dir) || return(0); 316b8851fccSafresh1 my @count = $dh->read(); 317b8851fccSafresh1 $dh->close(); 318b8851fccSafresh1 return(@count == 2 ? 1 : 0); 319b8851fccSafresh1 } 320b8851fccSafresh1 321b8851fccSafresh1 # Find all the installed packages 322b8851fccSafresh1 print("Finding all installed modules...\n"); 323b8851fccSafresh1 my $installed = ExtUtils::Installed->new(); 324b8851fccSafresh1 325b8851fccSafresh1 foreach my $module (grep(!/^Perl$/, $installed->modules())) { 326b8851fccSafresh1 my $version = $installed->version($module) || "???"; 327b8851fccSafresh1 print("Found module $module Version $version\n"); 328b8851fccSafresh1 print("Do you want to delete $module? [n] "); 329b8851fccSafresh1 my $r = <STDIN>; chomp($r); 330b8851fccSafresh1 if ($r && $r =~ /^y/i) { 331b8851fccSafresh1 # Remove all the files 332b8851fccSafresh1 foreach my $file (sort($installed->files($module))) { 333b8851fccSafresh1 print("rm $file\n"); 334b8851fccSafresh1 unlink($file); 335b8851fccSafresh1 } 336b8851fccSafresh1 my $pf = $installed->packlist($module)->packlist_file(); 337b8851fccSafresh1 print("rm $pf\n"); 338b8851fccSafresh1 unlink($pf); 339b8851fccSafresh1 foreach my $dir (sort($installed->directory_tree($module))) { 340b8851fccSafresh1 if (emptydir($dir)) { 341b8851fccSafresh1 print("rmdir $dir\n"); 342b8851fccSafresh1 rmdir($dir); 343b8851fccSafresh1 } 344b8851fccSafresh1 } 345b8851fccSafresh1 } 346b8851fccSafresh1 } 347b8851fccSafresh1 348b8851fccSafresh1=head1 AUTHOR 349b8851fccSafresh1 350b8851fccSafresh1Alan Burlison <Alan.Burlison@uk.sun.com> 351b8851fccSafresh1 352b8851fccSafresh1=cut 353