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