1use 5.008;
2use strict;
3use warnings;
4
5package Perl::PrereqScanner;
6# ABSTRACT: a tool to scan your Perl code for its prerequisites
7$Perl::PrereqScanner::VERSION = '1.023';
8use Moose;
9
10use List::Util qw(max);
11use Params::Util qw(_CLASS);
12use Perl::PrereqScanner::Scanner;
13use PPI 1.215; # module_version, bug fixes
14use String::RewritePrefix 0.005 rewrite => {
15  -as => '__rewrite_scanner',
16  prefixes => { '' => 'Perl::PrereqScanner::Scanner::', '=' => '' },
17};
18
19use CPAN::Meta::Requirements 2.124; # normalized v-strings
20
21use namespace::autoclean;
22
23has scanners => (
24  is  => 'ro',
25  isa => 'ArrayRef[Perl::PrereqScanner::Scanner]',
26  init_arg => undef,
27  writer   => '_set_scanners',
28);
29
30sub __scanner_from_str {
31  my $class = __rewrite_scanner($_[0]);
32  confess "illegal class name: $class" unless _CLASS($class);
33  eval "require $class; 1" or die $@;
34  return $class->new;
35}
36
37sub __prepare_scanners {
38  my ($self, $specs) = @_;
39  my @scanners = map {; ref $_ ? $_ : __scanner_from_str($_) } @$specs;
40
41  return \@scanners;
42}
43
44sub BUILD {
45  my ($self, $arg) = @_;
46
47  my @scanners = @{ $arg->{scanners} || [ qw(Perl5 Superclass TestMore Moose Aliased POE) ] };
48  my @extra_scanners = @{ $arg->{extra_scanners} || [] };
49
50  my $scanners = $self->__prepare_scanners([ @scanners, @extra_scanners ]);
51
52  $self->_set_scanners($scanners);
53}
54
55#pod =method scan_string
56#pod
57#pod   my $prereqs = $scanner->scan_string( $perl_code );
58#pod
59#pod Given a string containing Perl source code, this method returns a
60#pod CPAN::Meta::Requirements object describing the modules it requires.
61#pod
62#pod This method will throw an exception if PPI fails to parse the code.
63#pod
64#pod B<Warning!>  It isn't entirely clear whether PPI prefers to receive
65#pod strings as octet strings or character strings.  For now, my advice
66#pod is to pass octet strings.
67#pod
68#pod =cut
69
70sub scan_string {
71  my ($self, $str) = @_;
72  my $ppi = PPI::Document->new( \$str );
73  confess "PPI parse failed: " . PPI::Document->errstr unless defined $ppi;
74
75  return $self->scan_ppi_document( $ppi );
76}
77
78#pod =method scan_file
79#pod
80#pod   my $prereqs = $scanner->scan_file( $path );
81#pod
82#pod Given a file path to a Perl document, this method returns a
83#pod CPAN::Meta::Requirements object describing the modules it requires.
84#pod
85#pod This method will throw an exception if PPI fails to parse the code.
86#pod
87#pod =cut
88
89sub scan_file {
90  my ($self, $path) = @_;
91  my $ppi = PPI::Document->new( $path );
92  confess "PPI failed to parse '$path': " . PPI::Document->errstr
93      unless defined $ppi;
94
95  return $self->scan_ppi_document( $ppi );
96}
97
98#pod =method scan_ppi_document
99#pod
100#pod   my $prereqs = $scanner->scan_ppi_document( $ppi_doc );
101#pod
102#pod Given a L<PPI::Document>, this method returns a CPAN::Meta::Requirements object
103#pod describing the modules it requires.
104#pod
105#pod =cut
106
107sub scan_ppi_document {
108  my ($self, $ppi_doc) = @_;
109
110  my $req = CPAN::Meta::Requirements->new;
111
112  for my $scanner (@{ $self->{scanners} }) {
113    $scanner->scan_for_prereqs($ppi_doc, $req);
114  }
115
116  return $req;
117}
118
119#pod =method scan_module
120#pod
121#pod   my $prereqs = $scanner->scan_module( $module_name );
122#pod
123#pod Given the name of a module, eg C<'PPI::Document'>,
124#pod this method returns a CPAN::Meta::Requirements object
125#pod describing the modules it requires.
126#pod
127#pod =cut
128
129sub scan_module {
130  my ($self, $module_name) = @_;
131
132  # consider rewriting to use Module::Which -- rjbs, 2013-11-03
133  require Module::Path;
134  if (defined(my $path = Module::Path::module_path($module_name))) {
135    return $self->scan_file($path);
136  }
137
138  confess "Failed to find file for module '$module_name'";
139}
140
1411;
142
143=pod
144
145=encoding UTF-8
146
147=head1 NAME
148
149Perl::PrereqScanner - a tool to scan your Perl code for its prerequisites
150
151=head1 VERSION
152
153version 1.023
154
155=head1 SYNOPSIS
156
157  use Perl::PrereqScanner;
158  my $scanner = Perl::PrereqScanner->new;
159  my $prereqs = $scanner->scan_ppi_document( $ppi_doc );
160  my $prereqs = $scanner->scan_file( $file_path );
161  my $prereqs = $scanner->scan_string( $perl_code );
162  my $prereqs = $scanner->scan_module( $module_name );
163
164=head1 DESCRIPTION
165
166The scanner will extract loosely your distribution prerequisites from your
167files.
168
169The extraction may not be perfect but tries to do its best. It will currently
170find the following prereqs:
171
172=over 4
173
174=item *
175
176plain lines beginning with C<use> or C<require> in your perl modules and scripts, including minimum perl version
177
178=item *
179
180regular inheritance declared with the C<base> and C<parent> pragmata
181
182=item *
183
184L<Moose> inheritance declared with the C<extends> keyword
185
186=item *
187
188L<Moose> roles included with the C<with> keyword
189
190=item *
191
192OO namespace aliasing using the C<aliased> module
193
194=back
195
196=head2 Scanner Plugins
197
198Perl::PrereqScanner works by running a series of scanners over a PPI::Document
199representing the code to scan.  By default the "Perl5", "Moose", "TestMore",
200"POE", and "Aliased" scanners are run.  You can supply your own scanners when
201constructing your PrereqScanner:
202
203  # Us only the Perl5 scanner:
204  my $scanner = Perl::PrereqScanner->new({ scanners => [ qw(Perl5) ] });
205
206  # Use any stock scanners, plus Example:
207  my $scanner = Perl::PrereqScanner->new({ extra_scanners => [ qw(Example) ] });
208
209=head1 METHODS
210
211=head2 scan_string
212
213  my $prereqs = $scanner->scan_string( $perl_code );
214
215Given a string containing Perl source code, this method returns a
216CPAN::Meta::Requirements object describing the modules it requires.
217
218This method will throw an exception if PPI fails to parse the code.
219
220B<Warning!>  It isn't entirely clear whether PPI prefers to receive
221strings as octet strings or character strings.  For now, my advice
222is to pass octet strings.
223
224=head2 scan_file
225
226  my $prereqs = $scanner->scan_file( $path );
227
228Given a file path to a Perl document, this method returns a
229CPAN::Meta::Requirements object describing the modules it requires.
230
231This method will throw an exception if PPI fails to parse the code.
232
233=head2 scan_ppi_document
234
235  my $prereqs = $scanner->scan_ppi_document( $ppi_doc );
236
237Given a L<PPI::Document>, this method returns a CPAN::Meta::Requirements object
238describing the modules it requires.
239
240=head2 scan_module
241
242  my $prereqs = $scanner->scan_module( $module_name );
243
244Given the name of a module, eg C<'PPI::Document'>,
245this method returns a CPAN::Meta::Requirements object
246describing the modules it requires.
247
248=for Pod::Coverage::TrustPod new
249
250=head1 SEE ALSO
251
252L<scan-perl-prereqs>, in this distribution, is a command-line interface to the scanner
253
254=head1 AUTHORS
255
256=over 4
257
258=item *
259
260Jerome Quelin
261
262=item *
263
264Ricardo Signes <rjbs@cpan.org>
265
266=back
267
268=head1 CONTRIBUTORS
269
270=for stopwords bowtie celogeek Christopher J. Madsen David Golden Steinbrunner Ed J Florian Ragwitz Jakob Voss Jerome Quelin Jérôme John SJ Anderson Karen Etheridge Mark Gardner Neil Bowers Randy Stauner Tina Mueller Vyacheslav Matjukhin
271
272=over 4
273
274=item *
275
276bowtie <bowtie@cpan.org>
277
278=item *
279
280celogeek <me@celogeek.com>
281
282=item *
283
284Christopher J. Madsen <perl@cjmweb.net>
285
286=item *
287
288David Golden <dagolden@cpan.org>
289
290=item *
291
292David Steinbrunner <dsteinbrunner@pobox.com>
293
294=item *
295
296Ed J <mohawk2@users.noreply.github.com>
297
298=item *
299
300Florian Ragwitz <rafl@debian.org>
301
302=item *
303
304Jakob Voss <voss@gbv.de>
305
306=item *
307
308Jerome Quelin <jquelin@gmail.com>
309
310=item *
311
312Jérôme Quelin <jquelin@gmail.com>
313
314=item *
315
316John SJ Anderson <genehack@genehack.org>
317
318=item *
319
320Karen Etheridge <ether@cpan.org>
321
322=item *
323
324Mark Gardner <gardnerm@gsicommerce.com>
325
326=item *
327
328Neil Bowers <neil@bowers.com>
329
330=item *
331
332Randy Stauner <rwstauner@cpan.org>
333
334=item *
335
336Tina Mueller <tinita@cpan.org>
337
338=item *
339
340Vyacheslav Matjukhin <mmcleric@yandex-team.ru>
341
342=back
343
344=head1 COPYRIGHT AND LICENSE
345
346This software is copyright (c) 2009 by Jerome Quelin.
347
348This is free software; you can redistribute it and/or modify it under
349the same terms as the Perl 5 programming language system itself.
350
351=cut
352
353__END__
354
355#pod =for Pod::Coverage::TrustPod
356#pod   new
357#pod
358#pod =head1 SYNOPSIS
359#pod
360#pod   use Perl::PrereqScanner;
361#pod   my $scanner = Perl::PrereqScanner->new;
362#pod   my $prereqs = $scanner->scan_ppi_document( $ppi_doc );
363#pod   my $prereqs = $scanner->scan_file( $file_path );
364#pod   my $prereqs = $scanner->scan_string( $perl_code );
365#pod   my $prereqs = $scanner->scan_module( $module_name );
366#pod
367#pod =head1 DESCRIPTION
368#pod
369#pod The scanner will extract loosely your distribution prerequisites from your
370#pod files.
371#pod
372#pod The extraction may not be perfect but tries to do its best. It will currently
373#pod find the following prereqs:
374#pod
375#pod =begin :list
376#pod
377#pod * plain lines beginning with C<use> or C<require> in your perl modules and scripts, including minimum perl version
378#pod
379#pod * regular inheritance declared with the C<base> and C<parent> pragmata
380#pod
381#pod * L<Moose> inheritance declared with the C<extends> keyword
382#pod
383#pod * L<Moose> roles included with the C<with> keyword
384#pod
385#pod * OO namespace aliasing using the C<aliased> module
386#pod
387#pod =end :list
388#pod
389#pod =head2 Scanner Plugins
390#pod
391#pod Perl::PrereqScanner works by running a series of scanners over a PPI::Document
392#pod representing the code to scan.  By default the "Perl5", "Moose", "TestMore",
393#pod "POE", and "Aliased" scanners are run.  You can supply your own scanners when
394#pod constructing your PrereqScanner:
395#pod
396#pod   # Us only the Perl5 scanner:
397#pod   my $scanner = Perl::PrereqScanner->new({ scanners => [ qw(Perl5) ] });
398#pod
399#pod   # Use any stock scanners, plus Example:
400#pod   my $scanner = Perl::PrereqScanner->new({ extra_scanners => [ qw(Example) ] });
401#pod
402#pod =head1 SEE ALSO
403#pod
404#pod L<scan-perl-prereqs>, in this distribution, is a command-line interface to the scanner
405#pod
406#pod =cut
407