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