Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / File / ShareDir.pm
CommitLineData
3fea05b9 1package File::ShareDir;
2
3=pod
4
5=head1 NAME
6
7File::ShareDir - Locate per-dist and per-module shared files
8
9=head1 SYNOPSIS
10
11 use File::ShareDir ':ALL';
12
13 # Where are distribution-level shared data files kept
14 $dir = dist_dir('File-ShareDir');
15
16 # Where are module-level shared data files kept
17 $dir = module_dir('File::ShareDir');
18
19 # Find a specific file in our dist/module shared dir
20 $file = dist_file( 'File-ShareDir', 'file/name.txt');
21 $file = module_file('File::ShareDir', 'file/name.txt');
22
23 # Like module_file, but search up the inheritance tree
24 $file = class_file( 'Foo::Bar', 'file/name.txt' );
25
26=head1 DESCRIPTION
27
28The intent of L<File::ShareDir> is to provide a companion to
29L<Class::Inspector> and L<File::HomeDir>, modules that take a
30process that is well-known by advanced Perl developers but gets a
31little tricky, and make it more available to the larger Perl community.
32
33Quite often you want or need your Perl module (CPAN or otherwise)
34to have access to a large amount of read-only data that is stored
35on the file-system at run-time.
36
37On a linux-like system, this would be in a place such as /usr/share,
38however Perl runs on a wide variety of different systems, and so
39the use of any one location is unreliable.
40
41Perl provides a little-known method for doing this, but almost
42nobody is aware that it exists. As a result, module authors often
43go through some very strange ways to make the data available to
44their code.
45
46The most common of these is to dump the data out to an enormous
47Perl data structure and save it into the module itself. The
48result are enormous multi-megabyte .pm files that chew up a
49lot of memory needlessly.
50
51Another method is to put the data "file" after the __DATA__ compiler
52tag and limit yourself to access as a filehandle.
53
54The problem to solve is really quite simple.
55
56 1. Write the data files to the system at install time.
57
58 2. Know where you put them at run-time.
59
60Perl's install system creates an "auto" directory for both
61every distribution and for every module file.
62
63These are used by a couple of different auto-loading systems
64to store code fragments generated at install time, and various
65other modules written by the Perl "ancient masters".
66
67But the same mechanism is available to any dist or module to
68store any sort of data.
69
70=head2 Using Data in your Module
71
72C<File::ShareDir> forms one half of a two part solution.
73
74Once the files have been installed to the correct directory,
75you can use C<File::ShareDir> to find your files again after
76the installation.
77
78For the installation half of the solution, see L<Module::Install>
79and its C<install_share> directive.
80
81=head1 FUNCTIONS
82
83C<File::ShareDir> provides four functions for locating files and
84directories.
85
86For greater maintainability, none of these are exported by default
87and you are expected to name the ones you want at use-time, or provide
88the C<':ALL'> tag. All of the following are equivalent.
89
90 # Load but don't import, and then call directly
91 use File::ShareDir;
92 $dir = File::ShareDir::dist_dir('My-Dist');
93
94 # Import a single function
95 use File::ShareDir 'dist_dir';
96 dist_dir('My-Dist');
97
98 # Import all the functions
99 use File::ShareDir ':ALL';
100 dist_dir('My-Dist');
101
102All of the functions will check for you that the dir/file actually
103exists, and that you have read permissions, or they will throw an
104exception.
105
106=cut
107
108use 5.005;
109use strict;
110use Carp 'croak';
111use Config ();
112use Exporter ();
113use File::Spec ();
114use Params::Util '_CLASS';
115use Class::Inspector ();
116
117use vars qw{$VERSION @ISA @EXPORT_OK %EXPORT_TAGS};
118BEGIN {
119 $VERSION = '1.01';
120 @ISA = qw{ Exporter };
121 @EXPORT_OK = qw{
122 dist_dir dist_file
123 module_dir module_file
124 class_dir class_file
125 };
126 %EXPORT_TAGS = (
127 ALL => [ @EXPORT_OK ],
128 );
129}
130
131use constant IS_MACOS => !!($^O eq 'MacOS');
132
133
134
135
136
137#####################################################################
138# Interface Functions
139
140=pod
141
142=head2 dist_dir
143
144 # Get a distribution's shared files directory
145 my $dir = dist_dir('My-Distribution');
146
147The C<dist_dir> function takes a single parameter of the name of an
148installed (CPAN or otherwise) distribution, and locates the shared
149data directory created at install time for it.
150
151Returns the directory path as a string, or dies if it cannot be
152located or is not readable.
153
154=cut
155
156sub dist_dir {
157 my $dist = _DIST(shift);
158 my $dir;
159
160 # Try the new version
161 $dir = _dist_dir_new( $dist );
162 return $dir if defined $dir;
163
164 # Fall back to the legacy version
165 $dir = _dist_dir_old( $dist );
166 return $dir if defined $dir;
167
168 # Ran out of options
169 croak("Failed to find share dir for dist '$dist'");
170}
171
172sub _dist_dir_new {
173 my $dist = shift;
174
175 # Create the subpath
176 my $path = File::Spec->catdir(
177 'auto', 'share', 'dist', $dist,
178 );
179
180 # Find the full dir withing @INC
181 foreach my $inc ( @INC ) {
182 next unless defined $inc and ! ref $inc;
183 my $dir = File::Spec->catdir( $inc, $path );
184 next unless -d $dir;
185 unless ( -r $dir ) {
186 croak("Found directory '$dir', but no read permissions");
187 }
188 return $dir;
189 }
190
191 return undef;
192}
193
194sub _dist_dir_old {
195 my $dist = shift;
196
197 # Create the subpath
198 my $path = File::Spec->catdir(
199 'auto', split( /-/, $dist ),
200 );
201
202 # Find the full dir within @INC
203 foreach my $inc ( @INC ) {
204 next unless defined $inc and ! ref $inc;
205 my $dir = File::Spec->catdir( $inc, $path );
206 next unless -d $dir;
207 unless ( -r $dir ) {
208 croak("Found directory '$dir', but no read permissions");
209 }
210 return $dir;
211 }
212
213 return undef;
214}
215
216=pod
217
218=head2 module_dir
219
220 # Get a module's shared files directory
221 my $dir = module_dir('My::Module');
222
223The C<module_dir> function takes a single parameter of the name of an
224installed (CPAN or otherwise) module, and locates the shared data
225directory created at install time for it.
226
227In order to find the directory, the module B<must> be loaded when
228calling this function.
229
230Returns the directory path as a string, or dies if it cannot be
231located or is not readable.
232
233=cut
234
235sub module_dir {
236 my $module = _MODULE(shift);
237 my $dir;
238
239 # Try the new version
240 $dir = _module_dir_new( $module );
241 return $dir if defined $dir;
242
243 # Fall back to the legacy version
244 return _module_dir_old( $module );
245}
246
247sub _module_dir_new {
248 my $module = shift;
249
250 # Create the subpath
251 my $path = File::Spec->catdir(
252 'auto', 'share', 'module',
253 _module_subdir( $module ),
254 );
255
256 # Find the full dir withing @INC
257 foreach my $inc ( @INC ) {
258 next unless defined $inc and ! ref $inc;
259 my $dir = File::Spec->catdir( $inc, $path );
260 next unless -d $dir;
261 unless ( -r $dir ) {
262 croak("Found directory '$dir', but no read permissions");
263 }
264 return $dir;
265 }
266
267 return undef;
268}
269
270sub _module_dir_old {
271 my $module = shift;
272 my $short = Class::Inspector->filename($module);
273 my $long = Class::Inspector->loaded_filename($module);
274 $short =~ tr{/}{:} if IS_MACOS;
275 substr( $short, -3, 3, '' );
276 $long =~ m/^(.*)\Q$short\E\.pm\z/s or die("Failed to find base dir");
277 my $dir = File::Spec->catdir( "$1", 'auto', $short );
278 unless ( -d $dir ) {
279 croak("Directory '$dir', does not exist");
280 }
281 unless ( -r $dir ) {
282 croak("Directory '$dir', no read permissions");
283 }
284 return $dir;
285}
286
287=pod
288
289=head2 dist_file
290
291 # Find a file in our distribution shared dir
292 my $dir = dist_file('My-Distribution', 'file/name.txt');
293
294The C<dist_file> function takes two params of the distribution name
295and file name, locates the dist dir, and then finds the file within
296it, verifying that the file actually exists, and that it is readable.
297
298The filename should be a relative path in the format of your local
299filesystem. It will simply added to the directory using L<File::Spec>'s
300C<catfile> method.
301
302Returns the file path as a string, or dies if the file or the dist's
303directory cannot be located, or the file is not readable.
304
305=cut
306
307sub dist_file {
308 my $dist = _DIST(shift);
309 my $file = _FILE(shift);
310
311 # Try the new version first
312 my $path = _dist_file_new( $dist, $file );
313 return $path if defined $path;
314
315 # Hand off to the legacy version
316 return _dist_file_old( $dist, $file );;
317}
318
319sub _dist_file_new {
320 my $dist = shift;
321 my $file = shift;
322
323 # If it exists, what should the path be
324 my $dir = _dist_dir_new( $dist );
325 my $path = File::Spec->catfile( $dir, $file );
326
327 # Does the file exist
328 return undef unless -e $path;
329 unless ( -f $path ) {
330 croak("Found dist_file '$path', but not a file");
331 }
332 unless ( -r $path ) {
333 croak("File '$path', no read permissions");
334 }
335
336 return $path;
337}
338
339sub _dist_file_old {
340 my $dist = shift;
341 my $file = shift;
342
343 # Create the subpath
344 my $path = File::Spec->catfile(
345 'auto', split( /-/, $dist ), $file,
346 );
347
348 # Find the full dir withing @INC
349 foreach my $inc ( @INC ) {
350 next unless defined $inc and ! ref $inc;
351 my $full = File::Spec->catdir( $inc, $path );
352 next unless -e $full;
353 unless ( -r $full ) {
354 croak("Directory '$full', no read permissions");
355 }
356 return $full;
357 }
358
359 # Couldn't find it
360 croak("Failed to find shared file '$file' for dist '$dist'");
361}
362
363=pod
364
365=head2 module_file
366
367 # Find a file in our module shared dir
368 my $dir = module_file('My::Module', 'file/name.txt');
369
370The C<module_file> function takes two params of the module name
371and file name. It locates the module dir, and then finds the file within
372it, verifying that the file actually exists, and that it is readable.
373
374In order to find the directory, the module B<must> be loaded when
375calling this function.
376
377The filename should be a relative path in the format of your local
378filesystem. It will simply added to the directory using L<File::Spec>'s
379C<catfile> method.
380
381Returns the file path as a string, or dies if the file or the dist's
382directory cannot be located, or the file is not readable.
383
384=cut
385
386sub module_file {
387 my $module = _MODULE(shift);
388 my $file = _FILE(shift);
389 my $dir = module_dir($module);
390 my $path = File::Spec->catfile($dir, $file);
391 unless ( -e $path ) {
392 croak("File '$file' does not exist in module dir");
393 }
394 unless ( -r $path ) {
395 croak("File '$file' cannot be read, no read permissions");
396 }
397 $path;
398}
399
400=pod
401
402=head2 class_file
403
404 # Find a file in our module shared dir, or in our parent class
405 my $dir = class_file('My::Module', 'file/name.txt');
406
407The C<module_file> function takes two params of the module name
408and file name. It locates the module dir, and then finds the file within
409it, verifying that the file actually exists, and that it is readable.
410
411In order to find the directory, the module B<must> be loaded when
412calling this function.
413
414The filename should be a relative path in the format of your local
415filesystem. It will simply added to the directory using L<File::Spec>'s
416C<catfile> method.
417
418If the file is NOT found for that module, C<class_file> will scan up
419the module's @ISA tree, looking for the file in all of the parent
420classes.
421
422This allows you to, in effect, "subclass" shared files.
423
424Returns the file path as a string, or dies if the file or the dist's
425directory cannot be located, or the file is not readable.
426
427=cut
428
429sub class_file {
430 my $module = _MODULE(shift);
431 my $file = _FILE(shift);
432
433 # Get the super path ( not including UNIVERSAL )
434 # Rather than using Class::ISA, we'll use an inlined version
435 # that implements the same basic algorithm.
436 my @path = ();
437 my @queue = ( $module );
438 my %seen = ( $module => 1 );
439 while ( my $cl = shift @queue ) {
440 push @path, $cl;
441 no strict 'refs';
442 unshift @queue, grep { ! $seen{$_}++ }
443 map { s/^::/main::/; s/\'/::/g; $_ }
444 ( @{"${cl}::ISA"} );
445 }
446
447 # Search up the path
448 foreach my $class ( @path ) {
449 local $@;
450 my $dir = eval {
451 module_dir($class);
452 };
453 next if $@;
454 my $path = File::Spec->catfile($dir, $file);
455 unless ( -e $path ) {
456 next;
457 }
458 unless ( -r $path ) {
459 croak("File '$file' cannot be read, no read permissions");
460 }
461 return $path;
462 }
463 croak("File '$file' does not exist in class or parent shared files");
464}
465
466
467
468
469#####################################################################
470# Support Functions
471
472sub _module_subdir {
473 my $module = shift;
474 $module =~ s/::/-/g;
475 return $module;
476}
477
478sub _dist_packfile {
479 my $module = shift;
480 my @dirs = grep { -e } ( $Config::Config{archlibexp}, $Config::Config{sitearchexp} );
481 my $file = File::Spec->catfile(
482 'auto', split( /::/, $module), '.packlist',
483 );
484
485 foreach my $dir ( @dirs ) {
486 my $path = File::Spec->catfile( $dir, $file );
487 next unless -f $path;
488
489 # Load the file
490 my $packlist = ExtUtils::Packlist->new($path);
491 unless ( $packlist ) {
492 die "Failed to load .packlist file for $module";
493 }
494
495 die "CODE INCOMPLETE";
496 }
497
498 die "CODE INCOMPLETE";
499}
500
501# Matches a valid distribution name
502### This is a total guess at this point
503sub _DIST {
504 if ( defined $_[0] and ! ref $_[0] and $_[0] =~ /^[a-z0-9+_-]+$/is ) {
505 return shift;
506 }
507 croak("Not a valid distribution name");
508}
509
510# A valid and loaded module name
511sub _MODULE {
512 my $module = _CLASS(shift) or croak("Not a valid module name");
513 if ( Class::Inspector->loaded($module) ) {
514 return $module;
515 }
516 croak("Module '$module' is not loaded");
517}
518
519# A valid file name
520sub _FILE {
521 my $file = shift;
522 unless ( defined $file and ! ref $file and length $file ) {
523 croak("Did not pass a file name");
524 }
525 if ( File::Spec->file_name_is_absolute($file) ) {
526 croak("Cannot use absolute file name '$file'");
527 }
528 $file;
529}
530
5311;
532
533=pod
534
535=head1 SUPPORT
536
537Bugs should always be submitted via the CPAN bug tracker
538
539L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-ShareDir>
540
541For other issues, contact the maintainer.
542
543=head1 AUTHOR
544
545Adam Kennedy E<lt>adamk@cpan.orgE<gt>
546
547=head1 SEE ALSO
548
549L<File::HomeDir>, L<Module::Install>, L<Module::Install::Share>,
550L<File::ShareDir::PAR>
551
552=head1 COPYRIGHT
553
554Copyright 2005 - 2009 Adam Kennedy.
555
556This program is free software; you can redistribute
557it and/or modify it under the same terms as Perl itself.
558
559The full text of the license can be found in the
560LICENSE file included with this module.
561
562=cut