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