File Coverage

blib/lib/Dependencies/Searcher.pm
Criterion Covered Total %
statement 138 170 81.1
branch 24 38 63.1
condition 23 27 85.1
subroutine 24 26 92.3
pod 11 11 100.0
total 220 272 80.8


line stmt bran cond sub pod time code
1             package Dependencies::Searcher;
2              
3 8     8   168385 use 5.010;
  8         19  
4 8     8   3694 use Data::Printer;
  8         171424  
  8         47  
5 8     8   706 use feature qw(say);
  8         8  
  8         634  
6             # Be careful, even if the Module::Corelist module is available since v5.8.9
7             # the is_core() method is only available since 2.99 module's version :)
8             # http://neilb.org/2013/09/21/adding-is-core.html
9 8     8   18940 use Module::CoreList;
  8         279511  
  8         78  
10 8     8   9377 use Module::Version 'get_version';
  8         637508  
  8         480  
11 8     8   3346 use autodie;
  8         92974  
  8         31  
12 8     8   34014 use Moose;
  8         2422192  
  8         48  
13 8     8   43975 use IO::File; # This comment creates a bug
  8         45763  
  8         831  
14 8     8   47 use File::HomeDir;
  8         13  
  8         318  
15 8     8   3550 use Version::Compare;
  8         1805  
  8         180  
16 8     8   3046 use Path::Class;
  8         158574  
  8         419  
17 8     8   3962 use ExtUtils::Installed;
  8         29228  
  8         237  
18 8     8   3450 use File::Next;
  8         10963  
  8         198  
19 8     8   3714 use PPI;
  8         704555  
  8         359  
20 8     8   58 use Term::ANSIColor;
  8         9  
  8         15890  
