]> rtime.felk.cvut.cz Git - coffee/buildroot.git/blob - utils/scancpan
genrandconfig: pass outputdir and buildrootdir as arguments
[coffee/buildroot.git] / utils / scancpan
1 #!/usr/bin/env perl
2
3 # This chunk of stuff was generated by App::FatPacker. To find the original
4 # file's code, look for the end of this BEGIN block or the string 'FATPACK'
5 BEGIN {
6 my %fatpacked;
7
8 $fatpacked{"MetaCPAN/API/Tiny.pm"} = <<'METACPAN_API_TINY';
9   package MetaCPAN::API::Tiny;
10   {
11     $MetaCPAN::API::Tiny::VERSION = '1.131730';
12   }
13   use strict;
14   use warnings;
15   # ABSTRACT: A Tiny API client for MetaCPAN
16
17   use Carp;
18   use JSON::PP 'encode_json', 'decode_json';
19   use HTTP::Tiny;
20
21
22   sub new {
23       my ($class, @args) = @_;
24
25       $#_ % 2 == 0
26           or croak 'Arguments must be provided as name/value pairs';
27
28       my %params = @args;
29
30       die 'ua_args must be an array reference'
31           if $params{ua_args} && ref($params{ua_args}) ne 'ARRAY';
32
33       my $self = +{
34           base_url => $params{base_url} || 'http://api.metacpan.org/v0',
35           ua => $params{ua} || HTTP::Tiny->new(
36               $params{ua_args}
37                   ? @{$params{ua_args}}
38                   : (agent => 'MetaCPAN::API::Tiny/'
39                       . ($MetaCPAN::API::VERSION || 'xx'))),
40       };
41
42       return bless($self, $class);
43   }
44
45   sub _build_extra_params {
46       my $self = shift;
47
48       @_ % 2 == 0
49           or croak 'Incorrect number of params, must be key/value';
50
51       my %extra = @_;
52       my $ua = $self->{ua};
53
54       foreach my $key (keys %extra)
55       {
56           # The implementation in HTTP::Tiny uses + instead of %20, fix that
57           $extra{$key} = $ua->_uri_escape($extra{$key});
58           $extra{$key} =~ s/\+/%20/g;
59       }
60
61       my $params = join '&', map { "$_=" . $extra{$_} } sort keys %extra;
62
63       return $params;
64   }
65
66
67   # /source/{author}/{release}/{path}
68   sub source {
69       my $self  = shift;
70       my %opts  = @_ ? @_ : ();
71       my $url   = '';
72       my $error = "Provide 'author' and 'release' and 'path'";
73
74       %opts or croak $error;
75
76       if (
77           defined ( my $author  = $opts{'author'}  ) &&
78           defined ( my $release = $opts{'release'} ) &&
79           defined ( my $path    = $opts{'path'}    )
80         ) {
81           $url = "source/$author/$release/$path";
82       } else {
83           croak $error;
84       }
85
86       $url = $self->{base_url} . "/$url";
87
88       my $result = $self->{ua}->get($url);
89       $result->{'success'}
90           or croak "Failed to fetch '$url': " . $result->{'reason'};
91
92       return $result->{'content'};
93   }
94
95
96   # /release/{distribution}
97   # /release/{author}/{release}
98   sub release {
99       my $self  = shift;
100       my %opts  = @_ ? @_ : ();
101       my $url   = '';
102       my $error = "Either provide 'distribution', or 'author' and 'release', " .
103                   "or 'search'";
104
105       %opts or croak $error;
106
107       my %extra_opts = ();
108
109       if ( defined ( my $dist = $opts{'distribution'} ) ) {
110           $url = "release/$dist";
111       } elsif (
112           defined ( my $author  = $opts{'author'}  ) &&
113           defined ( my $release = $opts{'release'} )
114         ) {
115           $url = "release/$author/$release";
116       } elsif ( defined ( my $search_opts = $opts{'search'} ) ) {
117           ref $search_opts && ref $search_opts eq 'HASH'
118               or croak $error;
119
120           %extra_opts = %{$search_opts};
121           $url        = 'release/_search';
122       } else {
123           croak $error;
124       }
125
126       return $self->fetch( $url, %extra_opts );
127   }
128
129
130   # /pod/{module}
131   # /pod/{author}/{release}/{path}
132   sub pod {
133       my $self  = shift;
134       my %opts  = @_ ? @_ : ();
135       my $url   = '';
136       my $error = "Either provide 'module' or 'author and 'release' and 'path'";
137
138       %opts or croak $error;
139
140       if ( defined ( my $module = $opts{'module'} ) ) {
141           $url = "pod/$module";
142       } elsif (
143           defined ( my $author  = $opts{'author'}  ) &&
144           defined ( my $release = $opts{'release'} ) &&
145           defined ( my $path    = $opts{'path'}    )
146         ) {
147           $url = "pod/$author/$release/$path";
148       } else {
149           croak $error;
150       }
151
152       # check content-type
153       my %extra = ();
154       if ( defined ( my $type = $opts{'content-type'} ) ) {
155           $type =~ m{^ text/ (?: html|plain|x-pod|x-markdown ) $}x
156               or croak 'Incorrect content-type provided';
157
158           $extra{headers}{'content-type'} = $type;
159       }
160
161       $url = $self->{base_url}. "/$url";
162
163       my $result = $self->{ua}->get( $url, \%extra );
164       $result->{'success'}
165           or croak "Failed to fetch '$url': " . $result->{'reason'};
166
167       return $result->{'content'};
168   }
169
170
171   # /module/{module}
172   sub module {
173       my $self = shift;
174       my $name = shift;
175
176       $name or croak 'Please provide a module name';
177
178       return $self->fetch("module/$name");
179   }
180
181
182   # file() is a synonym of module
183   sub file { goto &module }
184
185
186   # /author/{author}
187   sub author {
188       my $self = shift;
189       my ( $pause_id, $url, %extra_opts );
190
191       if ( @_ == 1 ) {
192           $url = 'author/' . shift;
193       } elsif ( @_ == 2 ) {
194           my %opts = @_;
195
196           if ( defined $opts{'pauseid'} ) {
197               $url = "author/" . $opts{'pauseid'};
198           } elsif ( defined $opts{'search'} ) {
199               my $search_opts = $opts{'search'};
200
201               ref $search_opts && ref $search_opts eq 'HASH'
202                   or croak "'search' key must be hashref";
203
204               %extra_opts = %{$search_opts};
205               $url        = 'author/_search';
206           } else {
207               croak 'Unknown option given';
208           }
209       } else {
210           croak 'Please provide an author PAUSEID or a "search"';
211       }
212
213       return $self->fetch( $url, %extra_opts );
214   }
215
216
217
218   sub fetch {
219       my $self    = shift;
220       my $url     = shift;
221       my $extra   = $self->_build_extra_params(@_);
222       my $base    = $self->{base_url};
223       my $req_url = $extra ? "$base/$url?$extra" : "$base/$url";
224
225       my $result  = $self->{ua}->get($req_url);
226       return $self->_decode_result( $result, $req_url );
227   }
228
229
230   sub post {
231       my $self  = shift;
232       my $url   = shift;
233       my $query = shift;
234       my $base  = $self->{base_url};
235
236       defined $url
237           or croak 'First argument of URL must be provided';
238
239       ref $query and ref $query eq 'HASH'
240           or croak 'Second argument of query hashref must be provided';
241
242       my $query_json = encode_json( $query );
243       my $result     = $self->{ua}->request(
244           'POST',
245           "$base/$url",
246           {
247               headers => { 'Content-Type' => 'application/json' },
248               content => $query_json,
249           }
250       );
251
252       return $self->_decode_result( $result, $url, $query_json );
253   }
254
255   sub _decode_result {
256       my $self = shift;
257       my ( $result, $url, $original ) = @_;
258       my $decoded_result;
259
260       ref $result and ref $result eq 'HASH'
261           or croak 'First argument must be hashref';
262
263       defined $url
264           or croak 'Second argument of a URL must be provided';
265
266       if ( defined ( my $success = $result->{'success'} ) ) {
267           my $reason = $result->{'reason'} || '';
268           $reason .= ( defined $original ? " (request: $original)" : '' );
269
270           $success or croak "Failed to fetch '$url': $reason";
271       } else {
272           croak 'Missing success in return value';
273       }
274
275       defined ( my $content = $result->{'content'} )
276           or croak 'Missing content in return value';
277
278       eval { $decoded_result = decode_json $content; 1 }
279       or do { croak "Couldn't decode '$content': $@" };
280
281       return $decoded_result;
282   }
283
284   1;
285
286   __END__
287
288   =pod
289
290   =head1 NAME
291
292   MetaCPAN::API::Tiny - A Tiny API client for MetaCPAN
293
294   =head1 VERSION
295
296   version 1.131730
297
298   =head1 DESCRIPTION
299
300   This is the Tiny version of L<MetaCPAN::API>. It implements a compatible API
301   with a few notable exceptions:
302
303   =over 4
304
305   =item Attributes are direct hash access
306
307   The attributes defined using Mo(o|u)se are now accessed via the blessed hash
308   directly. There are no accessors defined to access this elements.
309
310   =item Exception handling
311
312   Instead of using Try::Tiny, raw evals are used. This could potentially cause
313   issues, so just be aware.
314
315   =item Testing
316
317   Test::Fatal was replaced with an eval implementation of exception().
318   Test::TinyMocker usage is retained, but may be absorbed since it is pure perl
319
320   =back
321
322   =head1 CLASS_METHODS
323
324   =head2 new
325
326   new is the constructor for MetaCPAN::API::Tiny. In the non-tiny version of this
327   module, this is provided via Any::Moose built from the attributes defined. In
328   the tiny version, we define our own constructor. It takes the same arguments
329   and provides similar checks to MetaCPAN::API with regards to arguments passed.
330
331   =head1 PUBLIC_METHODS
332
333   =head2 source
334
335       my $source = $mcpan->source(
336           author  => 'DOY',
337           release => 'Moose-2.0201',
338           path    => 'lib/Moose.pm',
339       );
340
341   Searches MetaCPAN for a module or a specific release and returns the plain source.
342
343   =head2 release
344
345       my $result = $mcpan->release( distribution => 'Moose' );
346
347       # or
348       my $result = $mcpan->release( author => 'DOY', release => 'Moose-2.0001' );
349
350   Searches MetaCPAN for a dist.
351
352   You can do complex searches using 'search' parameter:
353
354       # example lifted from MetaCPAN docs
355       my $result = $mcpan->release(
356           search => {
357               author => "OALDERS AND ",
358               filter => "status:latest",
359               fields => "name",
360               size   => 1,
361           },
362       );
363
364   =head2 pod
365
366       my $result = $mcpan->pod( module => 'Moose' );
367
368       # or
369       my $result = $mcpan->pod(
370           author  => 'DOY',
371           release => 'Moose-2.0201',
372           path    => 'lib/Moose.pm',
373       );
374
375   Searches MetaCPAN for a module or a specific release and returns the POD.
376
377   =head2 module
378
379       my $result = $mcpan->module('MetaCPAN::API');
380
381   Searches MetaCPAN and returns a module's ".pm" file.
382
383   =head2 file
384
385   A synonym of L</module>
386
387   =head2 author
388
389       my $result1 = $mcpan->author('XSAWYERX');
390       my $result2 = $mcpan->author( pauseid => 'XSAWYERX' );
391
392   Searches MetaCPAN for a specific author.
393
394   You can do complex searches using 'search' parameter:
395
396       # example lifted from MetaCPAN docs
397       my $result = $mcpan->author(
398           search => {
399               q    => 'profile.name:twitter',
400               size => 1,
401           },
402       );
403
404   =head2 fetch
405
406       my $result = $mcpan->fetch('/release/distribution/Moose');
407
408       # with parameters
409       my $more = $mcpan->fetch(
410           '/release/distribution/Moose',
411           param => 'value',
412       );
413
414   This is a helper method for API implementations. It fetches a path from MetaCPAN, decodes the JSON from the content variable and returns it.
415
416   You don't really need to use it, but you can in case you want to write your own extension implementation to MetaCPAN::API.
417
418   It accepts an additional hash as "GET" parameters.
419
420   =head2 post
421
422       # /release&content={"query":{"match_all":{}},"filter":{"prefix":{"archive":"Cache-Cache-1.06"}}}
423       my $result = $mcpan->post(
424           'release',
425           {
426               query  => { match_all => {} },
427               filter => { prefix => { archive => 'Cache-Cache-1.06' } },
428           },
429       );
430
431   The POST equivalent of the "fetch()" method. It gets the path and JSON request.
432
433   =head1 THANKS
434
435   Overall the tests and code were ripped directly from MetaCPAN::API and
436   tiny-fied. A big thanks to Sawyer X for writing the original module.
437
438   =head1 AUTHOR
439
440   Nicholas R. Perez <nperez@cpan.org>
441
442   =head1 COPYRIGHT AND LICENSE
443
444   This software is copyright (c) 2013 by Nicholas R. Perez <nperez@cpan.org>.
445
446   This is free software; you can redistribute it and/or modify it under
447   the same terms as the Perl 5 programming language system itself.
448
449   =cut
450 METACPAN_API_TINY
451
452 s/^  //mg for values %fatpacked;
453
454 unshift @INC, sub {
455   if (my $fat = $fatpacked{$_[1]}) {
456     if ($] < 5.008) {
457       return sub {
458         return 0 unless length $fat;
459         $fat =~ s/^([^\n]*\n?)//;
460         $_ = $1;
461         return 1;
462       };
463     }
464     open my $fh, '<', \$fat
465       or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
466     return $fh;
467   }
468   return
469 };
470
471 } # END OF FATPACK CODE
472
473
474 use 5.010;
475 use strict;
476 use warnings;
477 use Fatal qw(open close);
478
479 use Getopt::Long;
480 use Pod::Usage;
481 use File::Basename;
482 use Module::CoreList;
483 use HTTP::Tiny;
484 use Safe;
485 use MetaCPAN::API::Tiny;
486
487 # Below, 5.024 should be aligned with the version of perl actually
488 # bundled in Buildroot:
489 die <<"MSG" if $] < 5.024;
490 This script needs a host perl with the same major version as Buildroot target perl.
491
492 Your current host perl is:
493     $^X
494     version $]
495
496 You may install a local one by running:
497     perlbrew install perl-5.24.0
498 MSG
499
500 my ($help, $man, $quiet, $force, $recommend, $test, $host);
501 my $target = 1;
502 GetOptions( 'help|?' => \$help,
503             'man' => \$man,
504             'quiet|q' => \$quiet,
505             'force|f' => \$force,
506             'host!' => \$host,
507             'target!' => \$target,
508             'recommend' => \$recommend,
509             'test' => \$test
510 ) or pod2usage(-exitval => 1);
511 pod2usage(-exitval => 0) if $help;
512 pod2usage(-exitval => 0, -verbose => 2) if $man;
513 pod2usage(-exitval => 1) if scalar @ARGV == 0;
514
515 my %dist;               # name -> metacpan data
516 my %need_target;        # name -> 1 if target package is needed
517 my %need_host;          # name -> 1 if host package is needed
518 my %need_dlopen;        # name -> 1 if requires dynamic library
519 my %deps_build;         # name -> list of host dependencies
520 my %deps_runtime;       # name -> list of target dependencies
521 my %deps_optional;      # name -> list of optional target dependencies
522 my %license_files;      # name -> list of license files
523 my %checksum;           # author -> list of checksum
524 my $mirror = 'http://cpan.metacpan.org';        # a CPAN mirror
525 my $mcpan = MetaCPAN::API::Tiny->new(base_url => 'http://fastapi.metacpan.org/v1');
526 my $ua = HTTP::Tiny->new();
527
528 sub get_checksum {
529     my ($url) = @_;
530     my($path) = $url =~ m|^[^:/?#]+://[^/?#]*([^?#]*)|;
531     my($basename, $dirname) = fileparse( $path );
532     unless ($checksum{$dirname}) {
533         my $url = $mirror . $dirname . q{CHECKSUMS};
534         my $response = $ua->get($url);
535         $checksum{$dirname} = $response->{content};
536     }
537     my $chksum = Safe->new->reval($checksum{$dirname});
538     return $chksum->{$basename}, $basename;
539 }
540
541 sub is_xs {
542     my ($manifest) = @_;
543     # This heuristic determines if a module is a native extension, by searching
544     # some file extension types in the MANIFEST of the distribution.
545     # It was inspired by http://deps.cpantesters.org/static/purity.html
546     return $manifest =~ m/\.(swg|xs|c|h|i)[\n\s]/;
547 }
548
549 sub find_license_files {
550     my ($manifest) = @_;
551     my @license_files;
552     foreach (split /\n/, $manifest) {
553         next if m|/|;
554         push @license_files, $_ if m/(ARTISTIC|COPYING|COPYRIGHT|LICENSE)/i;
555     }
556     if (scalar @license_files == 0 && $manifest =~ m/(README)[\n\s]/i) {
557         @license_files = ($1);
558     }
559     return \@license_files;
560 }
561
562 sub fetch {
563     my ($name, $need_target, $need_host, $top) = @_;
564     $need_target{$name} = $need_target if $need_target;
565     $need_host{$name} = $need_host if $need_host;
566     unless ($dist{$name} && !$top) {
567         say qq{fetch ${name}} unless $quiet;
568         my $result = $mcpan->release( distribution => $name );
569         $dist{$name} = $result;
570         my $manifest = $mcpan->source( author => $result->{author},
571                                        release => $name . q{-} . $result->{version},
572                                        path => 'MANIFEST' );
573         $need_dlopen{$name} = is_xs( $manifest );
574         $license_files{$name} = find_license_files( $manifest );
575         my %build = ();
576         my %runtime = ();
577         my %optional = ();
578         foreach my $dep (@{$result->{dependency}}) {
579             my $modname = ${$dep}{module};
580             next if $modname eq q{perl};
581             next if $modname =~ m|^Alien|;
582             next if $modname =~ m|^Win32|;
583             next if !($test && $top) && $modname =~ m|^Test|;
584             next if Module::CoreList::is_core( $modname, undef, $] );
585             # we could use the host Module::CoreList data, because host perl and
586             # target perl have the same major version
587             next if ${$dep}{phase} eq q{develop};
588             next if !($test && $top) && ${$dep}{phase} eq q{test};
589             my $distname = $mcpan->module( $modname )->{distribution};
590             if (${$dep}{phase} eq q{runtime}) {
591                 if (${$dep}{relationship} eq q{requires}) {
592                     $runtime{$distname} = 1;
593                 }
594                 else {
595                     $optional{$distname} = 1 if $recommend && $top;
596                 }
597             }
598             else { # configure, build
599                 $build{$distname} = 1;
600             }
601         }
602         $deps_build{$name} = [keys %build];
603         $deps_runtime{$name} = [keys %runtime];
604         $deps_optional{$name} = [keys %optional];
605         foreach my $distname (@{$deps_build{$name}}) {
606             fetch( $distname, 0, 1 );
607         }
608         foreach my $distname (@{$deps_runtime{$name}}) {
609             fetch( $distname, $need_target, $need_host );
610             $need_dlopen{$name} ||= $need_dlopen{$distname};
611         }
612         foreach my $distname (@{$deps_optional{$name}}) {
613             fetch( $distname, $need_target, $need_host );
614         }
615     }
616     return;
617 }
618
619 foreach my $distname (@ARGV) {
620     # Command-line's distributions
621     fetch( $distname, !!$target, !!$host, 1 );
622 }
623 say scalar keys %dist, q{ packages fetched.} unless $quiet;
624
625 # Buildroot package name: lowercase
626 sub fsname {
627     my $name = shift;
628     $name =~ s|_|-|g;
629     return q{perl-} . lc $name;
630 }
631
632 # Buildroot variable name: uppercase
633 sub brname {
634     my $name = shift;
635     $name =~ s|-|_|g;
636     return uc $name;
637 }
638
639 while (my ($distname, $dist) = each %dist) {
640     my $fsname = fsname( $distname );
641     my $dirname = q{package/} . $fsname;
642     my $cfgname = $dirname . q{/Config.in};
643     my $mkname = $dirname . q{/} . $fsname . q{.mk};
644     my $hashname = $dirname . q{/} . $fsname . q{.hash};
645     my $brname = brname( $fsname );
646     mkdir $dirname unless -d $dirname;
647     if ($need_target{$distname} && ($force || !-f $cfgname)) {
648         my $abstract = $dist->{abstract};
649         my $homepage = $dist->{resources}->{homepage} || qq{https://metacpan.org/release/${distname}};
650         say qq{write ${cfgname}} unless $quiet;
651         open my $fh, q{>}, $cfgname;
652         say {$fh} qq{config BR2_PACKAGE_${brname}};
653         say {$fh} qq{\tbool "${fsname}"};
654         say {$fh} qq{\tdepends on !BR2_STATIC_LIBS} if $need_dlopen{$distname};
655         foreach my $dep (sort @{$deps_runtime{$distname}}) {
656             my $brdep = brname( fsname( $dep ) );
657             say {$fh} qq{\tselect BR2_PACKAGE_${brdep}};
658         }
659         say {$fh} qq{\thelp};
660         say {$fh} qq{\t  ${abstract}\n} if $abstract;
661         say {$fh} qq{\t  ${homepage}};
662         if ($need_dlopen{$distname}) {
663             say {$fh} qq{\ncomment "${fsname} needs a toolchain w/ dynamic library"};
664             say {$fh} qq{\tdepends on BR2_STATIC_LIBS};
665         }
666         close $fh;
667     }
668     if ($force || !-f $mkname) {
669         my $version = $dist->{version};
670         my($path) = $dist->{download_url} =~ m|^[^:/?#]+://[^/?#]*([^?#]*)|;
671         # this URL contains only the scheme, auth and path parts (but no query and fragment parts)
672         # the scheme is not used, because the job is done by the BR download infrastructure
673         # the auth part is not used, because we use $(BR2_CPAN_MIRROR)
674         my($filename, $directories, $suffix) = fileparse( $path, q{tar.gz}, q{tgz} );
675         $directories =~ s|/$||;
676         my $dependencies = join q{ }, map( { q{host-} . fsname( $_ ); } sort @{$deps_build{$distname}} ),
677                                       map( { fsname( $_ ); } sort @{$deps_runtime{$distname}} );
678         my $host_dependencies = join q{ }, map { q{host-} . fsname( $_ ); } sort( @{$deps_build{$distname}},
679                                                                                   @{$deps_runtime{$distname}} );
680         my $license = ref $dist->{license} eq 'ARRAY'
681                     ? join q{ or }, @{$dist->{license}}
682                     : $dist->{license};
683         # BR requires license name as in http://spdx.org/licenses/
684         $license =~ s|apache_2_0|Apache-2.0|;
685         $license =~ s|artistic_2|Artistic-2.0|;
686         $license =~ s|mit|MIT|;
687         $license =~ s|openssl|OpenSSL|;
688         $license =~ s|perl_5|Artistic or GPL-1.0+|;
689         my $license_files = join q{ }, @{$license_files{$distname}};
690         say qq{write ${mkname}} unless $quiet;
691         open my $fh, q{>}, $mkname;
692         say {$fh} qq{################################################################################};
693         say {$fh} qq{#};
694         say {$fh} qq{# ${fsname}};
695         say {$fh} qq{#};
696         say {$fh} qq{################################################################################};
697         say {$fh} qq{};
698         say {$fh} qq{${brname}_VERSION = ${version}};
699         say {$fh} qq{${brname}_SOURCE = ${distname}-\$(${brname}_VERSION).${suffix}};
700         say {$fh} qq{${brname}_SITE = \$(BR2_CPAN_MIRROR)${directories}};
701         say {$fh} qq{${brname}_DEPENDENCIES = ${dependencies}} if $need_target{$distname} && $dependencies;
702         say {$fh} qq{HOST_${brname}_DEPENDENCIES = ${host_dependencies}} if $need_host{$distname} && $host_dependencies;
703         say {$fh} qq{${brname}_LICENSE = ${license}} if $license && $license ne q{unknown};
704         say {$fh} qq{${brname}_LICENSE_FILES = ${license_files}} if $license_files;
705         say {$fh} qq{};
706         foreach (sort @{$deps_optional{$distname}}) {
707             next if grep { $_ eq $distname; } @{$deps_runtime{$_}};     # avoid cyclic dependencies
708             my $opt_brname = brname( $_ );
709             my $opt_fsname = fsname( $_ );
710             say {$fh} qq{ifeq (\$(BR2_PACKAGE_PERL_${opt_brname}),y)};
711             say {$fh} qq{${brname}_DEPENDENCIES += ${opt_fsname}};
712             say {$fh} qq{endif};
713             say {$fh} qq{};
714         }
715         say {$fh} qq{\$(eval \$(perl-package))} if $need_target{$distname};
716         say {$fh} qq{\$(eval \$(host-perl-package))} if $need_host{$distname};
717         close $fh;
718     }
719     if ($force || !-f $hashname) {
720         my($checksum, $filename) = get_checksum($dist->{download_url});
721         my $md5 = $checksum->{md5};
722         my $sha256 = $checksum->{sha256};
723         say qq{write ${hashname}} unless $quiet;
724         open my $fh, q{>}, $hashname;
725         say {$fh} qq{# retrieved by scancpan from ${mirror}/};
726         say {$fh} qq{md5    ${md5} ${filename}};
727         say {$fh} qq{sha256 ${sha256} ${filename}};
728         close $fh;
729     }
730 }
731
732 my %pkg;
733 my $cfgname = q{package/Config.in};
734 if (-f $cfgname) {
735     open my $fh, q{<}, $cfgname;
736     while (<$fh>) {
737         chomp;
738         $pkg{$_} = 1 if m|package/perl-|;
739     }
740     close $fh;
741 }
742
743 foreach my $distname (keys %need_target) {
744     my $fsname = fsname( $distname );
745     $pkg{qq{\tsource "package/${fsname}/Config.in"}} = 1;
746 }
747
748 say qq{${cfgname} must contain the following lines:};
749 say join qq{\n}, sort keys %pkg;
750
751 __END__
752
753 =head1 NAME
754
755 utils/scancpan Try-Tiny Moo
756
757 =head1 SYNOPSIS
758
759 supports/scripts/scancpan [options] [distname ...]
760
761  Options:
762    -help
763    -man
764    -quiet
765    -force
766    -target/-notarget
767    -host/-nohost
768    -recommend
769    -test
770
771 =head1 OPTIONS
772
773 =over 8
774
775 =item B<-help>
776
777 Prints a brief help message and exits.
778
779 =item B<-man>
780
781 Prints the manual page and exits.
782
783 =item B<-quiet>
784
785 Executes without output
786
787 =item B<-force>
788
789 Forces the overwriting of existing files.
790
791 =item B<-target/-notarget>
792
793 Switches package generation for the target variant (the default is C<-target>).
794
795 =item B<-host/-nohost>
796
797 Switches package generation for the host variant (the default is C<-nohost>).
798
799 =item B<-recommend>
800
801 Adds I<recommended> dependencies.
802
803 =item B<-test>
804
805 Adds dependencies for test.
806
807 =back
808
809 =head1 DESCRIPTION
810
811 This script creates templates of the Buildroot package files for all the
812 Perl/CPAN distributions required by the specified distnames. The
813 dependencies and metadata are fetched from https://metacpan.org/.
814
815 After running this script, it is necessary to check the generated files.
816 You have to manually add the license files (PERL_FOO_LICENSE_FILES variable).
817 For distributions that link against a target library, you have to add the
818 buildroot package name for that library to the DEPENDENCIES variable.
819
820 See the Buildroot documentation for details on the usage of the Perl
821 infrastructure.
822
823 The major version of the host perl must be aligned on the target one,
824 in order to work with the right CoreList data.
825
826 =head1 LICENSE
827
828 Copyright (C) 2013-2016 by Francois Perrad <francois.perrad@gadz.org>
829
830 This program is free software; you can redistribute it and/or modify
831 it under the terms of the GNU General Public License as published by
832 the Free Software Foundation; either version 2 of the License, or
833 (at your option) any later version.
834
835 This program is distributed in the hope that it will be useful,
836 but WITHOUT ANY WARRANTY; without even the implied warranty of
837 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
838 General Public License for more details.
839
840 You should have received a copy of the GNU General Public License
841 along with this program; if not, write to the Free Software
842 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
843
844 This script is a part of Buildroot.
845
846 This script requires the module C<MetaCPAN::API::Tiny> (version 1.131730)
847 which was included at the beginning of this file by the tool C<fatpack>.
848
849 See L<http://search.cpan.org/~nperez/MetaCPAN-API-Tiny-1.131730/>.
850
851 See L<http://search.cpan.org/search?query=App-FatPacker&mode=dist>.
852
853 These both libraries are free software and may be distributed under the same
854 terms as perl itself.
855
856 And perl may be distributed under the terms of Artistic v1 or GPL v1 license.
857
858 =cut