File Coverage

blib/lib/Module/ScanDeps.pm
Criterion Covered Total %
statement 440 538 81.7
branch 195 278 70.1
condition 44 82 53.6
subroutine 46 52 88.4
pod 6 14 42.8
total 731 964 75.8


line stmt bran cond sub pod time code
1             package Module::ScanDeps;
2 16     16   1022614 use 5.008001;
  16         198  
3 16     16   95 use strict;
  16         34  
  16         395  
4 16     16   79 use warnings;
  16         38  
  16         623  
5 16     16   98 use vars qw( $VERSION @EXPORT @EXPORT_OK @ISA $CurrentPackage @IncludeLibs $ScanFileRE );
  16         30  
  16         2480  
6              
7             $VERSION = '1.33';
8             @EXPORT = qw( scan_deps scan_deps_runtime );
9             @EXPORT_OK = qw( scan_line scan_chunk add_deps scan_deps_runtime path_to_inc_name );
10              
11 16     16   122 use Config;
  16         29  
  16         1808  
12             require Exporter;
13             our @ISA = qw(Exporter);
14 16   66     2818 use constant is_insensitive_fs => (
15             -s $0
16             and (-s lc($0) || -1) == (-s uc($0) || -1)
17             and (-s lc($0) || -1) == -s $0
18 16     16   114 );
  16         36  
19              
20 16     16   7708 use version;
  16         31723  
  16         93  
21 16     16   1347 use File::Path ();
  16         34  
  16         227  
22 16     16   12489 use File::Temp ();
  16         331119  
  16         483  
23 16     16   7911 use FileHandle;
  16         39611  
  16         98  
24 16     16   15173 use Module::Metadata;
  16         100362  
  16         682  
25              
26             # NOTE: Keep the following imports exactly as specified, even if the Module::ScanDeps source
27             # doesn't reference some of them. See '"use lib" idioms' for the reason.
28 16     16   111 use Cwd (qw(abs_path));
  16         46  
  16         801  
29 16     16   98 use File::Spec;
  16         57  
  16         294  
30 16     16   7538 use File::Spec::Functions;
  16         12761  
  16         1196  
31 16     16   122 use File::Basename;
  16         61  
  16         10046  
