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