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.030;
2             # ABSTRACT: automatically extract prereqs from your modules
3              
4 6     6   4104 use Moose::Role;
  6         22  
  6         68  
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   33238 use Dist::Zilla::Pragmas;
  6         22  
  6         44  
27              
28 6     6   44 use namespace::autoclean;
  6         17  
  6         40  
29              
30 6     6   475 use MooseX::Types;
  6         22  
  6         68  
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 91 my $self = shift;
84              
85 29         199 require CPAN::Meta::Requirements;
86 29         137 require List::Util;
87 29         933 List::Util->VERSION(1.45); # uniq
88              
89             # not a hash, because order is important
90 29         296 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         116 my %reqs_by_phase;
99             my %runtime_final;
100 29         0 my @modules;
101              
102 29         105 for my $fileset (@sets) {
103 116         400 my ($phase, $method) = @$fileset;
104              
105 116         672 my $req = CPAN::Meta::Requirements->new;
106 116         2480 my $files = $self->$method;
107              
108 116         398 foreach my $file (@$files) {
109             # skip binary files
110 117 100       20988 next if $file->is_bytes;
111             # parse only perl files
112 98 100 100     401 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       330 next if $file->name =~ m{^t/(?:author|release)-[^/]*\.t$};
116              
117             # store module name, to trim it from require list later on
118 91         308 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       402 if ($this_thing[0] =~ /^t/) {
122 5         23 push @this_thing, ($this_thing[0]) x 2;
123 5         24 $this_thing[1] =~ s{^t/}{};
124 5         16 $this_thing[2] =~ s{^t/lib/}{};
125             } else {
126 86         315 $this_thing[0] =~ s{^lib/}{};
127             }
128 91         472 s{\.pm$}{} for @this_thing;
129 91         395 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         375 push @this_thing, $file->content =~ /^[^#]*?(?:^|\s)package\s+([^\s;#]+)/mg;
134 91         298 push @modules, @this_thing;
135              
136             # parse a file, and merge with existing prereqs
137 91         356 $self->log_debug([ 'scanning %s for %s prereqs', $file->name, $phase ]);
138 91         2819 my $file_req = $self->scan_file_reqs($file);
139              
140 91         1146021 $req->add_requirements($file_req);
141              
142             }
143              
144             # remove prereqs from skiplist
145 116 100       59510 for my $skip (@{ $self->skips || [] }) {
  116         3983  
146 92         459 my $re = qr/$skip/;
147              
148 92         359 foreach my $k ($req->required_modules) {
149 634 100       3134 $req->clear_requirement($k) if $k =~ $re;
150             }
151             }
152              
153             # remove prereqs shipped with current dist
154 116 100       868 if (@modules) {
155             $self->log_debug([
156             'excluding local packages: %s',
157 90     0   717 sub { join(', ', List::Util::uniq(@modules)) } ]
  0         0  
158             )
159             }
160 116         3240 $req->clear_requirement($_) for @modules;
161              
162 116         4012 $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         3155 my %got = %{ $req->as_string_hash };
  116         406  
166 116 100       20208 if ($phase eq 'runtime') {
167 29         372 %runtime_final = %got;
168             } else {
169             # do not test-require things required for runtime
170 87         467 delete $got{$_} for
171 1078 100       2312 grep { exists $got{$_} and $runtime_final{$_} ge $got{$_} }
172             keys %runtime_final;
173             }
174              
175 116         1023 $reqs_by_phase{$phase} = \%got;
176             }
177              
178 29         312 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.030
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 released
199             in the last two to three years. (That is, if the most recently released
200             version is v5.40, then this module should work on both v5.40 and v5.38.)
201              
202             Although it may work on older versions of perl, no guarantee is made that the
203             minimum required version will not be increased. The version may be increased
204             for any reason, and there is no promise that patches will be accepted to lower
205             the minimum required perl.
206              
207             =head1 ATTRIBUTES
208              
209             =head2 finder
210              
211             This is the name of a L<FileFinder|Dist::Zilla::Role::FileFinder>
212             whose files will be scanned to determine runtime prerequisites. It
213             may be specified multiple times. The default value is
214             C<:InstallModules> and C<:ExecFiles>.
215              
216             =head2 test_finder
217              
218             Just like C<finder>, but for test-phase prerequisites. The default
219             value is C<:TestFiles>.
220              
221             =head2 configure_finder
222              
223             Just like C<finder>, but for configure-phase prerequisites. There is
224             no default value; AutoPrereqs will not determine configure-phase
225             prerequisites unless you set configure_finder.
226              
227             =head2 develop_finder
228              
229             Just like <finder>, but for develop-phase prerequisites. The default value
230             is C<:ExtraTestFiles>.
231              
232             =head2 skips
233              
234             This is an arrayref of regular expressions, derived from all the 'skip' lines
235             in the configuration. Any module names matching any of these regexes will not
236             be registered as prerequisites.
237              
238             =head1 SEE ALSO
239              
240             L<Dist::Zilla::Plugin::AutoPrereqs>.
241              
242             =head1 CREDITS
243              
244             The role was provided by Olivier Mengué (DOLMEN) and Philippe Bruhat (BOOK) at Perl QA Hackathon 2016
245             (but it is just a refactor of the AutoPrereqs plugin).
246              
247             =head1 AUTHOR
248              
249             Ricardo SIGNES 😏 <cpan@semiotic.systems>
250              
251             =head1 COPYRIGHT AND LICENSE
252              
253             This software is copyright (c) 2023 by Ricardo SIGNES.
254              
255             This is free software; you can redistribute it and/or modify it under
256             the same terms as the Perl 5 programming language system itself.
257              
258             =cut
259              
260             __END__
261              
262             #pod =head1 SEE ALSO
263             #pod
264             #pod L<Dist::Zilla::Plugin::AutoPrereqs>.
265             #pod
266             #pod =head1 CREDITS
267             #pod
268             #pod The role was provided by Olivier Mengué (DOLMEN) and Philippe Bruhat (BOOK) at Perl QA Hackathon 2016
269             #pod (but it is just a refactor of the AutoPrereqs plugin).
270             #pod
271             #pod =cut
272