File Coverage

blib/lib/Dist/Zilla/Role/PrereqScanner.pm
Criterion Covered Total %
statement 57 58 98.2
branch 18 18 100.0
condition 3 3 100.0
subroutine 5 6 83.3
pod 0 1 0.0
total 83 86 96.5


line stmt bran cond sub pod time code
1             package Dist::Zilla::Role::PrereqScanner 6.037;
2             # ABSTRACT: automatically extract prereqs from your modules
3              
4 6     6   4984 use Moose::Role;
  6         17  
  6         59  
5             with(
6             'Dist::Zilla::Role::FileFinderUser' => {
7             default_finders => [ ':InstallModules', ':ExecFiles' ],
8             },
9             'Dist::Zilla::Role::FileFinderUser' => {
10             method => 'found_test_files',
11             finder_arg_names => [ 'test_finder' ],
12             default_finders => [ ':TestFiles' ],
13             },
14             'Dist::Zilla::Role::FileFinderUser' => {
15             method => 'found_configure_files',
16             finder_arg_names => [ 'configure_finder' ],
17             default_finders => [],
18             },
19             'Dist::Zilla::Role::FileFinderUser' => {
20             method => 'found_develop_files',
21             finder_arg_names => [ 'develop_finder' ],
22             default_finders => [ ':ExtraTestFiles' ],
23             },
24             );
25              
26 6     6   39730 use Dist::Zilla::Pragmas;
  6         15  
  6         55  
27              
28 6     6   52 use namespace::autoclean;
  6         14  
  6         83  
29              
30 6     6   683 use MooseX::Types;
  6         13  
  6         67  
