File Coverage

blib/lib/Module/ScanDeps.pm
Criterion Covered Total %
statement 469 556 84.3
branch 214 286 74.8
condition 30 58 51.7
subroutine 48 58 82.7
pod 6 14 42.8
total 767 972 78.9


line stmt bran cond sub pod time code
1             package Module::ScanDeps;
2 19     19   2138904 use 5.008001;
  19         80  
3 19     19   142 use strict;
  19         76  
  19         1134  
4 19     19   102 use warnings;
  19         34  
  19         1483  
5 19     19   119 use vars qw( $VERSION @EXPORT @EXPORT_OK @ISA $CurrentPackage @IncludeLibs $ScanFileRE );
  19         65  
  19         2717  
6              
7             $VERSION = '1.37';
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 19     19   119 use Config;
  19         36  
  19         1248  
12             require Exporter;
13             our @ISA = qw(Exporter);
14              
15 19     19   8299 use version;
  19         38491  
  19         106  
16 19     19   1560 use File::Path ();
  19         37  
  19         332  
17 19     19   15729 use File::Temp ();
  19         438844  
  19         632  
18 19     19   12480 use FileHandle;
  19         42331  
  19         1852  
19 19     19   23635 use Module::Metadata;
  19         142143  
  19         1065  
20 19     19   161 use List::Util qw ( any first );
  19         27  
  19         2084  
21              
22             # NOTE: Keep the following imports exactly as specified, even if the Module::ScanDeps source
23             # doesn't reference some of them. See '"use lib" idioms' for the reason.
24 19     19   147 use Cwd (qw(abs_path));
  19         29  
  19         1007  
25 19     19   107 use File::Spec;
  19         39  
  19         335  
26 19     19   8702 use File::Spec::Functions;
  19         14138  
  19         1583  
27 19     19   189 use File::Basename;
  19         72  
  19         1778  
28              
29 19     19   144 use constant is_insensitive_fs => File::Spec->case_tolerant();
  19         36  
  19         12082  