32              
33              
34             $ScanFileRE = qr/(?:^|\\|\/)(?:[^.]*|.*\.(?i:p[ml]|t|al))$/;
35              
36              
37             =head1 NAME
38              
39             Module::ScanDeps - Recursively scan Perl code for dependencies
40              
41             =head1 SYNOPSIS
42              
43             Via the command-line program L:
44              
45             % scandeps.pl *.pm # Print PREREQ_PM section for *.pm
46             % scandeps.pl -e "use utf8" # Read script from command line
47             % scandeps.pl -B *.pm # Include core modules
48             % scandeps.pl -V *.pm # Show autoload/shared/data files
49              
50             Used in a program;
51              
52             use Module::ScanDeps;
53              
54             # standard usage
55             my $hash_ref = scan_deps(
56             files => [ 'a.pl', 'b.pl' ],
57             recurse => 1,
58             );
59              
60             # shorthand; assume recurse == 1
61             my $hash_ref = scan_deps( 'a.pl', 'b.pl' );
62              
63             # App::Packer::Frontend compatible interface
64             # see App::Packer::Frontend for the structure returned by get_files
65             my $scan = Module::ScanDeps->new;
66             $scan->set_file( 'a.pl' );
67             $scan->set_options( add_modules => [ 'Test::More' ] );
68             $scan->calculate_info;
69             my $files = $scan->get_files;
70              
71             =head1 DESCRIPTION
72              
73             This module scans potential modules used by perl programs, and returns a
74             hash reference; its keys are the module names as appears in C<%INC>
75             (e.g. C); the values are hash references with this structure:
76              
77             {
78             file => '/usr/local/lib/perl5/5.8.0/Test/More.pm',
79             key => 'Test/More.pm',
80             type => 'module', # or 'autoload', 'data', 'shared'
81             used_by => [ 'Test/Simple.pm', ... ],
82             uses => [ 'Test/Other.pm', ... ],
83             }
84              
85             One function, C, is exported by default. Other
86             functions such as (C, C, C, C)
87             are exported upon request.
88              
89             Users of B may also use this module as the dependency-checking
90             frontend, by tweaking their F like below:
91              
92             use Module::ScanDeps;
93             ...
94             my $packer = App::Packer->new( frontend => 'Module::ScanDeps' );
95             ...
96              
97             Please see L for detailed explanation on
98             the structure returned by C.
99              
100             =head2 B
101              
102             $rv_ref = scan_deps(
103             files => \@files, recurse => $recurse,
104             rv => \%rv, skip => \%skip,
105             compile => $compile, execute => $execute,
106             );
107             $rv_ref = scan_deps(@files); # shorthand, with recurse => 1
108              
109             This function scans each file in C<@files>, registering their
110             dependencies into C<%rv>, and returns a reference to the updated
111             C<%rv>. The meaning of keys and values are explained above.
112              
113             If C<$recurse> is true, C will call itself recursively,
114             to perform a breadth-first search on text files (as defined by the
115             -T operator) found in C<%rv>.
116              
117             If the C<\%skip> is specified, files that exists as its keys are
118             skipped. This is used internally to avoid infinite recursion.
119              
120             If C<$compile> or C<$execute> is true, runs C in either
121             compile-only or normal mode, then inspects their C<%INC> after
122             termination to determine additional runtime dependencies.
123              
124             If C<$execute> is an array reference, passes C<@$execute>
125             as arguments to each file in C<@files> when it is run.
126              
127             If performance of the scanning process is a concern, C can be
128             set to a filename. The scanning results will be cached and written to the
129             file. This will speed up the scanning process on subsequent runs.
130              
131             Additionally, an option C is recognized. If set to true,
132             C issues a warning to STDERR for every module file that the
133             scanned code depends but that wasn't found. Please note that this may
134             also report numerous false positives. That is why by default, the heuristic
135             silently drops all dependencies it cannot find.
136              
137             =head2 B
138              
139             Like B, but skips the static scanning part.
140              
141             =head2 B
142              
143             @modules = scan_line($line);
144              
145             Splits a line into chunks (currently with the semicolon characters), and
146             return the union of C calls of them.
147              
148             If the line is C<__END__> or C<__DATA__>, a single C<__END__> element is
149             returned to signify the end of the program.
150              
151             Similarly, it returns a single C<__POD__> if the line matches C;
152             the caller is responsible for skipping appropriate number of lines
153             until C<=cut>, before calling C again.
154              
155             =head2 B
156              
157             $module = scan_chunk($chunk);
158             @modules = scan_chunk($chunk);
159              
160             Apply various heuristics to C<$chunk> to find and return the module
161             name(s) it contains. In scalar context, returns only the first module
162             or C.
163              
164             =head2 B
165              
166             $rv_ref = add_deps( rv => \%rv, modules => \@modules );
167             $rv_ref = add_deps( @modules ); # shorthand, without rv
168              
169             Resolves a list of module names to its actual on-disk location, by
170             finding in C<@INC> and C<@Module::ScanDeps::IncludeLibs>;
171             modules that cannot be found are skipped.
172              
173             This function populates the C<%rv> hash with module/filename pairs, and
174             returns a reference to it.
175              
176             =head2 B
177              
178             $perl_name = path_to_inc_name($path, $warn)
179              
180             Assumes C<$path> refers to a perl file and does it's best to return the
181             name as it would appear in %INC. Returns undef if no match was found
182             and a prints a warning to STDERR if C<$warn> is true.
183              
184             E.g. if C<$path> = perl/site/lib/Module/ScanDeps.pm then C<$perl_name>
185             will be Module/ScanDeps.pm.
186              
187             =head1 NOTES
188              
189             =head2 B<@Module::ScanDeps::IncludeLibs>
190              
191             You can set this global variable to specify additional directories in
192             which to search modules without modifying C<@INC> itself.
193              
194             =head2 B<$Module::ScanDeps::ScanFileRE>
195              
196             You can set this global variable to specify a regular expression to
197             identify what files to scan. By default it includes all files of
198             the following types: .pm, .pl, .t and .al. Additionally, all files
199             without a suffix are considered.
200              
201             For instance, if you want to scan all files then use the following:
202              
203             C<$Module::ScanDeps::ScanFileRE = qr/./>
204              
205             =head1 CAVEATS
206              
207             This module intentionally ignores the B hack on FreeBSD -- the
208             additional directory is removed from C<@INC> altogether.
209              
210             The static-scanning heuristic is not likely to be 100% accurate, especially
211             on modules that dynamically load other modules.
212              
213             Chunks that span multiple lines are not handled correctly. For example,
214             this one works:
215              
216             use base 'Foo::Bar';
217              
218             But this one does not:
219              
220             use base
221             'Foo::Bar';
222              
223             =cut
224              
225             my $SeenTk;
226             my %SeenRuntimeLoader;
227              
228             # match "use LOADER LIST" chunks; sets $1 to LOADER and $2 to LIST
229             my $LoaderRE =
230             qr/^ use \s+
231             ( asa
232             | base
233             | parent
234             | prefork
235             | POE
236             | encoding
237             | maybe
238             | only::matching
239             | Mojo::Base
240             | Catalyst
241             )(?!\:) \b \s* (.*)
242             /sx;
243              
244             # Pre-loaded module dependencies {{{
245             my %Preload = (
246             'AnyDBM_File.pm' => [qw( SDBM_File.pm )],
247             'AnyEvent.pm' => 'sub',
248             'Authen/SASL.pm' => 'sub',
249              
250             'B/Hooks/EndOfScope.pm' =>
251             [qw( B/Hooks/EndOfScope/PP.pm B/Hooks/EndOfScope/XS.pm )],
252             'Bio/AlignIO.pm' => 'sub',
253             'Bio/Assembly/IO.pm' => 'sub',
254             'Bio/Biblio/IO.pm' => 'sub',
255             'Bio/ClusterIO.pm' => 'sub',
256             'Bio/CodonUsage/IO.pm' => 'sub',
257             'Bio/DB/Biblio.pm' => 'sub',
258             'Bio/DB/Flat.pm' => 'sub',
259             'Bio/DB/GFF.pm' => 'sub',
260             'Bio/DB/Taxonomy.pm' => 'sub',
261             'Bio/Graphics/Glyph.pm' => 'sub',
262             'Bio/MapIO.pm' => 'sub',
263             'Bio/Matrix/IO.pm' => 'sub',
264             'Bio/Matrix/PSM/IO.pm' => 'sub',
265             'Bio/OntologyIO.pm' => 'sub',
266             'Bio/PopGen/IO.pm' => 'sub',
267             'Bio/Restriction/IO.pm' => 'sub',
268             'Bio/Root/IO.pm' => 'sub',
269             'Bio/SearchIO.pm' => 'sub',
270             'Bio/SeqIO.pm' => 'sub',
271             'Bio/Structure/IO.pm' => 'sub',
272             'Bio/TreeIO.pm' => 'sub',
273             'Bio/LiveSeq/IO.pm' => 'sub',
274             'Bio/Variation/IO.pm' => 'sub',
275              
276             'Catalyst.pm' => sub {
277             return ('Catalyst/Runtime.pm',
278             'Catalyst/Dispatcher.pm',
279             _glob_in_inc('Catalyst/DispatchType', 1));
280             },
281             'Catalyst/Engine.pm' => 'sub',
282             'CGI/Application/Plugin/Authentication.pm' =>
283             [qw( CGI/Application/Plugin/Authentication/Store/Cookie.pm )],
284             'CGI/Application/Plugin/AutoRunmode.pm' => [qw( Attribute/Handlers.pm )],
285             'charnames.pm' => \&_unicore,
286             'Class/Load.pm' => [qw( Class/Load/PP.pm )],
287             'Class/MakeMethods.pm' => 'sub',
288             'Class/MethodMaker.pm' => 'sub',
289             'Class/Plain.pm' => [qw( XS/Parse/Keyword.pm )],
290             'Config/Any.pm' =>'sub',
291             'Crypt/Random.pm' => sub {
292             _glob_in_inc('Crypt/Random/Provider', 1);
293             },
294             'Crypt/Random/Generator.pm' => sub {
295             _glob_in_inc('Crypt/Random/Provider', 1);
296             },
297              
298             'Date/Manip.pm' =>
299             [qw( Date/Manip/DM5.pm Date/Manip/DM6.pm )],
300             'Date/Manip/Base.pm' => sub {
301             _glob_in_inc('Date/Manip/Lang', 1);
302             },
303             'Date/Manip/TZ.pm' => sub {
304             return (_glob_in_inc('Date/Manip/TZ', 1),
305             _glob_in_inc('Date/Manip/Offset', 1));
306             },
307             'DateTime/Format/Builder/Parser.pm' => 'sub',
308             'DateTime/Format/Natural.pm' => 'sub',
309             'DateTime/Locale.pm' => 'sub',
310             'DateTime/TimeZone.pm' => 'sub',
311             'DBI.pm' => sub {
312             grep !/\bProxy\b/, _glob_in_inc('DBD', 1);
313             },
314             'DBIx/Class.pm' => 'sub',
315             'DBIx/SearchBuilder.pm' => 'sub',
316             'DBIx/Perlish.pm' => [qw( attributes.pm )],
317             'DBIx/ReportBuilder.pm' => 'sub',
318             'Device/ParallelPort.pm' => 'sub',
319             'Device/SerialPort.pm' =>
320             [qw( termios.ph asm/termios.ph sys/termiox.ph sys/termios.ph sys/ttycom.ph )],
321             'diagnostics.pm' => sub {
322             # shamelessly taken and adapted from diagnostics.pm
323 16     16   151 use Config;
  16         48  
  16         62832  
324             my($privlib, $archlib) = @Config{qw(privlibexp archlibexp)};
325             if ($^O eq 'VMS') {
326             require VMS::Filespec;
327             $privlib = VMS::Filespec::unixify($privlib);
328             $archlib = VMS::Filespec::unixify($archlib);
329             }
330              
331             for (
332             "pod/perldiag.pod",
333             "Pod/perldiag.pod",
334             "pod/perldiag-$Config{version}.pod",
335             "Pod/perldiag-$Config{version}.pod",
336             "pods/perldiag.pod",
337             "pods/perldiag-$Config{version}.pod",
338             ) {
339             return $_ if _find_in_inc($_);
340             }
341              
342             for (
343             "$archlib/pods/perldiag.pod",
344             "$privlib/pods/perldiag-$Config{version}.pod",
345             "$privlib/pods/perldiag.pod",
346             ) {
347             return $_ if -f $_;
348             }
349              
350             return 'pod/perldiag.pod';
351             },
352              
353             'Email/Send.pm' => 'sub',
354             'Event.pm' => sub {
355             map "Event/$_.pm", qw( idle io signal timer var );
356             },
357             'ExtUtils/MakeMaker.pm' => sub {
358             grep /\bMM_/, _glob_in_inc('ExtUtils', 1);
359             },
360              
361             'FFI/Platypus.pm' => 'sub',
362             'File/Basename.pm' => [qw( re.pm )],
363             'File/BOM.pm' => [qw( Encode/Unicode.pm )],
364             'File/HomeDir.pm' => 'sub',
365             'File/Spec.pm' => sub {
366             require File::Spec;
367             map { my $name = $_; $name =~ s!::!/!g; "$name.pm" } @File::Spec::ISA;
368             },
369             'Future/AsyncAwait.pm' => [qw( XS/Parse/Keyword.pm )],
370             'Future/AsyncAwait/Hooks.pm' => [qw( XS/Parse/Keyword.pm )],
371              
372             'Gtk2.pm' => [qw( Cairo.pm )], # Gtk2.pm does: eval "use Cairo;"
373              
374             'HTTP/Entity/Parser.pm' => 'sub',
375             'HTTP/Message.pm' => [qw( URI/URL.pm URI.pm )],
376              
377             'Image/ExifTool.pm' => sub {
378             return(
379             (map $_->{name}, _glob_in_inc('Image/ExifTool', 0)), # also *.pl files
380             qw( File/RandomAccess.pm ),
381             );
382             },
383             'Image/Info.pm' => sub {
384             return(
385             _glob_in_inc('Image/Info', 1),
386             qw( Image/TIFF.pm ),
387             );
388             },
389             'IO.pm' => [qw(
390             IO/Handle.pm IO/Seekable.pm IO/File.pm
391             IO/Pipe.pm IO/Socket.pm IO/Dir.pm
392             )],
393             'IO/Socket.pm' => [qw( IO/Socket/UNIX.pm )],
394             'IUP.pm' => 'sub',
395              
396             'JSON.pm' => sub {
397             # add JSON/PP*.pm, JSON/PP/*.pm
398             # and ignore other JSON::* modules (e.g. JSON/Syck.pm, JSON/Any.pm);
399             # but accept JSON::XS, too (because JSON.pm might use it if present)
400             return( grep /^JSON\/(PP|XS)/, _glob_in_inc('JSON', 1) );
401             },
402             'JSON/MaybeXS.pm' => [qw(
403             Cpanel/JSON/XS.pm JSON/XS.pm JSON/PP.pm
404             )],
405              
406             'List/Keywords.pm' => [qw( XS/Parse/Keyword.pm )],
407             'List/MoreUtils.pm' => 'sub',
408             'List/SomeUtils.pm' => 'sub',
409             'Locale/Maketext/Lexicon.pm' => 'sub',
410             'Locale/Maketext/GutsLoader.pm' => [qw( Locale/Maketext/Guts.pm )],
411             'Log/Any.pm' => 'sub',
412             'Log/Dispatch.pm' => 'sub',
413             'Log/Log4perl.pm' => 'sub',
414             'Log/Report/Dispatcher.pm' => 'sub',
415             'LWP/MediaTypes.pm' => [qw( LWP/media.types )],
416             'LWP/Parallel.pm' => sub {
417             _glob_in_inc( 'LWP/Parallel', 1 ),
418             qw(
419             LWP/ParallelUA.pm LWP/UserAgent.pm
420             LWP/RobotPUA.pm LWP/RobotUA.pm
421             ),
422             },
423             'LWP/Parallel/UserAgent.pm' => [qw( LWP/Parallel.pm )],
424             'LWP/UserAgent.pm' => sub {
425             return(
426             qw( URI/URL.pm URI/http.pm LWP/Protocol/http.pm ),
427             _glob_in_inc("LWP/Authen", 1),
428             _glob_in_inc("LWP/Protocol", 1),
429             );
430             },
431              
432             'Mail/Audit.pm' => 'sub',
433             'Math/BigInt.pm' => 'sub',
434             'Math/BigFloat.pm' => 'sub',
435             'Math/Symbolic.pm' => 'sub',
436             'MIME/Decoder.pm' => 'sub',
437             'MIME/Types.pm' => [qw( MIME/types.db )],
438             'Module/Build.pm' => 'sub',
439             'Module/Pluggable.pm' => sub {
440             _glob_in_inc('$CurrentPackage/Plugin', 1);
441             },
442             'Moo.pm' => [qw( Class/XSAccessor.pm )],
443             'Moose.pm' => sub {
444             _glob_in_inc('Moose', 1),
445             _glob_in_inc('Class/MOP', 1),
446             },
447             'MooseX/AttributeHelpers.pm' => 'sub',
448             'MooseX/POE.pm' => sub {
449             _glob_in_inc('MooseX/POE', 1),
450             _glob_in_inc('MooseX/Async', 1),
451             },
452             'Mozilla/CA.pm' => [qw( Mozilla/CA/cacert.pem )],
453             'MozRepl.pm' => sub {
454             qw( MozRepl/Log.pm MozRepl/Client.pm Module/Pluggable/Fast.pm ),
455             _glob_in_inc('MozRepl/Plugin', 1),
456             },
457             'Module/Implementation.pm' => \&_warn_of_runtime_loader,
458             'Module/Runtime.pm' => \&_warn_of_runtime_loader,
459             'Mojo/Util.pm' => sub { # html_entities.txt
460             map { $_->{name} } _glob_in_inc('Mojo/resources', 0)
461             },
462             'Mojo/IOLoop/TLS.pm' => sub { # server.{crt,key}
463             map { $_->{name} } _glob_in_inc('Mojo/IOLoop/resources', 0)
464             },
465              
466             'Net/DNS/Resolver.pm' => 'sub',
467             'Net/DNS/RR.pm' => 'sub',
468             'Net/FTP.pm' => 'sub',
469             'Net/HTTPS.pm' => [qw( IO/Socket/SSL.pm Net/SSL.pm )],
470             'Net/Server.pm' => 'sub',
471             'Net/SSH/Perl.pm' => 'sub',
472              
473             'Object/Pad.pm' => [qw( XS/Parse/Keyword.pm )],
474             'Object/Pad/Keyword/Accessor.pm' => [qw( XS/Parse/Keyword.pm )],
475              
476             'Package/Stash.pm' => [qw( Package/Stash/PP.pm Package/Stash/XS.pm )],
477             'Pango.pm' => [qw( Cairo.pm )], # Pango.pm does: eval "use Cairo;"
478             'PAR/Repository.pm' => 'sub',
479             'PAR/Repository/Client.pm' => 'sub',
480             'Params/Validate.pm' => 'sub',
481             'Parse/AFP.pm' => 'sub',
482             'Parse/Binary.pm' => 'sub',
483             'PDF/API2/Resource/Font.pm' => 'sub',
484             'PDF/API2/Basic/TTF/Font.pm' => sub {
485             _glob_in_inc('PDF/API2/Basic/TTF', 1);
486             },
487             'PDF/Writer.pm' => 'sub',
488             'PDL/NiceSlice.pm' => 'sub',
489             'Perl/Critic.pm' => 'sub', #not only Perl/Critic/Policy
490             'PerlIO.pm' => [qw( PerlIO/scalar.pm )],
491             'Pod/Simple/Transcode.pm' => [qw( Pod/Simple/TranscodeDumb.pm Pod/Simple/TranscodeSmart.pm )],
492             'Pod/Usage.pm' => sub { # from Pod::Usage (as of 1.61)
493             $] >= 5.005_58 ? 'Pod/Text.pm' : 'Pod/PlainText.pm'
494             },
495             'POE.pm' => [qw( POE/Kernel.pm POE/Session.pm )],
496             'POE/Component/Client/HTTP.pm' => sub {
497             _glob_in_inc('POE/Component/Client/HTTP', 1),
498             qw( POE/Filter/HTTPChunk.pm POE/Filter/HTTPHead.pm ),
499             },
500             'POE/Kernel.pm' => sub {
501             _glob_in_inc('POE/XS/Resource', 1),
502             _glob_in_inc('POE/Resource', 1),
503             _glob_in_inc('POE/XS/Loop', 1),
504             _glob_in_inc('POE/Loop', 1),
505             },
506             'POSIX.pm' => sub {
507             map $_->{name},
508             _glob_in_inc('auto/POSIX/SigAction', 0), # *.al files
509             _glob_in_inc('auto/POSIX/SigRt', 0), # *.al files
510             },
511             'PPI.pm' => 'sub',
512              
513             'Regexp/Common.pm' => 'sub',
514             'RPC/XML/ParserFactory.pm' => sub {
515             _glob_in_inc('RPC/XML/Parser', 1);
516             },
517              
518             'SerialJunk.pm' => [qw(
519             termios.ph asm/termios.ph sys/termiox.ph sys/termios.ph sys/ttycom.ph
520             )],
521             'SOAP/Lite.pm' => sub {
522             _glob_in_inc('SOAP/Transport', 1),
523             _glob_in_inc('SOAP/Lite', 1),
524             },
525             'Socket/GetAddrInfo.pm' => 'sub',
526             'Specio/PartialDump.pm' => \&_unicore,
527             'SQL/Parser.pm' => sub {
528             _glob_in_inc('SQL/Dialects', 1);
529             },
530             'SQL/Translator/Schema.pm' => sub {
531             _glob_in_inc('SQL/Translator', 1);
532             },
533             'Sub/Exporter/Progressive.pm' => [qw( Sub/Exporter.pm )],
534             'SVK/Command.pm' => sub {
535             _glob_in_inc('SVK', 1);
536             },
537             'SVN/Core.pm' => sub {
538             _glob_in_inc('SVN', 1),
539             map { $_->{name} } _glob_in_inc('auto/SVN', 0), # *.so, *.bs files
540             },
541             'Syntax/Keyword/Combine/Keys.pm' => [qw( XS/Parse/Keyword.pm )],
542             'Syntax/Keyword/Defer.pm' => [qw( XS/Parse/Keyword.pm )],
543             'Syntax/Keyword/Dynamically.pm' => [qw( XS/Parse/Keyword.pm )],
544             'Syntax/Keyword/Inplace.pm' => [qw( XS/Parse/Keyword.pm )],
545             'Syntax/Keyword/Match.pm' => [qw( XS/Parse/Keyword.pm )],
546             'Syntax/Keyword/Try.pm' => [qw( XS/Parse/Keyword.pm )],
547              
548             'Template.pm' => 'sub',
549             'Term/ReadLine.pm' => 'sub',
550             'Test/Deep.pm' => 'sub',
551             'threads/shared.pm' => [qw( attributes.pm )],
552             # anybody using threads::shared is likely to declare variables
553             # with attribute :shared
554             'Tk.pm' => sub {
555             $SeenTk = 1;
556             qw( Tk/FileSelect.pm Encode/Unicode.pm );
557             },
558             'Tk/Balloon.pm' => [qw( Tk/balArrow.xbm )],
559             'Tk/BrowseEntry.pm' => [qw( Tk/cbxarrow.xbm Tk/arrowdownwin.xbm )],
560             'Tk/ColorEditor.pm' => [qw( Tk/ColorEdit.xpm )],
561             'Tk/DragDrop/Common.pm' => sub {
562             _glob_in_inc('Tk/DragDrop', 1),
563             },
564             'Tk/FBox.pm' => [qw( Tk/folder.xpm Tk/file.xpm )],
565             'Tk/Getopt.pm' => [qw( Tk/openfolder.xpm Tk/win.xbm )],
566             'Tk/Toplevel.pm' => [qw( Tk/Wm.pm )],
567              
568             'Unicode/Normalize.pm' => \&_unicore,
569             'Unicode/UCD.pm' => \&_unicore,
570             'URI.pm' => sub { grep !/urn/, _glob_in_inc('URI', 1) },
571             'utf8_heavy.pl' => \&_unicore,
572              
573             'Win32/EventLog.pm' => [qw( Win32/IPC.pm )],
574             'Win32/Exe.pm' => 'sub',
575             'Win32/TieRegistry.pm' => [qw( Win32API/Registry.pm )],
576             'Win32/SystemInfo.pm' => [qw( Win32/cpuspd.dll )],
577             'Wx.pm' => [qw( attributes.pm )],
578              
579             'XML/Parser.pm' => sub {
580             _glob_in_inc('XML/Parser/Style', 1),
581             _glob_in_inc('XML/Parser/Encodings', 1),
582             },
583             'XML/SAX.pm' => [qw( XML/SAX/ParserDetails.ini ) ],
584             'XML/Twig.pm' => [qw( URI.pm )], # or URI::File or LWP
585             'XML/Twig/XPath.pm' => [qw( XML/XPathEngine.pm XML/XPath.pm )],
586             'XMLRPC/Lite.pm' => sub {
587             _glob_in_inc('XMLRPC/Transport', 1);
588             },
589             'XS/Parse/Keyword/FromPerl.pm' => [qw( XS/Parse/Keyword.pm )],
590              
591             'YAML.pm' => [qw( YAML/Loader.pm YAML/Dumper.pm )],
592             'YAML/Any.pm' => sub {
593             # try to figure out what YAML::Any would have used
594             my $impl = eval "use YAML::Any; YAML::Any->implementation;";
595             return _mod2pm($impl) unless $@;
596              
597             _glob_in_inc('YAML', 1); # fallback
598             },
599             );
600              
601             # }}}
602              
603             sub path_to_inc_name($$) {
604 155     155 1 58195 my $path = shift;
605 155         326 my $warn = shift;
606 155         244 my $inc_name;
607              
608 155 100       607 if ($path =~ m/\.pm$/io) {
609 34 50       753 die "$path doesn't exist" unless (-f $path);
610 34         314 my $module_info = Module::Metadata->new_from_file($path);
611 34 50       14482 die "Module::Metadata error: $!" unless defined($module_info);
612 34         143 $inc_name = $module_info->name();
613 34 50       219 if (defined($inc_name)) {
614 34         105 $inc_name =~ s|\:\:|\/|og;
615 34         200 $inc_name .= '.pm';
616             } else {
617 0 0       0 warn "# Couldn't find include name for $path\n" if $warn;
618             }
619             } else {
620             # Bad solution!
621 121         1252 (my $vol, my $dir, $inc_name) = File::Spec->splitpath($path);
622             }
623              
624 155         844 return $inc_name;
625             }
626              
627             my $Keys = 'files|keys|recurse|rv|skip|first|execute|compile|warn_missing|cache_cb|cache_file';
628             sub scan_deps {
629 39 100 66 39 1 636580 my %args = (
630             rv => {},
631             (@_ and $_[0] =~ /^(?:$Keys)$/o) ? @_ : (files => [@_], recurse => 1)
632             );
633              
634 39 100       231 if (!defined($args{keys})) {
635 38         104 $args{keys} = [map {path_to_inc_name($_, $args{warn_missing})} @{$args{files}}];
  55         235  
  38         129  
636             }
637 39         121 my $cache_file = $args{cache_file};
638 39         68 my $using_cache;
639 39 100       174 if ($cache_file) {
640 8         64 require Module::ScanDeps::Cache;
641 8         48 $using_cache = Module::ScanDeps::Cache::init_from_file($cache_file);
642 8 50       24 if( $using_cache ){
643 8         27 $args{cache_cb} = Module::ScanDeps::Cache::get_cache_cb();
644             }else{
645 0         0 my @missing = Module::ScanDeps::Cache::prereq_missing();
646 0         0 warn join(' ',
647             "Can not use cache_file: Needs Modules [",
648             @missing,
649             "]\n",);
650             }
651             }
652 39         99 my ($type, $path);
653 39         97 foreach my $input_file (@{$args{files}}) {
  39         141  
654 56 100       630 if ($input_file !~ $ScanFileRE) {
655             warn "Skipping input file $input_file"
656             . " because it doesn't match \$Module::ScanDeps::ScanFileRE\n"
657 1 50       4 if $args{warn_missing};
658 1         3 next;
659             }
660              
661 55         177 $type = _gettype($input_file);
662 55         114 $path = $input_file;
663 55 100       162 if ($type eq 'module') {
664             # necessary because add_deps does the search for shared libraries and such
665             add_deps(
666             used_by => undef,
667             rv => $args{rv},
668             modules => [path_to_inc_name($path, $args{warn_missing})],
669             skip => undef,
670             warn_missing => $args{warn_missing},
671 11         76 );
672             }
673             else {
674             _add_info(
675             rv => $args{rv},
676 44         166 module => path_to_inc_name($path, $args{warn_missing}),
677             file => $path,
678             used_by => undef,
679             type => $type,
680             );
681             }
682             }
683              
684             {
685             ## "use lib" idioms
686             #
687             # We want to correctly interprete stuff like
688             #
689             # use FindBin;
690             # use lib "$FindBin/../lib";
691             #
692             # Find out what $FindBin::Bin etc would have been if "use FindBin" had been
693             # called in the first file to analyze.
694             #
695             # Notes:
696             # (1) We don't want to reimplement FindBin, hence fake $0 locally (as the path of the
697             # first file analyzed) and call FindBin::again().
698             # (2) If the caller of scan_deps() itself uses FindBin, we don't want to overwrite
699             # the value of "their" $FindBin::Bin.
700             #
701             # Other idioms seen sometimes:
702             #
703             # use lib "$ENV{FOO}/path";
704             # use lib File::Spec->catdir($FindBin::Bin, qw[.. qqlib] );
705             # use lib catdir(dirname($0), "perl");
706             # use lib dirname(abs_path($0));
707             #
708             # In order to correctly interprete these, the modules referenced have to be imported.
709              
710              
711 39         92 require FindBin;
  39         6019  
712              
713 39         12633 local $FindBin::Bin;
714 39         88 local $FindBin::RealBin;
715 39         83 local $FindBin::Script;
716 39         63 local $FindBin::RealScript;
717              
718 39         102 my $_0 = $args{files}[0];
719 39         114 local *0 = \$_0;
720 39         277 FindBin->again();
721              
722 39         7703 our $Bin = $FindBin::Bin;
723 39         96 our $RealBin = $FindBin::RealBin;
724 39         81 our $Script = $FindBin::Script;
725 39         72 our $RealScript = $FindBin::RealScript;
726              
727 39         119 scan_deps_static(\%args);
728             }
729              
730 39 50 33     301 if ($args{execute} or $args{compile}) {
731             scan_deps_runtime(
732             rv => $args{rv},
733             files => $args{files},
734             execute => $args{execute},
735             compile => $args{compile},
736             skip => $args{skip}
737 0         0 );
738             }
739              
740 39 100       128 if ( $using_cache ){
741 8         29 Module::ScanDeps::Cache::store_cache();
742             }
743              
744             # do not include the input files themselves as dependencies!
745 39         2386 delete $args{rv}{$_} foreach @{$args{files}};
  39         175  
746              
747 39         258 return ($args{rv});
748             }
749              
750             sub scan_deps_static {
751 111     111 0 273 my ($args) = @_;
752             my ($files, $keys, $recurse, $rv,
753             $skip, $first, $execute, $compile,
754             $cache_cb, $_skip)
755 111         547 = @$args{qw( files keys recurse rv
756             skip first execute compile
757             cache_cb _skip )};
758              
759 111   50     338 $rv ||= {};
760 111 100 100     343 $_skip ||= { %{$skip || {}} };
  39         328  
761              
762 111         245 foreach my $file (@{$files}) {
  111         296  
763 2140         2725 my $key = shift @{$keys};
  2140         3381  
764 2140 100       6870 next if $_skip->{$file}++;
765             next if is_insensitive_fs()
766 553         808 and $file ne lc($file) and $_skip->{lc($file)}++;
767 553 100       5400 next unless $file =~ $ScanFileRE;
768              
769 549         1144 my @pm;
770             my $found_in_cache;
771 549 100       1120 if ($cache_cb){
772 51         70 my $pm_aref;
773             # cache_cb populates \@pm on success
774 51         169 $found_in_cache = $cache_cb->(action => 'read',
775             key => $key,
776             file => $file,
777             modules => \@pm,
778             );
779 51 100       1564 unless( $found_in_cache ){
780 20         43 @pm = scan_file($file);
781 20         82 $cache_cb->(action => 'write',
782             key => $key,
783             file => $file,
784             modules => \@pm,
785             );
786             }
787             }else{ # no caching callback given
788 498         1128 @pm = scan_file($file);
789             }
790              
791 549         3051 foreach my $pm (@pm){
792             add_deps(
793             used_by => $key,
794             rv => $args->{rv},
795             modules => [$pm],
796             skip => $args->{skip},
797             warn_missing => $args->{warn_missing},
798 1583         7939 );
799              
800 1583 100       5134 my @preload = _get_preload($pm) or next;
801              
802             add_deps(
803             used_by => $key,
804             rv => $args->{rv},
805             modules => \@preload,
806             skip => $args->{skip},
807             warn_missing => $args->{warn_missing},
808 47         302 );
809             }
810             }
811              
812             # Top-level recursion handling {{{
813              
814             # prevent utf8.pm from being scanned
815 111 50       395 $_skip->{$rv->{"utf8.pm"}{file}}++ if $rv->{"utf8.pm"};
816              
817 111         353 while ($recurse) {
818 72         175 my $count = keys %$rv;
819 72 50       360 my @files = sort grep { defined $_->{file} && -T $_->{file} } values %$rv;
  2290         131249  
820             scan_deps_static({
821             files => [ map $_->{file}, @files ],
822 72         2926 keys => [ map $_->{key}, @files ],
823             rv => $rv,
824             skip => $skip,
825             recurse => 0,
826             cache_cb => $cache_cb,
827             _skip => $_skip,
828             });
829 72 100       982 last if $count == keys %$rv;
830             }
831              
832             # }}}
833              
834 111         471 return $rv;
835             }
836              
837             sub scan_deps_runtime {
838 15 50 33 15 1 43843 my %args = (
839             rv => {},
840             (@_ and $_[0] =~ /^(?:$Keys)$/o) ? @_ : (files => [@_], recurse => 1)
841             );
842             my ($files, $rv, $execute, $compile) =
843 15         100 @args{qw( files rv execute compile )};
844              
845 15 50       57 $files = (ref($files)) ? $files : [$files];
846              
847 15 100       71 if ($compile) {
    50          
848 5         18 foreach my $file (@$files) {
849 5 50       57 next unless $file =~ $ScanFileRE;
850              
851 5         22 _merge_rv(_info2rv(_compile_or_execute($file)), $rv);
852             }
853             }
854             elsif ($execute) {
855 10         44 foreach my $file (@$files) {
856 10 100       138 $execute = [] unless ref $execute; # make sure it's an array ref
857              
858 10         57 _merge_rv(_info2rv(_compile_or_execute($file, $execute)), $rv);
859             }
860             }
861              
862 15         675 return ($rv);
863             }
864              
865             sub scan_file{
866 518     518 0 851 my $file = shift;
867 518         826 my %found;
868 518 50       23014 open my $fh, $file or die "Cannot open $file: $!";
869              
870 518         2192 $SeenTk = 0;
871             # Line-by-line scanning
872             LINE:
873 518         11465 while (my $line = <$fh>) {
874 106075         165588 chomp($line);
875 106075         167007 foreach my $pm (scan_line($line)) {
876 2448 100       5422 last LINE if $pm eq '__END__';
877              
878 2201 100       3890 if ($pm eq '__POD__') {
879 224         591 while ($line = <$fh>) {
880 11561 100       24836 next LINE if $line =~ /^=cut/;
881             }
882             }
883              
884             # Skip Tk hits from Term::ReadLine and Tcl::Tk
885 1979         6070 my $pathsep = qr/\/|\\|::/;
886 1979 50       4165 if ($pm =~ /^Tk\b/) {
887 0 0       0 next if $file =~ /(?:^|${pathsep})Term${pathsep}ReadLine\.pm$/;
888 0 0       0 next if $file =~ /(?:^|${pathsep})Tcl${pathsep}Tk\W/;
889             }
890 1979   33     7637 $SeenTk ||= $pm =~ /Tk\.pm$/;
891              
892 1979         8780 $found{$pm}++;
893             }
894             }
895 518 50       8878 close $fh or die "Cannot close $file: $!";
896 518         4448 return keys %found;
897             }
898              
899             sub scan_line {
900 106097     106097 1 179418 my $line = shift;
901 106097         134062 my %found;
902              
903 106097 100       196223 return '__END__' if $line =~ /^__(?:END|DATA)__$/;
904 105850 100       172185 return '__POD__' if $line =~ /^=\w/;
905              
906 105626         181160 $line =~ s/\s*#.*$//;
907              
908             CHUNK:
909 105626         224798 foreach (split(/;/, $line)) {
910 83763         253681 s/^\s*//;
911 83763         145684 s/^\w+:\s*//; # remove LABEL:
912 83763         128878 s/^(?:do\s*)?\{\s*//; # handle single line blocks like 'do { package foo; use xx; }'
913 83763         128315 s/\s*\}$//;
914              
915 83763 100       159696 if (/^package\s+(\w+)/) {
916 515         1496 $CurrentPackage = $1;
917 515         884 $CurrentPackage =~ s{::}{/}g;
918 515         1260 next CHUNK;
919             }
920             # use VERSION:
921 83248 100       162132 if (/^(?:use|require)\s+v?(\d[\d\._]*)/) {
922             # include feature.pm if we have 5.9.5 or better
923 86 100       2040 if (version->new($1) >= version->new("5.9.5")) {
924             # seems to catch 5.9, too (but not 5.9.4)
925 13         56 $found{"feature.pm"}++;
926             }
927 86         384 next CHUNK;
928             }
929              
930 83162 100       173975 if (my ($pragma, $args) = /^use \s+ (autouse|if) \s+ (.+)/x)
931             {
932             # NOTE: There are different ways the MODULE may
933             # be specified for the "autouse" and "if" pragmas, e.g.
934             # use autouse Module => qw(func1 func2);
935             # use autouse "Module", qw(func1);
936             # To avoid to parse them ourself, we simply try to eval the
937             # string after the pragma (in a list context). The MODULE
938             # should be the first ("autouse") or second ("if") element
939             # of the list.
940 11         19 my $module;
941             {
942 16     16   142 no strict; no warnings;
  16     16   47  
  16         550  
  16         106  
  16         48  
  16         3685  
  11         17  
943 11 100       28 if ($pragma eq "autouse") {
944 5         311 ($module) = eval $args;
945             }
946             else {
947             # The syntax of the "if" pragma is
948             # use if COND, MODULE => ARGUMENTS
949             # The COND may contain undefined functions (i.e. undefined
950             # in Module::ScanDeps' context) which would throw an
951             # exception. Sneak "1 || " in front of COND so that
952             # COND will not be evaluated. This will work in most
953             # cases, but there are operators with lower precedence
954             # than "||" which will cause this trick to fail.
955 6         361 (undef, $module) = eval "1 || $args";
956             }
957             # punt if there was a syntax error
958 11 50 33     76 return if $@ or !defined $module;
959             };
960 11         29 $found{_mod2pm($pragma)}++;
961 11         24 $found{_mod2pm($module)}++;
962 11         36 next CHUNK;
963             }
964              
965 83151 100       181673 if (my ($how, $libs) = /^(use \s+ lib \s+ | (?:unshift|push) \s+ \@INC \s+ ,) (.+)/x)
966             {
967 4 50       60 my $archname = defined($Config{archname}) ? $Config{archname} : '';
968 4 50       37 my $ver = defined($Config{version}) ? $Config{version} : '';
969 16     16   148 foreach my $dir (do { no strict; no warnings; eval $libs }) {
  16     16   34  
  16         533  
  16         94  
  16         35  
  16         98101  
  4         10  
  4         276  
970 4 50       46 next unless defined $dir;
971 4         13 my @dirs = $dir;
972 4 50       33 push @dirs, "$dir/$ver", "$dir/$archname", "$dir/$ver/$archname"
973             if $how =~ /lib/;
974 4         10 foreach (@dirs) {
975 16 100       303 unshift(@INC, $_) if -d $_;
976             }
977             }
978 4         18 next CHUNK;
979             }
980              
981 83147         138199 $found{$_}++ for scan_chunk($_);
982             }
983              
984 105626         373944 return sort keys %found;
985             }
986              
987              
988             # convert module name to file name
989             sub _mod2pm {
990 1911     1911   3297 my $mod = shift;
991 1911         3756 $mod =~ s!::!/!g;
992 1911         5920 return "$mod.pm";
993             }
994              
995             # parse a comma-separated list of string literals and qw() lists
996             sub _parse_list {
997 1260     1260   1908 my $list = shift;
998              
999             # split $list on anything that's not a word character or ":"
1000             # and ignore "q", "qq" and "qw"
1001 1260 100       4418 return grep { length and !/^:|^q[qw]?$/ } split(/[^\w:]+/, $list);
  2299         12529  
1002             }
1003              
1004             sub scan_chunk {
1005 83160     83160 1 130584 my $chunk = shift;
1006              
1007             # Module name extraction heuristics {{{
1008 83160         114411 my $module = eval {
1009 83160         129504 local $_ = $chunk;
1010 83160         217579 s/^\s*//;
1011              
1012             # "if", "while" etc: analyze the expression
1013 83160         151204 s/^(?:if|elsif|unless|while|until) \s* \( \s*//x;
1014              
1015             # "eval" with a block: analyze the block
1016 83160         110574 s/^eval \s* \{ \s*//x;
1017              
1018             # "eval" with an expression that's a string literal:
1019             # analyze the string
1020 83160         110442 s/^eval \s+ (?:['"]|qq?\s*\W) \s*//x;
1021              
1022             # "use LOADER LIST"
1023             # TODO: There's many more of these "loader" type modules on CPAN!
1024 83160 100       278668 if (my ($loader, $list) = $_ =~ $LoaderRE) {
1025 49         131 my @mods = _parse_list($list);
1026              
1027 49 100       178 if ($loader eq "Catalyst") {
1028             # "use Catalyst 'Foo'" looks for "Catalyst::Plugin::Foo",
1029             # but "use Catalyst +Foo" looks for "Foo"
1030             @mods = map {
1031 2 100       5 ($list =~ /([+-])\Q$_\E(?:$|[^\w:])/)
  5 100       290  
1032             ? ($1 eq "-"
1033             ? () # "-Foo": it's a flag, eg. "-Debug", skip it
1034             : $_) # "+Foo": look for "Foo"
1035             : "Catalyst::Plugin::$_"
1036             # "Foo": look for "Catalyst::Plugin::Foo"
1037             } @mods;
1038             }
1039 49         114 return [ map { _mod2pm($_) } $loader, @mods ];
  114         229  
1040             }
1041              
1042 83111 50 33     262299 if (/^use \s+ Class::Autouse \b \s* (.*)/sx
1043             or /^Class::Autouse \s* -> \s* autouse \s* (.*)/sx) {
1044 0         0 return [ map { _mod2pm($_) } "Class::Autouse", _parse_list($1) ];
  0         0  
1045             }
1046              
1047             # generic "use ..."
1048 83111 100       176713 if (s/^(?:use|no) \s+//x) {
1049 1209         2475 my ($mod) = _parse_list($_); # just the first word
1050 1209         2898 return _mod2pm($mod);
1051             }
1052              
1053 81902 100       158166 if (s/^(require|do) [\s(]+//x) {
1054 628 100 100     4089 return ($1 eq "require" && /^([\w:]+)/)
1055             ? _mod2pm($1) # bareword ("require" only)
1056             : $_; # maybe string literal?
1057             }
1058              
1059 81274 100       149544 if (/(<[^>]*[^\$\w>][^>]*>)/) {
1060 358         1045 my $diamond = $1;
1061 358 100       1040 return "File/Glob.pm" if $diamond =~ /[*?\[\]{}~\\]/;
1062             }
1063              
1064 81243 50       148011 return "DBD/$1.pm" if /\bdbi:(\w+):/i;
1065              
1066             # Moose/Moo/Mouse style inheritance or composition
1067 81243 100       147586 if (s/^(with|extends)\s+//) {
1068 2         6 return [ map { _mod2pm($_) } _parse_list($_) ];
  4         11  
1069             }
1070              
1071             # check for stuff like
1072             # decode("klingon", ...)
1073             # open FH, "<:encoding(klingon)", ...
1074 81241 100       219603 if (my ($args) = /\b(?:open|binmode)\b(.*)/) {
1075 172         332 my @mods;
1076 172 100       442 push @mods, qw( PerlIO.pm PerlIO/encoding.pm Encode.pm ), _find_encoding($1)
1077             if $args =~ /:encoding\((.*?)\)/;
1078 172         1349 while ($args =~ /:(\w+)(?:\((.*?)\))?/g) {
1079 6         41 push @mods, "PerlIO/$1.pm";
1080 6 100       39 push @mods, "Encode.pm", _find_encoding($2) if $1 eq "encoding";
1081             }
1082 172 100       413 push @mods, "PerlIO.pm" if @mods;
1083 172 100       423 return \@mods if @mods;
1084             }
1085 81237 100       143879 if (/\b(?:en|de)code\(\s*['"]?([-\w]+)/) {
1086 2         9 return [qw( Encode.pm ), _find_encoding($1)];
1087             }
1088              
1089 81235 50       131392 if ($SeenTk) {
1090 0         0 my @modules;
1091 0         0 while (/->\s*([A-Z]\w+)/g) {
1092 0         0 push @modules, "Tk/$1.pm";
1093             }
1094 0         0 while (/->\s*Scrolled\W+([A-Z]\w+)/g) {
1095 0         0 push @modules, "Tk/$1.pm";
1096 0         0 push @modules, "Tk/Scrollbar.pm";
1097             }
1098 0 0       0 if (/->\s*setPalette/g) {
1099             push @modules,
1100 0         0 map { "Tk/$_.pm" }
  0         0  
1101             qw( Button Canvas Checkbutton Entry
1102             Frame Label Labelframe Listbox
1103             Menubutton Menu Message Radiobutton
1104             Scale Scrollbar Spinbox Text );
1105             }
1106 0         0 return \@modules;
1107             }
1108              
1109             # Module::Runtime
1110 81235 50       146334 return $_ if s/^(?:require_module|use_module|use_package_optimistically) \s* \( \s*//x;
1111              
1112             # Test::More
1113 81235 100       139585 return $_ if s/^(?:require_ok|use_ok) \s* \( \s*//x;
1114              
1115 81233         157114 return;
1116             };
1117              
1118             # }}}
1119              
1120 83160 100       224489 return unless defined($module);
1121 1927 50       3805 return wantarray ? @$module : $module->[0] if ref($module);
    100          
1122              
1123             # extract contents from string literals
1124 1870 100       5882 if ($module =~ /^(['"]) (.*?) \1/x) {
    50          
1125 29         100 $module = $2;
1126             }
1127             elsif ($module =~ s/^qq? \s* (\W)//x) {
1128 0         0 (my $closing = $1) =~ tr:([{<:)]}>:;
1129 0         0 $module =~ s/\Q$closing\E.*//;
1130             }
1131              
1132 1870         3162 $module =~ s/::/\//g;
1133 1870 100       4767 return if $module =~ /^(?:[\d\._]+|'.*[^']|".*[^"])$/;
1134              
1135 1869 100       4396 $module .= ".pm" unless $module =~ /\./;
1136 1869         8009 return $module;
1137             }
1138              
1139             sub _find_encoding {
1140 4     4   18 my ($enc) = @_;
1141 4 50 33     27 return unless $] >= 5.008 and eval { require Encode; %Encode::ExtModule };
  4         1720  
  4         22381  
1142              
1143 4 50       9 my $mod = eval { $Encode::ExtModule{ Encode::find_encoding($enc)->name } } or return;
  4         15  
1144 0         0 return _mod2pm($mod);
1145             }
1146              
1147             sub _add_info {
1148 2087     2087   8891 my %args = @_;
1149 2087         6149 my ($rv, $module, $file, $used_by, $type) = @args{qw/rv module file used_by type/};
1150              
1151 2087 50 33     7704 return unless defined($module) and defined($file);
1152              
1153             # Ensure file is always absolute
1154 2087         23066 $file = File::Spec->rel2abs($file);
1155 2087         5194 $file =~ s|\\|\/|go;
1156              
1157             # Avoid duplicates that can arise due to case differences that don't actually
1158             # matter on a case tolerant system
1159 2087 50       6161 if (File::Spec->case_tolerant()) {
1160 0         0 foreach my $key (keys %$rv) {
1161 0 0       0 if (lc($key) eq lc($module)) {
1162 0         0 $module = $key;
1163 0         0 last;
1164             }
1165             }
1166 0 0       0 if (defined($used_by)) {
1167 0 0       0 if (lc($used_by) eq lc($module)) {
1168 0         0 $used_by = $module;
1169             } else {
1170 0         0 foreach my $key (keys %$rv) {
1171 0 0       0 if (lc($key) eq lc($used_by)) {
1172 0         0 $used_by = $key;
1173 0         0 last;
1174             }
1175             }
1176             }
1177             }
1178             }
1179              
1180 2087   100     8532 $rv->{$module} ||= {
1181             file => $file,
1182             key => $module,
1183             type => $type,
1184             };
1185              
1186 2087 100 100     7631 if (defined($used_by) and $used_by ne $module) {
1187 1942         5140 push @{ $rv->{$module}{used_by} }, $used_by
1188             if ( (!File::Spec->case_tolerant() && !grep { $_ eq $used_by } @{ $rv->{$module}{used_by} })
1189 2020 100 66     5356 or ( File::Spec->case_tolerant() && !grep { lc($_) eq lc($used_by) } @{ $rv->{$module}{used_by} }));
  0   33     0  
  0   66     0  
1190              
1191             # We assume here that another _add_info will be called to provide the other parts of $rv->{$used_by}
1192 1942         7041 push @{ $rv->{$used_by}{uses} }, $module
1193             if ( (!File::Spec->case_tolerant() && !grep { $_ eq $module } @{ $rv->{$used_by}{uses} })
1194 2020 100 66     5510 or ( File::Spec->case_tolerant() && !grep { lc($_) eq lc($module) } @{ $rv->{$used_by}{uses} }));
  0   33     0  
  0   100     0  
1195             }
1196             }
1197              
1198             # This subroutine relies on not being called for modules that have already been visited
1199             sub add_deps {
1200 1641 0 33 1641 1 14159 my %args =
    50          
1201             ((@_ and $_[0] =~ /^(?:modules|rv|used_by|warn_missing)$/)
1202             ? @_
1203             : (rv => (ref($_[0]) ? shift(@_) : undef), modules => [@_]));
1204              
1205 1641   50     4416 my $rv = $args{rv} || {};
1206 1641   100     4754 my $skip = $args{skip} || {};
1207 1641         2750 my $used_by = $args{used_by};
1208              
1209 1641         2413 foreach my $module (@{ $args{modules} }) {
  1641         3276  
1210             my $file = _find_in_inc($module)
1211 1995 100       3792 or _warn_of_missing_module($module, $args{warn_missing}), next;
1212 1918 100       6806 next if $skip->{$file};
1213              
1214 1917 100       4414 if (exists $rv->{$module}) {
1215 1514         4249 _add_info( rv => $rv, module => $module,
1216             file => $file, used_by => $used_by,
1217             type => undef );
1218 1514         3520 next;
1219             }
1220              
1221 403         1242 _add_info( rv => $rv, module => $module,
1222             file => $file, used_by => $used_by,
1223             type => _gettype($file) );
1224              
1225 403 100       2183 if ((my $path = $module) =~ s/\.p[mh]$//i) {
1226              
1227 387         1276 foreach (_glob_in_inc("auto/$path")) {
1228 180 100       2339 next if $_->{name} =~ m{^auto/$path/.*/}; # weed out subdirs
1229 135 100       2936 next if $_->{name} =~ m{/(?:\.exists|\.packlist)$|\Q$Config{lib_ext}\E$};
1230              
1231             _add_info( rv => $rv, module => $_->{name},
1232             file => $_->{file}, used_by => $module,
1233 126         571 type => _gettype($_->{name}) );
1234             }
1235              
1236             ### Now, handle module and distribution share dirs
1237             # convert 'Module/Name' to 'Module-Name'
1238 387         1044 my $modname = $path;
1239 387         1396 $modname =~ s|/|-|g;
1240             # TODO: get real distribution name related to module name
1241 387         656 my $distname = $modname;
1242 387         1138 foreach (_glob_in_inc("auto/share/module/$modname")) {
1243             _add_info( rv => $rv, module => $_->{name},
1244 0         0 file => $_->{file}, used_by => $module,
1245             type => 'data' );
1246             }
1247 387         1487 foreach (_glob_in_inc("auto/share/dist/$distname")) {
1248             _add_info( rv => $rv, module => $_->{name},
1249 0         0 file => $_->{file}, used_by => $module,
1250             type => 'data' );
1251             }
1252             }
1253             } # end for modules
1254 1641         4415 return $rv;
1255             }
1256              
1257             sub _find_in_inc {
1258 1995     1995   2911 my $file = shift;
1259 1995 50       3808 return unless defined $file;
1260              
1261 1995         11792 foreach my $dir (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) {
1262 19535 100       231771 return "$dir/$file" if -f "$dir/$file";
1263             }
1264              
1265             # absolute file names
1266 77 50       702 return $file if -f $file;
1267              
1268 77         559 return;
1269             }
1270              
1271             sub _glob_in_inc {
1272 1183     1183   2319 my ($subdir, $pm_only) = @_;
1273              
1274 1183         5688 require File::Find;
1275              
1276 1183         2308 $subdir =~ s/\$CurrentPackage/$CurrentPackage/;
1277              
1278 1183         1666 my @files;
1279 1183         6072 foreach my $inc (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) {
1280 13669         37041 my $dir = "$inc/$subdir";
1281 13669 100       133869 next unless -d $dir;
1282              
1283             # canonicalize $inc as newer versions of File::Find return
1284             # a canonicalized $File::Find::name
1285 83         408 (my $canon = $inc) =~ s|\\|\/|g;
1286             File::Find::find(
1287             sub {
1288 696 100   696   24848 return unless -f $_;
1289 549 50 66     3091 return if $pm_only and !/\.p[mh]$/i;
1290 549         1377 (my $name = $File::Find::name) =~ s|\\|\/|g;
1291 549         3852 $name =~ s|^\Q$canon\E/||;
1292 549 100       9852 push @files, $pm_only ? $name
1293             : { file => $File::Find::name, name => $name };
1294             },
1295 83         7628 $dir
1296             );
1297             }
1298              
1299 1183         4621 return @files;
1300             }
1301              
1302             # like _glob_in_inc, but looks only at the first level
1303             # (i.e. the children of $subdir)
1304             # NOTE: File::Find has no public notion of the depth of the traversal
1305             # in its "wanted" callback, so it's not helpful
1306             sub _glob_in_inc_1 {
1307 0     0   0 my ($subdir, $pm_only) = @_;
1308              
1309 0         0 $subdir =~ s/\$CurrentPackage/$CurrentPackage/;
1310              
1311 0         0 my @files;
1312 0         0 foreach my $inc (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) {
1313 0         0 my $dir = "$inc/$subdir";
1314 0 0       0 next unless -d $dir;
1315              
1316 0 0       0 opendir(my $dh, $dir) or next;
1317 0         0 my @names = map { "$subdir/$_" } grep { -f "$dir/$_" } readdir $dh;
  0         0  
  0         0  
1318 0         0 closedir $dh;
1319              
1320 0         0 push @files, $pm_only ? ( grep { /\.p[mh]$/i } @names )
1321 0 0       0 : ( map { { file => "$inc/$_", name => $_ } } @names );
  0         0  
1322             }
1323              
1324 0         0 return @files;
1325             }
1326              
1327             my $unicore_stuff;
1328             sub _unicore {
1329 0   0 0   0 $unicore_stuff ||= [ 'utf8_heavy.pl', map $_->{name}, _glob_in_inc('unicore', 0) ];
1330 0         0 return @$unicore_stuff;
1331             }
1332              
1333             # App::Packer compatibility functions
1334              
1335             sub new {
1336 1     1 0 710 my ($class, $self) = @_;
1337 1   50     9 return bless($self ||= {}, $class);
1338             }
1339              
1340             sub set_file {
1341 1     1 0 4 my $self = shift;
1342 1         2 my $script = shift;
1343              
1344 1         18 my ($vol, $dir, $file) = File::Spec->splitpath($script);
1345             $self->{main} = {
1346 1         8 key => $file,
1347             file => $script,
1348             };
1349             }
1350              
1351             sub set_options {
1352 0     0 0 0 my $self = shift;
1353 0         0 my %args = @_;
1354 0         0 foreach my $module (@{ $args{add_modules} }) {
  0         0  
1355 0 0       0 $module = _mod2pm($module) unless $module =~ /\.p[mh]$/i;
1356             my $file = _find_in_inc($module)
1357 0 0       0 or _warn_of_missing_module($module, $args{warn_missing}), next;
1358 0         0 $self->{files}{$module} = $file;
1359             }
1360             }
1361              
1362             sub calculate_info {
1363 1     1 0 5 my $self = shift;
1364             my $rv = scan_deps(
1365 1         5 'keys' => [ $self->{main}{key}, sort keys %{ $self->{files} }, ],
1366             files => [ $self->{main}{file},
1367 1         2 map { $self->{files}{$_} } sort keys %{ $self->{files} },
  0         0  
  1         4  
1368             ],
1369             recurse => 1,
1370             );
1371              
1372             my $info = {
1373             main => { file => $self->{main}{file},
1374             store_as => $self->{main}{key},
1375             },
1376 1         10 };
1377              
1378 1         5 my %cache = ($self->{main}{key} => $info->{main});
1379 1         3 foreach my $key (sort keys %{ $self->{files} }) {
  1         4  
1380 0         0 my $file = $self->{files}{$key};
1381              
1382             $cache{$key} = $info->{modules}{$key} = {
1383             file => $file,
1384             store_as => $key,
1385 0         0 used_by => [ $self->{main}{key} ],
1386             };
1387             }
1388              
1389 1         2 foreach my $key (sort keys %{$rv}) {
  1         64  
1390 127         186 my $val = $rv->{$key};
1391 127 100       202 if ($cache{ $val->{key} }) {
1392 1 50       5 defined($val->{used_by}) or next;
1393 0         0 push @{ $info->{ $val->{type} }->{ $val->{key} }->{used_by} },
1394 0         0 @{ $val->{used_by} };
  0         0  
1395             }
1396             else {
1397             $cache{ $val->{key} } = $info->{ $val->{type} }->{ $val->{key} } =
1398             { file => $val->{file},
1399             store_as => $val->{key},
1400             used_by => $val->{used_by},
1401 126         494 };
1402             }
1403             }
1404              
1405 1         10 $self->{info} = { main => $info->{main} };
1406              
1407 1         2 foreach my $type (sort keys %{$info}) {
  1         7  
1408 4 100       11 next if $type eq 'main';
1409              
1410 3         5 my @val;
1411 3 50       10 if (UNIVERSAL::isa($info->{$type}, 'HASH')) {
1412 3         6 foreach my $val (sort values %{ $info->{$type} }) {
  3         269  
1413 126         221 @{ $val->{used_by} } = map $cache{$_} || "!!$_!!",
1414 126   33     159 @{ $val->{used_by} };
  126         550  
1415 126         199 push @val, $val;
1416             }
1417             }
1418              
1419 3 100       9 $type = 'modules' if $type eq 'module';
1420 3         121 $self->{info}{$type} = \@val;
1421             }
1422             }
1423              
1424             sub get_files {
1425 1     1 0 60 my $self = shift;
1426 1         14 return $self->{info};
1427             }
1428              
1429             sub add_preload_rule {
1430 0     0 0 0 my ($pm, $rule) = @_;
1431 0 0       0 die qq[a preload rule for "$pm" already exists] if $Preload{$pm};
1432 0         0 $Preload{$pm} = $rule;
1433             }
1434              
1435             # scan_deps_runtime utility functions
1436              
1437             # compile $file if $execute is undef,
1438             # otherwise execute $file with arguments @$execute
1439             sub _compile_or_execute {
1440 15     15   45 my ($file, $execute) = @_;
1441              
1442 15         162 local $ENV{MSD_ORIGINAL_FILE} = $file;
1443              
1444 15         71 my ($ih, $instrumented_file) = File::Temp::tempfile(UNLINK => 1);
1445              
1446 15         6911 my (undef, $data_file) = File::Temp::tempfile(UNLINK => 1);
1447 15         6874 local $ENV{MSD_DATA_FILE} = $data_file;
1448              
1449             # spoof $0 (to $file) so that FindBin works as expected
1450             # NOTE: We don't directly assign to $0 as it has magic (i.e.
1451             # assigning has side effects and may actually fail, cf. perlvar(1)).
1452             # Instead we alias *0 to a package variable holding the correct value.
1453 15         115 print $ih <<'...';
1454             BEGIN { my $_0 = $ENV{MSD_ORIGINAL_FILE}; *0 = \$_0; }
1455             ...
1456              
1457             # NOTE: When compiling the block will run as the last CHECK block;
1458             # when executing the block will run as the first END block and
1459             # the programs continues.
1460 15 100       92 print $ih
1461             $execute ? "END\n" : "CHECK\n",
1462             <<'...';
1463             {
1464             require DynaLoader;
1465             my @_dl_shared_objects = @DynaLoader::dl_shared_objects;
1466             my @_dl_modules = @DynaLoader::dl_modules;
1467              
1468             # save %INC etc so that requires below don't pollute them
1469             my %_INC = %INC;
1470             my @_INC = @INC;
1471              
1472             require Cwd;
1473             require Data::Dumper;
1474             require Config;
1475             my $dlext = $Config::Config{dlext};
1476              
1477             foreach my $k (keys %_INC)
1478             {
1479             # NOTES:
1480             # (1) An unsuccessful "require" may store an undefined value into %INC.
1481             # (2) If a key in %INC was located via a CODE or ARRAY ref or
1482             # blessed object in @INC the corresponding value in %INC contains
1483             # the ref from @INC.
1484             # (3) Some modules (e.g. Moose) fake entries in %INC, e.g.
1485             # "Class/MOP/Class/Immutable/Moose/Meta/Class.pm" => "(set by Moose)"
1486             # On some architectures (e.g. Windows) Cwd::abs_path() will throw
1487             # an exception for such a pathname.
1488              
1489             my $v = $_INC{$k};
1490             if (defined $v && !ref $v && -e $v)
1491             {
1492             $_INC{$k} = Cwd::abs_path($v);
1493             }
1494             else
1495             {
1496             delete $_INC{$k};
1497             }
1498             }
1499              
1500             # drop refs from @_INC
1501             @_INC = grep { !ref $_ } @_INC;
1502              
1503             my @dlls = grep { defined $_ && -e $_ } Module::ScanDeps::DataFeed::_dl_shared_objects();
1504             my @shared_objects = @dlls;
1505             push @shared_objects, grep { -e $_ } map { (my $bs = $_) =~ s/\.\Q$dlext\E$/.bs/; $bs } @dlls;
1506              
1507             # write data file
1508             my $data_file = $ENV{MSD_DATA_FILE};
1509             open my $fh, ">", $data_file
1510             or die "Couldn't open $data_file: $!\n";
1511             print $fh Data::Dumper::Dumper(
1512             {
1513             '%INC' => \%_INC,
1514             '@INC' => \@_INC,
1515             dl_shared_objects => \@shared_objects,
1516             });
1517             close $fh;
1518              
1519             sub Module::ScanDeps::DataFeed::_dl_shared_objects {
1520             if (@_dl_shared_objects) {
1521             return @_dl_shared_objects;
1522             }
1523             elsif (@_dl_modules) {
1524             return map { Module::ScanDeps::DataFeed::_dl_mod2filename($_) } @_dl_modules;
1525             }
1526             return;
1527             }
1528              
1529             sub Module::ScanDeps::DataFeed::_dl_mod2filename {
1530             my $mod = shift;
1531              
1532             return if $mod eq 'B';
1533             return unless defined &{"$mod\::bootstrap"};
1534              
1535              
1536             # cf. DynaLoader.pm
1537             my @modparts = split(/::/, $mod);
1538             my $modfname = defined &DynaLoader::mod2fname ? DynaLoader::mod2fname(\@modparts) : $modparts[-1];
1539             my $modpname = join('/', @modparts);
1540              
1541             foreach my $dir (@_INC) {
1542             my $file = "$dir/auto/$modpname/$modfname.$dlext";
1543             return $file if -e $file;
1544             }
1545             return;
1546             }
1547             } # END or CHECK
1548             ...
1549              
1550             # append the file to compile or execute
1551             {
1552 15 50       26 open my $fh, "<", $file or die "Couldn't open $file: $!";
  15         738  
1553 15         655 print $ih qq[#line 1 "$file"\n], <$fh>;
1554 15         291 close $fh;
1555             }
1556 15         630 close $ih;
1557              
1558             # run the instrumented file
1559             my $rc = system(
1560             $^X,
1561             $execute ? () : ("-c"),
1562 15 100       566860 (map { "-I$_" } @IncludeLibs),
  0 100       0  
1563             $instrumented_file,
1564             $execute ? @$execute : ());
1565              
1566 15 0       340 die $execute
    50          
1567             ? "SYSTEM ERROR in executing $file @$execute: $rc"
1568             : "SYSTEM ERROR in compiling $file: $rc"
1569             unless $rc == 0;
1570              
1571 15 50 0     21045 my $info = do $data_file
1572             or die "error extracting info from -c/-x file: ", ($@ || "can't read $data_file: $!");
1573              
1574 15         981 return $info;
1575             }
1576              
1577             # create a new hashref, applying fixups
1578             sub _info2rv {
1579 15     15   85 my ($info) = @_;
1580              
1581 15         56 my $rv = {};
1582              
1583 180         491 my $incs = join('|', sort { length($b) <=> length($a) }
1584 105         340 map { s:\\:/:g; s:^(/.*?)/+$:$1:; quotemeta($_) }
  105         284  
  105         722  
1585 15         55 @{ $info->{'@INC'} });
  15         74  
1586 15         66 my $i = is_insensitive_fs() ? "i" : "";
1587 15         425 my $strip_inc_prefix = qr{^(?$i:$incs)/};
1588              
1589 15         261 require File::Spec;
1590              
1591 15         57 foreach my $key (keys %{ $info->{'%INC'} }) {
  15         168  
1592 149         417 (my $path = $info->{'%INC'}{$key}) =~ s:\\:/:g;
1593              
1594 149         528 $rv->{$key} = {
1595             'used_by' => [],
1596             'file' => $path,
1597             'type' => _gettype($path),
1598             'key' => $key
1599             };
1600             }
1601              
1602 15         76 foreach my $path (@{ $info->{dl_shared_objects} }) {
  15         50  
1603 15         38 $path =~ s:\\:/:g;
1604 15         292 (my $key = $path) =~ s/$strip_inc_prefix//;
1605              
1606 15         154 $rv->{$key} = {
1607             'used_by' => [],
1608             'file' => $path,
1609             'type' => 'shared',
1610             'key' => $key
1611             };
1612             }
1613              
1614 15         283 return $rv;
1615             }
1616              
1617             sub _gettype {
1618 733     733   1512 my $name = shift;
1619              
1620 733 100       3506 return 'autoload' if $name =~ /\.(?:ix|al|bs)$/i;
1621 657 100       4088 return 'module' if $name =~ /\.p[mh]$/i;
1622 110 100       1661 return 'shared' if $name =~ /\.\Q$Config{dlext}\E$/i;
1623 62         248 return 'data';
1624             }
1625              
1626             # merge all keys from $rv_sub into the $rv mega-ref
1627             sub _merge_rv {
1628 15     15   111 my ($rv_sub, $rv) = @_;
1629              
1630 15         47 my $key;
1631 15         186 foreach $key (keys(%$rv_sub)) {
1632 164         313 my %mark;
1633 164 50 33     607 if ($rv->{$key} and _not_dup($key, $rv, $rv_sub)) {
    50          
1634             warn "Different modules for file '$key' were found.\n"
1635             . " -> Using '" . abs_path($rv_sub->{$key}{file}) . "'.\n"
1636 0         0 . " -> Ignoring '" . abs_path($rv->{$key}{file}) . "'.\n";
1637             $rv->{$key}{used_by} = [
1638             grep (!$mark{$_}++,
1639 0         0 @{ $rv->{$key}{used_by} },
1640 0         0 @{ $rv_sub->{$key}{used_by} })
  0         0  
1641             ];
1642 0         0 @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} };
  0         0  
  0         0  
1643 0         0 $rv->{$key}{file} = $rv_sub->{$key}{file};
1644             }
1645             elsif ($rv->{$key}) {
1646             $rv->{$key}{used_by} = [
1647             grep (!$mark{$_}++,
1648 0         0 @{ $rv->{$key}{used_by} },
1649 0         0 @{ $rv_sub->{$key}{used_by} })
  0         0  
1650             ];
1651 0         0 @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} };
  0         0  
  0         0  
1652             }
1653             else {
1654             $rv->{$key} = {
1655 164         916 used_by => [ @{ $rv_sub->{$key}{used_by} } ],
1656             file => $rv_sub->{$key}{file},
1657             key => $rv_sub->{$key}{key},
1658             type => $rv_sub->{$key}{type}
1659 164         266 };
1660              
1661 164         357 @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} };
  164         622  
  164         337  
1662             }
1663             }
1664             }
1665              
1666             sub _not_dup {
1667 0     0   0 my ($key, $rv1, $rv2) = @_;
1668 0 0       0 if (File::Spec->case_tolerant()) {
1669 0         0 return lc(abs_path($rv1->{$key}{file})) ne lc(abs_path($rv2->{$key}{file}));
1670             }
1671             else {
1672 0         0 return abs_path($rv1->{$key}{file}) ne abs_path($rv2->{$key}{file});
1673             }
1674             }
1675              
1676             sub _warn_of_runtime_loader {
1677 0     0   0 my $module = shift;
1678 0 0       0 return if $SeenRuntimeLoader{$module}++;
1679 0         0 $module =~ s/\.pm$//;
1680 0         0 $module =~ s|/|::|g;
1681 0         0 warn "# Use of runtime loader module $module detected. Results of static scanning may be incomplete.\n";
1682 0         0 return;
1683             }
1684              
1685             sub _warn_of_missing_module {
1686 77     77   165 my $module = shift;
1687 77         148 my $warn = shift;
1688 77 50       237 return if not $warn;
1689 0 0       0 return if not $module =~ /\.p[ml]$/;
1690 0 0       0 warn "# Could not find source file '$module' in \@INC or \@IncludeLibs. Skipping it.\n"
1691             if not -f $module;
1692             }
1693              
1694             sub _get_preload1 {
1695 1990     1990   3300 my $pm = shift;
1696 1990 100       6668 my $preload = $Preload{$pm} or return();
1697 56 100       460 if ($preload eq 'sub') {
    100          
1698 1         7 $pm =~ s/\.p[mh]$//i;
1699 1         6 return _glob_in_inc($pm, 1);
1700             }
1701             elsif (UNIVERSAL::isa($preload, 'CODE')) {
1702 29         122 return $preload->($pm);
1703             }
1704 26         99 return @$preload;
1705             }
1706              
1707             sub _get_preload {
1708 1990     1990   3833 my ($pm, $seen) = @_;
1709 1990   100     6869 $seen ||= {};
1710 1990         4366 $seen->{$pm}++;
1711 1990         2867 my @preload;
1712              
1713 1990         3536 foreach $pm (_get_preload1($pm))
1714             {
1715 406 50       781 next if $seen->{$pm};
1716 406         773 $seen->{$pm}++;
1717 406         704 push @preload, $pm, _get_preload($pm, $seen);
1718             }
1719 1990         6777 return @preload;
1720             }
1721              
1722             1;
1723             __END__