31              
32             #pod =attr finder
33             #pod
34             #pod This is the name of a L<FileFinder|Dist::Zilla::Role::FileFinder>
35             #pod whose files will be scanned to determine runtime prerequisites. It
36             #pod may be specified multiple times. The default value is
37             #pod C<:InstallModules> and C<:ExecFiles>.
38             #pod
39             #pod =attr test_finder
40             #pod
41             #pod Just like C<finder>, but for test-phase prerequisites. The default
42             #pod value is C<:TestFiles>.
43             #pod
44             #pod =attr configure_finder
45             #pod
46             #pod Just like C<finder>, but for configure-phase prerequisites. There is
47             #pod no default value; AutoPrereqs will not determine configure-phase
48             #pod prerequisites unless you set configure_finder.
49             #pod
50             #pod =attr develop_finder
51             #pod
52             #pod Just like <finder>, but for develop-phase prerequisites. The default value
53             #pod is C<:ExtraTestFiles>.
54             #pod
55             #pod =attr skips
56             #pod
57             #pod This is an arrayref of regular expressions, derived from all the 'skip' lines
58             #pod in the configuration. Any module names matching any of these regexes will not
59             #pod be registered as prerequisites.
60             #pod
61             #pod =cut
62              
63             has skips => (
64             is => 'ro',
65             isa => 'ArrayRef[Str]',
66             );
67              
68             around mvp_multivalue_args => sub {
69             my ($orig, $self) = @_;
70             ($self->$orig, 'skips')
71             };
72             around mvp_aliases => sub {
73             my ($orig, $self) = @_;
74             my $aliases = $self->$orig;
75             $aliases->{skip} = 'skips';
76             return $aliases
77             };
78              
79              
80             requires 'scan_file_reqs';
81              
82             sub scan_prereqs {
83 29     29 0 82 my $self = shift;
84              
85 29         351 require CPAN::Meta::Requirements;
86 29         133 require List::Util;
87 29         840 List::Util->VERSION(1.45); # uniq
88              
89             # not a hash, because order is important
90 29         352 my @sets = (
91             # phase => file finder method
92             [ configure => 'found_configure_files' ], # must come before runtime
93             [ runtime => 'found_files' ],
94             [ test => 'found_test_files' ],
95             [ develop => 'found_develop_files' ],
96             );
97              
98 29         119 my %reqs_by_phase;
99             my %runtime_final;
100 29         0 my @modules;
101              
102 29         91 for my $fileset (@sets) {
103 116         399 my ($phase, $method) = @$fileset;
104              
105 116         990 my $req = CPAN::Meta::Requirements->new;
106 116         2919 my $files = $self->$method;
107              
108 116         421 foreach my $file (@$files) {
109             # skip binary files
110 117 100       31222 next if $file->is_bytes;
111             # parse only perl files
112 98 100 100     647 next unless $file->name =~ /\.(?:pm|pl|t|psgi)$/i
113             || $file->content =~ /^#!(?:.*)perl(?:$|\s)/;
114             # RT#76305 skip extra tests produced by ExtraTests plugin
115 93 100       353 next if $file->name =~ m{^t/(?:author|release)-[^/]*\.t$};
116              
117             # store module name, to trim it from require list later on
118 91         290 my @this_thing = $file->name;
119              
120             # t/lib/Foo.pm is treated as providing t::lib::Foo, lib::Foo, and Foo
121 91 100       345 if ($this_thing[0] =~ /^t/) {
122 5         26 push @this_thing, ($this_thing[0]) x 2;
123 5         40 $this_thing[1] =~ s{^t/}{};
124 5         20 $this_thing[2] =~ s{^t/lib/}{};
125             } else {
126 86         325 $this_thing[0] =~ s{^lib/}{};
127             }
128 91         472 s{\.pm$}{} for @this_thing;
129 91         446 s{/}{::}g for @this_thing;
130              
131             # this is a bunk heuristic and can still capture strings from pod - the
132             # proper thing to do is grab all packages from Module::Metadata
133 91         395 push @this_thing, $file->content =~ /^[^#]*?(?:^|\s)package\s+([^\s;#]+)/mg;
134 91         338 push @modules, @this_thing;
135              
136             # parse a file, and merge with existing prereqs
137 91         461 $self->log_debug([ 'scanning %s for %s prereqs', $file->name, $phase ]);
138 91         4634 my $file_req = $self->scan_file_reqs($file);
139              
140 91         1334461 $req->add_requirements($file_req);
141              
142             }
143              
144             # remove prereqs from skiplist
145 116 100       89933 for my $skip (@{ $self->skips || [] }) {
  116         5599  
146 92         637 my $re = qr/$skip/;
147              
148 92         473 foreach my $k ($req->required_modules) {
149 634 100       3576 $req->clear_requirement($k) if $k =~ $re;
150             }
151             }
152              
153             # remove prereqs shipped with current dist
154 116 100       925 if (@modules) {
155             $self->log_debug([
156             'excluding local packages: %s',
157 90     0   931 sub { join(', ', List::Util::uniq(@modules)) } ]
  0         0  
158             )
159             }
160 116         5215 $req->clear_requirement($_) for @modules;
161              
162 116         4327 $req->clear_requirement($_) for qw(Config DB Errno NEXT Pod::Functions); # never indexed
163              
164             # we're done, return what we've found
165 116         3417 my %got = %{ $req->as_string_hash };
  116         406  
166 116 100       23181 if ($phase eq 'runtime') {
167 29         441 %runtime_final = %got;
168             } else {
169             # do not test-require things required for runtime
170 87         768 delete $got{$_} for
171 1078 100       2646 grep { exists $got{$_} and $runtime_final{$_} ge $got{$_} }
172             keys %runtime_final;
173             }
174              
175 116         1280 $reqs_by_phase{$phase} = \%got;
176             }
177              
178 29         380 return \%reqs_by_phase
179             }
180              
181             1;
182              
183             =pod
184              
185             =encoding UTF-8
186              
187             =head1 NAME
188              
189             Dist::Zilla::Role::PrereqScanner - automatically extract prereqs from your modules
190              
191             =head1 VERSION
192              
193             version 6.037
194              
195             =head1 PERL VERSION
196              
197             This module should work on any version of perl still receiving updates from
198             the Perl 5 Porters. This means it should work on any version of perl
199             released in the last two to three years. (That is, if the most recently
200             released version is v5.40, then this module should work on both v5.40 and
201             v5.38.)
202              
203             Although it may work on older versions of perl, no guarantee is made that the
204             minimum required version will not be increased. The version may be increased
205             for any reason, and there is no promise that patches will be accepted to
206             lower the minimum required perl.
207              
208             =head1 ATTRIBUTES
209              
210             =head2 finder
211              
212             This is the name of a L<FileFinder|Dist::Zilla::Role::FileFinder>
213             whose files will be scanned to determine runtime prerequisites. It
214             may be specified multiple times. The default value is
215             C<:InstallModules> and C<:ExecFiles>.
216              
217             =head2 test_finder
218              
219             Just like C<finder>, but for test-phase prerequisites. The default
220             value is C<:TestFiles>.
221              
222             =head2 configure_finder
223              
224             Just like C<finder>, but for configure-phase prerequisites. There is
225             no default value; AutoPrereqs will not determine configure-phase
226             prerequisites unless you set configure_finder.
227              
228             =head2 develop_finder
229              
230             Just like <finder>, but for develop-phase prerequisites. The default value
231             is C<:ExtraTestFiles>.
232              
233             =head2 skips
234              
235             This is an arrayref of regular expressions, derived from all the 'skip' lines
236             in the configuration. Any module names matching any of these regexes will not
237             be registered as prerequisites.
238              
239             =head1 SEE ALSO
240              
241             L<Dist::Zilla::Plugin::AutoPrereqs>.
242              
243             =head1 CREDITS
244              
245             The role was provided by Olivier Mengué (DOLMEN) and Philippe Bruhat (BOOK) at Perl QA Hackathon 2016
246             (but it is just a refactor of the AutoPrereqs plugin).
247              
248             =head1 AUTHOR
249              
250             Ricardo SIGNES 😏 <cpan@semiotic.systems>
251              
252             =head1 COPYRIGHT AND LICENSE
253              
254             This software is copyright (c) 2026 by Ricardo SIGNES.
255              
256             This is free software; you can redistribute it and/or modify it under
257             the same terms as the Perl 5 programming language system itself.
258              
259             =cut
260              
261             __END__
262              
263             #pod =head1 SEE ALSO
264             #pod
265             #pod L<Dist::Zilla::Plugin::AutoPrereqs>.
266             #pod
267             #pod =head1 CREDITS
268             #pod
269             #pod The role was provided by Olivier Mengué (DOLMEN) and Philippe Bruhat (BOOK) at Perl QA Hackathon 2016
270             #pod (but it is just a refactor of the AutoPrereqs plugin).
271             #pod
272             #pod =cut
273