30              
31             $ScanFileRE = qr/(?:^|\\|\/)(?:[^.]*|.*\.(?i:p[ml]|t|al))$/;
32              
33             my %_glob_cache;
34             my %_file_cache;
35             my $_cached_inc = "";
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 19     19   152 use Config;
  19         31  
  19         254545  
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             'MooX/HandlesVia.pm' => sub {
453             _glob_in_inc('Data/Perl', 1)
454             },
455             'Mozilla/CA.pm' => [qw( Mozilla/CA/cacert.pem )],
456             'MozRepl.pm' => sub {
457             qw( MozRepl/Log.pm MozRepl/Client.pm Module/Pluggable/Fast.pm ),
458             _glob_in_inc('MozRepl/Plugin', 1),
459             },
460             'Module/Implementation.pm' => \&_warn_of_runtime_loader,
461             'Module/Runtime.pm' => \&_warn_of_runtime_loader,
462             'Mojo/Util.pm' => sub { # html_entities.txt
463             map { $_->{name} } _glob_in_inc('Mojo/resources', 0)
464             },
465             'Mojo/IOLoop/TLS.pm' => sub { # server.{crt,key}
466             map { $_->{name} } _glob_in_inc('Mojo/IOLoop/resources', 0)
467             },
468              
469             'Net/DNS/Resolver.pm' => 'sub',
470             'Net/DNS/RR.pm' => 'sub',
471             'Net/FTP.pm' => 'sub',
472             'Net/HTTPS.pm' => [qw( IO/Socket/SSL.pm Net/SSL.pm )],
473             'Net/Server.pm' => 'sub',
474             'Net/SSH/Perl.pm' => 'sub',
475              
476             'Object/Pad.pm' => [qw( XS/Parse/Keyword.pm )],
477             'Object/Pad/Keyword/Accessor.pm' => [qw( XS/Parse/Keyword.pm )],
478              
479             'Package/Stash.pm' => [qw( Package/Stash/PP.pm Package/Stash/XS.pm )],
480             'Pango.pm' => [qw( Cairo.pm )], # Pango.pm does: eval "use Cairo;"
481             'PAR/Repository.pm' => 'sub',
482             'PAR/Repository/Client.pm' => 'sub',
483             'Params/Validate.pm' => 'sub',
484             'Parse/AFP.pm' => 'sub',
485             'Parse/Binary.pm' => 'sub',
486             'PDF/API2/Resource/Font.pm' => 'sub',
487             'PDF/API2/Basic/TTF/Font.pm' => sub {
488             _glob_in_inc('PDF/API2/Basic/TTF', 1);
489             },
490             'PDF/Writer.pm' => 'sub',
491             'PDL/NiceSlice.pm' => 'sub',
492             'Perl/Critic.pm' => 'sub', #not only Perl/Critic/Policy
493             'PerlIO.pm' => [qw( PerlIO/scalar.pm )],
494             'Pod/Simple/Transcode.pm' => [qw( Pod/Simple/TranscodeDumb.pm Pod/Simple/TranscodeSmart.pm )],
495             'Pod/Usage.pm' => sub { # from Pod::Usage (as of 1.61)
496             $] >= 5.005_58 ? 'Pod/Text.pm' : 'Pod/PlainText.pm'
497             },
498             'POE.pm' => [qw( POE/Kernel.pm POE/Session.pm )],
499             'POE/Component/Client/HTTP.pm' => sub {
500             _glob_in_inc('POE/Component/Client/HTTP', 1),
501             qw( POE/Filter/HTTPChunk.pm POE/Filter/HTTPHead.pm ),
502             },
503             'POE/Kernel.pm' => sub {
504             _glob_in_inc('POE/XS/Resource', 1),
505             _glob_in_inc('POE/Resource', 1),
506             _glob_in_inc('POE/XS/Loop', 1),
507             _glob_in_inc('POE/Loop', 1),
508             },
509             'POSIX.pm' => sub {
510             map $_->{name},
511             _glob_in_inc('auto/POSIX/SigAction', 0), # *.al files
512             _glob_in_inc('auto/POSIX/SigRt', 0), # *.al files
513             },
514             'PPI.pm' => 'sub',
515              
516             'Regexp/Common.pm' => 'sub',
517             'RPC/XML/ParserFactory.pm' => sub {
518             _glob_in_inc('RPC/XML/Parser', 1);
519             },
520              
521             'SerialJunk.pm' => [qw(
522             termios.ph asm/termios.ph sys/termiox.ph sys/termios.ph sys/ttycom.ph
523             )],
524             'SOAP/Lite.pm' => sub {
525             _glob_in_inc('SOAP/Transport', 1),
526             _glob_in_inc('SOAP/Lite', 1),
527             },
528             'Socket/GetAddrInfo.pm' => 'sub',
529             'Specio/PartialDump.pm' => \&_unicore,
530             'SQL/Parser.pm' => sub {
531             _glob_in_inc('SQL/Dialects', 1);
532             },
533             'SQL/Translator/Schema.pm' => sub {
534             _glob_in_inc('SQL/Translator', 1);
535             },
536             'Sub/Exporter/Progressive.pm' => [qw( Sub/Exporter.pm )],
537             'SVK/Command.pm' => sub {
538             _glob_in_inc('SVK', 1);
539             },
540             'SVN/Core.pm' => sub {
541             _glob_in_inc('SVN', 1),
542             map { $_->{name} } _glob_in_inc('auto/SVN', 0), # *.so, *.bs files
543             },
544             'Syntax/Keyword/Combine/Keys.pm' => [qw( XS/Parse/Keyword.pm )],
545             'Syntax/Keyword/Defer.pm' => [qw( XS/Parse/Keyword.pm )],
546             'Syntax/Keyword/Dynamically.pm' => [qw( XS/Parse/Keyword.pm )],
547             'Syntax/Keyword/Inplace.pm' => [qw( XS/Parse/Keyword.pm )],
548             'Syntax/Keyword/Match.pm' => [qw( XS/Parse/Keyword.pm )],
549             'Syntax/Keyword/Try.pm' => [qw( XS/Parse/Keyword.pm )],
550              
551             'Template.pm' => 'sub',
552             'Term/ReadLine.pm' => 'sub',
553             'Test/Deep.pm' => 'sub',
554             'threads/shared.pm' => [qw( attributes.pm )],
555             # anybody using threads::shared is likely to declare variables
556             # with attribute :shared
557             'Tk.pm' => sub {
558             $SeenTk = 1;
559             qw( Tk/FileSelect.pm Encode/Unicode.pm );
560             },
561             'Tk/Balloon.pm' => [qw( Tk/balArrow.xbm )],
562             'Tk/BrowseEntry.pm' => [qw( Tk/cbxarrow.xbm Tk/arrowdownwin.xbm )],
563             'Tk/ColorEditor.pm' => [qw( Tk/ColorEdit.xpm )],
564             'Tk/DragDrop/Common.pm' => sub {
565             _glob_in_inc('Tk/DragDrop', 1),
566             },
567             'Tk/FBox.pm' => [qw( Tk/folder.xpm Tk/file.xpm )],
568             'Tk/Getopt.pm' => [qw( Tk/openfolder.xpm Tk/win.xbm )],
569             'Tk/Toplevel.pm' => [qw( Tk/Wm.pm )],
570              
571             'Unicode/Normalize.pm' => \&_unicore,
572             'Unicode/UCD.pm' => \&_unicore,
573             'URI.pm' => sub { grep !/urn/, _glob_in_inc('URI', 1) },
574             'utf8_heavy.pl' => \&_unicore,
575              
576             'Win32/EventLog.pm' => [qw( Win32/IPC.pm )],
577             'Win32/Exe.pm' => 'sub',
578             'Win32/TieRegistry.pm' => [qw( Win32API/Registry.pm )],
579             'Win32/SystemInfo.pm' => [qw( Win32/cpuspd.dll )],
580             'Wx.pm' => [qw( attributes.pm )],
581              
582             'XML/Parser.pm' => sub {
583             _glob_in_inc('XML/Parser/Style', 1),
584             _glob_in_inc('XML/Parser/Encodings', 1),
585             },
586             'XML/SAX.pm' => [qw( XML/SAX/ParserDetails.ini ) ],
587             'XML/Twig.pm' => [qw( URI.pm )], # or URI::File or LWP
588             'XML/Twig/XPath.pm' => [qw( XML/XPathEngine.pm XML/XPath.pm )],
589             'XMLRPC/Lite.pm' => sub {
590             _glob_in_inc('XMLRPC/Transport', 1);
591             },
592             'XS/Parse/Keyword/FromPerl.pm' => [qw( XS/Parse/Keyword.pm )],
593              
594             'YAML.pm' => [qw( YAML/Loader.pm YAML/Dumper.pm )],
595             'YAML/Any.pm' => sub {
596             # try to figure out what YAML::Any would have used
597             my $impl = eval "use YAML::Any; YAML::Any->implementation;";
598             return _mod2pm($impl) unless $@;
599              
600             _glob_in_inc('YAML', 1); # fallback
601             },
602             );
603              
604             # }}}
605              
606             sub path_to_inc_name($$) {
607 157     157 1 231936 my $path = shift;
608 157         369 my $warn = shift;
609 157         254 my $inc_name;
610              
611 157 100       727 if ($path =~ m/\.pm$/io) {
612 34 50       1029 die "$path doesn't exist" unless (-f $path);
613 34         386 my $module_info = Module::Metadata->new_from_file($path);
614 34 50       15653 die "Module::Metadata error: $!" unless defined($module_info);
615 34         160 $inc_name = $module_info->name();
616 34 50       182 if (defined($inc_name)) {
617 34         105 $inc_name =~ s|\:\:|\/|og;
618 34         208 $inc_name .= '.pm';
619             } else {
620 0 0       0 warn "# Couldn't find include name for $path\n" if $warn;
621             }
622             } else {
623             # Bad solution!
624 123         1809 (my $vol, my $dir, $inc_name) = File::Spec->splitpath($path);
625             }
626              
627 157         923 return $inc_name;
628             }
629              
630             my $Keys = 'files|keys|recurse|rv|skip|first|execute|compile|warn_missing|cache_cb|cache_file';
631             sub scan_deps {
632 40 100 66 40 1 3292446 my %args = (
633             rv => {},
634             (@_ and $_[0] =~ /^(?:$Keys)$/o) ? @_ : (files => [@_], recurse => 1)
635             );
636              
637 40 100       256 if (!defined($args{keys})) {
638 39         99 $args{keys} = [map {path_to_inc_name($_, $args{warn_missing})} @{$args{files}}];
  56         322  
  39         155  
639             }
640 40         124 my $cache_file = $args{cache_file};
641 40         92 my $using_cache;
642 40 100       219 if ($cache_file) {
643 8         92 require Module::ScanDeps::Cache;
644 8         50 $using_cache = Module::ScanDeps::Cache::init_from_file($cache_file);
645 8 50       35 if( $using_cache ){
646 8         29 $args{cache_cb} = Module::ScanDeps::Cache::get_cache_cb();
647             }else{
648 0         0 my @missing = Module::ScanDeps::Cache::prereq_missing();
649 0         0 warn join(' ',
650             "Can not use cache_file: Needs Modules [",
651             @missing,
652             "]\n",);
653             }
654             }
655 40         115 my ($type, $path);
656 40         100 foreach my $input_file (@{$args{files}}) {
  40         181  
657 57 100       814 if ($input_file !~ $ScanFileRE) {
658             warn "Skipping input file $input_file"
659             . " because it doesn't match \$Module::ScanDeps::ScanFileRE\n"
660 1 50       3 if $args{warn_missing};
661 1         2 next;
662             }
663              
664 56         235 $type = _gettype($input_file);
665 56         134 $path = $input_file;
666 56 100       202 if ($type eq 'module') {
667             # necessary because add_deps does the search for shared libraries and such
668             add_deps(
669             used_by => undef,
670             rv => $args{rv},
671             modules => [path_to_inc_name($path, $args{warn_missing})],
672             skip => undef,
673             warn_missing => $args{warn_missing},
674 11         65 );
675             }
676             else {
677             _add_info(
678             rv => $args{rv},
679 45         282 module => path_to_inc_name($path, $args{warn_missing}),
680             file => $path,
681             used_by => undef,
682             type => $type,
683             );
684             }
685             }
686              
687             {
688             ## "use lib" idioms
689             #
690             # We want to correctly interprete stuff like
691             #
692             # use FindBin;
693             # use lib "$FindBin/../lib";
694             #
695             # Find out what $FindBin::Bin etc would have been if "use FindBin" had been
696             # called in the first file to analyze.
697             #
698             # Notes:
699             # (1) We don't want to reimplement FindBin, hence fake $0 locally (as the path of the
700             # first file analyzed) and call FindBin::again().
701             # (2) If the caller of scan_deps() itself uses FindBin, we don't want to overwrite
702             # the value of "their" $FindBin::Bin.
703             #
704             # Other idioms seen sometimes:
705             #
706             # use lib "$ENV{FOO}/path";
707             # use lib File::Spec->catdir($FindBin::Bin, qw[.. qqlib] );
708             # use lib catdir(dirname($0), "perl");
709             # use lib dirname(abs_path($0));
710             #
711             # In order to correctly interprete these, the modules referenced have to be imported.
712              
713              
714 40         78 require FindBin;
  40         7781  
715              
716 40         16737 local $FindBin::Bin;
717             #local $FindBin::RealBin;
718             #local $FindBin::Script;
719             #local $FindBin::RealScript;
720              
721 40         118 my $_0 = $args{files}[0];
722 40         121 local *0 = \$_0;
723 40         387 FindBin->again();
724              
725 40         9021 scan_deps_static(\%args);
726             }
727              
728 40 50 33     311 if ($args{execute} or $args{compile}) {
729             scan_deps_runtime(
730             rv => $args{rv},
731             files => $args{files},
732             execute => $args{execute},
733             compile => $args{compile},
734             skip => $args{skip}
735 0         0 );
736             }
737              
738 40 100       118 if ( $using_cache ){
739 8         25 Module::ScanDeps::Cache::store_cache();
740             }
741              
742             # do not include the input files themselves as dependencies!
743 40         4214 delete $args{rv}{$_} foreach @{$args{files}};
  40         207  
744              
745 40         374 return ($args{rv});
746             }
747              
748             sub scan_deps_static {
749 125     125 0 375 my ($args) = @_;
750             my ($files, $keys, $recurse, $rv,
751             $skip, $first, $execute, $compile,
752             $cache_cb, $_skip)
753 125         727 = @$args{qw( files keys recurse rv
754             skip first execute compile
755             cache_cb _skip )};
756              
757 125   50     402 $rv ||= {};
758 125 100 100     416 $_skip ||= { %{$skip || {}} };
  40         299  
759              
760 125         235 foreach my $file (@{$files}) {
  125         333  
761 3303         4269 my $key = shift @{$keys};
  3303         5184  
762 3303 100       9584 next if $_skip->{$file}++;
763             next if is_insensitive_fs()
764 699         949 and $file ne lc($file) and $_skip->{lc($file)}++;
765 699 100       7566 next unless $file =~ $ScanFileRE;
766              
767 693         1376 my @pm;
768             my $found_in_cache;
769 693 100       1473 if ($cache_cb){
770 51         75 my $pm_aref;
771             # cache_cb populates \@pm on success
772 51         193 $found_in_cache = $cache_cb->(action => 'read',
773             key => $key,
774             file => $file,
775             modules => \@pm,
776             );
777 51 100       2402 unless( $found_in_cache ){
778 20         56 @pm = scan_file($file);
779 20         98 $cache_cb->(action => 'write',
780             key => $key,
781             file => $file,
782             modules => \@pm,
783             );
784             }
785             }else{ # no caching callback given
786 642         1705 @pm = scan_file($file);
787             }
788              
789 693         5969 foreach my $pm (@pm){
790             add_deps(
791             used_by => $key,
792             rv => $args->{rv},
793             modules => [$pm],
794             skip => $args->{skip},
795             warn_missing => $args->{warn_missing},
796 2375         11660 );
797              
798 2375 100       9238 my @preload = _get_preload($pm) or next;
799              
800             add_deps(
801             used_by => $key,
802             rv => $args->{rv},
803             modules => \@preload,
804             skip => $args->{skip},
805             warn_missing => $args->{warn_missing},
806 84         401 );
807             }
808             }
809              
810             # Top-level recursion handling {{{
811              
812             # prevent utf8.pm from being scanned
813 125 100       406 $_skip->{$rv->{"utf8.pm"}{file}}++ if $rv->{"utf8.pm"};
814              
815 125         346 while ($recurse) {
816 85         174 my $count = keys %$rv;
817 85 50       553 my @files = sort grep { defined $_->{file} && -T $_->{file} } values %$rv;
  3531         165625  
818             scan_deps_static({
819             files => [ map $_->{file}, @files ],
820 85         5343 keys => [ map $_->{key}, @files ],
821             rv => $rv,
822             skip => $skip,
823             recurse => 0,
824             cache_cb => $cache_cb,
825             _skip => $_skip,
826             });
827 85 100       1924 last if $count == keys %$rv;
828             }
829              
830             # }}}
831              
832 125         577 return $rv;
833             }
834              
835             sub scan_deps_runtime {
836 18 50 33 18 1 84771 my %args = (
837             rv => {},
838             (@_ and $_[0] =~ /^(?:$Keys)$/o) ? @_ : (files => [@_], recurse => 1)
839             );
840             my ($files, $rv, $execute, $compile) =
841 18         119 @args{qw( files rv execute compile )};
842              
843 18 50       73 $files = (ref($files)) ? $files : [$files];
844              
845 18 100       83 if ($compile) {
    50          
846 5         15 foreach my $file (@$files) {
847 5 50       86 next unless $file =~ $ScanFileRE;
848              
849 5         30 _merge_rv(_info2rv(_compile_or_execute($file)), $rv);
850             }
851             }
852             elsif ($execute) {
853 13         59 foreach my $file (@$files) {
854 13 100       51 $execute = [] unless ref $execute; # make sure it's an array ref
855              
856 13         140 _merge_rv(_info2rv(_compile_or_execute($file, $execute)), $rv);
857             }
858             }
859              
860 18         706 return ($rv);
861             }
862              
863             sub scan_file{
864 662     662 0 1090 my $file = shift;
865 662         1002 my %found;
866 662 50       35972 open my $fh, "<", $file or die "Cannot open $file: $!";
867              
868 662         1780 $SeenTk = 0;
869             # Line-by-line scanning
870             LINE:
871 662         13082 while (my $line = <$fh>) {
872 175801         255448 chomp($line);
873 175801         278215 foreach my $pm (scan_line($line)) {
874 3693 100       7977 last LINE if $pm eq '__END__';
875              
876 3340 100       6003 if ($pm eq '__POD__') {
877 217         618 while ($line = <$fh>) {
878 17834 100       38172 next LINE if $line =~ /^=cut/;
879             }
880             }
881              
882             # Skip Tk hits from Term::ReadLine and Tcl::Tk
883 3125         9007 my $pathsep = qr/\/|\\|::/;
884 3125 50       6497 if ($pm =~ /^Tk\b/) {
885 0 0       0 next if $file =~ /(?:^|${pathsep})Term${pathsep}ReadLine\.pm$/;
886 0 0       0 next if $file =~ /(?:^|${pathsep})Tcl${pathsep}Tk\W/;
887             }
888 3125   33     12865 $SeenTk ||= $pm =~ /Tk\.pm$/;
889              
890 3125         13639 $found{$pm}++;
891             }
892             }
893 662 50       12475 close $fh or die "Cannot close $file: $!";
894 662         6161 return keys %found;
895             }
896              
897             sub scan_line {
898 175823     175823 1 418867 my $line = shift;
899 175823         222479 my %found;
900              
901 175823 100       336337 return '__END__' if $line =~ /^__(?:END|DATA)__$/;
902 175470 100       297865 return '__POD__' if $line =~ /^=\w/;
903              
904 175253         304981 $line =~ s/\s*#.*$//;
905              
906             CHUNK:
907 175253         344801 foreach (split(/;/, $line)) {
908 131344         350349 s/^\s*//;
909 131344         218983 s/^\w+:\s*//; # remove LABEL:
910 131344         234145 s/^(?:do\s*)?\{\s*//; # handle single line blocks like 'do { package foo; use xx; }'
911 131344         227859 s/\s*\}$//;
912              
913 131344 100       239251 if (/^package\s+(\w+)/) {
914 671         2101 $CurrentPackage = $1;
915 671         1186 $CurrentPackage =~ s{::}{/}g;
916 671         1651 next CHUNK;
917             }
918             # use VERSION:
919 130673 100       258604 if (/^(?:use|require)\s+v?(\d[\d\._]*)/) {
920             # include feature.pm if we have 5.9.5 or better
921 112 100       2822 if (version->new($1) >= version->new("5.9.5")) {
922             # seems to catch 5.9, too (but not 5.9.4)
923 19         67 $found{"feature.pm"}++;
924             }
925 112         559 next CHUNK;
926             }
927              
928 130561 100       302639 if (my ($pragma, $args) = /^(?:use|no) \s+ (autouse|if) \s+ (.+)/x)
929             {
930             # NOTE: There are different ways the MODULE may
931             # be specified for the "autouse" and "if" pragmas, e.g.
932             # use autouse Module => qw(func1 func2);
933             # use autouse "Module", qw(func1);
934 14         22 my $module;
935 14 100       45 if ($pragma eq "autouse") {
936 5         14 ($module) = _parse_module_list($args);
937             }
938             else {
939             # The syntax of the "if" pragma is
940             # use if COND, MODULE => ARGUMENTS
941             # NOTE: This works only for simple conditions.
942 9         35 $args =~ s/.*? (?:,|=>) \s*//x;
943 9         30 ($module) = _parse_module_list($args);
944             }
945 14         44 $found{_mod2pm($pragma)}++;
946 14 50       44 $found{_mod2pm($module)}++ if $module;
947 14         39 next CHUNK;
948             }
949              
950 130547 100       309340 if (my ($how, $libs) = /^(use \s+ lib \s+ | (?:unshift|push) \s+ \@INC \s*,\s*) (.+)/x)
951             {
952 6 50       143 my $archname = defined($Config{archname}) ? $Config{archname} : '';
953 6 50       54 my $ver = defined($Config{version}) ? $Config{version} : '';
954 6         38 while ((my $dir, $libs) = _parse_libs($libs))
955             {
956 4 50       11 next unless defined $dir;
957 4         8 my @dirs = $dir;
958 4 50       64 push @dirs, "$dir/$ver", "$dir/$archname", "$dir/$ver/$archname"
959             if $how =~ /lib/;
960 4         9 foreach (@dirs) {
961 16 100       303 unshift(@INC, $_) if -d $_;
962             }
963             }
964 6         21 next CHUNK;
965             }
966              
967 130541         214418 $found{$_}++ for scan_chunk($_);
968             }
969              
970 175253         592268 return sort keys %found;
971             }
972              
973              
974             # convert module name to file name
975             sub _mod2pm {
976 2972     2972   4776 my $mod = shift;
977 2972         6111 $mod =~ s!::!/!g;
978 2972         9420 return "$mod.pm";
979             }
980              
981             # parse a comma-separated list of module names (as string literals or qw() lists)
982             sub _parse_module_list {
983 2106     2106   3093 my $list = shift;
984              
985             # split $list on anything that's not a word character or ":"
986             # and ignore "q", "qq" and "qw"
987 2106 100       7836 return grep { length and !/^:|^q[qw]?$/ } split(/[^\w:]+/, $list);
  3962         28429  
988             }
989              
990             # incrementally parse a comma separated list library paths:
991             # returning a pair: the contents of the first strings literal and the remainder of the string
992             # - for "string", 'string', q/string/, qq/string/ also unescape \\ and \)
993             # - for qw(foo bar quux) return ("foo", qw(bar quux))
994             # - otherwise skip over the first comma and return (undef, "remainder")
995             # - return () if the string is exhausted
996             # - as a special case, if the string starts with $FindBin::Bin, replace it with our $Bin
997             sub _parse_libs {
998 34     34   205319 local $_ = shift;
999              
1000 34         212 s/^[\s,()]*//;
1001 34 100       172 return if $_ eq "";
1002              
1003 29 100       243 if (s/^(['"]) ((?:\\.|.)*?) \1//x) {
1004 9         30 return (_unescape($1, $2), $_);
1005             }
1006 20 100       188 if (s/^qq? \s* (\W)//x) {
1007 12         37 my $opening_delim = $1;
1008 12         31 (my $closing_delim = $opening_delim) =~ tr:([{<:)]}>:;
1009 12         457 s/^((?:\\.|.)*?) \Q$closing_delim\E//x;
1010 12         54 return (_unescape($opening_delim, $1), $_);
1011             }
1012              
1013 8 100       46 if (s/^qw \s* (\W)//x) {
1014 6         19 my $opening_delim = $1;
1015 6         18 (my $closing_delim = $opening_delim) =~ tr:([{<:)]}>:;
1016 6         177 s/^((?:\\.|.)*?) \Q$closing_delim\E//x;
1017 6         38 my $contents = $1;
1018 6         22 my @list = split(" ", $contents);
1019 6 50       35 return (undef, $_) unless @list;
1020 6         15 my $first = shift @list;
1021 6 100       17 return (_unescape($opening_delim, $first),
1022             @list ? "qw${opening_delim}@list${closing_delim}$_" : $_);
1023             }
1024              
1025             # nothing recognizable in the first list item, skip to the next
1026 2 50       8 if (s/^.*? ,//x) {
1027 0         0 return (undef, $_);
1028             }
1029 2         9 return; # list exhausted
1030             }
1031              
1032              
1033             sub _unescape {
1034 27     27   102 my ($delim, $str) = @_;
1035 27         1338 $str =~ s/\\([\\\Q$delim\E])/$1/g;
1036 27         84 $str =~ s/^\$FindBin::Bin\b/$FindBin::Bin/;
1037              
1038 27         195 return $str;
1039             }
1040              
1041              
1042              
1043             sub scan_chunk {
1044 130554     130554 1 443606 my $chunk = shift;
1045              
1046             # Module name extraction heuristics {{{
1047 130554         184288 my $module = eval {
1048 130554         201513 local $_ = $chunk;
1049 130554         318823 s/^\s*//;
1050              
1051             # "if", "while" etc: analyze the expression
1052 130554         226553 s/^(?:if|elsif|unless|while|until) \s* \( \s*//x;
1053              
1054             # "eval" with a block: analyze the block
1055 130554         186463 s/^eval \s* \{ \s*//x;
1056              
1057             # "eval" with an expression that's a string literal:
1058             # analyze the string
1059 130554         184711 s/^eval \s+ (?:['"]|qq?\s*\W) \s*//x;
1060              
1061             # "use LOADER LIST"
1062             # TODO: There's many more of these "loader" type modules on CPAN!
1063 130554 100       479580 if (my ($loader, $list) = $_ =~ $LoaderRE) {
1064 94         270 my @mods = _parse_module_list($list);
1065              
1066 94 100       277 if ($loader eq "Catalyst") {
1067             # "use Catalyst 'Foo'" looks for "Catalyst::Plugin::Foo",
1068             # but "use Catalyst +Foo" looks for "Foo"
1069             @mods = map {
1070 2 100       7 ($list =~ /([+-])\Q$_\E(?:$|[^\w:])/)
  5 100       813  
1071             ? ($1 eq "-"
1072             ? () # "-Foo": it's a flag, eg. "-Debug", skip it
1073             : $_) # "+Foo": look for "Foo"
1074             : "Catalyst::Plugin::$_"
1075             # "Foo": look for "Catalyst::Plugin::Foo"
1076             } @mods;
1077             }
1078 94         183 return [ map { _mod2pm($_) } $loader, @mods ];
  212         383  
1079             }
1080              
1081 130460 50 33     437236 if (/^use \s+ Class::Autouse \b \s* (.*)/sx
1082             or /^Class::Autouse \s* -> \s* autouse \s* (.*)/sx) {
1083 0         0 return [ map { _mod2pm($_) } "Class::Autouse", _parse_module_list($1) ];
  0         0  
1084             }
1085              
1086             # generic "use ..."
1087 130460 100       273723 if (s/^(?:use|no) \s+//x) {
1088 1996         3901 my ($mod) = _parse_module_list($_); # just the first word
1089 1996         4754 return _mod2pm($mod);
1090             }
1091              
1092 128464 100       269011 if (s/^(require|do) [\s(]+//x) {
1093 825 100 100     5649 return ($1 eq "require" && /^([\w:]+)/)
1094             ? _mod2pm($1) # bareword ("require" only)
1095             : $_; # maybe string literal?
1096             }
1097              
1098 127639 100       260778 if (/(<[^>]*[^\$\w>][^>]*>)/) {
1099 903         2027 my $diamond = $1;
1100 903 100       2145 return "File/Glob.pm" if $diamond =~ /[*?\[\]{}~\\]/;
1101             }
1102              
1103 127576 50       236219 return "DBD/$1.pm" if /\bdbi:(\w+):/i;
1104              
1105             # Moose/Moo/Mouse style inheritance or composition
1106 127576 100       254618 if (s/^(with|extends)\s+//) {
1107 2         7 return [ map { _mod2pm($_) } _parse_module_list($_) ];
  4         9  
1108             }
1109              
1110             # check for stuff like
1111             # decode("klingon", ...)
1112             # open FH, "<:encoding(klingon)", ...
1113 127574 100       399223 if (my ($args) = /\b(?:open|binmode)\b(.*)/) {
1114 222         373 my @mods;
1115 222 100       520 push @mods, qw( PerlIO.pm PerlIO/encoding.pm Encode.pm ), _find_encoding($1)
1116             if $args =~ /:encoding\((.*?)\)/;
1117 222         3481 while ($args =~ /:(\w+)(?:\((.*?)\))?/g) {
1118 12         179 push @mods, "PerlIO/$1.pm";
1119 12 100       87 push @mods, "Encode.pm", _find_encoding($2) if $1 eq "encoding";
1120             }
1121 222 100       586 push @mods, "PerlIO.pm" if @mods;
1122 222 100       487 return \@mods if @mods;
1123             }
1124 127565 100       248327 if (/\b(?:en|de)code\(\s*['"]?([-\w]+)/) {
1125 10         43 return [qw( Encode.pm ), _find_encoding($1)];
1126             }
1127              
1128 127555 50       221637 if ($SeenTk) {
1129 0         0 my @modules;
1130 0         0 while (/->\s*([A-Z]\w+)/g) {
1131 0         0 push @modules, "Tk/$1.pm";
1132             }
1133 0         0 while (/->\s*Scrolled\W+([A-Z]\w+)/g) {
1134 0         0 push @modules, "Tk/$1.pm";
1135 0         0 push @modules, "Tk/Scrollbar.pm";
1136             }
1137 0 0       0 if (/->\s*setPalette/g) {
1138             push @modules,
1139 0         0 map { "Tk/$_.pm" }
  0         0  
1140             qw( Button Canvas Checkbutton Entry
1141             Frame Label Labelframe Listbox
1142             Menubutton Menu Message Radiobutton
1143             Scale Scrollbar Spinbox Text );
1144             }
1145 0         0 return \@modules;
1146             }
1147              
1148             # Module::Runtime
1149 127555 50       242375 return $_ if s/^(?:require_module|use_module|use_package_optimistically) \s* \( \s*//x;
1150              
1151             # Test::More
1152 127555 100       240162 return $_ if s/^(?:require_ok|use_ok) \s* \( \s*//x;
1153              
1154 127553         242917 return;
1155             };
1156              
1157             # }}}
1158              
1159 130554 100       349891 return unless defined($module);
1160 3001 50       6280 return wantarray ? @$module : $module->[0] if ref($module);
    100          
1161              
1162             # extract contents from string literals
1163 2886 100       19335 if ($module =~ /^(['"]) (.*?) \1/x) {
    50          
1164 35         134 $module = $2;
1165             }
1166             elsif ($module =~ s/^qq? \s* (\W)//x) {
1167 0         0 (my $closing = $1) =~ tr:([{<:)]}>:;
1168 0         0 $module =~ s/\Q$closing\E.*//;
1169             }
1170              
1171 2886         4360 $module =~ s/::/\//g;
1172 2886 100       8135 return if $module =~ /^(?:[\d\._]+|'.*[^']|".*[^"])$/;
1173              
1174 2885 100       7026 $module .= ".pm" unless $module =~ /\./;
1175 2885         12904 return $module;
1176             }
1177              
1178             sub _find_encoding {
1179 16     16   72 my ($enc) = @_;
1180 16 50 33     92 return unless $] >= 5.008 and eval { require Encode; %Encode::ExtModule };
  16         146  
  16         68  
1181              
1182 16 50       24 my $mod = eval { $Encode::ExtModule{ Encode::find_encoding($enc)->name } } or return;
  16         111  
1183 0         0 return _mod2pm($mod);
1184             }
1185              
1186             sub _add_info {
1187 3541     3541   14046 my %args = @_;
1188 3541         10528 my ($rv, $module, $file, $used_by, $type) = @args{qw/rv module file used_by type/};
1189              
1190 3541 50 33     12193 return unless defined($module) and defined($file);
1191              
1192             # Ensure file is always absolute
1193 3541         33758 $file = File::Spec->rel2abs($file);
1194 3541         7330 $file =~ s|\\|\/|go;
1195              
1196             # Avoid duplicates that can arise due to case differences that don't actually
1197             # matter on a case tolerant system
1198 3541         4440 if (is_insensitive_fs) {
1199             if (!exists $rv->{$module}) {
1200             my $lc_module = lc $module;
1201 0     0   0 my $key = first {lc($_) eq $lc_module} keys %$rv;
1202             if (defined $key) {
1203             $module = $key
1204             };
1205             }
1206             if (defined($used_by)) {
1207             if (lc($used_by) eq lc($module)) {
1208             $used_by = $module;
1209             } else {
1210             if (!exists $rv->{$used_by}) {
1211             my $lc_used_by = lc $used_by;
1212 0     0   0 my $key = first {lc($_) eq $lc_used_by} keys %$rv;
1213             if (defined $key) {
1214             $used_by = $key
1215             };
1216             }
1217             }
1218             }
1219             }
1220              
1221 3541   100     12907 $rv->{$module} ||= {
1222             file => $file,
1223             key => $module,
1224             type => $type,
1225             };
1226              
1227 3541 100 100     11838 if (defined($used_by) and $used_by ne $module) {
1228 3467         4353 if (is_insensitive_fs) {
1229             my $lc_used_by = lc $used_by;
1230             my $lc_module = lc $module;
1231             push @{ $rv->{$module}{used_by} }, $used_by
1232 0     0   0 if !any { lc($_) eq $lc_used_by } @{ $rv->{$module}{used_by} };
1233             # We assume here that another _add_info will be called to provide the other parts of $rv->{$used_by}
1234             push @{ $rv->{$used_by}{uses} }, $module
1235 0     0   0 if !any { lc($_) eq $lc_module } @{ $rv->{$used_by}{uses} };
1236             }
1237             else {
1238 3385         8592 push @{ $rv->{$module}{used_by} }, $used_by
1239 3467 100   68738   11562 if !any { $_ eq $used_by } @{ $rv->{$module}{used_by} };
  68738         94123  
  3467         27329  
1240             # We assume here that another _add_info will be called to provide the other parts of $rv->{$used_by}
1241 3385         12480 push @{ $rv->{$used_by}{uses} }, $module
1242 3467 100   47008   12993 if !any { $_ eq $module } @{ $rv->{$used_by}{uses} };
  47008         57146  
  3467         10213  
1243             }
1244             }
1245             }
1246              
1247             # This subroutine relies on not being called for modules that have already been visited
1248             sub add_deps {
1249 2470 0 33 2470 1 21467 my %args =
    50          
1250             ((@_ and $_[0] =~ /^(?:modules|rv|used_by|warn_missing)$/)
1251             ? @_
1252             : (rv => (ref($_[0]) ? shift(@_) : undef), modules => [@_]));
1253              
1254 2470   50     6003 my $rv = $args{rv} || {};
1255 2470   100     6862 my $skip = $args{skip} || {};
1256 2470         4075 my $used_by = $args{used_by};
1257              
1258 2470         3172 foreach my $module (@{ $args{modules} }) {
  2470         4852  
1259             my $file = _find_in_inc($module)
1260 3482 100       6599 or _warn_of_missing_module($module, $args{warn_missing}), next;
1261 3361 100       7048 next if $skip->{$file};
1262              
1263 3360 100       7038 if (exists $rv->{$module}) {
1264 2813         7021 _add_info( rv => $rv, module => $module,
1265             file => $file, used_by => $used_by,
1266             type => undef );
1267 2813         8378 next;
1268             }
1269              
1270 547         1643 _add_info( rv => $rv, module => $module,
1271             file => $file, used_by => $used_by,
1272             type => _gettype($file) );
1273              
1274 547 100       9443 if ((my $path = $module) =~ s/\.p[mh]$//i) {
1275              
1276 531         1620 foreach (_glob_in_inc("auto/$path")) {
1277 235 100       3253 next if $_->{name} =~ m{^auto/$path/.*/}; # weed out subdirs
1278 159 100       3827 next if $_->{name} =~ m{/(?:\.exists|\.packlist)$|\Q$Config{lib_ext}\E$};
1279              
1280             _add_info( rv => $rv, module => $_->{name},
1281             file => $_->{file}, used_by => $module,
1282 136         653 type => _gettype($_->{name}) );
1283             }
1284              
1285             ### Now, handle module and distribution share dirs
1286             # convert 'Module/Name' to 'Module-Name'
1287 531         1120 my $modname = $path;
1288 531         1584 $modname =~ s|/|-|g;
1289             # TODO: get real distribution name related to module name
1290 531         863 my $distname = $modname;
1291 531         1408 foreach (_glob_in_inc("auto/share/module/$modname")) {
1292             _add_info( rv => $rv, module => $_->{name},
1293 0         0 file => $_->{file}, used_by => $module,
1294             type => 'data' );
1295             }
1296 531         1464 foreach (_glob_in_inc("auto/share/dist/$distname")) {
1297             _add_info( rv => $rv, module => $_->{name},
1298 0         0 file => $_->{file}, used_by => $module,
1299             type => 'data' );
1300             }
1301             }
1302             } # end for modules
1303 2470         7423 return $rv;
1304             }
1305              
1306             # invalidate %_file_cache and %_glob_cache in case @INC changes
1307             sub _validate_cached_inc
1308             {
1309 5121     5121   19271 my $inc = join("\0", @INC, @IncludeLibs);
1310 5121 100       11661 return if $inc eq $_cached_inc;
1311              
1312             # blow away the caches
1313 15         35 %_file_cache = ();
1314 15         31 %_glob_cache = ();
1315 15         72 $_cached_inc = $inc;
1316             }
1317              
1318             sub _find_in_inc {
1319 3482     3482   5251 my $file = shift;
1320 3482 50       6054 return unless defined $file;
1321              
1322 3482         6933 _validate_cached_inc();
1323 3482         7355 my $cached_val = $_file_cache{$file};
1324 3482 100       9445 return $cached_val if $cached_val;
1325              
1326 639         3265 foreach my $dir (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) {
1327 4572 100       71239 if (-f "$dir/$file") {
1328 518         1930 $_file_cache{$file} = "$dir/$file";
1329 518         2188 return "$dir/$file"
1330             };
1331             }
1332              
1333             # absolute file names
1334 121 50       743 return $file if -f $file;
1335              
1336 121         799 return;
1337             }
1338              
1339             sub _glob_in_inc {
1340 1639     1639   2988 my ($subdir, $pm_only) = @_;
1341              
1342 1639         7992 require File::Find;
1343              
1344 1639         2812 $subdir =~ s/\$CurrentPackage/$CurrentPackage/;
1345              
1346 1639         3252 _validate_cached_inc();
1347 1639         3042 my $cached_val = $_glob_cache{$subdir};
1348 1639 100       3591 return @$cached_val if $cached_val;
1349              
1350 1502         1958 my @files;
1351 1502         6156 foreach my $inc (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) {
1352 13537         18386 my $dir = "$inc/$subdir";
1353 13537 100       115896 next unless -d $dir;
1354              
1355             # canonicalize $inc (ie. use "/" as filename separator exclusively)
1356             # as newer versions of File::Find return a canonicalized $File::Find::name
1357 114         379 (my $canon = $inc) =~ s|\\|/|g;
1358             File::Find::find(
1359             sub {
1360 524 100   524   28619 return unless -f $_;
1361 331 50 66     1289 return if $pm_only and !/\.p[mh]$/i;
1362 331         807 (my $file = $File::Find::name) =~ s|\\|/|g;
1363 331         3443 (my $name = $file) =~ s|^\Q$canon\E/||;
1364 331 100       15312 push @files, $pm_only ? $name
1365             : { file => $file, name => $name };
1366             },
1367 114         9478 $dir
1368             );
1369             }
1370              
1371 1502         5167 $_glob_cache{$subdir} = \@files;
1372              
1373 1502         4229 return @files;
1374             }
1375              
1376             # like _glob_in_inc, but looks only at the first level
1377             # (i.e. the children of $subdir)
1378             # NOTE: File::Find has no public notion of the depth of the traversal
1379             # in its "wanted" callback, so it's not helpful
1380             sub _glob_in_inc_1 {
1381 0     0   0 my ($subdir, $pm_only) = @_;
1382              
1383 0         0 $subdir =~ s/\$CurrentPackage/$CurrentPackage/;
1384              
1385 0         0 my @files;
1386 0         0 foreach my $inc (grep !/\bBSDPAN\b/, @INC, @IncludeLibs) {
1387 0         0 my $dir = "$inc/$subdir";
1388 0 0       0 next unless -d $dir;
1389              
1390 0 0       0 opendir(my $dh, $dir) or next;
1391 0         0 my @names = map { "$subdir/$_" } grep { -f "$dir/$_" } readdir $dh;
  0         0  
  0         0  
1392 0         0 closedir $dh;
1393              
1394 0         0 push @files, $pm_only ? ( grep { /\.p[mh]$/i } @names )
1395 0 0       0 : ( map { { file => "$inc/$_", name => $_ } } @names );
  0         0  
1396             }
1397              
1398 0         0 return @files;
1399             }
1400              
1401             my $unicore_stuff;
1402             sub _unicore {
1403 0   0 0   0 $unicore_stuff ||= [ 'utf8_heavy.pl', map $_->{name}, _glob_in_inc('unicore', 0) ];
1404 0         0 return @$unicore_stuff;
1405             }
1406              
1407             # App::Packer compatibility functions
1408              
1409             sub new {
1410 1     1 0 1300 my ($class, $self) = @_;
1411 1   50     10 return bless($self ||= {}, $class);
1412             }
1413              
1414             sub set_file {
1415 1     1 0 6 my $self = shift;
1416 1         3 my $script = shift;
1417              
1418 1         24 my ($vol, $dir, $file) = File::Spec->splitpath($script);
1419             $self->{main} = {
1420 1         10 key => $file,
1421             file => $script,
1422             };
1423             }
1424              
1425             sub set_options {
1426 0     0 0 0 my $self = shift;
1427 0         0 my %args = @_;
1428 0         0 foreach my $module (@{ $args{add_modules} }) {
  0         0  
1429 0 0       0 $module = _mod2pm($module) unless $module =~ /\.p[mh]$/i;
1430             my $file = _find_in_inc($module)
1431 0 0       0 or _warn_of_missing_module($module, $args{warn_missing}), next;
1432 0         0 $self->{files}{$module} = $file;
1433             }
1434             }
1435              
1436             sub calculate_info {
1437 1     1 0 5 my $self = shift;
1438             my $rv = scan_deps(
1439 1         6 'keys' => [ $self->{main}{key}, sort keys %{ $self->{files} }, ],
1440             files => [ $self->{main}{file},
1441 1         3 map { $self->{files}{$_} } sort keys %{ $self->{files} },
  0         0  
  1         4  
1442             ],
1443             recurse => 1,
1444             );
1445              
1446             my $info = {
1447             main => { file => $self->{main}{file},
1448             store_as => $self->{main}{key},
1449             },
1450 1         13 };
1451              
1452 1         5 my %cache = ($self->{main}{key} => $info->{main});
1453 1         2 foreach my $key (sort keys %{ $self->{files} }) {
  1         4  
1454 0         0 my $file = $self->{files}{$key};
1455              
1456             $cache{$key} = $info->{modules}{$key} = {
1457             file => $file,
1458             store_as => $key,
1459 0         0 used_by => [ $self->{main}{key} ],
1460             };
1461             }
1462              
1463 1         20 foreach my $key (sort keys %{$rv}) {
  1         112  
1464 155         325 my $val = $rv->{$key};
1465 155 100       360 if ($cache{ $val->{key} }) {
1466 1 50       7 defined($val->{used_by}) or next;
1467 0         0 push @{ $info->{ $val->{type} }->{ $val->{key} }->{used_by} },
1468 0         0 @{ $val->{used_by} };
  0         0  
1469             }
1470             else {
1471             $cache{ $val->{key} } = $info->{ $val->{type} }->{ $val->{key} } =
1472             { file => $val->{file},
1473             store_as => $val->{key},
1474             used_by => $val->{used_by},
1475 154         736 };
1476             }
1477             }
1478              
1479 1         28 $self->{info} = { main => $info->{main} };
1480              
1481 1         2 foreach my $type (sort keys %{$info}) {
  1         7  
1482 4 100       13 next if $type eq 'main';
1483              
1484 3         7 my @val;
1485 3 50       13 if (UNIVERSAL::isa($info->{$type}, 'HASH')) {
1486 3         6 foreach my $val (sort values %{ $info->{$type} }) {
  3         589  
1487 154         376 @{ $val->{used_by} } = map $cache{$_} || "!!$_!!",
1488 154   33     204 @{ $val->{used_by} };
  154         1041  
1489 154         327 push @val, $val;
1490             }
1491             }
1492              
1493 3 100       13 $type = 'modules' if $type eq 'module';
1494 3         333 $self->{info}{$type} = \@val;
1495             }
1496             }
1497              
1498             sub get_files {
1499 1     1 0 39 my $self = shift;
1500 1         26 return $self->{info};
1501             }
1502              
1503             sub add_preload_rule {
1504 0     0 0 0 my ($pm, $rule) = @_;
1505 0 0       0 die qq[a preload rule for "$pm" already exists] if $Preload{$pm};
1506 0         0 $Preload{$pm} = $rule;
1507             }
1508              
1509             # scan_deps_runtime utility functions
1510              
1511             # compile $file if $execute is undef,
1512             # otherwise execute $file with arguments @$execute
1513             sub _compile_or_execute {
1514 18     18   58 my ($file, $execute) = @_;
1515              
1516 18         430 local $ENV{MSD_ORIGINAL_FILE} = $file;
1517              
1518 18         99 my ($ih, $instrumented_file) = File::Temp::tempfile(UNLINK => 1);
1519              
1520 18         10216 my (undef, $data_file) = File::Temp::tempfile(UNLINK => 1);
1521 18         10376 local $ENV{MSD_DATA_FILE} = $data_file;
1522              
1523             # spoof $0 (to $file) so that FindBin works as expected
1524             # NOTE: We don't directly assign to $0 as it has magic (i.e.
1525             # assigning has side effects and may actually fail, cf. perlvar(1)).
1526             # Instead we alias *0 to a package variable holding the correct value.
1527 18         100 print $ih <<'...';
1528             BEGIN { my $_0 = $ENV{MSD_ORIGINAL_FILE}; *0 = \$_0; }
1529             ...
1530              
1531             # NOTE: When compiling the block will run as the last CHECK block;
1532             # when executing the block will run as the first END block and
1533             # the programs continues.
1534 18 100       127 print $ih
1535             $execute ? "END\n" : "CHECK\n",
1536             <<'...';
1537             {
1538             require DynaLoader;
1539             my @_dl_shared_objects = @DynaLoader::dl_shared_objects;
1540             my @_dl_modules = @DynaLoader::dl_modules;
1541              
1542             # save %INC etc so that requires below don't pollute them
1543             my %_INC = %INC;
1544             my @_INC = @INC;
1545              
1546             require Cwd;
1547             require Data::Dumper;
1548             require Config;
1549             my $dlext = $Config::Config{dlext};
1550              
1551             foreach my $k (keys %_INC)
1552             {
1553             # NOTES:
1554             # (1) An unsuccessful "require" may store an undefined value into %INC.
1555             # (2) If a key in %INC was located via a CODE or ARRAY ref or
1556             # blessed object in @INC the corresponding value in %INC contains
1557             # the ref from @INC.
1558             # (3) Some modules (e.g. Moose) fake entries in %INC, e.g.
1559             # "Class/MOP/Class/Immutable/Moose/Meta/Class.pm" => "(set by Moose)"
1560             # On some architectures (e.g. Windows) Cwd::abs_path() will throw
1561             # an exception for such a pathname.
1562              
1563             my $v = $_INC{$k};
1564             if (defined $v && !ref $v && -e $v)
1565             {
1566             $_INC{$k} = Cwd::abs_path($v);
1567             }
1568             else
1569             {
1570             delete $_INC{$k};
1571             }
1572             }
1573              
1574             # drop refs from @_INC
1575             @_INC = grep { !ref $_ } @_INC;
1576              
1577             my @dlls = grep { defined $_ && -e $_ } Module::ScanDeps::DataFeed::_dl_shared_objects();
1578             my @shared_objects = @dlls;
1579             push @shared_objects, grep { -e $_ } map { (my $bs = $_) =~ s/\.\Q$dlext\E$/.bs/; $bs } @dlls;
1580              
1581             # write data file
1582             my $data_file = $ENV{MSD_DATA_FILE};
1583             open my $fh, ">", $data_file
1584             or die "Couldn't open $data_file: $!\n";
1585             print $fh Data::Dumper::Dumper(
1586             {
1587             '%INC' => \%_INC,
1588             '@INC' => \@_INC,
1589             dl_shared_objects => \@shared_objects,
1590             });
1591             close $fh;
1592              
1593             sub Module::ScanDeps::DataFeed::_dl_shared_objects {
1594             if (@_dl_shared_objects) {
1595             return @_dl_shared_objects;
1596             }
1597             elsif (@_dl_modules) {
1598             return map { Module::ScanDeps::DataFeed::_dl_mod2filename($_) } @_dl_modules;
1599             }
1600             return;
1601             }
1602              
1603             sub Module::ScanDeps::DataFeed::_dl_mod2filename {
1604             my $mod = shift;
1605              
1606             return if $mod eq 'B';
1607             return unless defined &{"$mod\::bootstrap"};
1608              
1609              
1610             # cf. DynaLoader.pm
1611             my @modparts = split(/::/, $mod);
1612             my $modfname = defined &DynaLoader::mod2fname ? DynaLoader::mod2fname(\@modparts) : $modparts[-1];
1613             my $modpname = join('/', @modparts);
1614              
1615             foreach my $dir (@_INC) {
1616             my $file = "$dir/auto/$modpname/$modfname.$dlext";
1617             return $file if -e $file;
1618             }
1619             return;
1620             }
1621             } # END or CHECK
1622             ...
1623              
1624             # append the file to compile or execute
1625             {
1626 18 50       43 open my $fh, "<", $file or die "Couldn't open $file: $!";
  18         768  
1627 18         3816 print $ih qq[#line 1 "$file"\n], <$fh>;
1628 18         331 close $fh;
1629             }
1630 18         853 close $ih;
1631              
1632             # run the instrumented file
1633             my $rc = system(
1634             $^X,
1635             $execute ? () : ("-c"),
1636 18 100       10048897 (map { "-I$_" } @IncludeLibs),
  0 100       0  
1637             $instrumented_file,
1638             $execute ? @$execute : ());
1639              
1640 18 0       1029 die $execute
    50          
1641             ? "SYSTEM ERROR in executing $file @$execute: $rc"
1642             : "SYSTEM ERROR in compiling $file: $rc"
1643             unless $rc == 0;
1644              
1645 18 50 0     29079 my $info = do $data_file
1646             or die "error extracting info from -c/-x file: ", ($@ || "can't read $data_file: $!");
1647              
1648 18         1013 return $info;
1649             }
1650              
1651             # create a new hashref, applying fixups
1652             sub _info2rv {
1653 18     18   114 my ($info) = @_;
1654              
1655 18         53 my $rv = {};
1656              
1657 224         505 my $incs = join('|', sort { length($b) <=> length($a) }
1658 144         322 map { s|\\|/|g; s|/+$||; quotemeta($_) }
  144         561  
  144         752  
1659 18         63 @{ $info->{'@INC'} });
  18         130  
1660 18         100 my $i = is_insensitive_fs() ? "i" : "";
1661 18         1291 my $strip_inc_prefix = qr{^(?$i:$incs)/};
1662              
1663 18         248 require File::Spec;
1664              
1665 18         140 foreach my $key (keys %{ $info->{'%INC'} }) {
  18         399  
1666 1482         9656 (my $path = $info->{'%INC'}{$key}) =~ s|\\|/|g;
1667              
1668             # NOTE: %INC may contain (as keys) absolute pathnames,
1669             # e.g. for autosplit .ix and .al files. In the latter case,
1670             # the key may also start with "./" if found via a relative path in @INC.
1671 1482         2059 $key =~ s|\\|/|g;
1672 1482         2074 $key =~ s|^\./||;
1673 1482         3905 $key =~ s/$strip_inc_prefix//;
1674              
1675 1482         2885 $rv->{$key} = {
1676             'used_by' => [],
1677             'file' => $path,
1678             'type' => _gettype($path),
1679             'key' => $key
1680             };
1681             }
1682              
1683 18         191 foreach my $path (@{ $info->{dl_shared_objects} }) {
  18         59  
1684 215         348 $path =~ s|\\|/|g;
1685 215         1099 (my $key = $path) =~ s/$strip_inc_prefix//;
1686              
1687 215         1337 $rv->{$key} = {
1688             'used_by' => [],
1689             'file' => $path,
1690             'type' => 'shared',
1691             'key' => $key
1692             };
1693             }
1694              
1695 18         342 return $rv;
1696             }
1697              
1698             sub _gettype {
1699 2221     2221   3777 my $name = shift;
1700              
1701 2221 100       9636 return 'autoload' if $name =~ /\.(?:ix|al|bs)$/i;
1702 2136 100       12041 return 'module' if $name =~ /\.p[mh]$/i;
1703 154 100       3305 return 'shared' if $name =~ /\.\Q$Config{dlext}\E$/i;
1704 99         496 return 'data';
1705             }
1706              
1707             # merge all keys from $rv_sub into the $rv mega-ref
1708             sub _merge_rv {
1709 18     18   75 my ($rv_sub, $rv) = @_;
1710              
1711 18         37 my $key;
1712 18         450 foreach $key (keys(%$rv_sub)) {
1713 1697         6196 my %mark;
1714 1697 50 33     4536 if ($rv->{$key} and _not_dup($key, $rv, $rv_sub)) {
    50          
1715             warn "Different modules for file '$key' were found.\n"
1716             . " -> Using '" . abs_path($rv_sub->{$key}{file}) . "'.\n"
1717 0         0 . " -> Ignoring '" . abs_path($rv->{$key}{file}) . "'.\n";
1718             $rv->{$key}{used_by} = [
1719             grep (!$mark{$_}++,
1720 0         0 @{ $rv->{$key}{used_by} },
1721 0         0 @{ $rv_sub->{$key}{used_by} })
  0         0  
1722             ];
1723 0         0 @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} };
  0         0  
  0         0  
1724 0         0 $rv->{$key}{file} = $rv_sub->{$key}{file};
1725             }
1726             elsif ($rv->{$key}) {
1727             $rv->{$key}{used_by} = [
1728             grep (!$mark{$_}++,
1729 0         0 @{ $rv->{$key}{used_by} },
1730 0         0 @{ $rv_sub->{$key}{used_by} })
  0         0  
1731             ];
1732 0         0 @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} };
  0         0  
  0         0  
1733             }
1734             else {
1735             $rv->{$key} = {
1736 1697         7672 used_by => [ @{ $rv_sub->{$key}{used_by} } ],
1737             file => $rv_sub->{$key}{file},
1738             key => $rv_sub->{$key}{key},
1739             type => $rv_sub->{$key}{type}
1740 1697         2260 };
1741              
1742 1697         2445 @{ $rv->{$key}{used_by} } = grep length, @{ $rv->{$key}{used_by} };
  1697         4454  
  1697         3173  
1743             }
1744             }
1745             }
1746              
1747             sub _not_dup {
1748 0     0   0 my ($key, $rv1, $rv2) = @_;
1749 0         0 if (is_insensitive_fs) {
1750             return lc(abs_path($rv1->{$key}{file})) ne lc(abs_path($rv2->{$key}{file}));
1751             }
1752             else {
1753 0         0 return abs_path($rv1->{$key}{file}) ne abs_path($rv2->{$key}{file});
1754             }
1755             }
1756              
1757             sub _warn_of_runtime_loader {
1758 0     0   0 my $module = shift;
1759 0 0       0 return if $SeenRuntimeLoader{$module}++;
1760 0         0 $module =~ s/\.pm$//;
1761 0         0 $module =~ s|/|::|g;
1762 0         0 warn "# Use of runtime loader module $module detected. Results of static scanning may be incomplete.\n";
1763 0         0 return;
1764             }
1765              
1766             sub _warn_of_missing_module {
1767 121     121   215 my $module = shift;
1768 121         234 my $warn = shift;
1769 121 50       358 return if not $warn;
1770 0 0       0 return if not $module =~ /\.p[ml]$/;
1771 0 0       0 warn "# Could not find source file '$module' in \@INC or \@IncludeLibs. Skipping it.\n"
1772             if not -f $module;
1773             }
1774              
1775             sub _get_preload1 {
1776 3477     3477   4936 my $pm = shift;
1777 3477 100       10061 my $preload = $Preload{$pm} or return();
1778 97 100       579 if ($preload eq 'sub') {
    100          
1779 10         49 $pm =~ s/\.p[mh]$//i;
1780 10         64 return _glob_in_inc($pm, 1);
1781             }
1782             elsif (UNIVERSAL::isa($preload, 'CODE')) {
1783 46         161 return $preload->($pm);
1784             }
1785 41         122 return @$preload;
1786             }
1787              
1788             sub _get_preload {
1789 3477     3477   191481 my ($pm, $seen) = @_;
1790 3477   100     10941 $seen ||= {};
1791 3477         7314 $seen->{$pm}++;
1792 3477         4654 my @preload;
1793              
1794 3477         5908 foreach $pm (_get_preload1($pm))
1795             {
1796 1105 100       1951 next if $seen->{$pm};
1797 1101         2090 $seen->{$pm}++;
1798 1101         1773 push @preload, $pm, _get_preload($pm, $seen);
1799             }
1800 3477         12044 return @preload;
1801             }
1802              
1803             1;
1804             __END__