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