21             # For test only during dev
22             #use Dependencies::Searcher;
23              
24             our $VERSION = '0.066_005';
25              
26             =head1 NAME
27              
28             Dependencies::Searcher - Search for modules used by a distribution.
29              
30             =cut
31              
32             =head1 SYNOPSIS
33              
34             use Dependencies::Searcher;
35              
36             my $searcher = Dependencies::Searcher->new();
37             my @elements = $searcher->get_files();
38             my @uses = $searcher->get_modules($path, "use");
39             my @uniq_modules = $searcher->uniq(@uses);
40              
41             $searcher->dissociate(@uniq_modules);
42              
43             $searcher->generate_report($searcher->non_core_modules);
44              
45             # Prints to cpanfile
46             # requires 'Data::Printer', '0.35';
47             # requires Moose, '2.0602';
48             # requires IPC::Cmd;
49             # requires Module::Version;
50             # ...
51              
52             Synopsis from Dependencies::Searcher::AckRequester
53              
54             # Places to search...
55             my @path = ("./lib", "./Makefile.PL", "./script");
56              
57             # Params for Ack
58             my @params = ('--perl', '-hi', $pattern, @path);
59              
60             # Absolute path to the Ack binary
61             my $ack_path = $requester->get_path();
62              
63             # Build the command for IPC::Cmd
64             my $cmd_use = $requester->build_cmd(@params);
65              
66             # Execute the command and retrieve the output
67             my @moduls = $requester->search($cmd_use);
68              
69             =cut
70              
71             =head1 DESCRIPTION
72              
73             Maybe you don't want to have to list all the dependencies of your Perl
74             application by hand and want an automated way to retrieve this
75             list. It can also be handy sometimes to check if your Makefile.PL
76             contains all the prerequisite that are needed for your distribution.
77              
78             This module's aim is to keep track of the list of modules your
79             distribution need, under the form of a L<cpanfile|cpanfile>.
80              
81             Dependencies::Searcher will check for any I<requires> or I<use> in
82             your module repository, and report it into a cpanfile. Any duplicated
83             entry will be removed and modules versions will be checked and made
84             available. Core modules will be ommited because you don't need to
85             install them (except in some special cases, see C<dissociate()>
86             documentation).
87              
88             This project has begun because I was sometimes forgetting to keep
89             track of my dependencies, and I just wanted to run a simple script
90             that updated the list in a convenient way.
91              
92             =cut
93              
94             =head1 Not a dependencies-tree tool
95              
96             Dependencies::Searcher only finds direct dependencies, not
97             dependencies of dependencies, it scans recursively directories of your
98             module to find declared dependencies.
99              
100             Once you generated a cpanfile with Dependencies::Searcher, it's
101             possible to pass this list to the Perl toolchain (cpanminus)
102             that will take care of any recursive dependencies.
103              
104             =cut
105              
106             # Init parameters
107             has 'non_core_modules' => (
108             traits => ['Array'],
109             is => 'rw',
110             isa => 'ArrayRef[Str]',
111             default => sub { [] },
112             handles => {
113             add_non_core_module => 'push',
114             count_non_core_modules => 'count',
115             },
116             );
117              
118             has 'core_modules' => (
119             traits => ['Array'],
120             is => 'rw',
121             isa => 'ArrayRef[Str]',
122             default => sub { [] },
123             handles => {
124             add_core_module => 'push',
125             count_core_modules => 'count',
126             },
127             );
128              
129             has 'full_path' => (
130             is => 'rw',
131             isa => 'Str',
132             );
133              
134              
135             sub get_modules {
136             # @path contains files and directories
137 2     2 1 12 my ($self, $pattern, @path) = @_;
138              
139 2         39 say("Search pattern : " . $pattern);
140              
141 2         8 my @moduls = $self->search($pattern, @path);
142 2         179 say("Found $pattern modules : " . @moduls);
143              
144 2 100       8 if ( defined $moduls[0]) {
145 1 50 33     8 if ($moduls[0] =~ m/^use/ or $moduls[0] =~ m/^require/) {
146 1         8 return @moduls;
147             } else {
148             # Not really useful since we don't use ack no more
149 0         0 die "Failed to retrieve modules with Ack";
150             }
151             } else {
152 1         90 say "No use or require found !";
153             }
154             }
155              
156             sub get_files {
157 3     3 1 3030 my $self = shift;
158             # Path::Class functions allows a more portable module
159 3         19 my $lib_dir = dir('lib');
160 3         378 my $make_file = file('Makefile.PL');
161 3         214 my $script_dir = dir('script');
162             # note1 t/ is ignored
163             # note2 any type of interesting file or dir should be listed here
164              
165 3         84 my @structure;
166              
167 3 50       17 if (-d $lib_dir) {
168 3         126 $structure[0] = $lib_dir;
169             } else {
170             # TODO : TEST IF THE PATH IS OK ???
171             # What can you do if a module don't have a lib/ dir ?
172 0         0 die "Don't look like we are working on a Perl module";
173             }
174              
175 3 50       12 if (-f $make_file) {
176 3         47 $structure[1] = $make_file;
177             }
178              
179 3 50       13 if (-d $script_dir) {
180 0         0 $structure[2] = $script_dir;
181             }
182              
183 3         94 return @structure;
184             }
185              
186             # Generate a "1" when merging if one of both is empty
187             # Will be clean in avoid_superfluous() method
188             sub merge_dependencies {
189 0     0 1 0 my ($self, @uses, @requires) = @_;
190 0         0 my @merged_dependencies = (@uses, @requires);
191 0         0 say("Merged use and require dependencies");
192 0         0 return @merged_dependencies;
193             }
194              
195             # Remove special cases that aren't need at all
196             sub avoid_superfluous {
197 1     1 1 1059 my ($self, @merged) = @_;
198 1         3 my @real_modules;
199             # Push everything except the special cases that are totally useless
200 1         3 foreach my $module ( @merged ) {
201 9 100 100     124 push(@real_modules, $module) unless
      100        
      100        
      100        
      100        
      100        
      100        
202              
203             $module =~ m/say/
204              
205             # Describes a minimal Perl version
206             # BUG #76, see above
207             or $module =~ m/^use\s[0-9]\.[0-9]+?/
208             or $module =~ m/^use\sautodie?/
209             or $module =~ m/^use\swarnings/
210             # Kind of bug generated by merge_dependencies() when there is
211             # only one array to merge
212             or $module =~ m/^1$/
213             or $module =~ m/^use\sDependencies::Searcher/
214             # Bug #76, a regex in this module created a match ("\s" module) in the
215             # patterns search
216             # https://git.framasoft.org/smonff/dependencies-searcher/issues/76
217             or $module =~ m/\/\^use\\s/
218             or $module =~m/^use\s\\s/;
219             }
220 1         6 return @real_modules;
221             }
222              
223             # Clean correct lines that can't be removed
224             sub clean_everything {
225 1     1 1 21122 my ($self, @dirty_modules) = @_;
226 1         2 my @clean_modules = ();
227              
228 1         4 foreach my $module ( @dirty_modules ) {
229              
230 26         1686 say("Dirty module : " . $module);
231              
232             # remove the 'use' and the space next
233 26         143 $module =~ s{
234             use \s
235             }
236             {}xi; # Empty subtitution
237              
238             # remove the require, quotes and the space next
239             # but returns the captured module name (non-greedy)
240             # i = not case-sensitive
241 26         47 $module =~ s{
242             requires \s
243             '
244             (.*?)
245             '
246             }{$1}xi; # Note -> don't insert spaces here
247              
248             # Remove the ';' at the end of the line
249 26         40 $module =~ s/ ; //xi;
250              
251             # Remove any qw(xxxxx xxxxx) or qw[xxx xxxxx]
252             # '\(' are for real 'qw()' parenthesis not for grouping
253             # Also removes empty qw()
254              
255             # With spaces and parenthesis e.g. qw( foo bar )
256 26         90 $module =~ s{
257             \s qw
258             \(
259             (\s*[A-Za-z]+(\s*[A-Za-z]*))*\s*
260             \)
261             }{}xi;
262              
263             # Without spaces, with parenthesis e.g. qw(foo bar) and optionnal [method_names
264 26         33 $module =~ s{
265             \s qw
266             \(
267             ([A-Za-z]+(_[A-Za-z]+)*(\s*[A-Za-z]*))*
268             \)
269             }{}xi;
270              
271             # With square brackets e.g. qw[foo bar] and optionnal [method_names]
272 26         31 $module =~ s{
273             \s qw
274             \[
275             ([A-Za-z]+(_[A-Za-z]+)*(\s*[A-Za-z]*))*
276             \]
277             }
278             {}xi; # Empty subtitution
279             # With spaces and parenthesis e.g. qw/ foo bar /
280 26         32 $module =~ s{
281             \s qw
282             \/
283             (\s[A-Za-z]+(_[A-Za-z]+)*(\s*[A-Za-z]*))*\s
284             \/
285             }
286             {}xi; # Empty subtitution
287              
288             # Remove method names between quotes (those that can be used
289             # without class instantiation)
290 26         23 $module =~ s{
291             \s
292             '
293             [A-Za-z]+(_[A-Za-z]+)*
294             '
295             }
296             {}xi; # Empty subtitution
297              
298             # Remove dirty bases and quotes.
299             # This regex that substitute My::Module::Name
300             # to a "base 'My::Module::Name'" by capturing
301             # the name in a non-greedy way
302 26         23 $module =~ s{
303             base \s
304             '
305             (.*?)
306             '
307             }
308             {$1}xi;
309              
310             # Remove some warning sugar
311 26         19 $module =~ s{
312             ([a-z]+)
313             \s FATAL
314             \s =>
315             \s 'all'
316             }
317             {$1}xi;
318              
319             # Remove version numbers
320             # See "a-regex-for-version-number-parsing" :
321             # http://stackoverflow.com/questions/82064/
322 26         31 $module =~ s{
323             \s
324             (\*|\d+(\.\d+)
325             {0,2}
326             (\.\*)?)$
327             }
328             {}x;
329              
330             # Remove configuration stuff like env_debug => 'LM_DEBUG' but
331             # the quoted words have been removed before
332 26         27 $module =~ s{
333             \s
334             ([A-Za-z]+(_[A-Za-z]+)*( \s*[A-Za-z]*))*
335             \s
336             =>
337             }
338             {}xi;
339              
340              
341 26         1610 say("Clean module : " . $module);
342 26         74 push @clean_modules, $module;
343             }
344 1         15 return @clean_modules;
345             }
346              
347              
348             sub uniq {
349 0     0 1 0 my ($self, @many_modules) = @_;
350 0         0 my @unique_modules = ();
351 0         0 my %seen = ();
352 0         0 foreach my $element ( @many_modules ) {
353 0 0       0 next if $seen{ $element }++;
354 0         0 say("Uniq element added : " . $element);
355 0         0 push @unique_modules, $element;
356             }
357 0         0 return @unique_modules;
358             }
359              
360             sub dissociate {
361 1     1 1 339 my ($self, @common_modules) = @_;
362              
363 1         3 foreach my $nc_module (@common_modules) {
364              
365 6         21 my $core_list_answer = Module::CoreList::is_core($nc_module);
366              
367 6 50 33     145277 if (
368             # "$]" is Perl version
369             (exists $Module::CoreList::version{ $] }{"$nc_module"})
370             or
371             # In case module don't have a version number
372             ($core_list_answer == 1)
373             ) {
374              
375             # A module can be in core but the wanted version can be
376             # more fresh than the core one...
377             # Return the most recent version
378 0         0 my $mversion_version = get_version($nc_module);
379             # Return the corelist version
380 0         0 my $corelist_version = $Module::CoreList::version{ $] }{"$nc_module"};
381              
382 0         0 say("Mversion version : " . $mversion_version);
383 0         0 say("Corelist version : " . $corelist_version);
384              
385             # Version::Compare warns about versions numbers with '_'
386             # are 'non-numeric values'
387 0         0 $corelist_version =~ s/_/./;
388 0         0 $mversion_version =~ s/_/./;
389              
390             # It's a fix for this bug
391             # https://github.com/smonff/dependencies-searcher/issues/25
392             # Recent versions of corelist modules are not include in
393             # all Perl versions corelist
394 0 0       0 if (&Version::Compare::version_compare(
395             $mversion_version, $corelist_version
396             ) == 1) {
397 0         0 say(
398             $nc_module . " version " . $mversion_version .
399             " is in use but " .
400             $corelist_version .
401             " is in core list"
402             );
403 0         0 $self->add_non_core_module($nc_module);
404 0         0 say(
405             $nc_module .
406             " is in core but has been added to non core " .
407             "because it's a fresh core"
408             );
409 0         0 next;
410             }
411              
412             # Add to core_module
413              
414             # The "Moose" trait way
415             # http://metacpan.org/module/Moose::Meta::Attribute::Native::Trait::Array
416 0         0 $self->add_core_module($nc_module);
417 0         0 say($nc_module . " is core");
418              
419             } else {
420 6         1945 $self->add_non_core_module($nc_module);
421 6         771 say($nc_module . " is not in core");
422             }
423             }
424             }
425              
426             # Open a file handle to > cpanfile
427             sub generate_report {
428              
429 1     1 1 9 my $self = shift;
430              
431             #
432             # TODO !!! Check if the module is installed already with
433             # ExtUtils::Installed. If it it not, cry that
434             # Dependencies::Searcher is designed to be used in the complete env
435             #
436              
437 1 50       6 open my $cpanfile_fh, '>', 'cpanfile' or die "Can't open cpanfile : $:!";
438              
439 1         1777 foreach my $module_name ( @{$self->non_core_modules} ) {
  1         33  
440              
441 6         23 my $version = get_version($module_name);
442              
443             # if not undef
444 6 50       1114 if ($version) {
445 0         0 say("Module + version : " . $module_name . " " . $version);
446              
447             # Add the "requires $module_name\n" to the next line of the file
448 0         0 chomp($module_name, $version);
449              
450 0 0       0 if ($version =~ m/[0-9]\.[0-9]+/ ) {
451 0         0 say $cpanfile_fh "requires '$module_name', '$version';";
452             } # else : other case ?
453              
454             } else {
455 6         543 say("Module + version : " . $module_name);
456 6         36 say $cpanfile_fh "requires '$module_name';";
457             }
458              
459             }
460              
461 1         7 close $cpanfile_fh;
462 1         849 say("File has been generated and is waiting for you");
463             }
464              
465              
466             # under rewriting since we removed Dependencies::Searcher::AckRequester
467             sub search {
468 3     3 1 9 my ($self, $pattern, @paths) = @_;
469              
470 3         4 my @modules;
471              
472 3         13 my $iter = File::Next::files(@paths);
473              
474             # Each path is a file in the chosen hierarchy
475 3         338 while ( defined ( my $path = $iter->() )) {
476              
477 6         26928 my $namespace;
478 6         28 my $file = file($path);
479              
480             # We have to test that we don't include namespaces of the
481             # analyzed module
482 6         409 my $document = PPI::Document->new($file->stringify);
483             # Testing that the file is actually a module, not for example Makefile.PL
484 6         524206 my $is_module = $document->find_first('PPI::Statement::Package');
485 6 100       20354 if ($is_module) {
486 3         17 $namespace = $is_module->namespace;
487             }
488              
489 6         106 my @lines = $file->slurp;
490              
491 6         3269 for my $line (@lines) {
492              
493             # if ( $line =~ /use\\s/m ) {
494             # say "$line !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
495             #}
496              
497             # Remove carriage return
498 2742         1649 chomp $line;
499             # Remove tabs
500 2742         2170 $line =~ s/^\t+//;
501              
502 2742 100       3825 if ($is_module) {
503             # We jump to the next line if we found the analyzed
504             # package in the array of lines
505 2583 100       4345 if ($line =~ /use $namespace/ ) {
506 6         21 $self->coucou("Module namespace ($line) has been ommited ")
507             }
508 2583 100       3418 next if $line =~ $namespace;
509             }
510              
511 2700 100       4495 if ($line =~ /^$pattern\s/ ) {
512 40         182 say($line);
513              
514             # Clean the result
515 40         59 $line = $self->rm_comments($line);
516              
517 40         61 push(@modules, $line);
518             }
519             }
520             }
521              
522             # TODO retrieve modules from Makefile.PL (it cannot be based on
523             # the use of a "pattern" in this case)
524              
525 3         3888 p @modules;
526 3         5666 return @modules;
527             }
528              
529             # It could be a simplist implementation, but I didn't find something
530             # more clever. Comments appreciated.
531             sub rm_comments {
532              
533 40     40 1 41 my ($self, $dirt) = @_;
534              
535             # Case #1: "use" declaration line with a comment after
536             # The regex add the terminal semicolon at the end of the line to
537             # make the difference between comments and code, because "use" is
538             # a word that you can find often in a POD section, more much in
539             # the beginning of line than you could think
540 40 100       66 if ( $dirt =~ /#.+/ ) {
541 2         7 say("Entered rm_comment for $dirt");
542 2         7 $dirt =~ s/#(.*)//;
543             # Delete spaces
544 2         6 $dirt =~ s/\s*//xi;
545             }
546              
547             # $pattern = "$pattern" . qr/.+;$/; # <= This is an horrible bug,
548             # because we could have a
549             # comment here
550              
551             # Case #2 pod
552             # TODO
553              
554              
555 40         33 my $clean = $dirt;
556              
557 40         46 return $clean;
558             }
559              
560             sub coucou {
561 6     6 1 9 my ($self, $thing, $color) = @_;
562 6 50       12 if (not $color) {
563 6         9 $color = 'red';
564             }
565 6         21 say(color("$color"), $thing);
566             }
567              
568              
569             1;
570              
571             __END__
572              
573             =pod
574              
575             =head1 SUBROUTINES/METHODS
576              
577             =head2 get_files()
578              
579             C<get_files()> returns an array containing which file or directories has
580             been found in the current root distribution directory. We suppose it
581             can find dependancies in 3 different places :
582              
583             =over 2
584              
585             =item * files in C<lib/> directory, recursively
586              
587             =item * C<Makefile.PL>
588              
589             =item * C<script/> directory, i.e. if we use a Catalyst application
590              
591             =item * maybe it should look in C<t/> directory (todo)
592              
593             =back
594              
595             If the C<lib/> directory don't exist, the program die because we
596             consider we are not into a plain old Perl Module.
597              
598             This is work in progress, if you know other places where we can find
599             stuff, please report a bug.
600              
601             =cut
602              
603             =head2 get_modules("pattern", @elements)
604              
605             You must pass a pattern to search for, and the elements (files or
606             directories) where you want to search (array of strings from C<get_files()>).
607              
608             These patterns should be C<^use> or C<^require>.
609              
610             Then, the search() subroutine will be used to retrieve modules names
611             into lines containing patterns and return them into an array
612             (containing also some dirt).
613              
614             =cut
615              
616             =head2 merge_dependencies(@modules, @modules)
617              
618             Simple helper method that will merge C<use> and C<require> arrays if you
619             search for both. Return an uniq array. It got a little caveat, see
620             CAVEATS.
621              
622             =cut
623              
624             =head2 avoid_superfluous(@modules)
625              
626             Move dependencies lines from an array to an another unless it is
627             considered as a "superfluous" unneccessary case : minimal Perl versions, C<use autodie>,
628             C<use warnings>. These stuff has to be B<removed>, not cleaned. Return a I<real
629             modules> array (I<real interresting> modules).
630              
631             =cut
632              
633             =head2 clean_everything(@modules)
634              
635             After removing irrelevant stuff, we need to B<clean> what is leaving
636             and is considered as being crap (not strictly <CName::Of::Module>) but
637             needs some cleaning. We are going to remove everything but the module
638             name (even version numbers).
639              
640             This code section is well commented (because it is regex-based) so,
641             please refer to it directly.
642              
643             It returns an array of I<clean modules>.
644              
645             =cut
646              
647             =head2 uniq(@modules)
648              
649             Make each array element uniq, because one dependency can be found many
650             times. Return an array of unique modules.
651              
652             =cut
653              
654             =head2 dissociate(@modules)
655              
656             Dissociate I<core> / I<non-core> modules using the awesome
657             C<Module::Corelist::is_core method>, that search in the current Perl
658             version if the module is from Perl core or not. Note that results can
659             be different according to the environment.
660              
661             More, B<you can have two versions of the same module installed on your
662             environment> (even if you use L<local::lib|local::lib> when you
663             install a recent version of a file that has been integrated into Perl
664             core (this version hasn't necessary been merged into core).
665              
666             So C<dissociate()> checks both and compares it, to be sure that the found core
667             module is the "integrated" version, not a fresh one that you have
668             installed yourself. If it is fresh, the module is considered as a I<non-core>.
669              
670             This method don't return anything, but it stores found dependencies on the two
671             C<core_modules> and C<non_core_modules> L<Moose|Moose> attributes arrays.
672              
673             =cut
674              
675             =head2 generate_report()
676              
677             Generate the C<cpanfile> for L<Carton|Carton>, based on data contained into
678             C<core_modules> and C<non_core_modules> attributes, with optionnal
679             version number (if version number can't be found, dependency name is
680             print alone).
681              
682             Generate a hash containing the modules could be achieved. Someday.
683              
684             =cut
685              
686             =head2 get_path()
687              
688             Returns the L<ack> full path if installed. Set the C<full_path>
689             L<Moose> attribute that will be used by ICP::Cmd. It verify also that
690             L<Ack> is reachable or warns about it.
691              
692             =cut
693              
694             =head2 build_cmd(@params)
695              
696             C<build_cmd()> takes as parameter all the arguments Ack will
697             need. L<Dependencies::Searcher> defines it like this :
698              
699             =over 4
700              
701             =item * C<--perl> : tells to search in Perl like files (C<*.pm>, C<*.pl>, etc.)
702              
703             =item * C<-hi> : suppress the prefixing filename on output + ignore
704             case
705              
706             =item * C<$pattern> : must be passed from your implementation
707              
708             =item * C<@path> : files and directories where L<ack> will go
709              
710             All these params are merged in an only array reference that is returned for
711             later use with L<IPC::Cmd>.
712              
713             =back
714              
715             =cut
716              
717             =head2 ack($params_array_ref)
718              
719             Execute the L<IPC::Cmd> command that calls C<ack> and returns an array of
720             potentially interesting lines, containing dependencies names but some
721             crap inside too.
722              
723             =cut
724              
725             =head2 rm_comments($line_that_should_be_cleaned_of_any_comment)
726              
727             Supposed to remove any comment from a line.
728              
729             =cut
730              
731             =head2 coucou($tring, $color)
732              
733             Stupid DIY logger
734              
735             =cut
736              
737             =head1 CAVEATS
738              
739             =head2 Some are able to do it using a one-liner (fun))
740              
741             Command Line Magic (@climagic) tweeted 4:17 PM on lun., nov. 25, 2013
742              
743             # Not perfect, but gives you a start on the Perl modules in use.
744             grep -rh ^use --include="*.pl" --include="*.pm" . | sort | uniq -c
745              
746             See original Tweet https://twitter.com/climagic/status/404992356513902592
747              
748             Though, it don't really work, that's why I made this module.
749              
750             =cut
751              
752             =head1 SOURCE CODE
753              
754             L<https://git.framasoft.org/smonff/dependencies-searcher>
755              
756             =head1 BUGS, ISSUES, TODOs
757              
758             The simpliest way report any bugs or feature requests is to use Gitlab:
759              
760             https://git.framasoft.org/smonff/dependencies-searcher/issues
761              
762             If your prefer, you can use the very perlish way and send a mail to
763             C<bug-dependencies-searcher at rt.cpan.org>, or using the web
764             interface at
765             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Dependencies-Searcher>.
766             I will be notified, and then you'll automatically be notified of
767             progress on your bug as I make changes.
768              
769             =head1 SUPPORT
770              
771             You can find documentation for this module with the perldoc command.
772              
773             perldoc Dependencies::Searcher
774              
775             You can also look for information at:
776              
777             See https://github.com/smonff/dependencies-searcher/
778              
779             =over 2
780              
781             =item * RT: CPAN's request tracker (report bugs here)
782              
783             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Dependencies-Searcher>
784              
785             =item * AnnoCPAN: Annotated CPAN documentation
786              
787             L<http://annocpan.org/dist/Dependencies-Searcher>
788              
789             =item * CPAN Ratings
790              
791             L<http://cpanratings.perl.org/d/Dependencies-Searcher>
792              
793             =item * Search CPAN
794              
795             L<http://search.cpan.org/dist/Dependencies-Searcher/>
796              
797             =back
798              
799             =head1 AUTHOR
800              
801             smonff, C<< <smonff at riseup.net> >>
802              
803             =head1 CONTRIBUTORS
804              
805             =over
806              
807             =item * Nikolay Mishin (mishin) helped to make it more cross-platform
808              
809             =item * Alexandr Ciornii (chorny) advises on version numbers
810              
811             =back
812              
813             =cut
814              
815             =head1 ACKNOWLEDGEMENTS
816              
817             =over
818              
819             =item L<Module::Extract::Use|Module::Extract::Use>
820              
821             Was the main inspiration for this one. Not recursive though...
822              
823             See L<https://metacpan.org/module/Module::Extract::Use>
824              
825             =item L<Module::CoreList|Module::CoreList>
826              
827             What modules shipped with versions of perl. I use it extensively to detect
828             if the module is from Perl Core or not.
829              
830             See L<http://perldoc.perl.org/Module/CoreList.html>
831              
832             =back
833              
834             See also :
835              
836             =over 2
837              
838             =item * https://metacpan.org/module/Module::ScanDeps
839              
840             =item * https://metacpan.org/module/Perl::PrereqScanner
841              
842             =item * http://stackoverflow.com/questions/17771725/
843              
844             =item * https://metacpan.org/module/Dist::Zilla::Plugin::AutoPrereqs
845              
846             =back
847              
848             =head1 LICENSE AND COPYRIGHT
849              
850             Copyright 2013-2016 Sebastien Feugere.
851              
852             This program is free software; you can redistribute it and/or modify it
853             under the terms of either: the GNU General Public License as published
854             by the Free Software Foundation; or the Artistic License.
855              
856             See L<http://dev.perl.org/licenses/> for more information.
857              
858              
859             =cut
860              
861