File Coverage

blib/lib/Module/ScanDeps.pm
Criterion Covered Total %
statement 444 543 81.7
branch 193 278 69.4
condition 49 88 55.6
subroutine 48 54 88.8
pod 6 14 42.8
total 740 977 75.7


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