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