line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CPAN::FindDependencies; |
2
|
|
|
|
|
|
|
|
3
|
11
|
|
|
11
|
|
1047087
|
use strict; |
|
11
|
|
|
|
|
122
|
|
|
11
|
|
|
|
|
339
|
|
4
|
11
|
|
|
11
|
|
64
|
use warnings; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
346
|
|
5
|
11
|
|
|
11
|
|
107
|
use vars qw(@net_log $VERSION @ISA @EXPORT_OK); |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
826
|
|
6
|
|
|
|
|
|
|
|
7
|
11
|
|
|
11
|
|
9976
|
use Archive::Tar; |
|
11
|
|
|
|
|
1091818
|
|
|
11
|
|
|
|
|
847
|
|
8
|
11
|
|
|
11
|
|
7920
|
use Archive::Zip; |
|
11
|
|
|
|
|
847283
|
|
|
11
|
|
|
|
|
577
|
|
9
|
11
|
|
|
11
|
|
108
|
use File::Temp qw(tempfile); |
|
11
|
|
|
|
|
27
|
|
|
11
|
|
|
|
|
782
|
|
10
|
11
|
|
|
11
|
|
6319
|
use File::Type; |
|
11
|
|
|
|
|
97909
|
|
|
11
|
|
|
|
|
562
|
|
11
|
11
|
|
|
11
|
|
7997
|
use LWP::UserAgent; |
|
11
|
|
|
|
|
503897
|
|
|
11
|
|
|
|
|
510
|
|
12
|
11
|
|
|
11
|
|
40330
|
use Module::CoreList; |
|
11
|
|
|
|
|
1556052
|
|
|
11
|
|
|
|
|
162
|
|
13
|
11
|
|
|
11
|
|
7782
|
use Scalar::Util qw(blessed); |
|
11
|
|
|
|
|
30
|
|
|
11
|
|
|
|
|
1941
|
|
14
|
11
|
|
|
11
|
|
8353
|
use CPAN::Meta; |
|
11
|
|
|
|
|
325786
|
|
|
11
|
|
|
|
|
450
|
|
15
|
11
|
|
|
11
|
|
5993
|
use CPAN::FindDependencies::Dependency; |
|
11
|
|
|
|
|
39
|
|
|
11
|
|
|
|
|
429
|
|
16
|
11
|
|
|
11
|
|
4694
|
use CPAN::FindDependencies::MakeMaker qw(getreqs_from_mm); |
|
11
|
|
|
|
|
35
|
|
|
11
|
|
|
|
|
672
|
|
17
|
11
|
|
|
11
|
|
5475
|
use Parse::CPAN::Packages; |
|
11
|
|
|
|
|
10075182
|
|
|
11
|
|
|
|
|
437
|
|
18
|
11
|
|
|
11
|
|
6056
|
use URI::file; |
|
11
|
|
|
|
|
56847
|
|
|
11
|
|
|
|
|
776
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
require Exporter; |
21
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
22
|
|
|
|
|
|
|
@EXPORT_OK = qw(finddeps); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
$VERSION = '2.99_01'; |
25
|
|
|
|
|
|
|
|
26
|
11
|
|
|
11
|
|
95
|
use constant MAXINT => ~0; |
|
11
|
|
|
|
|
26
|
|
|
11
|
|
|
|
|
30567
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 NAME |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
CPAN::FindDependencies - find dependencies for modules on the CPAN |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 SYNOPSIS |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
use CPAN::FindDependencies; |
35
|
|
|
|
|
|
|
my @dependencies = CPAN::FindDependencies::finddeps("CPAN"); |
36
|
|
|
|
|
|
|
foreach my $dep (@dependencies) { |
37
|
|
|
|
|
|
|
print ' ' x $dep->depth(); |
38
|
|
|
|
|
|
|
print $dep->name().' ('.$dep->distribution().")\n"; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head1 INCOMPATIBLE CHANGES |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
Up to version 2.49 you used the C<02packages> argument to specify where a |
44
|
|
|
|
|
|
|
cached C<02packages.details.txt.gz> could be found. That argument no longer |
45
|
|
|
|
|
|
|
exists as of version 3.00, use the C<mirror> argument instead. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Up to version 2.49, C<maxdepth =E<gt> 0> would incorrectly return the whole |
48
|
|
|
|
|
|
|
tree. From version 3.00 it cuts the tree off at its root so will only return |
49
|
|
|
|
|
|
|
the module that you asked about. Not very useful, but correct. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
In version 2.49 you used the C<configreqs> argument to specify that you were |
52
|
|
|
|
|
|
|
interested in configure-time requirements as well as build- and run-time |
53
|
|
|
|
|
|
|
requirements. That option no longer exists as of version 3.00, it will always |
54
|
|
|
|
|
|
|
report on configure, build, test, and run-time requirements. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 HOW IT WORKS |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
The module uses the CPAN packages index to map modules to distributions and |
60
|
|
|
|
|
|
|
vice versa, and then fetches distributions' metadata or Makefile.PL files from |
61
|
|
|
|
|
|
|
a CPAN mirror to determine pre-requisites. This means that a |
62
|
|
|
|
|
|
|
working interwebnet connection is required. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 FUNCTIONS |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
There is just one function, which is not exported by default |
67
|
|
|
|
|
|
|
although you can make that happen in the usual fashion. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 finddeps |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Takes a single compulsory parameter, the name of a module |
72
|
|
|
|
|
|
|
(ie Some::Module); and the following optional |
73
|
|
|
|
|
|
|
named parameters: |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=over |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item nowarnings |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Warnings about modules where we can't find their META.yml or Makefile.PL, and |
80
|
|
|
|
|
|
|
so can't divine their pre-requisites, will be suppressed; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=item fatalerrors |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
Failure to get a module's dependencies will be a fatal error |
85
|
|
|
|
|
|
|
instead of merely emitting a warning; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item perl |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Use this version of perl to figure out what's in core. If not |
90
|
|
|
|
|
|
|
specified, it defaults to 5.005. Three part version numbers |
91
|
|
|
|
|
|
|
(eg 5.8.8) are supported but discouraged. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item cachedir |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
A directory to use for caching. It defaults to no caching. Even if |
96
|
|
|
|
|
|
|
caching is turned on, this is only for META.yml or Makefile.PL files. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
The cache is never automatically cleared out. It is your responsibility |
99
|
|
|
|
|
|
|
to clear out old data. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item maxdepth |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Cuts off the dependency tree at the specified depth. Your specified |
104
|
|
|
|
|
|
|
module is at depth 0, your dependencies at depth 1, their dependencies |
105
|
|
|
|
|
|
|
at depth 2, and so on. |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
If you don't specify any maxdepth at all it will grovel over the |
108
|
|
|
|
|
|
|
entire tree. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=item mirror |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
This can be provided more than once, if for example you want to use |
113
|
|
|
|
|
|
|
a private L<Pinto> repository for your own code while using a public |
114
|
|
|
|
|
|
|
CPAN mirror for open source dependencies. The argument comes in two parts |
115
|
|
|
|
|
|
|
separated by a comma - the base URL from which to fetch files, and |
116
|
|
|
|
|
|
|
optionally the URL or a file from which to fetch the index |
117
|
|
|
|
|
|
|
C<02packages.details.txt.gz> file to use with that mirror. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
mirror https://cpan.mydomain.net,file:///home/me/mycache/02packages.txt.gz |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
If you want to use the default CPAN mirror (https://cpan.metacpan.org/) |
122
|
|
|
|
|
|
|
but also specify an index location you can use C<DEFAULT> for the mirror URL. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
So for example, to use your own special private mirror, including fetching |
125
|
|
|
|
|
|
|
02packages from it, but also use the default mirror with a cached local |
126
|
|
|
|
|
|
|
copy of its 02packages, specify two mirrors thus: |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
mirror => 'https://cpan.mydomain.net', |
129
|
|
|
|
|
|
|
mirror => 'DEFAULT,file:///home/me/mycache/02packages.txt.gz' |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
The index is cached for three minutes or until your process finishes, whichever is soonest. This is because it is HUMUNGOUS and parsing it takes ages even when it's loaded from a local disk, and I don't want the tests to take forever. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=item usemakefilepl |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
If set to true, then for any module that doesn't have a META.yml, |
136
|
|
|
|
|
|
|
try to use its Makefile.PL as well. Note that this involves |
137
|
|
|
|
|
|
|
downloading code from the Internet and running it. This obviously |
138
|
|
|
|
|
|
|
opens you up to all kinds of bad juju, hence why it is disabled |
139
|
|
|
|
|
|
|
by default. NB that this fetches Makefile.PL from |
140
|
|
|
|
|
|
|
L<https://fastapi.metacpan.org> B<only> so will not work for private mirrors. |
141
|
|
|
|
|
|
|
This is a deliberate choice, your own private code ought to be packaged |
142
|
|
|
|
|
|
|
properly with a META file, you should only care about divining dependencies |
143
|
|
|
|
|
|
|
from Makefile.PL if you rely on really old stuff on public CPAN mirrors. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item recommended |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Adds recommended modules to the list of dependencies, if set to a true value. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=item suggested |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Adds suggested modules to the list of dependencies, if set to a true value. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=back |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
It returns a list of CPAN::FindDependencies::Dependency objects, whose |
157
|
|
|
|
|
|
|
useful methods are: |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=over |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item name |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
The module's name; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item distribution |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
The distribution containing this module; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item version |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
The minimum required version of his module (if specified in the requirer's |
172
|
|
|
|
|
|
|
pre-requisites list); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item depth |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
How deep in the dependency tree this module is; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item warning |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
If any warning was generated (even if suppressed) for the module, |
181
|
|
|
|
|
|
|
it will be recorded here. |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=back |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Any modules listed as dependencies but which are in the perl core |
186
|
|
|
|
|
|
|
distribution for the version of perl you specified are suppressed. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
These objects are returned in a semi-defined order. You can be sure |
189
|
|
|
|
|
|
|
that a module will be immediately followed by one of its dependencies, |
190
|
|
|
|
|
|
|
then that dependency's dependencies, and so on, followed by the 'root' |
191
|
|
|
|
|
|
|
module's next dependency, and so on. You can reconstruct the tree |
192
|
|
|
|
|
|
|
by paying attention to the depth of each object. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
The ordering of any particular module's immediate 'children' can be |
195
|
|
|
|
|
|
|
assumed to be random - it's actually hash key order. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=head1 SECURITY |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
If you set C<usemakefilepl> to a true value, this module may download code |
200
|
|
|
|
|
|
|
from the internet and execute it. You should think carefully before enabling |
201
|
|
|
|
|
|
|
that feature. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head1 BUGS/WARNINGS/LIMITATIONS |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
You must have web access to L<http://metacpan.org/> and (unless |
206
|
|
|
|
|
|
|
you tell it where else to look for the index) |
207
|
|
|
|
|
|
|
L<http://www.cpan.org/>, or have all the data cached locally.. |
208
|
|
|
|
|
|
|
If any |
209
|
|
|
|
|
|
|
metadata or Makefile.PL files are missing, the distribution's dependencies will |
210
|
|
|
|
|
|
|
not be found and a warning will be spat out. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Startup can be slow, especially if it needs to fetch the index from |
213
|
|
|
|
|
|
|
the interweb. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Dynamic dependencies - for example, dependencies that only apply on some |
216
|
|
|
|
|
|
|
platforms - can't be reliably resolved. They *may* be resolved if you use the |
217
|
|
|
|
|
|
|
unsafe Makefile.PL, but even that can't be relied on. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 FEEDBACK |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
I welcome feedback about my code, including constructive criticism |
222
|
|
|
|
|
|
|
and bug reports. The best bug reports include files that I can add |
223
|
|
|
|
|
|
|
to the test suite, which fail with the current code in my git repo and |
224
|
|
|
|
|
|
|
will pass once I've fixed the bug |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Feature requests are far more likely to get implemented if you submit |
227
|
|
|
|
|
|
|
a patch yourself. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head1 SOURCE CODE REPOSITORY |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
L<git://github.com/DrHyde/perl-modules-CPAN-FindDependencies.git> |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head1 SEE ALSO |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
L<CPAN> |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
L<http://deps.cpantesters.org/> |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
L<http://metacpan.org> |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head1 AUTHOR, LICENCE and COPYRIGHT |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Copyright 2007 - 2019 David Cantrell E<lt>F<david@cantrell.org.uk>E<gt> |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
This software is free-as-in-speech software, and may be used, |
246
|
|
|
|
|
|
|
distributed, and modified under the terms of either the GNU |
247
|
|
|
|
|
|
|
General Public Licence version 2 or the Artistic Licence. It's |
248
|
|
|
|
|
|
|
up to you which one you use. The full text of the licences can |
249
|
|
|
|
|
|
|
be found in the files GPL2.txt and ARTISTIC.txt, respectively. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=head1 THANKS TO |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
Stephan Loyd (for fixing problems with some META.yml files) |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Alexandr Ciornii (for a patch to stop it segfaulting on Windows) |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
Brian Phillips (for the code to report on required versions of modules) |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
Ian Tegebo (for the code to extract deps from Makefile.PL) |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Georg Oechsler (for the code to also list 'recommended' modules) |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
Jonathan Stowe (for making it work through HTTP proxies) |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
Kenneth Olwing (for support for 'configure_requires') |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head1 CONSPIRACY |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
This module is also free-as-in-mason software. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=cut |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
my $default_mirror = 'https://cpan.metacpan.org/'; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub finddeps { |
276
|
19
|
|
|
19
|
1
|
162165
|
@net_log = (); |
277
|
19
|
|
|
|
|
164
|
my($module, @args) = @_; |
278
|
|
|
|
|
|
|
|
279
|
19
|
|
|
|
|
234
|
my $self = bless({ indices => [], mirrors => [], seen => {} }, __PACKAGE__); |
280
|
|
|
|
|
|
|
|
281
|
19
|
|
|
|
|
121
|
while(@args) { |
282
|
61
|
|
|
|
|
359
|
my $optname = shift(@args); |
283
|
61
|
|
|
|
|
279
|
my $optarg = shift(@args); |
284
|
61
|
100
|
|
|
|
272
|
if($optname ne 'mirror' ) { |
285
|
45
|
|
|
|
|
519
|
$self->{$optname} = $optarg |
286
|
|
|
|
|
|
|
} else { |
287
|
16
|
|
|
|
|
91
|
my($mirror, $packages) = split(/,/, $optarg); |
288
|
16
|
100
|
|
|
|
96
|
$mirror = $default_mirror if($mirror eq 'DEFAULT'); |
289
|
16
|
100
|
|
|
|
155
|
$mirror .= '/' unless($mirror =~ m{/$}); |
290
|
16
|
100
|
|
|
|
81
|
$packages = "${mirror}modules/02packages.details.txt.gz" |
291
|
|
|
|
|
|
|
unless($packages); |
292
|
|
|
|
|
|
|
($mirror, $packages) = map { |
293
|
16
|
100
|
|
|
|
62
|
$_ =~ /^https?:\/\// ? $_ : ''.URI::file->new_abs($_); |
|
32
|
|
|
|
|
14591
|
|
294
|
|
|
|
|
|
|
} ($mirror, $packages); |
295
|
16
|
|
|
|
|
147932
|
push @{$self->{mirrors}}, { |
|
16
|
|
|
|
|
944
|
|
296
|
|
|
|
|
|
|
mirror => $mirror, |
297
|
|
|
|
|
|
|
packages => $packages |
298
|
|
|
|
|
|
|
}; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
19
|
100
|
|
|
|
122
|
unless(@{$self->{mirrors}}) { |
|
19
|
|
|
|
|
239
|
|
302
|
5
|
|
|
|
|
11
|
push @{$self->{mirrors}}, { |
|
5
|
|
|
|
|
38
|
|
303
|
|
|
|
|
|
|
mirror => $default_mirror, |
304
|
|
|
|
|
|
|
packages => "${default_mirror}modules/02packages.details.txt.gz" |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
19
|
100
|
|
|
|
300
|
$self->{maxdepth} = MAXINT unless(defined($self->{maxdepth})); |
309
|
|
|
|
|
|
|
|
310
|
19
|
|
100
|
|
|
508
|
$self->{perl} ||= 5.005; |
311
|
|
|
|
|
|
|
die(__PACKAGE__.": $self->{perl} is a broken version number\n") |
312
|
19
|
50
|
|
|
|
351
|
if($self->{perl} =~ /[^0-9.]/); |
313
|
19
|
100
|
|
|
|
292
|
if($self->{perl} =~ /\..*\./) { |
314
|
2
|
|
|
|
|
37
|
my @parts = split(/\./, $self->{perl}); |
315
|
2
|
|
|
|
|
38
|
$self->{perl} = $parts[0] + $parts[1] / 1000 + $parts[2] / 1000000; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
19
|
|
|
|
|
314
|
my $first_found = $self->_first_found($module); |
319
|
19
|
50
|
|
|
|
1055
|
return $self->_finddeps( |
320
|
|
|
|
|
|
|
module => $module, |
321
|
|
|
|
|
|
|
version => ($first_found ? $first_found->version() : 0) |
322
|
|
|
|
|
|
|
); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# indices are cached for performance, cos even if the |
326
|
|
|
|
|
|
|
# file is fetched from disk uncompressing/parsing take ages. |
327
|
|
|
|
|
|
|
# the cache lasts three minutes. |
328
|
|
|
|
|
|
|
our %_parsed_index_cache = (); |
329
|
|
|
|
|
|
|
sub _indices { |
330
|
136
|
|
|
136
|
|
377
|
my $self = shift; |
331
|
136
|
100
|
|
|
|
304
|
if(!@{$self->{indices}}) { |
|
136
|
|
|
|
|
594
|
|
332
|
19
|
|
|
0
|
|
587
|
local $SIG{__WARN__} = sub {}; |
333
|
|
|
|
|
|
|
$self->{indices} = [map { |
334
|
21
|
|
|
|
|
108
|
my $url = $_->{packages}; |
335
|
21
|
100
|
100
|
|
|
340
|
if(!(exists($_parsed_index_cache{$url}) && $_parsed_index_cache{$url}->{expiry} > time())) { |
336
|
14
|
|
|
|
|
223
|
$_parsed_index_cache{$url}->{expiry} = time() + 180; |
337
|
14
|
|
50
|
|
|
195
|
$_parsed_index_cache{$url}->{index} = Parse::CPAN::Packages->new( |
338
|
|
|
|
|
|
|
$self->_get($url) || die(__PACKAGE__.": Couldn't fetch 02packages index file from $url\n") |
339
|
|
|
|
|
|
|
); |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
$_parsed_index_cache{$url}->{index} |
342
|
19
|
|
|
|
|
110
|
} @{$self->{mirrors}}] |
|
21
|
|
|
|
|
191570373
|
|
|
19
|
|
|
|
|
114
|
|
343
|
|
|
|
|
|
|
} |
344
|
136
|
|
|
|
|
307
|
return @{$self->{indices}}; |
|
136
|
|
|
|
|
1338
|
|
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# look through all the mirrors' 02packages for a module and return a |
348
|
|
|
|
|
|
|
# Parse::CPAN::Packages::Package for the first one it finds |
349
|
|
|
|
|
|
|
sub _first_found { |
350
|
84
|
|
|
84
|
|
259
|
my $self = shift; |
351
|
84
|
|
|
|
|
259
|
my $module = shift; |
352
|
84
|
|
|
|
|
464
|
return (map { $_->package($module) } grep { $_->package($module) } $self->_indices())[0]; |
|
81
|
|
|
|
|
3191
|
|
|
96
|
|
|
|
|
12688
|
|
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub _yell { |
356
|
14
|
|
|
14
|
|
47
|
my $self = shift; |
357
|
14
|
|
|
|
|
50
|
my $msg = shift; |
358
|
14
|
|
|
|
|
67
|
$msg = __PACKAGE__.": $msg"; |
359
|
14
|
100
|
|
|
|
95
|
$msg = "$msg\n" unless(substr($msg, -1, 1) eq "\n"); |
360
|
14
|
100
|
|
|
|
76
|
if(!$self->{nowarnings}) { |
361
|
3
|
100
|
|
|
|
12
|
if($self->{fatalerrors} ) { |
362
|
1
|
|
|
|
|
20
|
die('FATAL: '.$msg); |
363
|
|
|
|
|
|
|
} else { |
364
|
2
|
|
|
|
|
32
|
warn('WARNING: '.$msg); |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub _get { |
370
|
40
|
|
|
40
|
|
152
|
my $self = shift; |
371
|
40
|
|
|
|
|
133
|
my $url = shift; |
372
|
40
|
|
|
|
|
711
|
my $ua = LWP::UserAgent->new(); |
373
|
40
|
|
|
|
|
50904
|
$ua->env_proxy(); |
374
|
40
|
|
|
|
|
52702
|
$ua->agent(__PACKAGE__."/$VERSION"); |
375
|
40
|
|
|
|
|
2764
|
push @net_log, $url; |
376
|
40
|
|
|
|
|
313
|
my $response = $ua->get($url); |
377
|
40
|
100
|
|
|
|
7482816
|
if($response->is_success()) { |
378
|
28
|
|
|
|
|
597
|
return $response->content(); |
379
|
|
|
|
|
|
|
} else { |
380
|
12
|
|
|
|
|
566
|
return undef; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
sub _incore { |
385
|
178
|
|
|
178
|
|
579
|
my $self = shift; |
386
|
178
|
|
|
|
|
641
|
my %args = @_; |
387
|
178
|
|
|
|
|
2416
|
my $core = $Module::CoreList::version{$args{perl}}{$args{module}}; |
388
|
178
|
100
|
|
|
|
605
|
$core =~ s/_/00/g if($core); |
389
|
178
|
|
|
|
|
402
|
$args{version} =~ s/_/00/g; |
390
|
178
|
100
|
100
|
|
|
2011
|
return ($core && $core >= $args{version}) ? $core : undef; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub _finddeps { |
394
|
190
|
|
|
190
|
|
599
|
my $self = shift; |
395
|
190
|
|
|
|
|
821
|
my %args = @_; |
396
|
190
|
|
|
|
|
577
|
my( $module, $depth, $version) = @args{qw(module depth version)}; |
397
|
190
|
|
100
|
|
|
698
|
$depth ||= 0; |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
return () if( |
400
|
|
|
|
|
|
|
$module eq 'perl' || |
401
|
|
|
|
|
|
|
$self->_incore( |
402
|
|
|
|
|
|
|
module => $module, |
403
|
|
|
|
|
|
|
perl => $self->{perl}, |
404
|
190
|
100
|
100
|
|
|
1143
|
version => $version) |
405
|
|
|
|
|
|
|
); |
406
|
|
|
|
|
|
|
|
407
|
65
|
|
|
|
|
142
|
my $dist = do { |
408
|
65
|
|
|
|
|
201
|
my $package = $self->_first_found($module); |
409
|
65
|
100
|
|
|
|
2800
|
$package ? $package->distribution() : undef; |
410
|
|
|
|
|
|
|
}; |
411
|
|
|
|
|
|
|
|
412
|
65
|
100
|
|
|
|
917
|
return () unless(blessed($dist)); |
413
|
|
|
|
|
|
|
|
414
|
62
|
|
|
|
|
1568
|
my $author = $dist->cpanid(); |
415
|
62
|
|
|
|
|
1705
|
my $distname = $dist->distvname(); |
416
|
|
|
|
|
|
|
|
417
|
62
|
100
|
|
|
|
919
|
return () if($self->{seen}->{$distname}++); |
418
|
|
|
|
|
|
|
|
419
|
55
|
|
|
|
|
1176
|
my %reqs = $self->_getreqs( |
420
|
|
|
|
|
|
|
author => $author, |
421
|
|
|
|
|
|
|
distname => $distname, |
422
|
|
|
|
|
|
|
distfile => $dist->filename(), |
423
|
|
|
|
|
|
|
); |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
return ( |
426
|
|
|
|
|
|
|
CPAN::FindDependencies::Dependency->_new( |
427
|
|
|
|
|
|
|
depth => $depth, |
428
|
|
|
|
|
|
|
distribution => $dist, |
429
|
|
|
|
|
|
|
cpanmodule => $module, |
430
|
|
|
|
|
|
|
indices => [$self->_indices()], |
431
|
|
|
|
|
|
|
version => $version || 0, |
432
|
|
|
|
|
|
|
$reqs{'-warning'} ? (warning => $reqs{'-warning'}) : () |
433
|
|
|
|
|
|
|
), |
434
|
|
|
|
|
|
|
(!exists($reqs{'-warning'}) && $depth != $self->{maxdepth}) ? (map { |
435
|
|
|
|
|
|
|
# print "Looking at $_\n"; |
436
|
52
|
100
|
100
|
|
|
38863
|
$self->_finddeps( |
|
|
100
|
100
|
|
|
|
|
437
|
|
|
|
|
|
|
module => $_, |
438
|
|
|
|
|
|
|
depth => $depth + 1, |
439
|
171
|
|
|
|
|
677
|
version => $reqs{$_} |
440
|
|
|
|
|
|
|
); |
441
|
|
|
|
|
|
|
} sort keys %reqs) : () |
442
|
|
|
|
|
|
|
); |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# caching wrapper around _get |
446
|
|
|
|
|
|
|
# can be asked to fetch a .meta, an archive, or a Makefile.PL, |
447
|
|
|
|
|
|
|
# so it knows how to figure out what the cache filename is |
448
|
|
|
|
|
|
|
# for those, based on the URL |
449
|
|
|
|
|
|
|
# can be asked to get whichever first succeeds of multiple options. |
450
|
|
|
|
|
|
|
# currently those are always a metadata file or an archive, which |
451
|
|
|
|
|
|
|
# will resolve to the same cache file. |
452
|
|
|
|
|
|
|
sub _get_cached { |
453
|
127
|
|
|
127
|
|
313
|
my $self = shift; |
454
|
127
|
|
|
|
|
564
|
my %args = @_; |
455
|
127
|
|
|
|
|
405
|
my($src, $post_process) = @args{qw(src post_process)}; |
456
|
127
|
|
|
|
|
217
|
my $contents; |
457
|
|
|
|
|
|
|
# asked to check multiple sources? Return the first which has |
458
|
|
|
|
|
|
|
# content (or what's cached) |
459
|
127
|
100
|
|
|
|
399
|
if(ref($src)) { |
460
|
55
|
|
|
|
|
116
|
foreach my $this_url (@{$src}) { |
|
55
|
|
|
|
|
173
|
|
461
|
67
|
100
|
|
|
|
392
|
last if($contents = $self->_get_cached( |
462
|
|
|
|
|
|
|
post_process => $post_process, |
463
|
|
|
|
|
|
|
src => $this_url |
464
|
|
|
|
|
|
|
)); |
465
|
|
|
|
|
|
|
} |
466
|
55
|
|
|
|
|
252
|
return $contents; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
72
|
|
|
|
|
170
|
my $cachefile = $src; |
470
|
72
|
100
|
|
|
|
315
|
if($cachefile =~ /Makefile.PL/) { |
471
|
5
|
|
|
|
|
66
|
$cachefile =~ s{.*/([^/]+)/Makefile.PL$}{$1.MakefilePL}; |
472
|
|
|
|
|
|
|
} else { |
473
|
67
|
|
|
|
|
881
|
$cachefile =~ s{.*/(.*?)\.(meta|zip|tar\.bz2|tar\.gz|tgz)$}{$1.meta}; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
72
|
100
|
66
|
|
|
3492
|
if($self->{cachedir} && -d $self->{cachedir} && -r $self->{cachedir}."/$cachefile") { |
|
|
|
100
|
|
|
|
|
477
|
|
|
|
|
|
|
open(my $cachefh, $self->{cachedir}."/$cachefile") || |
478
|
46
|
50
|
|
|
|
2642
|
$self->_yell('Error reading '.$self->{cachedir}."/$cachefile: $!"); |
479
|
46
|
|
|
|
|
353
|
local $/ = undef; |
480
|
46
|
|
|
|
|
1847
|
$contents = <$cachefh>; |
481
|
46
|
|
|
|
|
841
|
close($cachefh); |
482
|
|
|
|
|
|
|
} else { |
483
|
26
|
|
|
|
|
161
|
$contents = $self->_get($src); |
484
|
26
|
100
|
66
|
|
|
3113
|
if($contents && $post_process ) { |
485
|
14
|
|
|
|
|
70
|
$contents = $post_process->($contents); |
486
|
|
|
|
|
|
|
} |
487
|
26
|
100
|
100
|
|
|
282
|
if($contents && $self->{cachedir} && -d $self->{cachedir}) { |
|
|
|
66
|
|
|
|
|
488
|
|
|
|
|
|
|
open(my $cachefh, '>', $self->{cachedir}."/$cachefile") || |
489
|
3
|
50
|
|
|
|
303
|
$self->_yell('Error writing '.$self->{cachedir}."/$cachefile: $!"); |
490
|
3
|
|
|
|
|
29
|
print $cachefh $contents; |
491
|
3
|
|
|
|
|
122
|
close($cachefh); |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
} |
494
|
72
|
|
|
|
|
574
|
return $contents; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub _getreqs { |
498
|
55
|
|
|
55
|
|
704
|
my $self = shift; |
499
|
55
|
|
|
|
|
355
|
my %args = @_; |
500
|
55
|
|
|
|
|
245
|
my($author, $distname, $distfile) = @args{qw(author distname distfile)}; |
501
|
|
|
|
|
|
|
|
502
|
55
|
|
|
|
|
106
|
my $meta_file; |
503
|
55
|
|
|
|
|
104
|
foreach my $source (@{$self->{mirrors}}) { |
|
55
|
|
|
|
|
283
|
|
504
|
|
|
|
|
|
|
$meta_file = $self->_get_cached( |
505
|
|
|
|
|
|
|
src => [ |
506
|
|
|
|
|
|
|
$source->{mirror}."authors/id/". |
507
|
|
|
|
|
|
|
substr($author, 0, 1).'/'. |
508
|
|
|
|
|
|
|
substr($author, 0, 2).'/'. |
509
|
|
|
|
|
|
|
"$author/$distname.meta", |
510
|
|
|
|
|
|
|
$source->{mirror}."authors/id/". |
511
|
|
|
|
|
|
|
substr($author, 0, 1).'/'. |
512
|
|
|
|
|
|
|
substr($author, 0, 2).'/'. |
513
|
|
|
|
|
|
|
"$author/$distfile" |
514
|
|
|
|
|
|
|
], |
515
|
|
|
|
|
|
|
post_process => sub { |
516
|
|
|
|
|
|
|
# _get_cached normally just returns a file, but we're |
517
|
|
|
|
|
|
|
# asking it to either fetch a metadata file or if that can't be |
518
|
|
|
|
|
|
|
# found fetch an archive from which we want to extract a file, |
519
|
|
|
|
|
|
|
# and then cache that extracted file's contents. This function |
520
|
|
|
|
|
|
|
# takes a blob of data and if it looks like a zip or a tarball |
521
|
|
|
|
|
|
|
# tries to extract a META.json or META.yml and return its content |
522
|
|
|
|
|
|
|
# (or the empty string if not found), otherwise if it doesn't |
523
|
|
|
|
|
|
|
# look like an archive, assume that the input was a valid metadata |
524
|
|
|
|
|
|
|
# file after all and just return it. |
525
|
14
|
|
|
14
|
|
46
|
my $file_data = shift; |
526
|
14
|
|
|
|
|
96
|
my $meta_file_re = qr/^([^\/]+\/)?META\.(json|yml)/; |
527
|
14
|
|
|
|
|
49
|
my $rval = ''; |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# We should be able to avoid writing to disk by something like |
530
|
|
|
|
|
|
|
# this but it doesn't work, for either zip or tar <shrug> |
531
|
|
|
|
|
|
|
# # my $tar = Archive::Tar->new(); |
532
|
|
|
|
|
|
|
# # $tar->read([string opened as file]) |
533
|
14
|
|
|
|
|
281
|
my($scopeguard, $tempfile) = tempfile('CPAN-FindDependencies-XXXXXXXX', UNLINK => 1, TMPDIR => 1); |
534
|
14
|
50
|
|
|
|
11695
|
open(my $fh, '>', "$tempfile") || die("Can't write $tempfile: $!\n"); |
535
|
14
|
|
|
|
|
88
|
binmode($fh); # Windows smells of wee |
536
|
14
|
|
|
|
|
1738
|
print $fh $file_data; |
537
|
14
|
|
|
|
|
1675
|
close($fh); |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
my $tar_extractor = sub { |
540
|
11
|
|
|
|
|
317
|
my $tar = Archive::Tar->new(shift()); |
541
|
|
|
|
|
|
|
# sort to ensure that we get JSON by preference, META.json |
542
|
|
|
|
|
|
|
# often contains more info |
543
|
11
|
100
|
|
|
|
126162
|
if(my @members = sort { $a cmp $b } grep { /$meta_file_re/ } $tar->list_files()) { |
|
0
|
|
|
|
|
0
|
|
|
88
|
|
|
|
|
3214
|
|
544
|
2
|
|
|
|
|
20
|
return $tar->get_content($members[0]) |
545
|
|
|
|
|
|
|
} |
546
|
14
|
|
|
|
|
231
|
}; |
547
|
|
|
|
|
|
|
|
548
|
14
|
100
|
|
|
|
281
|
if(File::Type->mime_type($file_data) eq 'application/zip') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
549
|
1
|
|
|
|
|
374
|
my $zip = Archive::Zip->new($tempfile); |
550
|
1
|
50
|
|
|
|
2708
|
if(my @members = sort { $a cmp $b } $zip->membersMatching($meta_file_re)) { |
|
0
|
|
|
|
|
0
|
|
551
|
1
|
|
|
|
|
111
|
$rval = $zip->contents($members[0]) |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} elsif(File::Type->mime_type($file_data) =~ m{^application/x-(gzip|tar)$}) { |
554
|
10
|
|
|
|
|
10698
|
$rval = $tar_extractor->($tempfile); |
555
|
|
|
|
|
|
|
} elsif(File::Type->mime_type($file_data) eq 'application/x-bzip2') { |
556
|
1
|
50
|
|
|
|
8428
|
open(my $fh, '-|', qw(bzip2 -dc), $tempfile) || |
557
|
|
|
|
|
|
|
$self->_yell("Can't unbzip2 $tempfile: $!"); |
558
|
1
|
50
|
|
|
|
44
|
if($fh) { $rval = $tar_extractor->($fh); } |
|
1
|
|
|
|
|
34
|
|
559
|
1
|
|
|
|
|
242
|
close($fh); |
560
|
2
|
|
|
|
|
4998
|
} else { $rval = $file_data; } # oh, it must have been a meta file |
561
|
14
|
|
|
|
|
2121
|
return $rval; |
562
|
|
|
|
|
|
|
}, |
563
|
55
|
|
|
|
|
1350
|
); |
564
|
55
|
100
|
|
|
|
935
|
last if($meta_file); |
565
|
|
|
|
|
|
|
} |
566
|
55
|
100
|
|
|
|
190
|
if ($meta_file) { |
567
|
46
|
|
|
|
|
125
|
my $meta_data = eval { CPAN::Meta->load_string($meta_file); }; |
|
46
|
|
|
|
|
641
|
|
568
|
46
|
100
|
66
|
|
|
1206646
|
if ($@ || !defined($meta_data)) { |
569
|
4
|
|
|
|
|
38
|
$self->_yell("$author/$distname: failed to parse metadata") |
570
|
|
|
|
|
|
|
} else { |
571
|
42
|
|
|
|
|
266
|
my $reqs = $meta_data->effective_prereqs(); |
572
|
|
|
|
|
|
|
return %{ |
573
|
42
|
|
|
|
|
51456
|
$reqs->merged_requirements( |
574
|
|
|
|
|
|
|
[qw(configure build test runtime)], |
575
|
|
|
|
|
|
|
[ |
576
|
|
|
|
|
|
|
'requires', |
577
|
|
|
|
|
|
|
($self->{recommended} ? 'recommends' : ()), |
578
|
42
|
100
|
|
|
|
587
|
($self->{suggested} ? 'suggests' : ()) |
|
|
50
|
|
|
|
|
|
579
|
|
|
|
|
|
|
] |
580
|
|
|
|
|
|
|
)->as_string_hash() |
581
|
|
|
|
|
|
|
}; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
} else { |
584
|
9
|
|
|
|
|
85
|
$self->_yell("$author/$distname: no metadata"); |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
# We could have failed to parse the metadata file, but we still want to try the Makefile.PL |
588
|
12
|
100
|
|
|
|
136
|
if(!$self->{usemakefilepl}) { |
589
|
7
|
|
|
|
|
59
|
return ('-warning', 'no metadata'); |
590
|
|
|
|
|
|
|
} else { |
591
|
5
|
|
|
|
|
32
|
my $makefilepl = $self->_get_cached( |
592
|
|
|
|
|
|
|
src => "https://fastapi.metacpan.org/source/$author/$distname/Makefile.PL", |
593
|
|
|
|
|
|
|
); |
594
|
5
|
50
|
|
|
|
37
|
if($makefilepl) { |
595
|
5
|
|
|
|
|
109
|
my $result = getreqs_from_mm($makefilepl); |
596
|
3
|
100
|
|
|
|
30
|
if ('HASH' eq ref $result) { |
597
|
2
|
|
|
|
|
16
|
return %{ $result }; |
|
2
|
|
|
|
|
158
|
|
598
|
|
|
|
|
|
|
} else { |
599
|
1
|
|
|
|
|
31
|
$self->_yell("$author/$distname: $result"); |
600
|
1
|
|
|
|
|
45
|
return ('-warning', $result); |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
} else { |
603
|
0
|
|
|
|
|
|
$self->_yell("$author/$distname: no metadata nor Makefile.PL"); |
604
|
0
|
|
|
|
|
|
return ('-warning', 'no metadata nor Makefile.PL'); |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
} |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
1; |