From: Yann E. MORIN Date: Sat, 1 Jul 2017 14:31:05 +0000 (+0200) Subject: tools: move scancpan out of support/scripts/ X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=bb5a6878c1802c793e6262f3fc6f7d38cd0df25d;p=buildroot.git tools: move scancpan out of support/scripts/ Move it to the top-level tools/ directory, so that it is easier to find for users. Signed-off-by: "Yann E. MORIN" Cc: "François Perrad" Cc: Thomas Petazzoni Cc: Arnout Vandecappelle Signed-off-by: Thomas Petazzoni --- diff --git a/support/scripts/scancpan b/support/scripts/scancpan deleted file mode 100755 index 62df225361..0000000000 --- a/support/scripts/scancpan +++ /dev/null @@ -1,858 +0,0 @@ -#!/usr/bin/env perl - -# This chunk of stuff was generated by App::FatPacker. To find the original -# file's code, look for the end of this BEGIN block or the string 'FATPACK' -BEGIN { -my %fatpacked; - -$fatpacked{"MetaCPAN/API/Tiny.pm"} = <<'METACPAN_API_TINY'; - package MetaCPAN::API::Tiny; - { - $MetaCPAN::API::Tiny::VERSION = '1.131730'; - } - use strict; - use warnings; - # ABSTRACT: A Tiny API client for MetaCPAN - - use Carp; - use JSON::PP 'encode_json', 'decode_json'; - use HTTP::Tiny; - - - sub new { - my ($class, @args) = @_; - - $#_ % 2 == 0 - or croak 'Arguments must be provided as name/value pairs'; - - my %params = @args; - - die 'ua_args must be an array reference' - if $params{ua_args} && ref($params{ua_args}) ne 'ARRAY'; - - my $self = +{ - base_url => $params{base_url} || 'http://api.metacpan.org/v0', - ua => $params{ua} || HTTP::Tiny->new( - $params{ua_args} - ? @{$params{ua_args}} - : (agent => 'MetaCPAN::API::Tiny/' - . ($MetaCPAN::API::VERSION || 'xx'))), - }; - - return bless($self, $class); - } - - sub _build_extra_params { - my $self = shift; - - @_ % 2 == 0 - or croak 'Incorrect number of params, must be key/value'; - - my %extra = @_; - my $ua = $self->{ua}; - - foreach my $key (keys %extra) - { - # The implementation in HTTP::Tiny uses + instead of %20, fix that - $extra{$key} = $ua->_uri_escape($extra{$key}); - $extra{$key} =~ s/\+/%20/g; - } - - my $params = join '&', map { "$_=" . $extra{$_} } sort keys %extra; - - return $params; - } - - - # /source/{author}/{release}/{path} - sub source { - my $self = shift; - my %opts = @_ ? @_ : (); - my $url = ''; - my $error = "Provide 'author' and 'release' and 'path'"; - - %opts or croak $error; - - if ( - defined ( my $author = $opts{'author'} ) && - defined ( my $release = $opts{'release'} ) && - defined ( my $path = $opts{'path'} ) - ) { - $url = "source/$author/$release/$path"; - } else { - croak $error; - } - - $url = $self->{base_url} . "/$url"; - - my $result = $self->{ua}->get($url); - $result->{'success'} - or croak "Failed to fetch '$url': " . $result->{'reason'}; - - return $result->{'content'}; - } - - - # /release/{distribution} - # /release/{author}/{release} - sub release { - my $self = shift; - my %opts = @_ ? @_ : (); - my $url = ''; - my $error = "Either provide 'distribution', or 'author' and 'release', " . - "or 'search'"; - - %opts or croak $error; - - my %extra_opts = (); - - if ( defined ( my $dist = $opts{'distribution'} ) ) { - $url = "release/$dist"; - } elsif ( - defined ( my $author = $opts{'author'} ) && - defined ( my $release = $opts{'release'} ) - ) { - $url = "release/$author/$release"; - } elsif ( defined ( my $search_opts = $opts{'search'} ) ) { - ref $search_opts && ref $search_opts eq 'HASH' - or croak $error; - - %extra_opts = %{$search_opts}; - $url = 'release/_search'; - } else { - croak $error; - } - - return $self->fetch( $url, %extra_opts ); - } - - - # /pod/{module} - # /pod/{author}/{release}/{path} - sub pod { - my $self = shift; - my %opts = @_ ? @_ : (); - my $url = ''; - my $error = "Either provide 'module' or 'author and 'release' and 'path'"; - - %opts or croak $error; - - if ( defined ( my $module = $opts{'module'} ) ) { - $url = "pod/$module"; - } elsif ( - defined ( my $author = $opts{'author'} ) && - defined ( my $release = $opts{'release'} ) && - defined ( my $path = $opts{'path'} ) - ) { - $url = "pod/$author/$release/$path"; - } else { - croak $error; - } - - # check content-type - my %extra = (); - if ( defined ( my $type = $opts{'content-type'} ) ) { - $type =~ m{^ text/ (?: html|plain|x-pod|x-markdown ) $}x - or croak 'Incorrect content-type provided'; - - $extra{headers}{'content-type'} = $type; - } - - $url = $self->{base_url}. "/$url"; - - my $result = $self->{ua}->get( $url, \%extra ); - $result->{'success'} - or croak "Failed to fetch '$url': " . $result->{'reason'}; - - return $result->{'content'}; - } - - - # /module/{module} - sub module { - my $self = shift; - my $name = shift; - - $name or croak 'Please provide a module name'; - - return $self->fetch("module/$name"); - } - - - # file() is a synonym of module - sub file { goto &module } - - - # /author/{author} - sub author { - my $self = shift; - my ( $pause_id, $url, %extra_opts ); - - if ( @_ == 1 ) { - $url = 'author/' . shift; - } elsif ( @_ == 2 ) { - my %opts = @_; - - if ( defined $opts{'pauseid'} ) { - $url = "author/" . $opts{'pauseid'}; - } elsif ( defined $opts{'search'} ) { - my $search_opts = $opts{'search'}; - - ref $search_opts && ref $search_opts eq 'HASH' - or croak "'search' key must be hashref"; - - %extra_opts = %{$search_opts}; - $url = 'author/_search'; - } else { - croak 'Unknown option given'; - } - } else { - croak 'Please provide an author PAUSEID or a "search"'; - } - - return $self->fetch( $url, %extra_opts ); - } - - - - sub fetch { - my $self = shift; - my $url = shift; - my $extra = $self->_build_extra_params(@_); - my $base = $self->{base_url}; - my $req_url = $extra ? "$base/$url?$extra" : "$base/$url"; - - my $result = $self->{ua}->get($req_url); - return $self->_decode_result( $result, $req_url ); - } - - - sub post { - my $self = shift; - my $url = shift; - my $query = shift; - my $base = $self->{base_url}; - - defined $url - or croak 'First argument of URL must be provided'; - - ref $query and ref $query eq 'HASH' - or croak 'Second argument of query hashref must be provided'; - - my $query_json = encode_json( $query ); - my $result = $self->{ua}->request( - 'POST', - "$base/$url", - { - headers => { 'Content-Type' => 'application/json' }, - content => $query_json, - } - ); - - return $self->_decode_result( $result, $url, $query_json ); - } - - sub _decode_result { - my $self = shift; - my ( $result, $url, $original ) = @_; - my $decoded_result; - - ref $result and ref $result eq 'HASH' - or croak 'First argument must be hashref'; - - defined $url - or croak 'Second argument of a URL must be provided'; - - if ( defined ( my $success = $result->{'success'} ) ) { - my $reason = $result->{'reason'} || ''; - $reason .= ( defined $original ? " (request: $original)" : '' ); - - $success or croak "Failed to fetch '$url': $reason"; - } else { - croak 'Missing success in return value'; - } - - defined ( my $content = $result->{'content'} ) - or croak 'Missing content in return value'; - - eval { $decoded_result = decode_json $content; 1 } - or do { croak "Couldn't decode '$content': $@" }; - - return $decoded_result; - } - - 1; - - __END__ - - =pod - - =head1 NAME - - MetaCPAN::API::Tiny - A Tiny API client for MetaCPAN - - =head1 VERSION - - version 1.131730 - - =head1 DESCRIPTION - - This is the Tiny version of L. It implements a compatible API - with a few notable exceptions: - - =over 4 - - =item Attributes are direct hash access - - The attributes defined using Mo(o|u)se are now accessed via the blessed hash - directly. There are no accessors defined to access this elements. - - =item Exception handling - - Instead of using Try::Tiny, raw evals are used. This could potentially cause - issues, so just be aware. - - =item Testing - - Test::Fatal was replaced with an eval implementation of exception(). - Test::TinyMocker usage is retained, but may be absorbed since it is pure perl - - =back - - =head1 CLASS_METHODS - - =head2 new - - new is the constructor for MetaCPAN::API::Tiny. In the non-tiny version of this - module, this is provided via Any::Moose built from the attributes defined. In - the tiny version, we define our own constructor. It takes the same arguments - and provides similar checks to MetaCPAN::API with regards to arguments passed. - - =head1 PUBLIC_METHODS - - =head2 source - - my $source = $mcpan->source( - author => 'DOY', - release => 'Moose-2.0201', - path => 'lib/Moose.pm', - ); - - Searches MetaCPAN for a module or a specific release and returns the plain source. - - =head2 release - - my $result = $mcpan->release( distribution => 'Moose' ); - - # or - my $result = $mcpan->release( author => 'DOY', release => 'Moose-2.0001' ); - - Searches MetaCPAN for a dist. - - You can do complex searches using 'search' parameter: - - # example lifted from MetaCPAN docs - my $result = $mcpan->release( - search => { - author => "OALDERS AND ", - filter => "status:latest", - fields => "name", - size => 1, - }, - ); - - =head2 pod - - my $result = $mcpan->pod( module => 'Moose' ); - - # or - my $result = $mcpan->pod( - author => 'DOY', - release => 'Moose-2.0201', - path => 'lib/Moose.pm', - ); - - Searches MetaCPAN for a module or a specific release and returns the POD. - - =head2 module - - my $result = $mcpan->module('MetaCPAN::API'); - - Searches MetaCPAN and returns a module's ".pm" file. - - =head2 file - - A synonym of L - - =head2 author - - my $result1 = $mcpan->author('XSAWYERX'); - my $result2 = $mcpan->author( pauseid => 'XSAWYERX' ); - - Searches MetaCPAN for a specific author. - - You can do complex searches using 'search' parameter: - - # example lifted from MetaCPAN docs - my $result = $mcpan->author( - search => { - q => 'profile.name:twitter', - size => 1, - }, - ); - - =head2 fetch - - my $result = $mcpan->fetch('/release/distribution/Moose'); - - # with parameters - my $more = $mcpan->fetch( - '/release/distribution/Moose', - param => 'value', - ); - - This is a helper method for API implementations. It fetches a path from MetaCPAN, decodes the JSON from the content variable and returns it. - - You don't really need to use it, but you can in case you want to write your own extension implementation to MetaCPAN::API. - - It accepts an additional hash as "GET" parameters. - - =head2 post - - # /release&content={"query":{"match_all":{}},"filter":{"prefix":{"archive":"Cache-Cache-1.06"}}} - my $result = $mcpan->post( - 'release', - { - query => { match_all => {} }, - filter => { prefix => { archive => 'Cache-Cache-1.06' } }, - }, - ); - - The POST equivalent of the "fetch()" method. It gets the path and JSON request. - - =head1 THANKS - - Overall the tests and code were ripped directly from MetaCPAN::API and - tiny-fied. A big thanks to Sawyer X for writing the original module. - - =head1 AUTHOR - - Nicholas R. Perez - - =head1 COPYRIGHT AND LICENSE - - This software is copyright (c) 2013 by Nicholas R. Perez . - - This is free software; you can redistribute it and/or modify it under - the same terms as the Perl 5 programming language system itself. - - =cut -METACPAN_API_TINY - -s/^ //mg for values %fatpacked; - -unshift @INC, sub { - if (my $fat = $fatpacked{$_[1]}) { - if ($] < 5.008) { - return sub { - return 0 unless length $fat; - $fat =~ s/^([^\n]*\n?)//; - $_ = $1; - return 1; - }; - } - open my $fh, '<', \$fat - or die "FatPacker error loading $_[1] (could be a perl installation issue?)"; - return $fh; - } - return -}; - -} # END OF FATPACK CODE - - -use 5.010; -use strict; -use warnings; -use Fatal qw(open close); - -use Getopt::Long; -use Pod::Usage; -use File::Basename; -use Module::CoreList; -use HTTP::Tiny; -use Safe; -use MetaCPAN::API::Tiny; - -# Below, 5.024 should be aligned with the version of perl actually -# bundled in Buildroot: -die <<"MSG" if $] < 5.024; -This script needs a host perl with the same major version as Buildroot target perl. - -Your current host perl is: - $^X - version $] - -You may install a local one by running: - perlbrew install perl-5.24.0 -MSG - -my ($help, $man, $quiet, $force, $recommend, $test, $host); -my $target = 1; -GetOptions( 'help|?' => \$help, - 'man' => \$man, - 'quiet|q' => \$quiet, - 'force|f' => \$force, - 'host!' => \$host, - 'target!' => \$target, - 'recommend' => \$recommend, - 'test' => \$test -) or pod2usage(-exitval => 1); -pod2usage(-exitval => 0) if $help; -pod2usage(-exitval => 0, -verbose => 2) if $man; -pod2usage(-exitval => 1) if scalar @ARGV == 0; - -my %dist; # name -> metacpan data -my %need_target; # name -> 1 if target package is needed -my %need_host; # name -> 1 if host package is needed -my %need_dlopen; # name -> 1 if requires dynamic library -my %deps_build; # name -> list of host dependencies -my %deps_runtime; # name -> list of target dependencies -my %deps_optional; # name -> list of optional target dependencies -my %license_files; # name -> list of license files -my %checksum; # author -> list of checksum -my $mirror = 'http://cpan.metacpan.org'; # a CPAN mirror -my $mcpan = MetaCPAN::API::Tiny->new(base_url => 'http://fastapi.metacpan.org/v1'); -my $ua = HTTP::Tiny->new(); - -sub get_checksum { - my ($url) = @_; - my($path) = $url =~ m|^[^:/?#]+://[^/?#]*([^?#]*)|; - my($basename, $dirname) = fileparse( $path ); - unless ($checksum{$dirname}) { - my $url = $mirror . $dirname . q{CHECKSUMS}; - my $response = $ua->get($url); - $checksum{$dirname} = $response->{content}; - } - my $chksum = Safe->new->reval($checksum{$dirname}); - return $chksum->{$basename}, $basename; -} - -sub is_xs { - my ($manifest) = @_; - # This heuristic determines if a module is a native extension, by searching - # some file extension types in the MANIFEST of the distribution. - # It was inspired by http://deps.cpantesters.org/static/purity.html - return $manifest =~ m/\.(swg|xs|c|h|i)[\n\s]/; -} - -sub find_license_files { - my ($manifest) = @_; - my @license_files; - foreach (split /\n/, $manifest) { - next if m|/|; - push @license_files, $_ if m/(ARTISTIC|COPYING|COPYRIGHT|LICENSE)/i; - } - if (scalar @license_files == 0 && $manifest =~ m/(README)[\n\s]/i) { - @license_files = ($1); - } - return \@license_files; -} - -sub fetch { - my ($name, $need_target, $need_host, $top) = @_; - $need_target{$name} = $need_target if $need_target; - $need_host{$name} = $need_host if $need_host; - unless ($dist{$name} && !$top) { - say qq{fetch ${name}} unless $quiet; - my $result = $mcpan->release( distribution => $name ); - $dist{$name} = $result; - my $manifest = $mcpan->source( author => $result->{author}, - release => $name . q{-} . $result->{version}, - path => 'MANIFEST' ); - $need_dlopen{$name} = is_xs( $manifest ); - $license_files{$name} = find_license_files( $manifest ); - my %build = (); - my %runtime = (); - my %optional = (); - foreach my $dep (@{$result->{dependency}}) { - my $modname = ${$dep}{module}; - next if $modname eq q{perl}; - next if $modname =~ m|^Alien|; - next if $modname =~ m|^Win32|; - next if !($test && $top) && $modname =~ m|^Test|; - next if Module::CoreList::is_core( $modname, undef, $] ); - # we could use the host Module::CoreList data, because host perl and - # target perl have the same major version - next if ${$dep}{phase} eq q{develop}; - next if !($test && $top) && ${$dep}{phase} eq q{test}; - my $distname = $mcpan->module( $modname )->{distribution}; - if (${$dep}{phase} eq q{runtime}) { - if (${$dep}{relationship} eq q{requires}) { - $runtime{$distname} = 1; - } - else { - $optional{$distname} = 1 if $recommend && $top; - } - } - else { # configure, build - $build{$distname} = 1; - } - } - $deps_build{$name} = [keys %build]; - $deps_runtime{$name} = [keys %runtime]; - $deps_optional{$name} = [keys %optional]; - foreach my $distname (@{$deps_build{$name}}) { - fetch( $distname, 0, 1 ); - } - foreach my $distname (@{$deps_runtime{$name}}) { - fetch( $distname, $need_target, $need_host ); - $need_dlopen{$name} ||= $need_dlopen{$distname}; - } - foreach my $distname (@{$deps_optional{$name}}) { - fetch( $distname, $need_target, $need_host ); - } - } - return; -} - -foreach my $distname (@ARGV) { - # Command-line's distributions - fetch( $distname, !!$target, !!$host, 1 ); -} -say scalar keys %dist, q{ packages fetched.} unless $quiet; - -# Buildroot package name: lowercase -sub fsname { - my $name = shift; - $name =~ s|_|-|g; - return q{perl-} . lc $name; -} - -# Buildroot variable name: uppercase -sub brname { - my $name = shift; - $name =~ s|-|_|g; - return uc $name; -} - -while (my ($distname, $dist) = each %dist) { - my $fsname = fsname( $distname ); - my $dirname = q{package/} . $fsname; - my $cfgname = $dirname . q{/Config.in}; - my $mkname = $dirname . q{/} . $fsname . q{.mk}; - my $hashname = $dirname . q{/} . $fsname . q{.hash}; - my $brname = brname( $fsname ); - mkdir $dirname unless -d $dirname; - if ($need_target{$distname} && ($force || !-f $cfgname)) { - my $abstract = $dist->{abstract}; - my $homepage = $dist->{resources}->{homepage} || qq{https://metacpan.org/release/${distname}}; - say qq{write ${cfgname}} unless $quiet; - open my $fh, q{>}, $cfgname; - say {$fh} qq{config BR2_PACKAGE_${brname}}; - say {$fh} qq{\tbool "${fsname}"}; - say {$fh} qq{\tdepends on !BR2_STATIC_LIBS} if $need_dlopen{$distname}; - foreach my $dep (sort @{$deps_runtime{$distname}}) { - my $brdep = brname( fsname( $dep ) ); - say {$fh} qq{\tselect BR2_PACKAGE_${brdep}}; - } - say {$fh} qq{\thelp}; - say {$fh} qq{\t ${abstract}\n} if $abstract; - say {$fh} qq{\t ${homepage}}; - if ($need_dlopen{$distname}) { - say {$fh} qq{\ncomment "${fsname} needs a toolchain w/ dynamic library"}; - say {$fh} qq{\tdepends on BR2_STATIC_LIBS}; - } - close $fh; - } - if ($force || !-f $mkname) { - my $version = $dist->{version}; - my($path) = $dist->{download_url} =~ m|^[^:/?#]+://[^/?#]*([^?#]*)|; - # this URL contains only the scheme, auth and path parts (but no query and fragment parts) - # the scheme is not used, because the job is done by the BR download infrastructure - # the auth part is not used, because we use $(BR2_CPAN_MIRROR) - my($filename, $directories, $suffix) = fileparse( $path, q{tar.gz}, q{tgz} ); - $directories =~ s|/$||; - my $dependencies = join q{ }, map( { q{host-} . fsname( $_ ); } sort @{$deps_build{$distname}} ), - map( { fsname( $_ ); } sort @{$deps_runtime{$distname}} ); - my $host_dependencies = join q{ }, map { q{host-} . fsname( $_ ); } sort( @{$deps_build{$distname}}, - @{$deps_runtime{$distname}} ); - my $license = ref $dist->{license} eq 'ARRAY' - ? join q{ or }, @{$dist->{license}} - : $dist->{license}; - # BR requires license name as in http://spdx.org/licenses/ - $license =~ s|apache_2_0|Apache-2.0|; - $license =~ s|artistic_2|Artistic-2.0|; - $license =~ s|mit|MIT|; - $license =~ s|openssl|OpenSSL|; - $license =~ s|perl_5|Artistic or GPL-1.0+|; - my $license_files = join q{ }, @{$license_files{$distname}}; - say qq{write ${mkname}} unless $quiet; - open my $fh, q{>}, $mkname; - say {$fh} qq{################################################################################}; - say {$fh} qq{#}; - say {$fh} qq{# ${fsname}}; - say {$fh} qq{#}; - say {$fh} qq{################################################################################}; - say {$fh} qq{}; - say {$fh} qq{${brname}_VERSION = ${version}}; - say {$fh} qq{${brname}_SOURCE = ${distname}-\$(${brname}_VERSION).${suffix}}; - say {$fh} qq{${brname}_SITE = \$(BR2_CPAN_MIRROR)${directories}}; - say {$fh} qq{${brname}_DEPENDENCIES = ${dependencies}} if $need_target{$distname} && $dependencies; - say {$fh} qq{HOST_${brname}_DEPENDENCIES = ${host_dependencies}} if $need_host{$distname} && $host_dependencies; - say {$fh} qq{${brname}_LICENSE = ${license}} if $license && $license ne q{unknown}; - say {$fh} qq{${brname}_LICENSE_FILES = ${license_files}} if $license_files; - say {$fh} qq{}; - foreach (sort @{$deps_optional{$distname}}) { - next if grep { $_ eq $distname; } @{$deps_runtime{$_}}; # avoid cyclic dependencies - my $opt_brname = brname( $_ ); - my $opt_fsname = fsname( $_ ); - say {$fh} qq{ifeq (\$(BR2_PACKAGE_PERL_${opt_brname}),y)}; - say {$fh} qq{${brname}_DEPENDENCIES += ${opt_fsname}}; - say {$fh} qq{endif}; - say {$fh} qq{}; - } - say {$fh} qq{\$(eval \$(perl-package))} if $need_target{$distname}; - say {$fh} qq{\$(eval \$(host-perl-package))} if $need_host{$distname}; - close $fh; - } - if ($force || !-f $hashname) { - my($checksum, $filename) = get_checksum($dist->{download_url}); - my $md5 = $checksum->{md5}; - my $sha256 = $checksum->{sha256}; - say qq{write ${hashname}} unless $quiet; - open my $fh, q{>}, $hashname; - say {$fh} qq{# retrieved by scancpan from ${mirror}/}; - say {$fh} qq{md5 ${md5} ${filename}}; - say {$fh} qq{sha256 ${sha256} ${filename}}; - close $fh; - } -} - -my %pkg; -my $cfgname = q{package/Config.in}; -if (-f $cfgname) { - open my $fh, q{<}, $cfgname; - while (<$fh>) { - chomp; - $pkg{$_} = 1 if m|package/perl-|; - } - close $fh; -} - -foreach my $distname (keys %need_target) { - my $fsname = fsname( $distname ); - $pkg{qq{\tsource "package/${fsname}/Config.in"}} = 1; -} - -say qq{${cfgname} must contain the following lines:}; -say join qq{\n}, sort keys %pkg; - -__END__ - -=head1 NAME - -support/scripts/scancpan Try-Tiny Moo - -=head1 SYNOPSIS - -supports/scripts/scancpan [options] [distname ...] - - Options: - -help - -man - -quiet - -force - -target/-notarget - -host/-nohost - -recommend - -test - -=head1 OPTIONS - -=over 8 - -=item B<-help> - -Prints a brief help message and exits. - -=item B<-man> - -Prints the manual page and exits. - -=item B<-quiet> - -Executes without output - -=item B<-force> - -Forces the overwriting of existing files. - -=item B<-target/-notarget> - -Switches package generation for the target variant (the default is C<-target>). - -=item B<-host/-nohost> - -Switches package generation for the host variant (the default is C<-nohost>). - -=item B<-recommend> - -Adds I dependencies. - -=item B<-test> - -Adds dependencies for test. - -=back - -=head1 DESCRIPTION - -This script creates templates of the Buildroot package files for all the -Perl/CPAN distributions required by the specified distnames. The -dependencies and metadata are fetched from https://metacpan.org/. - -After running this script, it is necessary to check the generated files. -You have to manually add the license files (PERL_FOO_LICENSE_FILES variable). -For distributions that link against a target library, you have to add the -buildroot package name for that library to the DEPENDENCIES variable. - -See the Buildroot documentation for details on the usage of the Perl -infrastructure. - -The major version of the host perl must be aligned on the target one, -in order to work with the right CoreList data. - -=head1 LICENSE - -Copyright (C) 2013-2016 by Francois Perrad - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -This script is a part of Buildroot. - -This script requires the module C (version 1.131730) -which was included at the beginning of this file by the tool C. - -See L. - -See L. - -These both libraries are free software and may be distributed under the same -terms as perl itself. - -And perl may be distributed under the terms of Artistic v1 or GPL v1 license. - -=cut diff --git a/tools/readme.txt b/tools/readme.txt index dd8a1d4494..20581740cb 100644 --- a/tools/readme.txt +++ b/tools/readme.txt @@ -11,6 +11,10 @@ get-developpers of Buildroot, so they can be Cc:ed on a mail. Accepts a patch as input, a package name or and architecture name. +scancpan + a script to create a Buildroot package by scanning a CPAN module + description. + size-stat-compare a script to compare the rootfs size between two differnt Buildroot configurations. This can be used to identify the size impact of diff --git a/tools/scancpan b/tools/scancpan new file mode 100755 index 0000000000..62df225361 --- /dev/null +++ b/tools/scancpan @@ -0,0 +1,858 @@ +#!/usr/bin/env perl + +# This chunk of stuff was generated by App::FatPacker. To find the original +# file's code, look for the end of this BEGIN block or the string 'FATPACK' +BEGIN { +my %fatpacked; + +$fatpacked{"MetaCPAN/API/Tiny.pm"} = <<'METACPAN_API_TINY'; + package MetaCPAN::API::Tiny; + { + $MetaCPAN::API::Tiny::VERSION = '1.131730'; + } + use strict; + use warnings; + # ABSTRACT: A Tiny API client for MetaCPAN + + use Carp; + use JSON::PP 'encode_json', 'decode_json'; + use HTTP::Tiny; + + + sub new { + my ($class, @args) = @_; + + $#_ % 2 == 0 + or croak 'Arguments must be provided as name/value pairs'; + + my %params = @args; + + die 'ua_args must be an array reference' + if $params{ua_args} && ref($params{ua_args}) ne 'ARRAY'; + + my $self = +{ + base_url => $params{base_url} || 'http://api.metacpan.org/v0', + ua => $params{ua} || HTTP::Tiny->new( + $params{ua_args} + ? @{$params{ua_args}} + : (agent => 'MetaCPAN::API::Tiny/' + . ($MetaCPAN::API::VERSION || 'xx'))), + }; + + return bless($self, $class); + } + + sub _build_extra_params { + my $self = shift; + + @_ % 2 == 0 + or croak 'Incorrect number of params, must be key/value'; + + my %extra = @_; + my $ua = $self->{ua}; + + foreach my $key (keys %extra) + { + # The implementation in HTTP::Tiny uses + instead of %20, fix that + $extra{$key} = $ua->_uri_escape($extra{$key}); + $extra{$key} =~ s/\+/%20/g; + } + + my $params = join '&', map { "$_=" . $extra{$_} } sort keys %extra; + + return $params; + } + + + # /source/{author}/{release}/{path} + sub source { + my $self = shift; + my %opts = @_ ? @_ : (); + my $url = ''; + my $error = "Provide 'author' and 'release' and 'path'"; + + %opts or croak $error; + + if ( + defined ( my $author = $opts{'author'} ) && + defined ( my $release = $opts{'release'} ) && + defined ( my $path = $opts{'path'} ) + ) { + $url = "source/$author/$release/$path"; + } else { + croak $error; + } + + $url = $self->{base_url} . "/$url"; + + my $result = $self->{ua}->get($url); + $result->{'success'} + or croak "Failed to fetch '$url': " . $result->{'reason'}; + + return $result->{'content'}; + } + + + # /release/{distribution} + # /release/{author}/{release} + sub release { + my $self = shift; + my %opts = @_ ? @_ : (); + my $url = ''; + my $error = "Either provide 'distribution', or 'author' and 'release', " . + "or 'search'"; + + %opts or croak $error; + + my %extra_opts = (); + + if ( defined ( my $dist = $opts{'distribution'} ) ) { + $url = "release/$dist"; + } elsif ( + defined ( my $author = $opts{'author'} ) && + defined ( my $release = $opts{'release'} ) + ) { + $url = "release/$author/$release"; + } elsif ( defined ( my $search_opts = $opts{'search'} ) ) { + ref $search_opts && ref $search_opts eq 'HASH' + or croak $error; + + %extra_opts = %{$search_opts}; + $url = 'release/_search'; + } else { + croak $error; + } + + return $self->fetch( $url, %extra_opts ); + } + + + # /pod/{module} + # /pod/{author}/{release}/{path} + sub pod { + my $self = shift; + my %opts = @_ ? @_ : (); + my $url = ''; + my $error = "Either provide 'module' or 'author and 'release' and 'path'"; + + %opts or croak $error; + + if ( defined ( my $module = $opts{'module'} ) ) { + $url = "pod/$module"; + } elsif ( + defined ( my $author = $opts{'author'} ) && + defined ( my $release = $opts{'release'} ) && + defined ( my $path = $opts{'path'} ) + ) { + $url = "pod/$author/$release/$path"; + } else { + croak $error; + } + + # check content-type + my %extra = (); + if ( defined ( my $type = $opts{'content-type'} ) ) { + $type =~ m{^ text/ (?: html|plain|x-pod|x-markdown ) $}x + or croak 'Incorrect content-type provided'; + + $extra{headers}{'content-type'} = $type; + } + + $url = $self->{base_url}. "/$url"; + + my $result = $self->{ua}->get( $url, \%extra ); + $result->{'success'} + or croak "Failed to fetch '$url': " . $result->{'reason'}; + + return $result->{'content'}; + } + + + # /module/{module} + sub module { + my $self = shift; + my $name = shift; + + $name or croak 'Please provide a module name'; + + return $self->fetch("module/$name"); + } + + + # file() is a synonym of module + sub file { goto &module } + + + # /author/{author} + sub author { + my $self = shift; + my ( $pause_id, $url, %extra_opts ); + + if ( @_ == 1 ) { + $url = 'author/' . shift; + } elsif ( @_ == 2 ) { + my %opts = @_; + + if ( defined $opts{'pauseid'} ) { + $url = "author/" . $opts{'pauseid'}; + } elsif ( defined $opts{'search'} ) { + my $search_opts = $opts{'search'}; + + ref $search_opts && ref $search_opts eq 'HASH' + or croak "'search' key must be hashref"; + + %extra_opts = %{$search_opts}; + $url = 'author/_search'; + } else { + croak 'Unknown option given'; + } + } else { + croak 'Please provide an author PAUSEID or a "search"'; + } + + return $self->fetch( $url, %extra_opts ); + } + + + + sub fetch { + my $self = shift; + my $url = shift; + my $extra = $self->_build_extra_params(@_); + my $base = $self->{base_url}; + my $req_url = $extra ? "$base/$url?$extra" : "$base/$url"; + + my $result = $self->{ua}->get($req_url); + return $self->_decode_result( $result, $req_url ); + } + + + sub post { + my $self = shift; + my $url = shift; + my $query = shift; + my $base = $self->{base_url}; + + defined $url + or croak 'First argument of URL must be provided'; + + ref $query and ref $query eq 'HASH' + or croak 'Second argument of query hashref must be provided'; + + my $query_json = encode_json( $query ); + my $result = $self->{ua}->request( + 'POST', + "$base/$url", + { + headers => { 'Content-Type' => 'application/json' }, + content => $query_json, + } + ); + + return $self->_decode_result( $result, $url, $query_json ); + } + + sub _decode_result { + my $self = shift; + my ( $result, $url, $original ) = @_; + my $decoded_result; + + ref $result and ref $result eq 'HASH' + or croak 'First argument must be hashref'; + + defined $url + or croak 'Second argument of a URL must be provided'; + + if ( defined ( my $success = $result->{'success'} ) ) { + my $reason = $result->{'reason'} || ''; + $reason .= ( defined $original ? " (request: $original)" : '' ); + + $success or croak "Failed to fetch '$url': $reason"; + } else { + croak 'Missing success in return value'; + } + + defined ( my $content = $result->{'content'} ) + or croak 'Missing content in return value'; + + eval { $decoded_result = decode_json $content; 1 } + or do { croak "Couldn't decode '$content': $@" }; + + return $decoded_result; + } + + 1; + + __END__ + + =pod + + =head1 NAME + + MetaCPAN::API::Tiny - A Tiny API client for MetaCPAN + + =head1 VERSION + + version 1.131730 + + =head1 DESCRIPTION + + This is the Tiny version of L. It implements a compatible API + with a few notable exceptions: + + =over 4 + + =item Attributes are direct hash access + + The attributes defined using Mo(o|u)se are now accessed via the blessed hash + directly. There are no accessors defined to access this elements. + + =item Exception handling + + Instead of using Try::Tiny, raw evals are used. This could potentially cause + issues, so just be aware. + + =item Testing + + Test::Fatal was replaced with an eval implementation of exception(). + Test::TinyMocker usage is retained, but may be absorbed since it is pure perl + + =back + + =head1 CLASS_METHODS + + =head2 new + + new is the constructor for MetaCPAN::API::Tiny. In the non-tiny version of this + module, this is provided via Any::Moose built from the attributes defined. In + the tiny version, we define our own constructor. It takes the same arguments + and provides similar checks to MetaCPAN::API with regards to arguments passed. + + =head1 PUBLIC_METHODS + + =head2 source + + my $source = $mcpan->source( + author => 'DOY', + release => 'Moose-2.0201', + path => 'lib/Moose.pm', + ); + + Searches MetaCPAN for a module or a specific release and returns the plain source. + + =head2 release + + my $result = $mcpan->release( distribution => 'Moose' ); + + # or + my $result = $mcpan->release( author => 'DOY', release => 'Moose-2.0001' ); + + Searches MetaCPAN for a dist. + + You can do complex searches using 'search' parameter: + + # example lifted from MetaCPAN docs + my $result = $mcpan->release( + search => { + author => "OALDERS AND ", + filter => "status:latest", + fields => "name", + size => 1, + }, + ); + + =head2 pod + + my $result = $mcpan->pod( module => 'Moose' ); + + # or + my $result = $mcpan->pod( + author => 'DOY', + release => 'Moose-2.0201', + path => 'lib/Moose.pm', + ); + + Searches MetaCPAN for a module or a specific release and returns the POD. + + =head2 module + + my $result = $mcpan->module('MetaCPAN::API'); + + Searches MetaCPAN and returns a module's ".pm" file. + + =head2 file + + A synonym of L + + =head2 author + + my $result1 = $mcpan->author('XSAWYERX'); + my $result2 = $mcpan->author( pauseid => 'XSAWYERX' ); + + Searches MetaCPAN for a specific author. + + You can do complex searches using 'search' parameter: + + # example lifted from MetaCPAN docs + my $result = $mcpan->author( + search => { + q => 'profile.name:twitter', + size => 1, + }, + ); + + =head2 fetch + + my $result = $mcpan->fetch('/release/distribution/Moose'); + + # with parameters + my $more = $mcpan->fetch( + '/release/distribution/Moose', + param => 'value', + ); + + This is a helper method for API implementations. It fetches a path from MetaCPAN, decodes the JSON from the content variable and returns it. + + You don't really need to use it, but you can in case you want to write your own extension implementation to MetaCPAN::API. + + It accepts an additional hash as "GET" parameters. + + =head2 post + + # /release&content={"query":{"match_all":{}},"filter":{"prefix":{"archive":"Cache-Cache-1.06"}}} + my $result = $mcpan->post( + 'release', + { + query => { match_all => {} }, + filter => { prefix => { archive => 'Cache-Cache-1.06' } }, + }, + ); + + The POST equivalent of the "fetch()" method. It gets the path and JSON request. + + =head1 THANKS + + Overall the tests and code were ripped directly from MetaCPAN::API and + tiny-fied. A big thanks to Sawyer X for writing the original module. + + =head1 AUTHOR + + Nicholas R. Perez + + =head1 COPYRIGHT AND LICENSE + + This software is copyright (c) 2013 by Nicholas R. Perez . + + This is free software; you can redistribute it and/or modify it under + the same terms as the Perl 5 programming language system itself. + + =cut +METACPAN_API_TINY + +s/^ //mg for values %fatpacked; + +unshift @INC, sub { + if (my $fat = $fatpacked{$_[1]}) { + if ($] < 5.008) { + return sub { + return 0 unless length $fat; + $fat =~ s/^([^\n]*\n?)//; + $_ = $1; + return 1; + }; + } + open my $fh, '<', \$fat + or die "FatPacker error loading $_[1] (could be a perl installation issue?)"; + return $fh; + } + return +}; + +} # END OF FATPACK CODE + + +use 5.010; +use strict; +use warnings; +use Fatal qw(open close); + +use Getopt::Long; +use Pod::Usage; +use File::Basename; +use Module::CoreList; +use HTTP::Tiny; +use Safe; +use MetaCPAN::API::Tiny; + +# Below, 5.024 should be aligned with the version of perl actually +# bundled in Buildroot: +die <<"MSG" if $] < 5.024; +This script needs a host perl with the same major version as Buildroot target perl. + +Your current host perl is: + $^X + version $] + +You may install a local one by running: + perlbrew install perl-5.24.0 +MSG + +my ($help, $man, $quiet, $force, $recommend, $test, $host); +my $target = 1; +GetOptions( 'help|?' => \$help, + 'man' => \$man, + 'quiet|q' => \$quiet, + 'force|f' => \$force, + 'host!' => \$host, + 'target!' => \$target, + 'recommend' => \$recommend, + 'test' => \$test +) or pod2usage(-exitval => 1); +pod2usage(-exitval => 0) if $help; +pod2usage(-exitval => 0, -verbose => 2) if $man; +pod2usage(-exitval => 1) if scalar @ARGV == 0; + +my %dist; # name -> metacpan data +my %need_target; # name -> 1 if target package is needed +my %need_host; # name -> 1 if host package is needed +my %need_dlopen; # name -> 1 if requires dynamic library +my %deps_build; # name -> list of host dependencies +my %deps_runtime; # name -> list of target dependencies +my %deps_optional; # name -> list of optional target dependencies +my %license_files; # name -> list of license files +my %checksum; # author -> list of checksum +my $mirror = 'http://cpan.metacpan.org'; # a CPAN mirror +my $mcpan = MetaCPAN::API::Tiny->new(base_url => 'http://fastapi.metacpan.org/v1'); +my $ua = HTTP::Tiny->new(); + +sub get_checksum { + my ($url) = @_; + my($path) = $url =~ m|^[^:/?#]+://[^/?#]*([^?#]*)|; + my($basename, $dirname) = fileparse( $path ); + unless ($checksum{$dirname}) { + my $url = $mirror . $dirname . q{CHECKSUMS}; + my $response = $ua->get($url); + $checksum{$dirname} = $response->{content}; + } + my $chksum = Safe->new->reval($checksum{$dirname}); + return $chksum->{$basename}, $basename; +} + +sub is_xs { + my ($manifest) = @_; + # This heuristic determines if a module is a native extension, by searching + # some file extension types in the MANIFEST of the distribution. + # It was inspired by http://deps.cpantesters.org/static/purity.html + return $manifest =~ m/\.(swg|xs|c|h|i)[\n\s]/; +} + +sub find_license_files { + my ($manifest) = @_; + my @license_files; + foreach (split /\n/, $manifest) { + next if m|/|; + push @license_files, $_ if m/(ARTISTIC|COPYING|COPYRIGHT|LICENSE)/i; + } + if (scalar @license_files == 0 && $manifest =~ m/(README)[\n\s]/i) { + @license_files = ($1); + } + return \@license_files; +} + +sub fetch { + my ($name, $need_target, $need_host, $top) = @_; + $need_target{$name} = $need_target if $need_target; + $need_host{$name} = $need_host if $need_host; + unless ($dist{$name} && !$top) { + say qq{fetch ${name}} unless $quiet; + my $result = $mcpan->release( distribution => $name ); + $dist{$name} = $result; + my $manifest = $mcpan->source( author => $result->{author}, + release => $name . q{-} . $result->{version}, + path => 'MANIFEST' ); + $need_dlopen{$name} = is_xs( $manifest ); + $license_files{$name} = find_license_files( $manifest ); + my %build = (); + my %runtime = (); + my %optional = (); + foreach my $dep (@{$result->{dependency}}) { + my $modname = ${$dep}{module}; + next if $modname eq q{perl}; + next if $modname =~ m|^Alien|; + next if $modname =~ m|^Win32|; + next if !($test && $top) && $modname =~ m|^Test|; + next if Module::CoreList::is_core( $modname, undef, $] ); + # we could use the host Module::CoreList data, because host perl and + # target perl have the same major version + next if ${$dep}{phase} eq q{develop}; + next if !($test && $top) && ${$dep}{phase} eq q{test}; + my $distname = $mcpan->module( $modname )->{distribution}; + if (${$dep}{phase} eq q{runtime}) { + if (${$dep}{relationship} eq q{requires}) { + $runtime{$distname} = 1; + } + else { + $optional{$distname} = 1 if $recommend && $top; + } + } + else { # configure, build + $build{$distname} = 1; + } + } + $deps_build{$name} = [keys %build]; + $deps_runtime{$name} = [keys %runtime]; + $deps_optional{$name} = [keys %optional]; + foreach my $distname (@{$deps_build{$name}}) { + fetch( $distname, 0, 1 ); + } + foreach my $distname (@{$deps_runtime{$name}}) { + fetch( $distname, $need_target, $need_host ); + $need_dlopen{$name} ||= $need_dlopen{$distname}; + } + foreach my $distname (@{$deps_optional{$name}}) { + fetch( $distname, $need_target, $need_host ); + } + } + return; +} + +foreach my $distname (@ARGV) { + # Command-line's distributions + fetch( $distname, !!$target, !!$host, 1 ); +} +say scalar keys %dist, q{ packages fetched.} unless $quiet; + +# Buildroot package name: lowercase +sub fsname { + my $name = shift; + $name =~ s|_|-|g; + return q{perl-} . lc $name; +} + +# Buildroot variable name: uppercase +sub brname { + my $name = shift; + $name =~ s|-|_|g; + return uc $name; +} + +while (my ($distname, $dist) = each %dist) { + my $fsname = fsname( $distname ); + my $dirname = q{package/} . $fsname; + my $cfgname = $dirname . q{/Config.in}; + my $mkname = $dirname . q{/} . $fsname . q{.mk}; + my $hashname = $dirname . q{/} . $fsname . q{.hash}; + my $brname = brname( $fsname ); + mkdir $dirname unless -d $dirname; + if ($need_target{$distname} && ($force || !-f $cfgname)) { + my $abstract = $dist->{abstract}; + my $homepage = $dist->{resources}->{homepage} || qq{https://metacpan.org/release/${distname}}; + say qq{write ${cfgname}} unless $quiet; + open my $fh, q{>}, $cfgname; + say {$fh} qq{config BR2_PACKAGE_${brname}}; + say {$fh} qq{\tbool "${fsname}"}; + say {$fh} qq{\tdepends on !BR2_STATIC_LIBS} if $need_dlopen{$distname}; + foreach my $dep (sort @{$deps_runtime{$distname}}) { + my $brdep = brname( fsname( $dep ) ); + say {$fh} qq{\tselect BR2_PACKAGE_${brdep}}; + } + say {$fh} qq{\thelp}; + say {$fh} qq{\t ${abstract}\n} if $abstract; + say {$fh} qq{\t ${homepage}}; + if ($need_dlopen{$distname}) { + say {$fh} qq{\ncomment "${fsname} needs a toolchain w/ dynamic library"}; + say {$fh} qq{\tdepends on BR2_STATIC_LIBS}; + } + close $fh; + } + if ($force || !-f $mkname) { + my $version = $dist->{version}; + my($path) = $dist->{download_url} =~ m|^[^:/?#]+://[^/?#]*([^?#]*)|; + # this URL contains only the scheme, auth and path parts (but no query and fragment parts) + # the scheme is not used, because the job is done by the BR download infrastructure + # the auth part is not used, because we use $(BR2_CPAN_MIRROR) + my($filename, $directories, $suffix) = fileparse( $path, q{tar.gz}, q{tgz} ); + $directories =~ s|/$||; + my $dependencies = join q{ }, map( { q{host-} . fsname( $_ ); } sort @{$deps_build{$distname}} ), + map( { fsname( $_ ); } sort @{$deps_runtime{$distname}} ); + my $host_dependencies = join q{ }, map { q{host-} . fsname( $_ ); } sort( @{$deps_build{$distname}}, + @{$deps_runtime{$distname}} ); + my $license = ref $dist->{license} eq 'ARRAY' + ? join q{ or }, @{$dist->{license}} + : $dist->{license}; + # BR requires license name as in http://spdx.org/licenses/ + $license =~ s|apache_2_0|Apache-2.0|; + $license =~ s|artistic_2|Artistic-2.0|; + $license =~ s|mit|MIT|; + $license =~ s|openssl|OpenSSL|; + $license =~ s|perl_5|Artistic or GPL-1.0+|; + my $license_files = join q{ }, @{$license_files{$distname}}; + say qq{write ${mkname}} unless $quiet; + open my $fh, q{>}, $mkname; + say {$fh} qq{################################################################################}; + say {$fh} qq{#}; + say {$fh} qq{# ${fsname}}; + say {$fh} qq{#}; + say {$fh} qq{################################################################################}; + say {$fh} qq{}; + say {$fh} qq{${brname}_VERSION = ${version}}; + say {$fh} qq{${brname}_SOURCE = ${distname}-\$(${brname}_VERSION).${suffix}}; + say {$fh} qq{${brname}_SITE = \$(BR2_CPAN_MIRROR)${directories}}; + say {$fh} qq{${brname}_DEPENDENCIES = ${dependencies}} if $need_target{$distname} && $dependencies; + say {$fh} qq{HOST_${brname}_DEPENDENCIES = ${host_dependencies}} if $need_host{$distname} && $host_dependencies; + say {$fh} qq{${brname}_LICENSE = ${license}} if $license && $license ne q{unknown}; + say {$fh} qq{${brname}_LICENSE_FILES = ${license_files}} if $license_files; + say {$fh} qq{}; + foreach (sort @{$deps_optional{$distname}}) { + next if grep { $_ eq $distname; } @{$deps_runtime{$_}}; # avoid cyclic dependencies + my $opt_brname = brname( $_ ); + my $opt_fsname = fsname( $_ ); + say {$fh} qq{ifeq (\$(BR2_PACKAGE_PERL_${opt_brname}),y)}; + say {$fh} qq{${brname}_DEPENDENCIES += ${opt_fsname}}; + say {$fh} qq{endif}; + say {$fh} qq{}; + } + say {$fh} qq{\$(eval \$(perl-package))} if $need_target{$distname}; + say {$fh} qq{\$(eval \$(host-perl-package))} if $need_host{$distname}; + close $fh; + } + if ($force || !-f $hashname) { + my($checksum, $filename) = get_checksum($dist->{download_url}); + my $md5 = $checksum->{md5}; + my $sha256 = $checksum->{sha256}; + say qq{write ${hashname}} unless $quiet; + open my $fh, q{>}, $hashname; + say {$fh} qq{# retrieved by scancpan from ${mirror}/}; + say {$fh} qq{md5 ${md5} ${filename}}; + say {$fh} qq{sha256 ${sha256} ${filename}}; + close $fh; + } +} + +my %pkg; +my $cfgname = q{package/Config.in}; +if (-f $cfgname) { + open my $fh, q{<}, $cfgname; + while (<$fh>) { + chomp; + $pkg{$_} = 1 if m|package/perl-|; + } + close $fh; +} + +foreach my $distname (keys %need_target) { + my $fsname = fsname( $distname ); + $pkg{qq{\tsource "package/${fsname}/Config.in"}} = 1; +} + +say qq{${cfgname} must contain the following lines:}; +say join qq{\n}, sort keys %pkg; + +__END__ + +=head1 NAME + +support/scripts/scancpan Try-Tiny Moo + +=head1 SYNOPSIS + +supports/scripts/scancpan [options] [distname ...] + + Options: + -help + -man + -quiet + -force + -target/-notarget + -host/-nohost + -recommend + -test + +=head1 OPTIONS + +=over 8 + +=item B<-help> + +Prints a brief help message and exits. + +=item B<-man> + +Prints the manual page and exits. + +=item B<-quiet> + +Executes without output + +=item B<-force> + +Forces the overwriting of existing files. + +=item B<-target/-notarget> + +Switches package generation for the target variant (the default is C<-target>). + +=item B<-host/-nohost> + +Switches package generation for the host variant (the default is C<-nohost>). + +=item B<-recommend> + +Adds I dependencies. + +=item B<-test> + +Adds dependencies for test. + +=back + +=head1 DESCRIPTION + +This script creates templates of the Buildroot package files for all the +Perl/CPAN distributions required by the specified distnames. The +dependencies and metadata are fetched from https://metacpan.org/. + +After running this script, it is necessary to check the generated files. +You have to manually add the license files (PERL_FOO_LICENSE_FILES variable). +For distributions that link against a target library, you have to add the +buildroot package name for that library to the DEPENDENCIES variable. + +See the Buildroot documentation for details on the usage of the Perl +infrastructure. + +The major version of the host perl must be aligned on the target one, +in order to work with the right CoreList data. + +=head1 LICENSE + +Copyright (C) 2013-2016 by Francois Perrad + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This script is a part of Buildroot. + +This script requires the module C (version 1.131730) +which was included at the beginning of this file by the tool C. + +See L. + +See L. + +These both libraries are free software and may be distributed under the same +terms as perl itself. + +And perl may be distributed under the terms of Artistic v1 or GPL v1 license. + +=cut