| Filename | /Users/ether/.perlbrew/libs/36.0@std/lib/perl5/File/ShareDir.pm |
| Statements | Executed 294 statements in 4.64ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 3.07ms | 3.25ms | File::ShareDir::BEGIN@131 |
| 28 | 1 | 1 | 671µs | 671µs | File::ShareDir::CORE:ftdir (opcode) |
| 14 | 1 | 1 | 158µs | 1.25ms | File::ShareDir::_search_inc_path |
| 14 | 1 | 1 | 156µs | 1.51ms | File::ShareDir::_dist_dir_new |
| 14 | 2 | 2 | 132µs | 1.77ms | File::ShareDir::dist_dir |
| 14 | 1 | 1 | 102µs | 102µs | File::ShareDir::CORE:fteread (opcode) |
| 14 | 1 | 1 | 72µs | 123µs | File::ShareDir::_DIST |
| 1 | 1 | 1 | 28µs | 28µs | File::ShareDir::BEGIN@120 |
| 14 | 1 | 1 | 25µs | 25µs | File::ShareDir::CORE:match (opcode) |
| 1 | 1 | 1 | 11µs | 47µs | File::ShareDir::BEGIN@411 |
| 1 | 1 | 1 | 7µs | 85µs | File::ShareDir::BEGIN@124 |
| 1 | 1 | 1 | 7µs | 40µs | File::ShareDir::BEGIN@125 |
| 1 | 1 | 1 | 6µs | 9µs | File::ShareDir::BEGIN@121 |
| 1 | 1 | 1 | 5µs | 18µs | File::ShareDir::BEGIN@126 |
| 1 | 1 | 1 | 4µs | 66µs | File::ShareDir::BEGIN@122 |
| 1 | 1 | 1 | 2µs | 2µs | File::ShareDir::BEGIN@128 |
| 1 | 1 | 1 | 2µs | 2µs | File::ShareDir::BEGIN@130 |
| 1 | 1 | 1 | 1µs | 1µs | File::ShareDir::BEGIN@129 |
| 0 | 0 | 0 | 0s | 0s | File::ShareDir::_FILE |
| 0 | 0 | 0 | 0s | 0s | File::ShareDir::_MODULE |
| 0 | 0 | 0 | 0s | 0s | File::ShareDir::__ANON__[:464] |
| 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::_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 | =begin html | ||||
| 10 | |||||
| 11 | <a href="https://travis-ci.org/perl5-utils/File-ShareDir"><img src="https://travis-ci.org/perl5-utils/File-ShareDir.svg?branch=master" alt="Travis CI"/></a> | ||||
| 12 | <a href='https://coveralls.io/github/perl5-utils/File-ShareDir?branch=master'><img src='https://coveralls.io/repos/github/perl5-utils/File-ShareDir/badge.svg?branch=master' alt='Coverage Status' /></a> | ||||
| 13 | <a href="https://saythanks.io/to/rehsack"><img src="https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg" alt="Say Thanks" /></a> | ||||
| 14 | |||||
| 15 | =end html | ||||
| 16 | |||||
| 17 | =head1 SYNOPSIS | ||||
| 18 | |||||
| 19 | use File::ShareDir ':ALL'; | ||||
| 20 | |||||
| 21 | # Where are distribution-level shared data files kept | ||||
| 22 | $dir = dist_dir('File-ShareDir'); | ||||
| 23 | |||||
| 24 | # Where are module-level shared data files kept | ||||
| 25 | $dir = module_dir('File::ShareDir'); | ||||
| 26 | |||||
| 27 | # Find a specific file in our dist/module shared dir | ||||
| 28 | $file = dist_file( 'File-ShareDir', 'file/name.txt'); | ||||
| 29 | $file = module_file('File::ShareDir', 'file/name.txt'); | ||||
| 30 | |||||
| 31 | # Like module_file, but search up the inheritance tree | ||||
| 32 | $file = class_file( 'Foo::Bar', 'file/name.txt' ); | ||||
| 33 | |||||
| 34 | =head1 DESCRIPTION | ||||
| 35 | |||||
| 36 | The intent of L<File::ShareDir> is to provide a companion to | ||||
| 37 | L<Class::Inspector> and L<File::HomeDir>, modules that take a | ||||
| 38 | process that is well-known by advanced Perl developers but gets a | ||||
| 39 | little tricky, and make it more available to the larger Perl community. | ||||
| 40 | |||||
| 41 | Quite often you want or need your Perl module (CPAN or otherwise) | ||||
| 42 | to have access to a large amount of read-only data that is stored | ||||
| 43 | on the file-system at run-time. | ||||
| 44 | |||||
| 45 | On a linux-like system, this would be in a place such as /usr/share, | ||||
| 46 | however Perl runs on a wide variety of different systems, and so | ||||
| 47 | the use of any one location is unreliable. | ||||
| 48 | |||||
| 49 | Perl provides a little-known method for doing this, but almost | ||||
| 50 | nobody is aware that it exists. As a result, module authors often | ||||
| 51 | go through some very strange ways to make the data available to | ||||
| 52 | their code. | ||||
| 53 | |||||
| 54 | The most common of these is to dump the data out to an enormous | ||||
| 55 | Perl data structure and save it into the module itself. The | ||||
| 56 | result are enormous multi-megabyte .pm files that chew up a | ||||
| 57 | lot of memory needlessly. | ||||
| 58 | |||||
| 59 | Another method is to put the data "file" after the __DATA__ compiler | ||||
| 60 | tag and limit yourself to access as a filehandle. | ||||
| 61 | |||||
| 62 | The problem to solve is really quite simple. | ||||
| 63 | |||||
| 64 | 1. Write the data files to the system at install time. | ||||
| 65 | |||||
| 66 | 2. Know where you put them at run-time. | ||||
| 67 | |||||
| 68 | Perl's install system creates an "auto" directory for both | ||||
| 69 | every distribution and for every module file. | ||||
| 70 | |||||
| 71 | These are used by a couple of different auto-loading systems | ||||
| 72 | to store code fragments generated at install time, and various | ||||
| 73 | other modules written by the Perl "ancient masters". | ||||
| 74 | |||||
| 75 | But the same mechanism is available to any dist or module to | ||||
| 76 | store any sort of data. | ||||
| 77 | |||||
| 78 | =head2 Using Data in your Module | ||||
| 79 | |||||
| 80 | C<File::ShareDir> forms one half of a two part solution. | ||||
| 81 | |||||
| 82 | Once the files have been installed to the correct directory, | ||||
| 83 | you can use C<File::ShareDir> to find your files again after | ||||
| 84 | the installation. | ||||
| 85 | |||||
| 86 | For the installation half of the solution, see L<File::ShareDir::Install> | ||||
| 87 | and its C<install_share> directive. | ||||
| 88 | |||||
| 89 | Using L<File::ShareDir::Install> together with L<File::ShareDir> | ||||
| 90 | allows one to rely on the files in appropriate C<dist_dir()> | ||||
| 91 | or C<module_dir()> in development phase, too. | ||||
| 92 | |||||
| 93 | =head1 FUNCTIONS | ||||
| 94 | |||||
| 95 | C<File::ShareDir> provides four functions for locating files and | ||||
| 96 | directories. | ||||
| 97 | |||||
| 98 | For greater maintainability, none of these are exported by default | ||||
| 99 | and you are expected to name the ones you want at use-time, or provide | ||||
| 100 | the C<':ALL'> tag. All of the following are equivalent. | ||||
| 101 | |||||
| 102 | # Load but don't import, and then call directly | ||||
| 103 | use File::ShareDir; | ||||
| 104 | $dir = File::ShareDir::dist_dir('My-Dist'); | ||||
| 105 | |||||
| 106 | # Import a single function | ||||
| 107 | use File::ShareDir 'dist_dir'; | ||||
| 108 | dist_dir('My-Dist'); | ||||
| 109 | |||||
| 110 | # Import all the functions | ||||
| 111 | use File::ShareDir ':ALL'; | ||||
| 112 | dist_dir('My-Dist'); | ||||
| 113 | |||||
| 114 | All of the functions will check for you that the dir/file actually | ||||
| 115 | exists, and that you have read permissions, or they will throw an | ||||
| 116 | exception. | ||||
| 117 | |||||
| 118 | =cut | ||||
| 119 | |||||
| 120 | 2 | 36µs | 1 | 28µs | # spent 28µs within File::ShareDir::BEGIN@120 which was called:
# once (28µs+0s) by JSON::Schema::Modern::BEGIN@27 at line 120 # spent 28µs making 1 call to File::ShareDir::BEGIN@120 |
| 121 | 2 | 24µs | 2 | 12µs | # spent 9µs (6+3) within File::ShareDir::BEGIN@121 which was called:
# once (6µs+3µs) by JSON::Schema::Modern::BEGIN@27 at line 121 # spent 9µs making 1 call to File::ShareDir::BEGIN@121
# spent 3µs making 1 call to strict::import |
| 122 | 2 | 48µs | 2 | 128µs | # spent 66µs (4+62) within File::ShareDir::BEGIN@122 which was called:
# once (4µs+62µs) by JSON::Schema::Modern::BEGIN@27 at line 122 # spent 66µs making 1 call to File::ShareDir::BEGIN@122
# spent 62µs making 1 call to warnings::import |
| 123 | |||||
| 124 | 2 | 25µs | 2 | 163µs | # spent 85µs (7+78) within File::ShareDir::BEGIN@124 which was called:
# once (7µs+78µs) by JSON::Schema::Modern::BEGIN@27 at line 124 # spent 85µs making 1 call to File::ShareDir::BEGIN@124
# spent 78µs making 1 call to base::import |
| 125 | 2 | 19µs | 2 | 73µs | # spent 40µs (7+33) within File::ShareDir::BEGIN@125 which was called:
# once (7µs+33µs) by JSON::Schema::Modern::BEGIN@27 at line 125 # spent 40µs making 1 call to File::ShareDir::BEGIN@125
# spent 33µs making 1 call to constant::import |
| 126 | 2 | 11µs | 2 | 31µs | # spent 18µs (5+13) within File::ShareDir::BEGIN@126 which was called:
# once (5µs+13µs) by JSON::Schema::Modern::BEGIN@27 at line 126 # spent 18µs making 1 call to File::ShareDir::BEGIN@126
# spent 13µs making 1 call to constant::import |
| 127 | |||||
| 128 | 2 | 9µs | 1 | 2µs | # spent 2µs within File::ShareDir::BEGIN@128 which was called:
# once (2µs+0s) by JSON::Schema::Modern::BEGIN@27 at line 128 # spent 2µs making 1 call to File::ShareDir::BEGIN@128 |
| 129 | 2 | 7µs | 1 | 1µs | # spent 1µs within File::ShareDir::BEGIN@129 which was called:
# once (1µs+0s) by JSON::Schema::Modern::BEGIN@27 at line 129 # spent 1µs making 1 call to File::ShareDir::BEGIN@129 |
| 130 | 2 | 9µs | 1 | 2µs | # spent 2µs within File::ShareDir::BEGIN@130 which was called:
# once (2µs+0s) by JSON::Schema::Modern::BEGIN@27 at line 130 # spent 2µs making 1 call to File::ShareDir::BEGIN@130 |
| 131 | 2 | 1.78ms | 1 | 3.25ms | # spent 3.25ms (3.07+177µs) within File::ShareDir::BEGIN@131 which was called:
# once (3.07ms+177µs) by JSON::Schema::Modern::BEGIN@27 at line 131 # spent 3.25ms making 1 call to File::ShareDir::BEGIN@131 |
| 132 | |||||
| 133 | our %DIST_SHARE; | ||||
| 134 | our %MODULE_SHARE; | ||||
| 135 | |||||
| 136 | our @CARP_NOT; | ||||
| 137 | 1 | 1µs | our @EXPORT_OK = qw{ | ||
| 138 | dist_dir | ||||
| 139 | dist_file | ||||
| 140 | module_dir | ||||
| 141 | module_file | ||||
| 142 | class_dir | ||||
| 143 | class_file | ||||
| 144 | }; | ||||
| 145 | 1 | 2µs | our %EXPORT_TAGS = ( | ||
| 146 | ALL => [@EXPORT_OK], | ||||
| 147 | ); | ||||
| 148 | 1 | 0s | our $VERSION = '1.118'; | ||
| 149 | |||||
| 150 | ##################################################################### | ||||
| 151 | # Interface Functions | ||||
| 152 | |||||
| 153 | =pod | ||||
| 154 | |||||
| 155 | =head2 dist_dir | ||||
| 156 | |||||
| 157 | # Get a distribution's shared files directory | ||||
| 158 | my $dir = dist_dir('My-Distribution'); | ||||
| 159 | |||||
| 160 | The C<dist_dir> function takes a single parameter of the name of an | ||||
| 161 | installed (CPAN or otherwise) distribution, and locates the shared | ||||
| 162 | data directory created at install time for it. | ||||
| 163 | |||||
| 164 | Returns the directory path as a string, or dies if it cannot be | ||||
| 165 | located or is not readable. | ||||
| 166 | |||||
| 167 | =cut | ||||
| 168 | |||||
| 169 | sub dist_dir | ||||
| 170 | # spent 1.77ms (132µs+1.64) within File::ShareDir::dist_dir which was called 14 times, avg 126µs/call:
# 8 times (85µs+918µs) by JSON::Schema::Modern::_get_or_load_resource at line 815 of JSON/Schema/Modern.pm, avg 125µs/call
# 6 times (47µs+719µs) by JSON::Schema::Modern::Document::OpenAPI::_add_vocab_and_default_schemas at line 204 of JSON/Schema/Modern/Document/OpenAPI.pm, avg 128µs/call | ||||
| 171 | 14 | 31µs | 14 | 123µs | my $dist = _DIST(shift); # spent 123µs making 14 calls to File::ShareDir::_DIST, avg 9µs/call |
| 172 | 14 | 3µs | my $dir; | ||
| 173 | |||||
| 174 | # Try the new version, then fall back to the legacy version | ||||
| 175 | 14 | 22µs | 14 | 1.51ms | $dir = _dist_dir_new($dist) || _dist_dir_old($dist); # spent 1.51ms making 14 calls to File::ShareDir::_dist_dir_new, avg 108µs/call |
| 176 | |||||
| 177 | 14 | 49µs | return $dir if defined $dir; | ||
| 178 | |||||
| 179 | # Ran out of options | ||||
| 180 | Carp::croak("Failed to find share dir for dist '$dist'"); | ||||
| 181 | } | ||||
| 182 | |||||
| 183 | sub _dist_dir_new | ||||
| 184 | # spent 1.51ms (156µs+1.36) within File::ShareDir::_dist_dir_new which was called 14 times, avg 108µs/call:
# 14 times (156µs+1.36ms) by File::ShareDir::dist_dir at line 175, avg 108µs/call | ||||
| 185 | 14 | 7µs | my $dist = shift; | ||
| 186 | |||||
| 187 | 14 | 11µs | return $DIST_SHARE{$dist} if exists $DIST_SHARE{$dist}; | ||
| 188 | |||||
| 189 | # Create the subpath | ||||
| 190 | 14 | 204µs | 28 | 130µs | my $path = File::Spec->catdir('auto', 'share', 'dist', $dist); # spent 113µs making 14 calls to File::Spec::Unix::catdir, avg 8µs/call
# spent 17µs making 14 calls to File::Spec::Unix::canonpath, avg 1µs/call |
| 191 | |||||
| 192 | # Find the full dir within @INC | ||||
| 193 | 14 | 76µs | 14 | 1.25ms | return _search_inc_path($path); # spent 1.25ms making 14 calls to File::ShareDir::_search_inc_path, avg 89µs/call |
| 194 | } | ||||
| 195 | |||||
| 196 | sub _dist_dir_old | ||||
| 197 | { | ||||
| 198 | my $dist = shift; | ||||
| 199 | |||||
| 200 | # Create the subpath | ||||
| 201 | my $path = File::Spec->catdir('auto', split(/-/, $dist),); | ||||
| 202 | |||||
| 203 | # Find the full dir within @INC | ||||
| 204 | return _search_inc_path($path); | ||||
| 205 | } | ||||
| 206 | |||||
| 207 | =pod | ||||
| 208 | |||||
| 209 | =head2 module_dir | ||||
| 210 | |||||
| 211 | # Get a module's shared files directory | ||||
| 212 | my $dir = module_dir('My::Module'); | ||||
| 213 | |||||
| 214 | The C<module_dir> function takes a single parameter of the name of an | ||||
| 215 | installed (CPAN or otherwise) module, and locates the shared data | ||||
| 216 | directory created at install time for it. | ||||
| 217 | |||||
| 218 | In order to find the directory, the module B<must> be loaded when | ||||
| 219 | calling this function. | ||||
| 220 | |||||
| 221 | Returns the directory path as a string, or dies if it cannot be | ||||
| 222 | located or is not readable. | ||||
| 223 | |||||
| 224 | =cut | ||||
| 225 | |||||
| 226 | sub module_dir | ||||
| 227 | { | ||||
| 228 | my $module = _MODULE(shift); | ||||
| 229 | |||||
| 230 | return $MODULE_SHARE{$module} if exists $MODULE_SHARE{$module}; | ||||
| 231 | |||||
| 232 | # Try the new version first, then fall back to the legacy version | ||||
| 233 | return _module_dir_new($module) || _module_dir_old($module); | ||||
| 234 | } | ||||
| 235 | |||||
| 236 | sub _module_dir_new | ||||
| 237 | { | ||||
| 238 | my $module = shift; | ||||
| 239 | |||||
| 240 | # Create the subpath | ||||
| 241 | my $path = File::Spec->catdir('auto', 'share', 'module', _module_subdir($module),); | ||||
| 242 | |||||
| 243 | # Find the full dir within @INC | ||||
| 244 | return _search_inc_path($path); | ||||
| 245 | } | ||||
| 246 | |||||
| 247 | sub _module_dir_old | ||||
| 248 | { | ||||
| 249 | my $module = shift; | ||||
| 250 | my $short = Class::Inspector->filename($module); | ||||
| 251 | my $long = Class::Inspector->loaded_filename($module); | ||||
| 252 | $short =~ tr{/}{:} if IS_MACOS; | ||||
| 253 | $short =~ tr{\\} {/} if IS_WIN32; | ||||
| 254 | $long =~ tr{\\} {/} if IS_WIN32; | ||||
| 255 | substr($short, -3, 3, ''); | ||||
| 256 | $long =~ m/^(.*)\Q$short\E\.pm\z/s or Carp::croak("Failed to find base dir"); | ||||
| 257 | my $dir = File::Spec->catdir("$1", 'auto', $short); | ||||
| 258 | |||||
| 259 | -d $dir or Carp::croak("Directory '$dir': No such directory"); | ||||
| 260 | -r $dir or Carp::croak("Directory '$dir': No read permission"); | ||||
| 261 | |||||
| 262 | return $dir; | ||||
| 263 | } | ||||
| 264 | |||||
| 265 | =pod | ||||
| 266 | |||||
| 267 | =head2 dist_file | ||||
| 268 | |||||
| 269 | # Find a file in our distribution shared dir | ||||
| 270 | my $dir = dist_file('My-Distribution', 'file/name.txt'); | ||||
| 271 | |||||
| 272 | The C<dist_file> function takes two parameters of the distribution name | ||||
| 273 | and file name, locates the dist directory, and then finds the file within | ||||
| 274 | it, verifying that the file actually exists, and that it is readable. | ||||
| 275 | |||||
| 276 | The filename should be a relative path in the format of your local | ||||
| 277 | filesystem. It will simply added to the directory using L<File::Spec>'s | ||||
| 278 | C<catfile> method. | ||||
| 279 | |||||
| 280 | Returns the file path as a string, or dies if the file or the dist's | ||||
| 281 | directory cannot be located, or the file is not readable. | ||||
| 282 | |||||
| 283 | =cut | ||||
| 284 | |||||
| 285 | sub dist_file | ||||
| 286 | { | ||||
| 287 | my $dist = _DIST(shift); | ||||
| 288 | my $file = _FILE(shift); | ||||
| 289 | |||||
| 290 | # Try the new version first, in doubt hand off to the legacy version | ||||
| 291 | my $path = _dist_file_new($dist, $file) || _dist_file_old($dist, $file); | ||||
| 292 | $path or Carp::croak("Failed to find shared file '$file' for dist '$dist'"); | ||||
| 293 | |||||
| 294 | -f $path or Carp::croak("File '$path': No such file"); | ||||
| 295 | -r $path or Carp::croak("File '$path': No read permission"); | ||||
| 296 | |||||
| 297 | return $path; | ||||
| 298 | } | ||||
| 299 | |||||
| 300 | sub _dist_file_new | ||||
| 301 | { | ||||
| 302 | my $dist = shift; | ||||
| 303 | my $file = shift; | ||||
| 304 | |||||
| 305 | # If it exists, what should the path be | ||||
| 306 | my $dir = _dist_dir_new($dist); | ||||
| 307 | return undef unless defined $dir; | ||||
| 308 | my $path = File::Spec->catfile($dir, $file); | ||||
| 309 | |||||
| 310 | # Does the file exist | ||||
| 311 | return undef unless -e $path; | ||||
| 312 | |||||
| 313 | return $path; | ||||
| 314 | } | ||||
| 315 | |||||
| 316 | sub _dist_file_old | ||||
| 317 | { | ||||
| 318 | my $dist = shift; | ||||
| 319 | my $file = shift; | ||||
| 320 | |||||
| 321 | # If it exists, what should the path be | ||||
| 322 | my $dir = _dist_dir_old($dist); | ||||
| 323 | return undef unless defined $dir; | ||||
| 324 | my $path = File::Spec->catfile($dir, $file); | ||||
| 325 | |||||
| 326 | # Does the file exist | ||||
| 327 | return undef unless -e $path; | ||||
| 328 | |||||
| 329 | return $path; | ||||
| 330 | } | ||||
| 331 | |||||
| 332 | =pod | ||||
| 333 | |||||
| 334 | =head2 module_file | ||||
| 335 | |||||
| 336 | # Find a file in our module shared dir | ||||
| 337 | my $dir = module_file('My::Module', 'file/name.txt'); | ||||
| 338 | |||||
| 339 | The C<module_file> function takes two parameters of the module name | ||||
| 340 | and file name. It locates the module directory, and then finds the file | ||||
| 341 | within it, verifying that the file actually exists, and that it is readable. | ||||
| 342 | |||||
| 343 | In order to find the directory, the module B<must> be loaded when | ||||
| 344 | calling this function. | ||||
| 345 | |||||
| 346 | The filename should be a relative path in the format of your local | ||||
| 347 | filesystem. It will simply added to the directory using L<File::Spec>'s | ||||
| 348 | C<catfile> method. | ||||
| 349 | |||||
| 350 | Returns the file path as a string, or dies if the file or the dist's | ||||
| 351 | directory cannot be located, or the file is not readable. | ||||
| 352 | |||||
| 353 | =cut | ||||
| 354 | |||||
| 355 | sub module_file | ||||
| 356 | { | ||||
| 357 | my $module = _MODULE(shift); | ||||
| 358 | my $file = _FILE(shift); | ||||
| 359 | my $dir = module_dir($module); | ||||
| 360 | my $path = File::Spec->catfile($dir, $file); | ||||
| 361 | |||||
| 362 | -e $path or Carp::croak("File '$path' does not exist in module dir"); | ||||
| 363 | -r $path or Carp::croak("File '$path': No read permission"); | ||||
| 364 | |||||
| 365 | return $path; | ||||
| 366 | } | ||||
| 367 | |||||
| 368 | =pod | ||||
| 369 | |||||
| 370 | =head2 class_file | ||||
| 371 | |||||
| 372 | # Find a file in our module shared dir, or in our parent class | ||||
| 373 | my $dir = class_file('My::Module', 'file/name.txt'); | ||||
| 374 | |||||
| 375 | The C<module_file> function takes two parameters of the module name | ||||
| 376 | and file name. It locates the module directory, and then finds the file | ||||
| 377 | within it, verifying that the file actually exists, and that it is readable. | ||||
| 378 | |||||
| 379 | In order to find the directory, the module B<must> be loaded when | ||||
| 380 | calling this function. | ||||
| 381 | |||||
| 382 | The filename should be a relative path in the format of your local | ||||
| 383 | filesystem. It will simply added to the directory using L<File::Spec>'s | ||||
| 384 | C<catfile> method. | ||||
| 385 | |||||
| 386 | If the file is NOT found for that module, C<class_file> will scan up | ||||
| 387 | the module's @ISA tree, looking for the file in all of the parent | ||||
| 388 | classes. | ||||
| 389 | |||||
| 390 | This allows you to, in effect, "subclass" shared files. | ||||
| 391 | |||||
| 392 | Returns the file path as a string, or dies if the file or the dist's | ||||
| 393 | directory cannot be located, or the file is not readable. | ||||
| 394 | |||||
| 395 | =cut | ||||
| 396 | |||||
| 397 | sub class_file | ||||
| 398 | { | ||||
| 399 | my $module = _MODULE(shift); | ||||
| 400 | my $file = _FILE(shift); | ||||
| 401 | |||||
| 402 | # Get the super path ( not including UNIVERSAL ) | ||||
| 403 | # Rather than using Class::ISA, we'll use an inlined version | ||||
| 404 | # that implements the same basic algorithm. | ||||
| 405 | my @path = (); | ||||
| 406 | my @queue = ($module); | ||||
| 407 | my %seen = ($module => 1); | ||||
| 408 | while (my $cl = shift @queue) | ||||
| 409 | { | ||||
| 410 | push @path, $cl; | ||||
| 411 | 2 | 769µs | 2 | 83µs | # spent 47µs (11+36) within File::ShareDir::BEGIN@411 which was called:
# once (11µs+36µs) by JSON::Schema::Modern::BEGIN@27 at line 411 # spent 47µs making 1 call to File::ShareDir::BEGIN@411
# spent 36µs making 1 call to strict::unimport |
| 412 | unshift @queue, grep { !$seen{$_}++ } | ||||
| 413 | map { my $s = $_; $s =~ s/^::/main::/; $s =~ s/\'/::/g; $s } (@{"${cl}::ISA"}); | ||||
| 414 | } | ||||
| 415 | |||||
| 416 | # Search up the path | ||||
| 417 | foreach my $class (@path) | ||||
| 418 | { | ||||
| 419 | my $dir = eval { module_dir($class); }; | ||||
| 420 | next if $@; | ||||
| 421 | my $path = File::Spec->catfile($dir, $file); | ||||
| 422 | -e $path or next; | ||||
| 423 | -r $path or Carp::croak("File '$file' cannot be read, no read permissions"); | ||||
| 424 | return $path; | ||||
| 425 | } | ||||
| 426 | Carp::croak("File '$file' does not exist in class or parent shared files"); | ||||
| 427 | } | ||||
| 428 | |||||
| 429 | ## no critic (BuiltinFunctions::ProhibitStringyEval) | ||||
| 430 | 1 | 43µs | 1 | 328µs | if (eval "use List::MoreUtils 0.428; 1;") # spent 328µs making 1 call to Exporter::Tiny::import # spent 729µs executing statements in string eval # includes 2.52ms spent executing 1 call to 1 sub defined therein. |
| 431 | { | ||||
| 432 | List::MoreUtils->import("firstres"); | ||||
| 433 | } | ||||
| 434 | else | ||||
| 435 | { | ||||
| 436 | ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) | ||||
| 437 | eval <<'END_OF_BORROWED_CODE'; | ||||
| 438 | sub firstres (&@) | ||||
| 439 | { | ||||
| 440 | my $test = shift; | ||||
| 441 | foreach (@_) | ||||
| 442 | { | ||||
| 443 | my $testval = $test->(); | ||||
| 444 | $testval and return $testval; | ||||
| 445 | } | ||||
| 446 | return undef; | ||||
| 447 | } | ||||
| 448 | END_OF_BORROWED_CODE | ||||
| 449 | } | ||||
| 450 | |||||
| 451 | ##################################################################### | ||||
| 452 | # Support Functions | ||||
| 453 | |||||
| 454 | sub _search_inc_path | ||||
| 455 | # spent 1.25ms (158µs+1.09) within File::ShareDir::_search_inc_path which was called 14 times, avg 89µs/call:
# 14 times (158µs+1.09ms) by File::ShareDir::_dist_dir_new at line 193, avg 89µs/call | ||||
| 456 | 14 | 6µs | my $path = shift; | ||
| 457 | |||||
| 458 | # Find the full dir within @INC | ||||
| 459 | my $dir = firstres( | ||||
| 460 | sub { | ||||
| 461 | 28 | 6µs | my $d; | ||
| 462 | 28 | 190µs | 84 | 115µs | $d = File::Spec->catdir($_, $path) if defined _STRING($_); # spent 88µs making 28 calls to File::Spec::Unix::catdir, avg 3µs/call
# spent 20µs making 28 calls to File::Spec::Unix::canonpath, avg 714ns/call
# spent 7µs making 28 calls to Params::Util::_STRING, avg 250ns/call |
| 463 | 28 | 806µs | 28 | 671µs | defined $d and -d $d ? $d : 0; # spent 671µs making 28 calls to File::ShareDir::CORE:ftdir, avg 24µs/call |
| 464 | }, | ||||
| 465 | @INC | ||||
| 466 | 14 | 86µs | 14 | 985µs | ) or return; # spent 985µs making 14 calls to List::MoreUtils::XS::firstres, avg 70µs/call |
| 467 | |||||
| 468 | 14 | 126µs | 14 | 102µs | Carp::croak("Found directory '$dir', but no read permissions") unless -r $dir; # spent 102µs making 14 calls to File::ShareDir::CORE:fteread, avg 7µs/call |
| 469 | |||||
| 470 | 14 | 31µs | return $dir; | ||
| 471 | } | ||||
| 472 | |||||
| 473 | sub _module_subdir | ||||
| 474 | { | ||||
| 475 | my $module = shift; | ||||
| 476 | $module =~ s/::/-/g; | ||||
| 477 | return $module; | ||||
| 478 | } | ||||
| 479 | |||||
| 480 | ## no critic (BuiltinFunctions::ProhibitStringyEval) | ||||
| 481 | 1 | 63µs | 1 | 76µs | if (eval "use Params::Util 1.07; 1;") # spent 76µs making 1 call to Exporter::import # spent 336µs executing statements in string eval # includes 811µs spent executing 1 call to 1 sub defined therein. |
| 482 | { | ||||
| 483 | Params::Util->import("_CLASS", "_STRING"); | ||||
| 484 | } | ||||
| 485 | else | ||||
| 486 | { | ||||
| 487 | ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval) | ||||
| 488 | eval <<'END_OF_BORROWED_CODE'; | ||||
| 489 | # Inlined from Params::Util pure perl version | ||||
| 490 | sub _CLASS ($) | ||||
| 491 | { | ||||
| 492 | return (defined $_[0] and !ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s) ? $_[0] : undef; | ||||
| 493 | } | ||||
| 494 | |||||
| 495 | sub _STRING ($) | ||||
| 496 | { | ||||
| 497 | (defined $_[0] and ! ref $_[0] and length($_[0])) ? $_[0] : undef; | ||||
| 498 | } | ||||
| 499 | END_OF_BORROWED_CODE | ||||
| 500 | } | ||||
| 501 | |||||
| 502 | # Maintainer note: The following private functions are used by | ||||
| 503 | # File::ShareDir::PAR. (It has to or else it would have to copy&fork) | ||||
| 504 | # So if you significantly change or even remove them, please | ||||
| 505 | # notify the File::ShareDir::PAR maintainer(s). Thank you! | ||||
| 506 | |||||
| 507 | # Matches a valid distribution name | ||||
| 508 | ### This is a total guess at this point | ||||
| 509 | sub _DIST ## no critic (Subroutines::RequireArgUnpacking) | ||||
| 510 | # spent 123µs (72+51) within File::ShareDir::_DIST which was called 14 times, avg 9µs/call:
# 14 times (72µs+51µs) by File::ShareDir::dist_dir at line 171, avg 9µs/call | ||||
| 511 | 14 | 123µs | 28 | 51µs | defined _STRING($_[0]) and $_[0] =~ /^[a-z0-9+_-]+$/is and return $_[0]; # spent 26µs making 14 calls to Params::Util::_STRING, avg 2µs/call
# spent 25µs making 14 calls to File::ShareDir::CORE:match, avg 2µs/call |
| 512 | Carp::croak("Not a valid distribution name"); | ||||
| 513 | } | ||||
| 514 | |||||
| 515 | # A valid and loaded module name | ||||
| 516 | sub _MODULE | ||||
| 517 | { | ||||
| 518 | my $module = _CLASS(shift) or Carp::croak("Not a valid module name"); | ||||
| 519 | Class::Inspector->loaded($module) and return $module; | ||||
| 520 | Carp::croak("Module '$module' is not loaded"); | ||||
| 521 | } | ||||
| 522 | |||||
| 523 | # A valid file name | ||||
| 524 | sub _FILE | ||||
| 525 | { | ||||
| 526 | my $file = shift; | ||||
| 527 | _STRING($file) or Carp::croak("Did not pass a file name"); | ||||
| 528 | File::Spec->file_name_is_absolute($file) and Carp::croak("Cannot use absolute file name '$file'"); | ||||
| 529 | return $file; | ||||
| 530 | } | ||||
| 531 | |||||
| 532 | 1 | 19µs | 1; | ||
| 533 | |||||
| 534 | =pod | ||||
| 535 | |||||
| 536 | =head1 EXTENDING | ||||
| 537 | |||||
| 538 | =head2 Overriding Directory Resolution | ||||
| 539 | |||||
| 540 | C<File::ShareDir> has two convenience hashes for people who have advanced usage | ||||
| 541 | requirements of C<File::ShareDir> such as using uninstalled C<share> | ||||
| 542 | directories during development. | ||||
| 543 | |||||
| 544 | # | ||||
| 545 | # Dist-Name => /absolute/path/for/DistName/share/dir | ||||
| 546 | # | ||||
| 547 | %File::ShareDir::DIST_SHARE | ||||
| 548 | |||||
| 549 | # | ||||
| 550 | # Module::Name => /absolute/path/for/Module/Name/share/dir | ||||
| 551 | # | ||||
| 552 | %File::ShareDir::MODULE_SHARE | ||||
| 553 | |||||
| 554 | Setting these values any time before the corresponding calls | ||||
| 555 | |||||
| 556 | dist_dir('Dist-Name') | ||||
| 557 | dist_file('Dist-Name','some/file'); | ||||
| 558 | |||||
| 559 | module_dir('Module::Name'); | ||||
| 560 | module_file('Module::Name','some/file'); | ||||
| 561 | |||||
| 562 | Will override the base directory for resolving those calls. | ||||
| 563 | |||||
| 564 | An example of where this would be useful is in a test for a module that | ||||
| 565 | depends on files installed into a share directory, to enable the tests | ||||
| 566 | to use the development copy without needing to install them first. | ||||
| 567 | |||||
| 568 | use File::ShareDir; | ||||
| 569 | use Cwd qw( getcwd ); | ||||
| 570 | use File::Spec::Functions qw( rel2abs catdir ); | ||||
| 571 | |||||
| 572 | $File::ShareDir::MODULE_SHARE{'Foo::Module'} = rel2abs(catfile(getcwd,'share')); | ||||
| 573 | |||||
| 574 | use Foo::Module; | ||||
| 575 | |||||
| 576 | # internal calls in Foo::Module to module_file('Foo::Module','bar') now resolves to | ||||
| 577 | # the source trees share/ directory instead of something in @INC | ||||
| 578 | |||||
| 579 | =head1 SUPPORT | ||||
| 580 | |||||
| 581 | Bugs should always be submitted via the CPAN request tracker, see below. | ||||
| 582 | |||||
| 583 | You can find documentation for this module with the perldoc command. | ||||
| 584 | |||||
| 585 | perldoc File::ShareDir | ||||
| 586 | |||||
| 587 | You can also look for information at: | ||||
| 588 | |||||
| 589 | =over 4 | ||||
| 590 | |||||
| 591 | =item * RT: CPAN's request tracker | ||||
| 592 | |||||
| 593 | L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=File-ShareDir> | ||||
| 594 | |||||
| 595 | =item * AnnoCPAN: Annotated CPAN documentation | ||||
| 596 | |||||
| 597 | L<http://annocpan.org/dist/File-ShareDir> | ||||
| 598 | |||||
| 599 | =item * CPAN Ratings | ||||
| 600 | |||||
| 601 | L<http://cpanratings.perl.org/s/File-ShareDir> | ||||
| 602 | |||||
| 603 | =item * CPAN Search | ||||
| 604 | |||||
| 605 | L<http://search.cpan.org/dist/File-ShareDir/> | ||||
| 606 | |||||
| 607 | =back | ||||
| 608 | |||||
| 609 | =head2 Where can I go for other help? | ||||
| 610 | |||||
| 611 | If you have a bug report, a patch or a suggestion, please open a new | ||||
| 612 | report ticket at CPAN (but please check previous reports first in case | ||||
| 613 | your issue has already been addressed). | ||||
| 614 | |||||
| 615 | Report tickets should contain a detailed description of the bug or | ||||
| 616 | enhancement request and at least an easily verifiable way of | ||||
| 617 | reproducing the issue or fix. Patches are always welcome, too. | ||||
| 618 | |||||
| 619 | =head2 Where can I go for help with a concrete version? | ||||
| 620 | |||||
| 621 | Bugs and feature requests are accepted against the latest version | ||||
| 622 | only. To get patches for earlier versions, you need to get an | ||||
| 623 | agreement with a developer of your choice - who may or not report the | ||||
| 624 | issue and a suggested fix upstream (depends on the license you have | ||||
| 625 | chosen). | ||||
| 626 | |||||
| 627 | =head2 Business support and maintenance | ||||
| 628 | |||||
| 629 | For business support you can contact the maintainer via his CPAN | ||||
| 630 | email address. Please keep in mind that business support is neither | ||||
| 631 | available for free nor are you eligible to receive any support | ||||
| 632 | based on the license distributed with this package. | ||||
| 633 | |||||
| 634 | =head1 AUTHOR | ||||
| 635 | |||||
| 636 | Adam Kennedy E<lt>adamk@cpan.orgE<gt> | ||||
| 637 | |||||
| 638 | =head2 MAINTAINER | ||||
| 639 | |||||
| 640 | Jens Rehsack E<lt>rehsack@cpan.orgE<gt> | ||||
| 641 | |||||
| 642 | =head1 SEE ALSO | ||||
| 643 | |||||
| 644 | L<File::ShareDir::Install>, | ||||
| 645 | L<File::ConfigDir>, L<File::HomeDir>, | ||||
| 646 | L<Module::Install>, L<Module::Install::Share>, | ||||
| 647 | L<File::ShareDir::PAR>, L<Dist::Zilla::Plugin::ShareDir> | ||||
| 648 | |||||
| 649 | =head1 COPYRIGHT | ||||
| 650 | |||||
| 651 | Copyright 2005 - 2011 Adam Kennedy, | ||||
| 652 | Copyright 2014 - 2018 Jens Rehsack. | ||||
| 653 | |||||
| 654 | This program is free software; you can redistribute | ||||
| 655 | it and/or modify it under the same terms as Perl itself. | ||||
| 656 | |||||
| 657 | The full text of the license can be found in the | ||||
| 658 | LICENSE file included with this module. | ||||
| 659 | |||||
| 660 | =cut | ||||
# spent 671µs within File::ShareDir::CORE:ftdir which was called 28 times, avg 24µs/call:
# 28 times (671µs+0s) by List::MoreUtils::XS::firstres at line 463, avg 24µs/call | |||||
# spent 102µs within File::ShareDir::CORE:fteread which was called 14 times, avg 7µs/call:
# 14 times (102µs+0s) by File::ShareDir::_search_inc_path at line 468, avg 7µs/call | |||||
# spent 25µs within File::ShareDir::CORE:match which was called 14 times, avg 2µs/call:
# 14 times (25µs+0s) by File::ShareDir::_DIST at line 511, avg 2µs/call |