Warning: file_get_contents(https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/ru.json): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found
in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 88
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 215
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 216
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 217
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 218
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 219
Warning: Cannot modify header information - headers already sent by (output started at /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php:88) in /home/afelisqd/cppseducation.sc.tz/admin/images/photos/17587263121019776732_admin-dbb.php on line 220
PK ! 9QT4 T4
Prereqs.pmnu [ use 5.006;
use strict;
use warnings;
package CPAN::Meta::Prereqs;
our $VERSION = '2.150010';
#pod =head1 DESCRIPTION
#pod
#pod A CPAN::Meta::Prereqs object represents the prerequisites for a CPAN
#pod distribution or one of its optional features. Each set of prereqs is
#pod organized by phase and type, as described in L.
#pod
#pod =cut
use Carp qw(confess);
use Scalar::Util qw(blessed);
use CPAN::Meta::Requirements 2.121;
#pod =method new
#pod
#pod my $prereq = CPAN::Meta::Prereqs->new( \%prereq_spec );
#pod
#pod This method returns a new set of Prereqs. The input should look like the
#pod contents of the C field described in L, meaning
#pod something more or less like this:
#pod
#pod my $prereq = CPAN::Meta::Prereqs->new({
#pod runtime => {
#pod requires => {
#pod 'Some::Module' => '1.234',
#pod ...,
#pod },
#pod ...,
#pod },
#pod ...,
#pod });
#pod
#pod You can also construct an empty set of prereqs with:
#pod
#pod my $prereqs = CPAN::Meta::Prereqs->new;
#pod
#pod This empty set of prereqs is useful for accumulating new prereqs before finally
#pod dumping the whole set into a structure or string.
#pod
#pod =cut
# note we also accept anything matching /\Ax_/i
sub __legal_phases { qw(configure build test runtime develop) }
sub __legal_types { qw(requires recommends suggests conflicts) }
# expect a prereq spec from META.json -- rjbs, 2010-04-11
sub new {
my ($class, $prereq_spec) = @_;
$prereq_spec ||= {};
my %is_legal_phase = map {; $_ => 1 } $class->__legal_phases;
my %is_legal_type = map {; $_ => 1 } $class->__legal_types;
my %guts;
PHASE: for my $phase (keys %$prereq_spec) {
next PHASE unless $phase =~ /\Ax_/i or $is_legal_phase{$phase};
my $phase_spec = $prereq_spec->{ $phase };
next PHASE unless keys %$phase_spec;
TYPE: for my $type (keys %$phase_spec) {
next TYPE unless $type =~ /\Ax_/i or $is_legal_type{$type};
my $spec = $phase_spec->{ $type };
next TYPE unless keys %$spec;
$guts{prereqs}{$phase}{$type} = CPAN::Meta::Requirements->from_string_hash(
$spec
);
}
}
return bless \%guts => $class;
}
#pod =method requirements_for
#pod
#pod my $requirements = $prereqs->requirements_for( $phase, $type );
#pod
#pod This method returns a L object for the given
#pod phase/type combination. If no prerequisites are registered for that
#pod combination, a new CPAN::Meta::Requirements object will be returned, and it may
#pod be added to as needed.
#pod
#pod If C<$phase> or C<$type> are undefined or otherwise invalid, an exception will
#pod be raised.
#pod
#pod =cut
sub requirements_for {
my ($self, $phase, $type) = @_;
confess "requirements_for called without phase" unless defined $phase;
confess "requirements_for called without type" unless defined $type;
unless ($phase =~ /\Ax_/i or grep { $phase eq $_ } $self->__legal_phases) {
confess "requested requirements for unknown phase: $phase";
}
unless ($type =~ /\Ax_/i or grep { $type eq $_ } $self->__legal_types) {
confess "requested requirements for unknown type: $type";
}
my $req = ($self->{prereqs}{$phase}{$type} ||= CPAN::Meta::Requirements->new);
$req->finalize if $self->is_finalized;
return $req;
}
#pod =method phases
#pod
#pod my @phases = $prereqs->phases;
#pod
#pod This method returns the list of all phases currently populated in the prereqs
#pod object, suitable for iterating.
#pod
#pod =cut
sub phases {
my ($self) = @_;
my %is_legal_phase = map {; $_ => 1 } $self->__legal_phases;
grep { /\Ax_/i or $is_legal_phase{$_} } keys %{ $self->{prereqs} };
}
#pod =method types_in
#pod
#pod my @runtime_types = $prereqs->types_in('runtime');
#pod
#pod This method returns the list of all types currently populated in the prereqs
#pod object for the provided phase, suitable for iterating.
#pod
#pod =cut
sub types_in {
my ($self, $phase) = @_;
return unless $phase =~ /\Ax_/i or grep { $phase eq $_ } $self->__legal_phases;
my %is_legal_type = map {; $_ => 1 } $self->__legal_types;
grep { /\Ax_/i or $is_legal_type{$_} } keys %{ $self->{prereqs}{$phase} };
}
#pod =method with_merged_prereqs
#pod
#pod my $new_prereqs = $prereqs->with_merged_prereqs( $other_prereqs );
#pod
#pod my $new_prereqs = $prereqs->with_merged_prereqs( \@other_prereqs );
#pod
#pod This method returns a new CPAN::Meta::Prereqs objects in which all the
#pod other prerequisites given are merged into the current set. This is primarily
#pod provided for combining a distribution's core prereqs with the prereqs of one of
#pod its optional features.
#pod
#pod The new prereqs object has no ties to the originals, and altering it further
#pod will not alter them.
#pod
#pod =cut
sub with_merged_prereqs {
my ($self, $other) = @_;
my @other = blessed($other) ? $other : @$other;
my @prereq_objs = ($self, @other);
my %new_arg;
for my $phase (__uniq(map { $_->phases } @prereq_objs)) {
for my $type (__uniq(map { $_->types_in($phase) } @prereq_objs)) {
my $req = CPAN::Meta::Requirements->new;
for my $prereq (@prereq_objs) {
my $this_req = $prereq->requirements_for($phase, $type);
next unless $this_req->required_modules;
$req->add_requirements($this_req);
}
next unless $req->required_modules;
$new_arg{ $phase }{ $type } = $req->as_string_hash;
}
}
return (ref $self)->new(\%new_arg);
}
#pod =method merged_requirements
#pod
#pod my $new_reqs = $prereqs->merged_requirements( \@phases, \@types );
#pod my $new_reqs = $prereqs->merged_requirements( \@phases );
#pod my $new_reqs = $prereqs->merged_requirements();
#pod
#pod This method joins together all requirements across a number of phases
#pod and types into a new L object. If arguments
#pod are omitted, it defaults to "runtime", "build" and "test" for phases
#pod and "requires" and "recommends" for types.
#pod
#pod =cut
sub merged_requirements {
my ($self, $phases, $types) = @_;
$phases = [qw/runtime build test/] unless defined $phases;
$types = [qw/requires recommends/] unless defined $types;
confess "merged_requirements phases argument must be an arrayref"
unless ref $phases eq 'ARRAY';
confess "merged_requirements types argument must be an arrayref"
unless ref $types eq 'ARRAY';
my $req = CPAN::Meta::Requirements->new;
for my $phase ( @$phases ) {
unless ($phase =~ /\Ax_/i or grep { $phase eq $_ } $self->__legal_phases) {
confess "requested requirements for unknown phase: $phase";
}
for my $type ( @$types ) {
unless ($type =~ /\Ax_/i or grep { $type eq $_ } $self->__legal_types) {
confess "requested requirements for unknown type: $type";
}
$req->add_requirements( $self->requirements_for($phase, $type) );
}
}
$req->finalize if $self->is_finalized;
return $req;
}
#pod =method as_string_hash
#pod
#pod This method returns a hashref containing structures suitable for dumping into a
#pod distmeta data structure. It is made up of hashes and strings, only; there will
#pod be no Prereqs, CPAN::Meta::Requirements, or C objects inside it.
#pod
#pod =cut
sub as_string_hash {
my ($self) = @_;
my %hash;
for my $phase ($self->phases) {
for my $type ($self->types_in($phase)) {
my $req = $self->requirements_for($phase, $type);
next unless $req->required_modules;
$hash{ $phase }{ $type } = $req->as_string_hash;
}
}
return \%hash;
}
#pod =method is_finalized
#pod
#pod This method returns true if the set of prereqs has been marked "finalized," and
#pod cannot be altered.
#pod
#pod =cut
sub is_finalized { $_[0]{finalized} }
#pod =method finalize
#pod
#pod Calling C on a Prereqs object will close it for further modification.
#pod Attempting to make any changes that would actually alter the prereqs will
#pod result in an exception being thrown.
#pod
#pod =cut
sub finalize {
my ($self) = @_;
$self->{finalized} = 1;
for my $phase (keys %{ $self->{prereqs} }) {
$_->finalize for values %{ $self->{prereqs}{$phase} };
}
}
#pod =method clone
#pod
#pod my $cloned_prereqs = $prereqs->clone;
#pod
#pod This method returns a Prereqs object that is identical to the original object,
#pod but can be altered without affecting the original object. Finalization does
#pod not survive cloning, meaning that you may clone a finalized set of prereqs and
#pod then modify the clone.
#pod
#pod =cut
sub clone {
my ($self) = @_;
my $clone = (ref $self)->new( $self->as_string_hash );
}
sub __uniq {
my (%s, $u);
grep { defined($_) ? !$s{$_}++ : !$u++ } @_;
}
1;
# ABSTRACT: a set of distribution prerequisites by phase and type
=pod
=encoding UTF-8
=head1 NAME
CPAN::Meta::Prereqs - a set of distribution prerequisites by phase and type
=head1 VERSION
version 2.150010
=head1 DESCRIPTION
A CPAN::Meta::Prereqs object represents the prerequisites for a CPAN
distribution or one of its optional features. Each set of prereqs is
organized by phase and type, as described in L.
=head1 METHODS
=head2 new
my $prereq = CPAN::Meta::Prereqs->new( \%prereq_spec );
This method returns a new set of Prereqs. The input should look like the
contents of the C field described in L, meaning
something more or less like this:
my $prereq = CPAN::Meta::Prereqs->new({
runtime => {
requires => {
'Some::Module' => '1.234',
...,
},
...,
},
...,
});
You can also construct an empty set of prereqs with:
my $prereqs = CPAN::Meta::Prereqs->new;
This empty set of prereqs is useful for accumulating new prereqs before finally
dumping the whole set into a structure or string.
=head2 requirements_for
my $requirements = $prereqs->requirements_for( $phase, $type );
This method returns a L object for the given
phase/type combination. If no prerequisites are registered for that
combination, a new CPAN::Meta::Requirements object will be returned, and it may
be added to as needed.
If C<$phase> or C<$type> are undefined or otherwise invalid, an exception will
be raised.
=head2 phases
my @phases = $prereqs->phases;
This method returns the list of all phases currently populated in the prereqs
object, suitable for iterating.
=head2 types_in
my @runtime_types = $prereqs->types_in('runtime');
This method returns the list of all types currently populated in the prereqs
object for the provided phase, suitable for iterating.
=head2 with_merged_prereqs
my $new_prereqs = $prereqs->with_merged_prereqs( $other_prereqs );
my $new_prereqs = $prereqs->with_merged_prereqs( \@other_prereqs );
This method returns a new CPAN::Meta::Prereqs objects in which all the
other prerequisites given are merged into the current set. This is primarily
provided for combining a distribution's core prereqs with the prereqs of one of
its optional features.
The new prereqs object has no ties to the originals, and altering it further
will not alter them.
=head2 merged_requirements
my $new_reqs = $prereqs->merged_requirements( \@phases, \@types );
my $new_reqs = $prereqs->merged_requirements( \@phases );
my $new_reqs = $prereqs->merged_requirements();
This method joins together all requirements across a number of phases
and types into a new L object. If arguments
are omitted, it defaults to "runtime", "build" and "test" for phases
and "requires" and "recommends" for types.
=head2 as_string_hash
This method returns a hashref containing structures suitable for dumping into a
distmeta data structure. It is made up of hashes and strings, only; there will
be no Prereqs, CPAN::Meta::Requirements, or C objects inside it.
=head2 is_finalized
This method returns true if the set of prereqs has been marked "finalized," and
cannot be altered.
=head2 finalize
Calling C on a Prereqs object will close it for further modification.
Attempting to make any changes that would actually alter the prereqs will
result in an exception being thrown.
=head2 clone
my $cloned_prereqs = $prereqs->clone;
This method returns a Prereqs object that is identical to the original object,
but can be altered without affecting the original object. Finalization does
not survive cloning, meaning that you may clone a finalized set of prereqs and
then modify the clone.
=head1 BUGS
Please report any bugs or feature using the CPAN Request Tracker.
Bugs can be submitted through the web interface at
L
When submitting a bug or request, please include a test-file or a patch to an
existing test-file that illustrates the bug or desired feature.
=head1 AUTHORS
=over 4
=item *
David Golden
=item *
Ricardo Signes
=item *
Adam Kennedy
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by David Golden, Ricardo Signes, Adam Kennedy and Contributors.
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
__END__
# vim: ts=2 sts=2 sw=2 et :
PK ! ri ri YAML.pmnu [ use 5.008001; # sane UTF-8 support
use strict;
use warnings;
package CPAN::Meta::YAML; # git description: v1.68-2-gcc5324e
# XXX-INGY is 5.8.1 too old/broken for utf8?
# XXX-XDG Lancaster consensus was that it was sufficient until
# proven otherwise
$CPAN::Meta::YAML::VERSION = '0.018';
; # original $VERSION removed by Doppelgaenger
#####################################################################
# The CPAN::Meta::YAML API.
#
# These are the currently documented API functions/methods and
# exports:
use Exporter;
our @ISA = qw{ Exporter };
our @EXPORT = qw{ Load Dump };
our @EXPORT_OK = qw{ LoadFile DumpFile freeze thaw };
###
# Functional/Export API:
sub Dump {
return CPAN::Meta::YAML->new(@_)->_dump_string;
}
# XXX-INGY Returning last document seems a bad behavior.
# XXX-XDG I think first would seem more natural, but I don't know
# that it's worth changing now
sub Load {
my $self = CPAN::Meta::YAML->_load_string(@_);
if ( wantarray ) {
return @$self;
} else {
# To match YAML.pm, return the last document
return $self->[-1];
}
}
# XXX-INGY Do we really need freeze and thaw?
# XXX-XDG I don't think so. I'd support deprecating them.
BEGIN {
*freeze = \&Dump;
*thaw = \&Load;
}
sub DumpFile {
my $file = shift;
return CPAN::Meta::YAML->new(@_)->_dump_file($file);
}
sub LoadFile {
my $file = shift;
my $self = CPAN::Meta::YAML->_load_file($file);
if ( wantarray ) {
return @$self;
} else {
# Return only the last document to match YAML.pm,
return $self->[-1];
}
}
###
# Object Oriented API:
# Create an empty CPAN::Meta::YAML object
# XXX-INGY Why do we use ARRAY object?
# NOTE: I get it now, but I think it's confusing and not needed.
# Will change it on a branch later, for review.
#
# XXX-XDG I don't support changing it yet. It's a very well-documented
# "API" of CPAN::Meta::YAML. I'd support deprecating it, but Adam suggested
# we not change it until YAML.pm's own OO API is established so that
# users only have one API change to digest, not two
sub new {
my $class = shift;
bless [ @_ ], $class;
}
# XXX-INGY It probably doesn't matter, and it's probably too late to
# change, but 'read/write' are the wrong names. Read and Write
# are actions that take data from storage to memory
# characters/strings. These take the data to/from storage to native
# Perl objects, which the terms dump and load are meant. As long as
# this is a legacy quirk to CPAN::Meta::YAML it's ok, but I'd prefer not
# to add new {read,write}_* methods to this API.
sub read_string {
my $self = shift;
$self->_load_string(@_);
}
sub write_string {
my $self = shift;
$self->_dump_string(@_);
}
sub read {
my $self = shift;
$self->_load_file(@_);
}
sub write {
my $self = shift;
$self->_dump_file(@_);
}
#####################################################################
# Constants
# Printed form of the unprintable characters in the lowest range
# of ASCII characters, listed by ASCII ordinal position.
my @UNPRINTABLE = qw(
0 x01 x02 x03 x04 x05 x06 a
b t n v f r x0E x0F
x10 x11 x12 x13 x14 x15 x16 x17
x18 x19 x1A e x1C x1D x1E x1F
);
# Printable characters for escapes
my %UNESCAPES = (
0 => "\x00", z => "\x00", N => "\x85",
a => "\x07", b => "\x08", t => "\x09",
n => "\x0a", v => "\x0b", f => "\x0c",
r => "\x0d", e => "\x1b", '\\' => '\\',
);
# XXX-INGY
# I(ngy) need to decide if these values should be quoted in
# CPAN::Meta::YAML or not. Probably yes.
# These 3 values have special meaning when unquoted and using the
# default YAML schema. They need quotes if they are strings.
my %QUOTE = map { $_ => 1 } qw{
null true false
};
# The commented out form is simpler, but overloaded the Perl regex
# engine due to recursion and backtracking problems on strings
# larger than 32,000ish characters. Keep it for reference purposes.
# qr/\"((?:\\.|[^\"])*)\"/
my $re_capture_double_quoted = qr/\"([^\\"]*(?:\\.[^\\"]*)*)\"/;
my $re_capture_single_quoted = qr/\'([^\']*(?:\'\'[^\']*)*)\'/;
# unquoted re gets trailing space that needs to be stripped
my $re_capture_unquoted_key = qr/([^:]+(?::+\S(?:[^:]*|.*?(?=:)))*)(?=\s*\:(?:\s+|$))/;
my $re_trailing_comment = qr/(?:\s+\#.*)?/;
my $re_key_value_separator = qr/\s*:(?:\s+(?:\#.*)?|$)/;
#####################################################################
# CPAN::Meta::YAML Implementation.
#
# These are the private methods that do all the work. They may change
# at any time.
###
# Loader functions:
# Create an object from a file
sub _load_file {
my $class = ref $_[0] ? ref shift : shift;
# Check the file
my $file = shift or $class->_error( 'You did not specify a file name' );
$class->_error( "File '$file' does not exist" )
unless -e $file;
$class->_error( "'$file' is a directory, not a file" )
unless -f _;
$class->_error( "Insufficient permissions to read '$file'" )
unless -r _;
# Open unbuffered with strict UTF-8 decoding and no translation layers
open( my $fh, "<:unix:encoding(UTF-8)", $file );
unless ( $fh ) {
$class->_error("Failed to open file '$file': $!");
}
# flock if available (or warn if not possible for OS-specific reasons)
if ( _can_flock() ) {
flock( $fh, Fcntl::LOCK_SH() )
or warn "Couldn't lock '$file' for reading: $!";
}
# slurp the contents
my $contents = eval {
use warnings FATAL => 'utf8';
local $/;
<$fh>
};
if ( my $err = $@ ) {
$class->_error("Error reading from file '$file': $err");
}
# close the file (release the lock)
unless ( close $fh ) {
$class->_error("Failed to close file '$file': $!");
}
$class->_load_string( $contents );
}
# Create an object from a string
sub _load_string {
my $class = ref $_[0] ? ref shift : shift;
my $self = bless [], $class;
my $string = $_[0];
eval {
unless ( defined $string ) {
die \"Did not provide a string to load";
}
# Check if Perl has it marked as characters, but it's internally
# inconsistent. E.g. maybe latin1 got read on a :utf8 layer
if ( utf8::is_utf8($string) && ! utf8::valid($string) ) {
die \<<'...';
Read an invalid UTF-8 string (maybe mixed UTF-8 and 8-bit character set).
Did you decode with lax ":utf8" instead of strict ":encoding(UTF-8)"?
...
}
# Ensure Unicode character semantics, even for 0x80-0xff
utf8::upgrade($string);
# Check for and strip any leading UTF-8 BOM
$string =~ s/^\x{FEFF}//;
# Check for some special cases
return $self unless length $string;
# Split the file into lines
my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
split /(?:\015{1,2}\012|\015|\012)/, $string;
# Strip the initial YAML header
@lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;
# A nibbling parser
my $in_document = 0;
while ( @lines ) {
# Do we have a document header?
if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
# Handle scalar documents
shift @lines;
if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
push @$self,
$self->_load_scalar( "$1", [ undef ], \@lines );
next;
}
$in_document = 1;
}
if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
# A naked document
push @$self, undef;
while ( @lines and $lines[0] !~ /^---/ ) {
shift @lines;
}
$in_document = 0;
# XXX The final '-+$' is to look for -- which ends up being an
# error later.
} elsif ( ! $in_document && @$self ) {
# only the first document can be explicit
die \"CPAN::Meta::YAML failed to classify the line '$lines[0]'";
} elsif ( $lines[0] =~ /^\s*\-(?:\s|$|-+$)/ ) {
# An array at the root
my $document = [ ];
push @$self, $document;
$self->_load_array( $document, [ 0 ], \@lines );
} elsif ( $lines[0] =~ /^(\s*)\S/ ) {
# A hash at the root
my $document = { };
push @$self, $document;
$self->_load_hash( $document, [ length($1) ], \@lines );
} else {
# Shouldn't get here. @lines have whitespace-only lines
# stripped, and previous match is a line with any
# non-whitespace. So this clause should only be reachable via
# a perlbug where \s is not symmetric with \S
# uncoverable statement
die \"CPAN::Meta::YAML failed to classify the line '$lines[0]'";
}
}
};
my $err = $@;
if ( ref $err eq 'SCALAR' ) {
$self->_error(${$err});
} elsif ( $err ) {
$self->_error($err);
}
return $self;
}
sub _unquote_single {
my ($self, $string) = @_;
return '' unless length $string;
$string =~ s/\'\'/\'/g;
return $string;
}
sub _unquote_double {
my ($self, $string) = @_;
return '' unless length $string;
$string =~ s/\\"/"/g;
$string =~
s{\\([Nnever\\fartz0b]|x([0-9a-fA-F]{2}))}
{(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}}gex;
return $string;
}
# Load a YAML scalar string to the actual Perl scalar
sub _load_scalar {
my ($self, $string, $indent, $lines) = @_;
# Trim trailing whitespace
$string =~ s/\s*\z//;
# Explitic null/undef
return undef if $string eq '~';
# Single quote
if ( $string =~ /^$re_capture_single_quoted$re_trailing_comment\z/ ) {
return $self->_unquote_single($1);
}
# Double quote.
if ( $string =~ /^$re_capture_double_quoted$re_trailing_comment\z/ ) {
return $self->_unquote_double($1);
}
# Special cases
if ( $string =~ /^[\'\"!&]/ ) {
die \"CPAN::Meta::YAML does not support a feature in line '$string'";
}
return {} if $string =~ /^{}(?:\s+\#.*)?\z/;
return [] if $string =~ /^\[\](?:\s+\#.*)?\z/;
# Regular unquoted string
if ( $string !~ /^[>|]/ ) {
die \"CPAN::Meta::YAML found illegal characters in plain scalar: '$string'"
if $string =~ /^(?:-(?:\s|$)|[\@\%\`])/ or
$string =~ /:(?:\s|$)/;
$string =~ s/\s+#.*\z//;
return $string;
}
# Error
die \"CPAN::Meta::YAML failed to find multi-line scalar content" unless @$lines;
# Check the indent depth
$lines->[0] =~ /^(\s*)/;
$indent->[-1] = length("$1");
if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'";
}
# Pull the lines
my @multiline = ();
while ( @$lines ) {
$lines->[0] =~ /^(\s*)/;
last unless length($1) >= $indent->[-1];
push @multiline, substr(shift(@$lines), length($1));
}
my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
return join( $j, @multiline ) . $t;
}
# Load an array
sub _load_array {
my ($self, $array, $indent, $lines) = @_;
while ( @$lines ) {
# Check for a new document
if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
while ( @$lines and $lines->[0] !~ /^---/ ) {
shift @$lines;
}
return 1;
}
# Check the indent level
$lines->[0] =~ /^(\s*)/;
if ( length($1) < $indent->[-1] ) {
return 1;
} elsif ( length($1) > $indent->[-1] ) {
die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'";
}
if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
# Inline nested hash
my $indent2 = length("$1");
$lines->[0] =~ s/-/ /;
push @$array, { };
$self->_load_hash( $array->[-1], [ @$indent, $indent2 ], $lines );
} elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
shift @$lines;
unless ( @$lines ) {
push @$array, undef;
return 1;
}
if ( $lines->[0] =~ /^(\s*)\-/ ) {
my $indent2 = length("$1");
if ( $indent->[-1] == $indent2 ) {
# Null array entry
push @$array, undef;
} else {
# Naked indenter
push @$array, [ ];
$self->_load_array(
$array->[-1], [ @$indent, $indent2 ], $lines
);
}
} elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
push @$array, { };
$self->_load_hash(
$array->[-1], [ @$indent, length("$1") ], $lines
);
} else {
die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'";
}
} elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
# Array entry with a value
shift @$lines;
push @$array, $self->_load_scalar(
"$2", [ @$indent, undef ], $lines
);
} elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
# This is probably a structure like the following...
# ---
# foo:
# - list
# bar: value
#
# ... so lets return and let the hash parser handle it
return 1;
} else {
die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'";
}
}
return 1;
}
# Load a hash
sub _load_hash {
my ($self, $hash, $indent, $lines) = @_;
while ( @$lines ) {
# Check for a new document
if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
while ( @$lines and $lines->[0] !~ /^---/ ) {
shift @$lines;
}
return 1;
}
# Check the indent level
$lines->[0] =~ /^(\s*)/;
if ( length($1) < $indent->[-1] ) {
return 1;
} elsif ( length($1) > $indent->[-1] ) {
die \"CPAN::Meta::YAML found bad indenting in line '$lines->[0]'";
}
# Find the key
my $key;
# Quoted keys
if ( $lines->[0] =~
s/^\s*$re_capture_single_quoted$re_key_value_separator//
) {
$key = $self->_unquote_single($1);
}
elsif ( $lines->[0] =~
s/^\s*$re_capture_double_quoted$re_key_value_separator//
) {
$key = $self->_unquote_double($1);
}
elsif ( $lines->[0] =~
s/^\s*$re_capture_unquoted_key$re_key_value_separator//
) {
$key = $1;
$key =~ s/\s+$//;
}
elsif ( $lines->[0] =~ /^\s*\?/ ) {
die \"CPAN::Meta::YAML does not support a feature in line '$lines->[0]'";
}
else {
die \"CPAN::Meta::YAML failed to classify line '$lines->[0]'";
}
if ( exists $hash->{$key} ) {
warn "CPAN::Meta::YAML found a duplicate key '$key' in line '$lines->[0]'";
}
# Do we have a value?
if ( length $lines->[0] ) {
# Yes
$hash->{$key} = $self->_load_scalar(
shift(@$lines), [ @$indent, undef ], $lines
);
} else {
# An indent
shift @$lines;
unless ( @$lines ) {
$hash->{$key} = undef;
return 1;
}
if ( $lines->[0] =~ /^(\s*)-/ ) {
$hash->{$key} = [];
$self->_load_array(
$hash->{$key}, [ @$indent, length($1) ], $lines
);
} elsif ( $lines->[0] =~ /^(\s*)./ ) {
my $indent2 = length("$1");
if ( $indent->[-1] >= $indent2 ) {
# Null hash entry
$hash->{$key} = undef;
} else {
$hash->{$key} = {};
$self->_load_hash(
$hash->{$key}, [ @$indent, length($1) ], $lines
);
}
}
}
}
return 1;
}
###
# Dumper functions:
# Save an object to a file
sub _dump_file {
my $self = shift;
require Fcntl;
# Check the file
my $file = shift or $self->_error( 'You did not specify a file name' );
my $fh;
# flock if available (or warn if not possible for OS-specific reasons)
if ( _can_flock() ) {
# Open without truncation (truncate comes after lock)
my $flags = Fcntl::O_WRONLY()|Fcntl::O_CREAT();
sysopen( $fh, $file, $flags );
unless ( $fh ) {
$self->_error("Failed to open file '$file' for writing: $!");
}
# Use no translation and strict UTF-8
binmode( $fh, ":raw:encoding(UTF-8)");
flock( $fh, Fcntl::LOCK_EX() )
or warn "Couldn't lock '$file' for reading: $!";
# truncate and spew contents
truncate $fh, 0;
seek $fh, 0, 0;
}
else {
open $fh, ">:unix:encoding(UTF-8)", $file;
}
# serialize and spew to the handle
print {$fh} $self->_dump_string;
# close the file (release the lock)
unless ( close $fh ) {
$self->_error("Failed to close file '$file': $!");
}
return 1;
}
# Save an object to a string
sub _dump_string {
my $self = shift;
return '' unless ref $self && @$self;
# Iterate over the documents
my $indent = 0;
my @lines = ();
eval {
foreach my $cursor ( @$self ) {
push @lines, '---';
# An empty document
if ( ! defined $cursor ) {
# Do nothing
# A scalar document
} elsif ( ! ref $cursor ) {
$lines[-1] .= ' ' . $self->_dump_scalar( $cursor );
# A list at the root
} elsif ( ref $cursor eq 'ARRAY' ) {
unless ( @$cursor ) {
$lines[-1] .= ' []';
next;
}
push @lines, $self->_dump_array( $cursor, $indent, {} );
# A hash at the root
} elsif ( ref $cursor eq 'HASH' ) {
unless ( %$cursor ) {
$lines[-1] .= ' {}';
next;
}
push @lines, $self->_dump_hash( $cursor, $indent, {} );
} else {
die \("Cannot serialize " . ref($cursor));
}
}
};
if ( ref $@ eq 'SCALAR' ) {
$self->_error(${$@});
} elsif ( $@ ) {
$self->_error($@);
}
join '', map { "$_\n" } @lines;
}
sub _has_internal_string_value {
my $value = shift;
my $b_obj = B::svref_2object(\$value); # for round trip problem
return $b_obj->FLAGS & B::SVf_POK();
}
sub _dump_scalar {
my $string = $_[1];
my $is_key = $_[2];
# Check this before checking length or it winds up looking like a string!
my $has_string_flag = _has_internal_string_value($string);
return '~' unless defined $string;
return "''" unless length $string;
if (Scalar::Util::looks_like_number($string)) {
# keys and values that have been used as strings get quoted
if ( $is_key || $has_string_flag ) {
return qq['$string'];
}
else {
return $string;
}
}
if ( $string =~ /[\x00-\x09\x0b-\x0d\x0e-\x1f\x7f-\x9f\'\n]/ ) {
$string =~ s/\\/\\\\/g;
$string =~ s/"/\\"/g;
$string =~ s/\n/\\n/g;
$string =~ s/[\x85]/\\N/g;
$string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g;
$string =~ s/([\x7f-\x9f])/'\x' . sprintf("%X",ord($1))/ge;
return qq|"$string"|;
}
if ( $string =~ /(?:^[~!@#%&*|>?:,'"`{}\[\]]|^-+$|\s|:\z)/ or
$QUOTE{$string}
) {
return "'$string'";
}
return $string;
}
sub _dump_array {
my ($self, $array, $indent, $seen) = @_;
if ( $seen->{refaddr($array)}++ ) {
die \"CPAN::Meta::YAML does not support circular references";
}
my @lines = ();
foreach my $el ( @$array ) {
my $line = (' ' x $indent) . '-';
my $type = ref $el;
if ( ! $type ) {
$line .= ' ' . $self->_dump_scalar( $el );
push @lines, $line;
} elsif ( $type eq 'ARRAY' ) {
if ( @$el ) {
push @lines, $line;
push @lines, $self->_dump_array( $el, $indent + 1, $seen );
} else {
$line .= ' []';
push @lines, $line;
}
} elsif ( $type eq 'HASH' ) {
if ( keys %$el ) {
push @lines, $line;
push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
} else {
$line .= ' {}';
push @lines, $line;
}
} else {
die \"CPAN::Meta::YAML does not support $type references";
}
}
@lines;
}
sub _dump_hash {
my ($self, $hash, $indent, $seen) = @_;
if ( $seen->{refaddr($hash)}++ ) {
die \"CPAN::Meta::YAML does not support circular references";
}
my @lines = ();
foreach my $name ( sort keys %$hash ) {
my $el = $hash->{$name};
my $line = (' ' x $indent) . $self->_dump_scalar($name, 1) . ":";
my $type = ref $el;
if ( ! $type ) {
$line .= ' ' . $self->_dump_scalar( $el );
push @lines, $line;
} elsif ( $type eq 'ARRAY' ) {
if ( @$el ) {
push @lines, $line;
push @lines, $self->_dump_array( $el, $indent + 1, $seen );
} else {
$line .= ' []';
push @lines, $line;
}
} elsif ( $type eq 'HASH' ) {
if ( keys %$el ) {
push @lines, $line;
push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
} else {
$line .= ' {}';
push @lines, $line;
}
} else {
die \"CPAN::Meta::YAML does not support $type references";
}
}
@lines;
}
#####################################################################
# DEPRECATED API methods:
# Error storage (DEPRECATED as of 1.57)
our $errstr = '';
# Set error
sub _error {
require Carp;
$errstr = $_[1];
$errstr =~ s/ at \S+ line \d+.*//;
Carp::croak( $errstr );
}
# Retrieve error
my $errstr_warned;
sub errstr {
require Carp;
Carp::carp( "CPAN::Meta::YAML->errstr and \$CPAN::Meta::YAML::errstr is deprecated" )
unless $errstr_warned++;
$errstr;
}
#####################################################################
# Helper functions. Possibly not needed.
# Use to detect nv or iv
use B;
# XXX-INGY Is flock CPAN::Meta::YAML's responsibility?
# Some platforms can't flock :-(
# XXX-XDG I think it is. When reading and writing files, we ought
# to be locking whenever possible. People (foolishly) use YAML
# files for things like session storage, which has race issues.
my $HAS_FLOCK;
sub _can_flock {
if ( defined $HAS_FLOCK ) {
return $HAS_FLOCK;
}
else {
require Config;
my $c = \%Config::Config;
$HAS_FLOCK = grep { $c->{$_} } qw/d_flock d_fcntl_can_lock d_lockf/;
require Fcntl if $HAS_FLOCK;
return $HAS_FLOCK;
}
}
# XXX-INGY Is this core in 5.8.1? Can we remove this?
# XXX-XDG Scalar::Util 1.18 didn't land until 5.8.8, so we need this
#####################################################################
# Use Scalar::Util if possible, otherwise emulate it
use Scalar::Util ();
BEGIN {
local $@;
if ( eval { Scalar::Util->VERSION(1.18); } ) {
*refaddr = *Scalar::Util::refaddr;
}
else {
eval <<'END_PERL';
# Scalar::Util failed to load or too old
sub refaddr {
my $pkg = ref($_[0]) or return undef;
if ( !! UNIVERSAL::can($_[0], 'can') ) {
bless $_[0], 'Scalar::Util::Fake';
} else {
$pkg = undef;
}
"$_[0]" =~ /0x(\w+)/;
my $i = do { no warnings 'portable'; hex $1 };
bless $_[0], $pkg if defined $pkg;
$i;
}
END_PERL
}
}
delete $CPAN::Meta::YAML::{refaddr};
1;
# XXX-INGY Doc notes I'm putting up here. Changing the doc when it's wrong
# but leaving grey area stuff up here.
#
# I would like to change Read/Write to Load/Dump below without
# changing the actual API names.
#
# It might be better to put Load/Dump API in the SYNOPSIS instead of the
# dubious OO API.
#
# null and bool explanations may be outdated.
=pod
=encoding UTF-8
=head1 NAME
CPAN::Meta::YAML - Read and write a subset of YAML for CPAN Meta files
=head1 VERSION
version 0.018
=head1 SYNOPSIS
use CPAN::Meta::YAML;
# reading a META file
open $fh, "<:utf8", "META.yml";
$yaml_text = do { local $/; <$fh> };
$yaml = CPAN::Meta::YAML->read_string($yaml_text)
or die CPAN::Meta::YAML->errstr;
# finding the metadata
$meta = $yaml->[0];
# writing a META file
$yaml_text = $yaml->write_string
or die CPAN::Meta::YAML->errstr;
open $fh, ">:utf8", "META.yml";
print $fh $yaml_text;
=head1 DESCRIPTION
This module implements a subset of the YAML specification for use in reading
and writing CPAN metadata files like F and F. It should
not be used for any other general YAML parsing or generation task.
NOTE: F (and F) files should be UTF-8 encoded. Users are
responsible for proper encoding and decoding. In particular, the C and
C methods do B support UTF-8 and should not be used.
=head1 SUPPORT
This module is currently derived from L by Adam Kennedy. If
there are bugs in how it parses a particular META.yml file, please file
a bug report in the YAML::Tiny bugtracker:
L
=head1 SEE ALSO
L, L, L
=head1 AUTHORS
=over 4
=item *
Adam Kennedy
=item *
David Golden
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Adam Kennedy.
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
__END__
# ABSTRACT: Read and write a subset of YAML for CPAN Meta files
PK ! qN N Converter.pmnu [ use 5.006;
use strict;
use warnings;
package CPAN::Meta::Converter;
our $VERSION = '2.150010';
#pod =head1 SYNOPSIS
#pod
#pod my $struct = decode_json_file('META.json');
#pod
#pod my $cmc = CPAN::Meta::Converter->new( $struct );
#pod
#pod my $new_struct = $cmc->convert( version => "2" );
#pod
#pod =head1 DESCRIPTION
#pod
#pod This module converts CPAN Meta structures from one form to another. The
#pod primary use is to convert older structures to the most modern version of
#pod the specification, but other transformations may be implemented in the
#pod future as needed. (E.g. stripping all custom fields or stripping all
#pod optional fields.)
#pod
#pod =cut
use CPAN::Meta::Validator;
use CPAN::Meta::Requirements;
use Parse::CPAN::Meta 1.4400 ();
# To help ExtUtils::MakeMaker bootstrap CPAN::Meta::Requirements on perls
# before 5.10, we fall back to the EUMM bundled compatibility version module if
# that's the only thing available. This shouldn't ever happen in a normal CPAN
# install of CPAN::Meta::Requirements, as version.pm will be picked up from
# prereqs and be available at runtime.
BEGIN {
eval "use version ()"; ## no critic
if ( my $err = $@ ) {
eval "use ExtUtils::MakeMaker::version" or die $err; ## no critic
}
}
# Perl 5.10.0 didn't have "is_qv" in version.pm
*_is_qv = version->can('is_qv') ? sub { $_[0]->is_qv } : sub { exists $_[0]->{qv} };
# We limit cloning to a maximum depth to bail out on circular data
# structures. While actual cycle detection might be technically better,
# we expect circularity in META data structures to be rare and generally
# the result of user error. Therefore, a depth counter is lower overhead.
our $DCLONE_MAXDEPTH = 1024;
our $_CLONE_DEPTH;
sub _dclone {
my ( $ref ) = @_;
return $ref unless my $reftype = ref $ref;
local $_CLONE_DEPTH = defined $_CLONE_DEPTH ? $_CLONE_DEPTH - 1 : $DCLONE_MAXDEPTH;
die "Depth Limit $DCLONE_MAXDEPTH Exceeded" if $_CLONE_DEPTH == 0;
return [ map { _dclone( $_ ) } @{$ref} ] if 'ARRAY' eq $reftype;
return { map { $_ => _dclone( $ref->{$_} ) } keys %{$ref} } if 'HASH' eq $reftype;
if ( 'SCALAR' eq $reftype ) {
my $new = _dclone(${$ref});
return \$new;
}
# We can't know if TO_JSON gives us cloned data, so refs must recurse
if ( eval { $ref->can('TO_JSON') } ) {
my $data = $ref->TO_JSON;
return ref $data ? _dclone( $data ) : $data;
}
# Just stringify everything else
return "$ref";
}
my %known_specs = (
'2' => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
'1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
'1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
'1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
'1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
'1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
);
my @spec_list = sort { $a <=> $b } keys %known_specs;
my ($LOWEST, $HIGHEST) = @spec_list[0,-1];
#--------------------------------------------------------------------------#
# converters
#
# called as $converter->($element, $field_name, $full_meta, $to_version)
#
# defined return value used for field
# undef return value means field is skipped
#--------------------------------------------------------------------------#
sub _keep { $_[0] }
sub _keep_or_one { defined($_[0]) ? $_[0] : 1 }
sub _keep_or_zero { defined($_[0]) ? $_[0] : 0 }
sub _keep_or_unknown { defined($_[0]) && length($_[0]) ? $_[0] : "unknown" }
sub _generated_by {
my $gen = shift;
my $sig = __PACKAGE__ . " version " . (__PACKAGE__->VERSION || "");
return $sig unless defined $gen and length $gen;
return $gen if $gen =~ /\Q$sig/;
return "$gen, $sig";
}
sub _listify { ! defined $_[0] ? undef : ref $_[0] eq 'ARRAY' ? $_[0] : [$_[0]] }
sub _prefix_custom {
my $key = shift;
$key =~ s/^(?!x_) # Unless it already starts with x_
(?:x-?)? # Remove leading x- or x (if present)
/x_/ix; # and prepend x_
return $key;
}
sub _ucfirst_custom {
my $key = shift;
$key = ucfirst $key unless $key =~ /[A-Z]/;
return $key;
}
sub _no_prefix_ucfirst_custom {
my $key = shift;
$key =~ s/^x_//;
return _ucfirst_custom($key);
}
sub _change_meta_spec {
my ($element, undef, undef, $version) = @_;
return {
version => $version,
url => $known_specs{$version},
};
}
my @open_source = (
'perl',
'gpl',
'apache',
'artistic',
'artistic_2',
'lgpl',
'bsd',
'gpl',
'mit',
'mozilla',
'open_source',
);
my %is_open_source = map {; $_ => 1 } @open_source;
my @valid_licenses_1 = (
@open_source,
'unrestricted',
'restrictive',
'unknown',
);
my %license_map_1 = (
( map { $_ => $_ } @valid_licenses_1 ),
artistic2 => 'artistic_2',
);
sub _license_1 {
my ($element) = @_;
return 'unknown' unless defined $element;
if ( $license_map_1{lc $element} ) {
return $license_map_1{lc $element};
}
else {
return 'unknown';
}
}
my @valid_licenses_2 = qw(
agpl_3
apache_1_1
apache_2_0
artistic_1
artistic_2
bsd
freebsd
gfdl_1_2
gfdl_1_3
gpl_1
gpl_2
gpl_3
lgpl_2_1
lgpl_3_0
mit
mozilla_1_0
mozilla_1_1
openssl
perl_5
qpl_1_0
ssleay
sun
zlib
open_source
restricted
unrestricted
unknown
);
# The "old" values were defined by Module::Build, and were often vague. I have
# made the decisions below based on reading Module::Build::API and how clearly
# it specifies the version of the license.
my %license_map_2 = (
(map { $_ => $_ } @valid_licenses_2),
apache => 'apache_2_0', # clearly stated as 2.0
artistic => 'artistic_1', # clearly stated as 1
artistic2 => 'artistic_2', # clearly stated as 2
gpl => 'open_source', # we don't know which GPL; punt
lgpl => 'open_source', # we don't know which LGPL; punt
mozilla => 'open_source', # we don't know which MPL; punt
perl => 'perl_5', # clearly Perl 5
restrictive => 'restricted',
);
sub _license_2 {
my ($element) = @_;
return [ 'unknown' ] unless defined $element;
$element = [ $element ] unless ref $element eq 'ARRAY';
my @new_list;
for my $lic ( @$element ) {
next unless defined $lic;
if ( my $new = $license_map_2{lc $lic} ) {
push @new_list, $new;
}
}
return @new_list ? \@new_list : [ 'unknown' ];
}
my %license_downgrade_map = qw(
agpl_3 open_source
apache_1_1 apache
apache_2_0 apache
artistic_1 artistic
artistic_2 artistic_2
bsd bsd
freebsd open_source
gfdl_1_2 open_source
gfdl_1_3 open_source
gpl_1 gpl
gpl_2 gpl
gpl_3 gpl
lgpl_2_1 lgpl
lgpl_3_0 lgpl
mit mit
mozilla_1_0 mozilla
mozilla_1_1 mozilla
openssl open_source
perl_5 perl
qpl_1_0 open_source
ssleay open_source
sun open_source
zlib open_source
open_source open_source
restricted restrictive
unrestricted unrestricted
unknown unknown
);
sub _downgrade_license {
my ($element) = @_;
if ( ! defined $element ) {
return "unknown";
}
elsif( ref $element eq 'ARRAY' ) {
if ( @$element > 1) {
if (grep { !$is_open_source{ $license_downgrade_map{lc $_} || 'unknown' } } @$element) {
return 'unknown';
}
else {
return 'open_source';
}
}
elsif ( @$element == 1 ) {
return $license_downgrade_map{lc $element->[0]} || "unknown";
}
}
elsif ( ! ref $element ) {
return $license_downgrade_map{lc $element} || "unknown";
}
return "unknown";
}
my $no_index_spec_1_2 = {
'file' => \&_listify,
'dir' => \&_listify,
'package' => \&_listify,
'namespace' => \&_listify,
};
my $no_index_spec_1_3 = {
'file' => \&_listify,
'directory' => \&_listify,
'package' => \&_listify,
'namespace' => \&_listify,
};
my $no_index_spec_2 = {
'file' => \&_listify,
'directory' => \&_listify,
'package' => \&_listify,
'namespace' => \&_listify,
':custom' => \&_prefix_custom,
};
sub _no_index_1_2 {
my (undef, undef, $meta) = @_;
my $no_index = $meta->{no_index} || $meta->{private};
return unless $no_index;
# cleanup wrong format
if ( ! ref $no_index ) {
my $item = $no_index;
$no_index = { dir => [ $item ], file => [ $item ] };
}
elsif ( ref $no_index eq 'ARRAY' ) {
my $list = $no_index;
$no_index = { dir => [ @$list ], file => [ @$list ] };
}
# common mistake: files -> file
if ( exists $no_index->{files} ) {
$no_index->{file} = delete $no_index->{files};
}
# common mistake: modules -> module
if ( exists $no_index->{modules} ) {
$no_index->{module} = delete $no_index->{modules};
}
return _convert($no_index, $no_index_spec_1_2);
}
sub _no_index_directory {
my ($element, $key, $meta, $version) = @_;
return unless $element;
# clean up wrong format
if ( ! ref $element ) {
my $item = $element;
$element = { directory => [ $item ], file => [ $item ] };
}
elsif ( ref $element eq 'ARRAY' ) {
my $list = $element;
$element = { directory => [ @$list ], file => [ @$list ] };
}
if ( exists $element->{dir} ) {
$element->{directory} = delete $element->{dir};
}
# common mistake: files -> file
if ( exists $element->{files} ) {
$element->{file} = delete $element->{files};
}
# common mistake: modules -> module
if ( exists $element->{modules} ) {
$element->{module} = delete $element->{modules};
}
my $spec = $version == 2 ? $no_index_spec_2 : $no_index_spec_1_3;
return _convert($element, $spec);
}
sub _is_module_name {
my $mod = shift;
return unless defined $mod && length $mod;
return $mod =~ m{^[A-Za-z][A-Za-z0-9_]*(?:::[A-Za-z0-9_]+)*$};
}
sub _clean_version {
my ($element) = @_;
return 0 if ! defined $element;
$element =~ s{^\s*}{};
$element =~ s{\s*$}{};
$element =~ s{^\.}{0.};
return 0 if ! length $element;
return 0 if ( $element eq 'undef' || $element eq '' );
my $v = eval { version->new($element) };
# XXX check defined $v and not just $v because version objects leak memory
# in boolean context -- dagolden, 2012-02-03
if ( defined $v ) {
return _is_qv($v) ? $v->normal : $element;
}
else {
return 0;
}
}
sub _bad_version_hook {
my ($v) = @_;
$v =~ s{^\s*}{};
$v =~ s{\s*$}{};
$v =~ s{[a-z]+$}{}; # strip trailing alphabetics
my $vobj = eval { version->new($v) };
return defined($vobj) ? $vobj : version->new(0); # or give up
}
sub _version_map {
my ($element) = @_;
return unless defined $element;
if ( ref $element eq 'HASH' ) {
# XXX turn this into CPAN::Meta::Requirements with bad version hook
# and then turn it back into a hash
my $new_map = CPAN::Meta::Requirements->new(
{ bad_version_hook => \&_bad_version_hook } # punt
);
while ( my ($k,$v) = each %$element ) {
next unless _is_module_name($k);
if ( !defined($v) || !length($v) || $v eq 'undef' || $v eq '' ) {
$v = 0;
}
# some weird, old META have bad yml with module => module
# so check if value is like a module name and not like a version
if ( _is_module_name($v) && ! version::is_lax($v) ) {
$new_map->add_minimum($k => 0);
$new_map->add_minimum($v => 0);
}
$new_map->add_string_requirement($k => $v);
}
return $new_map->as_string_hash;
}
elsif ( ref $element eq 'ARRAY' ) {
my $hashref = { map { $_ => 0 } @$element };
return _version_map($hashref); # clean up any weird stuff
}
elsif ( ref $element eq '' && length $element ) {
return { $element => 0 }
}
return;
}
sub _prereqs_from_1 {
my (undef, undef, $meta) = @_;
my $prereqs = {};
for my $phase ( qw/build configure/ ) {
my $key = "${phase}_requires";
$prereqs->{$phase}{requires} = _version_map($meta->{$key})
if $meta->{$key};
}
for my $rel ( qw/requires recommends conflicts/ ) {
$prereqs->{runtime}{$rel} = _version_map($meta->{$rel})
if $meta->{$rel};
}
return $prereqs;
}
my $prereqs_spec = {
configure => \&_prereqs_rel,
build => \&_prereqs_rel,
test => \&_prereqs_rel,
runtime => \&_prereqs_rel,
develop => \&_prereqs_rel,
':custom' => \&_prefix_custom,
};
my $relation_spec = {
requires => \&_version_map,
recommends => \&_version_map,
suggests => \&_version_map,
conflicts => \&_version_map,
':custom' => \&_prefix_custom,
};
sub _cleanup_prereqs {
my ($prereqs, $key, $meta, $to_version) = @_;
return unless $prereqs && ref $prereqs eq 'HASH';
return _convert( $prereqs, $prereqs_spec, $to_version );
}
sub _prereqs_rel {
my ($relation, $key, $meta, $to_version) = @_;
return unless $relation && ref $relation eq 'HASH';
return _convert( $relation, $relation_spec, $to_version );
}
BEGIN {
my @old_prereqs = qw(
requires
configure_requires
recommends
conflicts
);
for ( @old_prereqs ) {
my $sub = "_get_$_";
my ($phase,$type) = split qr/_/, $_;
if ( ! defined $type ) {
$type = $phase;
$phase = 'runtime';
}
no strict 'refs';
*{$sub} = sub { _extract_prereqs($_[2]->{prereqs},$phase,$type) };
}
}
sub _get_build_requires {
my ($data, $key, $meta) = @_;
my $test_h = _extract_prereqs($_[2]->{prereqs}, qw(test requires)) || {};
my $build_h = _extract_prereqs($_[2]->{prereqs}, qw(build requires)) || {};
my $test_req = CPAN::Meta::Requirements->from_string_hash($test_h);
my $build_req = CPAN::Meta::Requirements->from_string_hash($build_h);
$test_req->add_requirements($build_req)->as_string_hash;
}
sub _extract_prereqs {
my ($prereqs, $phase, $type) = @_;
return unless ref $prereqs eq 'HASH';
return scalar _version_map($prereqs->{$phase}{$type});
}
sub _downgrade_optional_features {
my (undef, undef, $meta) = @_;
return unless exists $meta->{optional_features};
my $origin = $meta->{optional_features};
my $features = {};
for my $name ( keys %$origin ) {
$features->{$name} = {
description => $origin->{$name}{description},
requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','requires'),
configure_requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','configure_requires'),
build_requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','build_requires'),
recommends => _extract_prereqs($origin->{$name}{prereqs},'runtime','recommends'),
conflicts => _extract_prereqs($origin->{$name}{prereqs},'runtime','conflicts'),
};
for my $k (keys %{$features->{$name}} ) {
delete $features->{$name}{$k} unless defined $features->{$name}{$k};
}
}
return $features;
}
sub _upgrade_optional_features {
my (undef, undef, $meta) = @_;
return unless exists $meta->{optional_features};
my $origin = $meta->{optional_features};
my $features = {};
for my $name ( keys %$origin ) {
$features->{$name} = {
description => $origin->{$name}{description},
prereqs => _prereqs_from_1(undef, undef, $origin->{$name}),
};
delete $features->{$name}{prereqs}{configure};
}
return $features;
}
my $optional_features_2_spec = {
description => \&_keep,
prereqs => \&_cleanup_prereqs,
':custom' => \&_prefix_custom,
};
sub _feature_2 {
my ($element, $key, $meta, $to_version) = @_;
return unless $element && ref $element eq 'HASH';
_convert( $element, $optional_features_2_spec, $to_version );
}
sub _cleanup_optional_features_2 {
my ($element, $key, $meta, $to_version) = @_;
return unless $element && ref $element eq 'HASH';
my $new_data = {};
for my $k ( keys %$element ) {
$new_data->{$k} = _feature_2( $element->{$k}, $k, $meta, $to_version );
}
return unless keys %$new_data;
return $new_data;
}
sub _optional_features_1_4 {
my ($element) = @_;
return unless $element;
$element = _optional_features_as_map($element);
for my $name ( keys %$element ) {
for my $drop ( qw/requires_packages requires_os excluded_os/ ) {
delete $element->{$name}{$drop};
}
}
return $element;
}
sub _optional_features_as_map {
my ($element) = @_;
return unless $element;
if ( ref $element eq 'ARRAY' ) {
my %map;
for my $feature ( @$element ) {
my (@parts) = %$feature;
$map{$parts[0]} = $parts[1];
}
$element = \%map;
}
return $element;
}
sub _is_urlish { defined $_[0] && $_[0] =~ m{\A[-+.a-z0-9]+:.+}i }
sub _url_or_drop {
my ($element) = @_;
return $element if _is_urlish($element);
return;
}
sub _url_list {
my ($element) = @_;
return unless $element;
$element = _listify( $element );
$element = [ grep { _is_urlish($_) } @$element ];
return unless @$element;
return $element;
}
sub _author_list {
my ($element) = @_;
return [ 'unknown' ] unless $element;
$element = _listify( $element );
$element = [ map { defined $_ && length $_ ? $_ : 'unknown' } @$element ];
return [ 'unknown' ] unless @$element;
return $element;
}
my $resource2_upgrade = {
license => sub { return _is_urlish($_[0]) ? _listify( $_[0] ) : undef },
homepage => \&_url_or_drop,
bugtracker => sub {
my ($item) = @_;
return unless $item;
if ( $item =~ m{^mailto:(.*)$} ) { return { mailto => $1 } }
elsif( _is_urlish($item) ) { return { web => $item } }
else { return }
},
repository => sub { return _is_urlish($_[0]) ? { url => $_[0] } : undef },
':custom' => \&_prefix_custom,
};
sub _upgrade_resources_2 {
my (undef, undef, $meta, $version) = @_;
return unless exists $meta->{resources};
return _convert($meta->{resources}, $resource2_upgrade);
}
my $bugtracker2_spec = {
web => \&_url_or_drop,
mailto => \&_keep,
':custom' => \&_prefix_custom,
};
sub _repo_type {
my ($element, $key, $meta, $to_version) = @_;
return $element if defined $element;
return unless exists $meta->{url};
my $repo_url = $meta->{url};
for my $type ( qw/git svn/ ) {
return $type if $repo_url =~ m{\A$type};
}
return;
}
my $repository2_spec = {
web => \&_url_or_drop,
url => \&_url_or_drop,
type => \&_repo_type,
':custom' => \&_prefix_custom,
};
my $resources2_cleanup = {
license => \&_url_list,
homepage => \&_url_or_drop,
bugtracker => sub { ref $_[0] ? _convert( $_[0], $bugtracker2_spec ) : undef },
repository => sub { my $data = shift; ref $data ? _convert( $data, $repository2_spec ) : undef },
':custom' => \&_prefix_custom,
};
sub _cleanup_resources_2 {
my ($resources, $key, $meta, $to_version) = @_;
return unless $resources && ref $resources eq 'HASH';
return _convert($resources, $resources2_cleanup, $to_version);
}
my $resource1_spec = {
license => \&_url_or_drop,
homepage => \&_url_or_drop,
bugtracker => \&_url_or_drop,
repository => \&_url_or_drop,
':custom' => \&_keep,
};
sub _resources_1_3 {
my (undef, undef, $meta, $version) = @_;
return unless exists $meta->{resources};
return _convert($meta->{resources}, $resource1_spec);
}
*_resources_1_4 = *_resources_1_3;
sub _resources_1_2 {
my (undef, undef, $meta) = @_;
my $resources = $meta->{resources} || {};
if ( $meta->{license_url} && ! $resources->{license} ) {
$resources->{license} = $meta->{license_url}
if _is_urlish($meta->{license_url});
}
return unless keys %$resources;
return _convert($resources, $resource1_spec);
}
my $resource_downgrade_spec = {
license => sub { return ref $_[0] ? $_[0]->[0] : $_[0] },
homepage => \&_url_or_drop,
bugtracker => sub { return $_[0]->{web} },
repository => sub { return $_[0]->{url} || $_[0]->{web} },
':custom' => \&_no_prefix_ucfirst_custom,
};
sub _downgrade_resources {
my (undef, undef, $meta, $version) = @_;
return unless exists $meta->{resources};
return _convert($meta->{resources}, $resource_downgrade_spec);
}
sub _release_status {
my ($element, undef, $meta) = @_;
return $element if $element && $element =~ m{\A(?:stable|testing|unstable)\z};
return _release_status_from_version(undef, undef, $meta);
}
sub _release_status_from_version {
my (undef, undef, $meta) = @_;
my $version = $meta->{version} || '';
return ( $version =~ /_/ ) ? 'testing' : 'stable';
}
my $provides_spec = {
file => \&_keep,
version => \&_keep,
};
my $provides_spec_2 = {
file => \&_keep,
version => \&_keep,
':custom' => \&_prefix_custom,
};
sub _provides {
my ($element, $key, $meta, $to_version) = @_;
return unless defined $element && ref $element eq 'HASH';
my $spec = $to_version == 2 ? $provides_spec_2 : $provides_spec;
my $new_data = {};
for my $k ( keys %$element ) {
$new_data->{$k} = _convert($element->{$k}, $spec, $to_version);
$new_data->{$k}{version} = _clean_version($element->{$k}{version})
if exists $element->{$k}{version};
}
return $new_data;
}
sub _convert {
my ($data, $spec, $to_version, $is_fragment) = @_;
my $new_data = {};
for my $key ( keys %$spec ) {
next if $key eq ':custom' || $key eq ':drop';
next unless my $fcn = $spec->{$key};
if ( $is_fragment && $key eq 'generated_by' ) {
$fcn = \&_keep;
}
die "spec for '$key' is not a coderef"
unless ref $fcn && ref $fcn eq 'CODE';
my $new_value = $fcn->($data->{$key}, $key, $data, $to_version);
$new_data->{$key} = $new_value if defined $new_value;
}
my $drop_list = $spec->{':drop'};
my $customizer = $spec->{':custom'} || \&_keep;
for my $key ( keys %$data ) {
next if $drop_list && grep { $key eq $_ } @$drop_list;
next if exists $spec->{$key}; # we handled it
$new_data->{ $customizer->($key) } = $data->{$key};
}
return $new_data;
}
#--------------------------------------------------------------------------#
# define converters for each conversion
#--------------------------------------------------------------------------#
# each converts from prior version
# special ":custom" field is used for keys not recognized in spec
my %up_convert = (
'2-from-1.4' => {
# PRIOR MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_2,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# CHANGED TO MANDATORY
'dynamic_config' => \&_keep_or_one,
# ADDED MANDATORY
'release_status' => \&_release_status,
# PRIOR OPTIONAL
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_upgrade_optional_features,
'provides' => \&_provides,
'resources' => \&_upgrade_resources_2,
# ADDED OPTIONAL
'description' => \&_keep,
'prereqs' => \&_prereqs_from_1,
# drop these deprecated fields, but only after we convert
':drop' => [ qw(
build_requires
configure_requires
conflicts
distribution_type
license_url
private
recommends
requires
) ],
# other random keys need x_ prefixing
':custom' => \&_prefix_custom,
},
'1.4-from-1.3' => {
# PRIOR MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_optional_features_1_4,
'provides' => \&_provides,
'recommends' => \&_version_map,
'requires' => \&_version_map,
'resources' => \&_resources_1_4,
# ADDED OPTIONAL
'configure_requires' => \&_keep,
# drop these deprecated fields, but only after we convert
':drop' => [ qw(
license_url
private
)],
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.3-from-1.2' => {
# PRIOR MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_optional_features_as_map,
'provides' => \&_provides,
'recommends' => \&_version_map,
'requires' => \&_version_map,
'resources' => \&_resources_1_3,
# drop these deprecated fields, but only after we convert
':drop' => [ qw(
license_url
private
)],
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.2-from-1.1' => {
# PRIOR MANDATORY
'version' => \&_keep,
# CHANGED TO MANDATORY
'license' => \&_license_1,
'name' => \&_keep,
'generated_by' => \&_generated_by,
# ADDED MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'meta-spec' => \&_change_meta_spec,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# ADDED OPTIONAL
'keywords' => \&_keep,
'no_index' => \&_no_index_1_2,
'optional_features' => \&_optional_features_as_map,
'provides' => \&_provides,
'resources' => \&_resources_1_2,
# drop these deprecated fields, but only after we convert
':drop' => [ qw(
license_url
private
)],
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.1-from-1.0' => {
# CHANGED TO MANDATORY
'version' => \&_keep,
# IMPLIED MANDATORY
'name' => \&_keep,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# ADDED OPTIONAL
'license_url' => \&_url_or_drop,
'private' => \&_keep,
# other random keys are OK if already valid
':custom' => \&_keep
},
);
my %down_convert = (
'1.4-from-2' => {
# MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_downgrade_license,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# OPTIONAL
'build_requires' => \&_get_build_requires,
'configure_requires' => \&_get_configure_requires,
'conflicts' => \&_get_conflicts,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_downgrade_optional_features,
'provides' => \&_provides,
'recommends' => \&_get_recommends,
'requires' => \&_get_requires,
'resources' => \&_downgrade_resources,
# drop these unsupported fields (after conversion)
':drop' => [ qw(
description
prereqs
release_status
)],
# custom keys will be left unchanged
':custom' => \&_keep
},
'1.3-from-1.4' => {
# MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_optional_features_as_map,
'provides' => \&_provides,
'recommends' => \&_version_map,
'requires' => \&_version_map,
'resources' => \&_resources_1_3,
# drop these unsupported fields, but only after we convert
':drop' => [ qw(
configure_requires
)],
# other random keys are OK if already valid
':custom' => \&_keep,
},
'1.2-from-1.3' => {
# MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_1_2,
'optional_features' => \&_optional_features_as_map,
'provides' => \&_provides,
'recommends' => \&_version_map,
'requires' => \&_version_map,
'resources' => \&_resources_1_3,
# other random keys are OK if already valid
':custom' => \&_keep,
},
'1.1-from-1.2' => {
# MANDATORY
'version' => \&_keep,
# IMPLIED MANDATORY
'name' => \&_keep,
'meta-spec' => \&_change_meta_spec,
# OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'private' => \&_keep,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# drop unsupported fields
':drop' => [ qw(
abstract
author
provides
no_index
keywords
resources
)],
# other random keys are OK if already valid
':custom' => \&_keep,
},
'1.0-from-1.1' => {
# IMPLIED MANDATORY
'name' => \&_keep,
'meta-spec' => \&_change_meta_spec,
'version' => \&_keep,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# other random keys are OK if already valid
':custom' => \&_keep,
},
);
my %cleanup = (
'2' => {
# PRIOR MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_2,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# CHANGED TO MANDATORY
'dynamic_config' => \&_keep_or_one,
# ADDED MANDATORY
'release_status' => \&_release_status,
# PRIOR OPTIONAL
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_cleanup_optional_features_2,
'provides' => \&_provides,
'resources' => \&_cleanup_resources_2,
# ADDED OPTIONAL
'description' => \&_keep,
'prereqs' => \&_cleanup_prereqs,
# drop these deprecated fields, but only after we convert
':drop' => [ qw(
build_requires
configure_requires
conflicts
distribution_type
license_url
private
recommends
requires
) ],
# other random keys need x_ prefixing
':custom' => \&_prefix_custom,
},
'1.4' => {
# PRIOR MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_optional_features_1_4,
'provides' => \&_provides,
'recommends' => \&_version_map,
'requires' => \&_version_map,
'resources' => \&_resources_1_4,
# ADDED OPTIONAL
'configure_requires' => \&_keep,
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.3' => {
# PRIOR MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'meta-spec' => \&_change_meta_spec,
'name' => \&_keep,
'version' => \&_keep,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'keywords' => \&_keep,
'no_index' => \&_no_index_directory,
'optional_features' => \&_optional_features_as_map,
'provides' => \&_provides,
'recommends' => \&_version_map,
'requires' => \&_version_map,
'resources' => \&_resources_1_3,
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.2' => {
# PRIOR MANDATORY
'version' => \&_keep,
# CHANGED TO MANDATORY
'license' => \&_license_1,
'name' => \&_keep,
'generated_by' => \&_generated_by,
# ADDED MANDATORY
'abstract' => \&_keep_or_unknown,
'author' => \&_author_list,
'meta-spec' => \&_change_meta_spec,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# ADDED OPTIONAL
'keywords' => \&_keep,
'no_index' => \&_no_index_1_2,
'optional_features' => \&_optional_features_as_map,
'provides' => \&_provides,
'resources' => \&_resources_1_2,
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.1' => {
# CHANGED TO MANDATORY
'version' => \&_keep,
# IMPLIED MANDATORY
'name' => \&_keep,
'meta-spec' => \&_change_meta_spec,
# PRIOR OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# ADDED OPTIONAL
'license_url' => \&_url_or_drop,
'private' => \&_keep,
# other random keys are OK if already valid
':custom' => \&_keep
},
'1.0' => {
# IMPLIED MANDATORY
'name' => \&_keep,
'meta-spec' => \&_change_meta_spec,
'version' => \&_keep,
# IMPLIED OPTIONAL
'build_requires' => \&_version_map,
'conflicts' => \&_version_map,
'distribution_type' => \&_keep,
'dynamic_config' => \&_keep_or_one,
'generated_by' => \&_generated_by,
'license' => \&_license_1,
'recommends' => \&_version_map,
'requires' => \&_version_map,
# other random keys are OK if already valid
':custom' => \&_keep,
},
);
# for a given field in a spec version, what fields will it feed
# into in the *latest* spec (i.e. v2); meta-spec omitted because
# we always expect a meta-spec to be generated
my %fragments_generate = (
'2' => {
'abstract' => 'abstract',
'author' => 'author',
'generated_by' => 'generated_by',
'license' => 'license',
'name' => 'name',
'version' => 'version',
'dynamic_config' => 'dynamic_config',
'release_status' => 'release_status',
'keywords' => 'keywords',
'no_index' => 'no_index',
'optional_features' => 'optional_features',
'provides' => 'provides',
'resources' => 'resources',
'description' => 'description',
'prereqs' => 'prereqs',
},
'1.4' => {
'abstract' => 'abstract',
'author' => 'author',
'generated_by' => 'generated_by',
'license' => 'license',
'name' => 'name',
'version' => 'version',
'build_requires' => 'prereqs',
'conflicts' => 'prereqs',
'distribution_type' => 'distribution_type',
'dynamic_config' => 'dynamic_config',
'keywords' => 'keywords',
'no_index' => 'no_index',
'optional_features' => 'optional_features',
'provides' => 'provides',
'recommends' => 'prereqs',
'requires' => 'prereqs',
'resources' => 'resources',
'configure_requires' => 'prereqs',
},
);
# this is not quite true but will work well enough
# as 1.4 is a superset of earlier ones
$fragments_generate{$_} = $fragments_generate{'1.4'} for qw/1.3 1.2 1.1 1.0/;
#--------------------------------------------------------------------------#
# Code
#--------------------------------------------------------------------------#
#pod =method new
#pod
#pod my $cmc = CPAN::Meta::Converter->new( $struct );
#pod
#pod The constructor should be passed a valid metadata structure but invalid
#pod structures are accepted. If no meta-spec version is provided, version 1.0 will
#pod be assumed.
#pod
#pod Optionally, you can provide a C argument after C<$struct>:
#pod
#pod my $cmc = CPAN::Meta::Converter->new( $struct, default_version => "1.4" );
#pod
#pod This is only needed when converting a metadata fragment that does not include a
#pod C field.
#pod
#pod =cut
sub new {
my ($class,$data,%args) = @_;
# create an attributes hash
my $self = {
'data' => $data,
'spec' => _extract_spec_version($data, $args{default_version}),
};
# create the object
return bless $self, $class;
}
sub _extract_spec_version {
my ($data, $default) = @_;
my $spec = $data->{'meta-spec'};
# is meta-spec there and valid?
return( $default || "1.0" ) unless defined $spec && ref $spec eq 'HASH'; # before meta-spec?
# does the version key look like a valid version?
my $v = $spec->{version};
if ( defined $v && $v =~ /^\d+(?:\.\d+)?$/ ) {
return $v if defined $v && grep { $v eq $_ } keys %known_specs; # known spec
return $v+0 if defined $v && grep { $v == $_ } keys %known_specs; # 2.0 => 2
}
# otherwise, use heuristics: look for 1.x vs 2.0 fields
return "2" if exists $data->{prereqs};
return "1.4" if exists $data->{configure_requires};
return( $default || "1.2" ); # when meta-spec was first defined
}
#pod =method convert
#pod
#pod my $new_struct = $cmc->convert( version => "2" );
#pod
#pod Returns a new hash reference with the metadata converted to a different form.
#pod C will die if any conversion/standardization still results in an
#pod invalid structure.
#pod
#pod Valid parameters include:
#pod
#pod =over
#pod
#pod =item *
#pod
#pod C -- Indicates the desired specification version (e.g. "1.0", "1.1" ... "1.4", "2").
#pod Defaults to the latest version of the CPAN Meta Spec.
#pod
#pod =back
#pod
#pod Conversion proceeds through each version in turn. For example, a version 1.2
#pod structure might be converted to 1.3 then 1.4 then finally to version 2. The
#pod conversion process attempts to clean-up simple errors and standardize data.
#pod For example, if C is given as a scalar, it will converted to an array
#pod reference containing the item. (Converting a structure to its own version will
#pod also clean-up and standardize.)
#pod
#pod When data are cleaned and standardized, missing or invalid fields will be
#pod replaced with sensible defaults when possible. This may be lossy or imprecise.
#pod For example, some badly structured META.yml files on CPAN have prerequisite
#pod modules listed as both keys and values:
#pod
#pod requires => { 'Foo::Bar' => 'Bam::Baz' }
#pod
#pod These would be split and each converted to a prerequisite with a minimum
#pod version of zero.
#pod
#pod When some mandatory fields are missing or invalid, the conversion will attempt
#pod to provide a sensible default or will fill them with a value of 'unknown'. For
#pod example a missing or unrecognized C field will result in a C
#pod field of 'unknown'. Fields that may get an 'unknown' include:
#pod
#pod =for :list
#pod * abstract
#pod * author
#pod * license
#pod
#pod =cut
sub convert {
my ($self, %args) = @_;
my $args = { %args };
my $new_version = $args->{version} || $HIGHEST;
my $is_fragment = $args->{is_fragment};
my ($old_version) = $self->{spec};
my $converted = _dclone($self->{data});
if ( $old_version == $new_version ) {
$converted = _convert( $converted, $cleanup{$old_version}, $old_version, $is_fragment );
unless ( $args->{is_fragment} ) {
my $cmv = CPAN::Meta::Validator->new( $converted );
unless ( $cmv->is_valid ) {
my $errs = join("\n", $cmv->errors);
die "Failed to clean-up $old_version metadata. Errors:\n$errs\n";
}
}
return $converted;
}
elsif ( $old_version > $new_version ) {
my @vers = sort { $b <=> $a } keys %known_specs;
for my $i ( 0 .. $#vers-1 ) {
next if $vers[$i] > $old_version;
last if $vers[$i+1] < $new_version;
my $spec_string = "$vers[$i+1]-from-$vers[$i]";
$converted = _convert( $converted, $down_convert{$spec_string}, $vers[$i+1], $is_fragment );
unless ( $args->{is_fragment} ) {
my $cmv = CPAN::Meta::Validator->new( $converted );
unless ( $cmv->is_valid ) {
my $errs = join("\n", $cmv->errors);
die "Failed to downconvert metadata to $vers[$i+1]. Errors:\n$errs\n";
}
}
}
return $converted;
}
else {
my @vers = sort { $a <=> $b } keys %known_specs;
for my $i ( 0 .. $#vers-1 ) {
next if $vers[$i] < $old_version;
last if $vers[$i+1] > $new_version;
my $spec_string = "$vers[$i+1]-from-$vers[$i]";
$converted = _convert( $converted, $up_convert{$spec_string}, $vers[$i+1], $is_fragment );
unless ( $args->{is_fragment} ) {
my $cmv = CPAN::Meta::Validator->new( $converted );
unless ( $cmv->is_valid ) {
my $errs = join("\n", $cmv->errors);
die "Failed to upconvert metadata to $vers[$i+1]. Errors:\n$errs\n";
}
}
}
return $converted;
}
}
#pod =method upgrade_fragment
#pod
#pod my $new_struct = $cmc->upgrade_fragment;
#pod
#pod Returns a new hash reference with the metadata converted to the latest version
#pod of the CPAN Meta Spec. No validation is done on the result -- you must
#pod validate after merging fragments into a complete metadata document.
#pod
#pod Available since version 2.141170.
#pod
#pod =cut
sub upgrade_fragment {
my ($self) = @_;
my ($old_version) = $self->{spec};
my %expected =
map {; $_ => 1 }
grep { defined }
map { $fragments_generate{$old_version}{$_} }
keys %{ $self->{data} };
my $converted = $self->convert( version => $HIGHEST, is_fragment => 1 );
for my $key ( keys %$converted ) {
next if $key =~ /^x_/i || $key eq 'meta-spec';
delete $converted->{$key} unless $expected{$key};
}
return $converted;
}
1;
# ABSTRACT: Convert CPAN distribution metadata structures
=pod
=encoding UTF-8
=head1 NAME
CPAN::Meta::Converter - Convert CPAN distribution metadata structures
=head1 VERSION
version 2.150010
=head1 SYNOPSIS
my $struct = decode_json_file('META.json');
my $cmc = CPAN::Meta::Converter->new( $struct );
my $new_struct = $cmc->convert( version => "2" );
=head1 DESCRIPTION
This module converts CPAN Meta structures from one form to another. The
primary use is to convert older structures to the most modern version of
the specification, but other transformations may be implemented in the
future as needed. (E.g. stripping all custom fields or stripping all
optional fields.)
=head1 METHODS
=head2 new
my $cmc = CPAN::Meta::Converter->new( $struct );
The constructor should be passed a valid metadata structure but invalid
structures are accepted. If no meta-spec version is provided, version 1.0 will
be assumed.
Optionally, you can provide a C argument after C<$struct>:
my $cmc = CPAN::Meta::Converter->new( $struct, default_version => "1.4" );
This is only needed when converting a metadata fragment that does not include a
C field.
=head2 convert
my $new_struct = $cmc->convert( version => "2" );
Returns a new hash reference with the metadata converted to a different form.
C will die if any conversion/standardization still results in an
invalid structure.
Valid parameters include:
=over
=item *
C -- Indicates the desired specification version (e.g. "1.0", "1.1" ... "1.4", "2").
Defaults to the latest version of the CPAN Meta Spec.
=back
Conversion proceeds through each version in turn. For example, a version 1.2
structure might be converted to 1.3 then 1.4 then finally to version 2. The
conversion process attempts to clean-up simple errors and standardize data.
For example, if C is given as a scalar, it will converted to an array
reference containing the item. (Converting a structure to its own version will
also clean-up and standardize.)
When data are cleaned and standardized, missing or invalid fields will be
replaced with sensible defaults when possible. This may be lossy or imprecise.
For example, some badly structured META.yml files on CPAN have prerequisite
modules listed as both keys and values:
requires => { 'Foo::Bar' => 'Bam::Baz' }
These would be split and each converted to a prerequisite with a minimum
version of zero.
When some mandatory fields are missing or invalid, the conversion will attempt
to provide a sensible default or will fill them with a value of 'unknown'. For
example a missing or unrecognized C field will result in a C
field of 'unknown'. Fields that may get an 'unknown' include:
=over 4
=item *
abstract
=item *
author
=item *
license
=back
=head2 upgrade_fragment
my $new_struct = $cmc->upgrade_fragment;
Returns a new hash reference with the metadata converted to the latest version
of the CPAN Meta Spec. No validation is done on the result -- you must
validate after merging fragments into a complete metadata document.
Available since version 2.141170.
=head1 BUGS
Please report any bugs or feature using the CPAN Request Tracker.
Bugs can be submitted through the web interface at
L
When submitting a bug or request, please include a test-file or a patch to an
existing test-file that illustrates the bug or desired feature.
=head1 AUTHORS
=over 4
=item *
David Golden
=item *
Ricardo Signes
=item *
Adam Kennedy
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by David Golden, Ricardo Signes, Adam Kennedy and Contributors.
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
__END__
# vim: ts=2 sts=2 sw=2 et :
PK !
T Validator.pmnu [ use 5.006;
use strict;
use warnings;
package CPAN::Meta::Validator;
our $VERSION = '2.150010';
#pod =head1 SYNOPSIS
#pod
#pod my $struct = decode_json_file('META.json');
#pod
#pod my $cmv = CPAN::Meta::Validator->new( $struct );
#pod
#pod unless ( $cmv->is_valid ) {
#pod my $msg = "Invalid META structure. Errors found:\n";
#pod $msg .= join( "\n", $cmv->errors );
#pod die $msg;
#pod }
#pod
#pod =head1 DESCRIPTION
#pod
#pod This module validates a CPAN Meta structure against the version of the
#pod the specification claimed in the C field of the structure.
#pod
#pod =cut
#--------------------------------------------------------------------------#
# This code copied and adapted from Test::CPAN::Meta
# by Barbie, for Miss Barbell Productions,
# L
#--------------------------------------------------------------------------#
#--------------------------------------------------------------------------#
# Specification Definitions
#--------------------------------------------------------------------------#
my %known_specs = (
'1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
'1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
'1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
'1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
'1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
);
my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } };
my $no_index_2 = {
'map' => { file => { list => { value => \&string } },
directory => { list => { value => \&string } },
'package' => { list => { value => \&string } },
namespace => { list => { value => \&string } },
':key' => { name => \&custom_2, value => \&anything },
}
};
my $no_index_1_3 = {
'map' => { file => { list => { value => \&string } },
directory => { list => { value => \&string } },
'package' => { list => { value => \&string } },
namespace => { list => { value => \&string } },
':key' => { name => \&string, value => \&anything },
}
};
my $no_index_1_2 = {
'map' => { file => { list => { value => \&string } },
dir => { list => { value => \&string } },
'package' => { list => { value => \&string } },
namespace => { list => { value => \&string } },
':key' => { name => \&string, value => \&anything },
}
};
my $no_index_1_1 = {
'map' => { ':key' => { name => \&string, list => { value => \&string } },
}
};
my $prereq_map = {
map => {
':key' => {
name => \&phase,
'map' => {
':key' => {
name => \&relation,
%$module_map1,
},
},
}
},
};
my %definitions = (
'2' => {
# REQUIRED
'abstract' => { mandatory => 1, value => \&string },
'author' => { mandatory => 1, list => { value => \&string } },
'dynamic_config' => { mandatory => 1, value => \&boolean },
'generated_by' => { mandatory => 1, value => \&string },
'license' => { mandatory => 1, list => { value => \&license } },
'meta-spec' => {
mandatory => 1,
'map' => {
version => { mandatory => 1, value => \&version},
url => { value => \&url },
':key' => { name => \&custom_2, value => \&anything },
}
},
'name' => { mandatory => 1, value => \&string },
'release_status' => { mandatory => 1, value => \&release_status },
'version' => { mandatory => 1, value => \&version },
# OPTIONAL
'description' => { value => \&string },
'keywords' => { list => { value => \&string } },
'no_index' => $no_index_2,
'optional_features' => {
'map' => {
':key' => {
name => \&string,
'map' => {
description => { value => \&string },
prereqs => $prereq_map,
':key' => { name => \&custom_2, value => \&anything },
}
}
}
},
'prereqs' => $prereq_map,
'provides' => {
'map' => {
':key' => {
name => \&module,
'map' => {
file => { mandatory => 1, value => \&file },
version => { value => \&version },
':key' => { name => \&custom_2, value => \&anything },
}
}
}
},
'resources' => {
'map' => {
license => { list => { value => \&url } },
homepage => { value => \&url },
bugtracker => {
'map' => {
web => { value => \&url },
mailto => { value => \&string},
':key' => { name => \&custom_2, value => \&anything },
}
},
repository => {
'map' => {
web => { value => \&url },
url => { value => \&url },
type => { value => \&string },
':key' => { name => \&custom_2, value => \&anything },
}
},
':key' => { value => \&string, name => \&custom_2 },
}
},
# CUSTOM -- additional user defined key/value pairs
# note we can only validate the key name, as the structure is user defined
':key' => { name => \&custom_2, value => \&anything },
},
'1.4' => {
'meta-spec' => {
mandatory => 1,
'map' => {
version => { mandatory => 1, value => \&version},
url => { mandatory => 1, value => \&urlspec },
':key' => { name => \&string, value => \&anything },
},
},
'name' => { mandatory => 1, value => \&string },
'version' => { mandatory => 1, value => \&version },
'abstract' => { mandatory => 1, value => \&string },
'author' => { mandatory => 1, list => { value => \&string } },
'license' => { mandatory => 1, value => \&license },
'generated_by' => { mandatory => 1, value => \&string },
'distribution_type' => { value => \&string },
'dynamic_config' => { value => \&boolean },
'requires' => $module_map1,
'recommends' => $module_map1,
'build_requires' => $module_map1,
'configure_requires' => $module_map1,
'conflicts' => $module_map2,
'optional_features' => {
'map' => {
':key' => { name => \&string,
'map' => { description => { value => \&string },
requires => $module_map1,
recommends => $module_map1,
build_requires => $module_map1,
conflicts => $module_map2,
':key' => { name => \&string, value => \&anything },
}
}
}
},
'provides' => {
'map' => {
':key' => { name => \&module,
'map' => {
file => { mandatory => 1, value => \&file },
version => { value => \&version },
':key' => { name => \&string, value => \&anything },
}
}
}
},
'no_index' => $no_index_1_3,
'private' => $no_index_1_3,
'keywords' => { list => { value => \&string } },
'resources' => {
'map' => { license => { value => \&url },
homepage => { value => \&url },
bugtracker => { value => \&url },
repository => { value => \&url },
':key' => { value => \&string, name => \&custom_1 },
}
},
# additional user defined key/value pairs
# note we can only validate the key name, as the structure is user defined
':key' => { name => \&string, value => \&anything },
},
'1.3' => {
'meta-spec' => {
mandatory => 1,
'map' => {
version => { mandatory => 1, value => \&version},
url => { mandatory => 1, value => \&urlspec },
':key' => { name => \&string, value => \&anything },
},
},
'name' => { mandatory => 1, value => \&string },
'version' => { mandatory => 1, value => \&version },
'abstract' => { mandatory => 1, value => \&string },
'author' => { mandatory => 1, list => { value => \&string } },
'license' => { mandatory => 1, value => \&license },
'generated_by' => { mandatory => 1, value => \&string },
'distribution_type' => { value => \&string },
'dynamic_config' => { value => \&boolean },
'requires' => $module_map1,
'recommends' => $module_map1,
'build_requires' => $module_map1,
'conflicts' => $module_map2,
'optional_features' => {
'map' => {
':key' => { name => \&string,
'map' => { description => { value => \&string },
requires => $module_map1,
recommends => $module_map1,
build_requires => $module_map1,
conflicts => $module_map2,
':key' => { name => \&string, value => \&anything },
}
}
}
},
'provides' => {
'map' => {
':key' => { name => \&module,
'map' => {
file => { mandatory => 1, value => \&file },
version => { value => \&version },
':key' => { name => \&string, value => \&anything },
}
}
}
},
'no_index' => $no_index_1_3,
'private' => $no_index_1_3,
'keywords' => { list => { value => \&string } },
'resources' => {
'map' => { license => { value => \&url },
homepage => { value => \&url },
bugtracker => { value => \&url },
repository => { value => \&url },
':key' => { value => \&string, name => \&custom_1 },
}
},
# additional user defined key/value pairs
# note we can only validate the key name, as the structure is user defined
':key' => { name => \&string, value => \&anything },
},
# v1.2 is misleading, it seems to assume that a number of fields where created
# within v1.1, when they were created within v1.2. This may have been an
# original mistake, and that a v1.1 was retro fitted into the timeline, when
# v1.2 was originally slated as v1.1. But I could be wrong ;)
'1.2' => {
'meta-spec' => {
mandatory => 1,
'map' => {
version => { mandatory => 1, value => \&version},
url => { mandatory => 1, value => \&urlspec },
':key' => { name => \&string, value => \&anything },
},
},
'name' => { mandatory => 1, value => \&string },
'version' => { mandatory => 1, value => \&version },
'license' => { mandatory => 1, value => \&license },
'generated_by' => { mandatory => 1, value => \&string },
'author' => { mandatory => 1, list => { value => \&string } },
'abstract' => { mandatory => 1, value => \&string },
'distribution_type' => { value => \&string },
'dynamic_config' => { value => \&boolean },
'keywords' => { list => { value => \&string } },
'private' => $no_index_1_2,
'$no_index' => $no_index_1_2,
'requires' => $module_map1,
'recommends' => $module_map1,
'build_requires' => $module_map1,
'conflicts' => $module_map2,
'optional_features' => {
'map' => {
':key' => { name => \&string,
'map' => { description => { value => \&string },
requires => $module_map1,
recommends => $module_map1,
build_requires => $module_map1,
conflicts => $module_map2,
':key' => { name => \&string, value => \&anything },
}
}
}
},
'provides' => {
'map' => {
':key' => { name => \&module,
'map' => {
file => { mandatory => 1, value => \&file },
version => { value => \&version },
':key' => { name => \&string, value => \&anything },
}
}
}
},
'resources' => {
'map' => { license => { value => \&url },
homepage => { value => \&url },
bugtracker => { value => \&url },
repository => { value => \&url },
':key' => { value => \&string, name => \&custom_1 },
}
},
# additional user defined key/value pairs
# note we can only validate the key name, as the structure is user defined
':key' => { name => \&string, value => \&anything },
},
# note that the 1.1 spec only specifies 'version' as mandatory
'1.1' => {
'name' => { value => \&string },
'version' => { mandatory => 1, value => \&version },
'license' => { value => \&license },
'generated_by' => { value => \&string },
'license_uri' => { value => \&url },
'distribution_type' => { value => \&string },
'dynamic_config' => { value => \&boolean },
'private' => $no_index_1_1,
'requires' => $module_map1,
'recommends' => $module_map1,
'build_requires' => $module_map1,
'conflicts' => $module_map2,
# additional user defined key/value pairs
# note we can only validate the key name, as the structure is user defined
':key' => { name => \&string, value => \&anything },
},
# note that the 1.0 spec doesn't specify optional or mandatory fields
# but we will treat version as mandatory since otherwise META 1.0 is
# completely arbitrary and pointless
'1.0' => {
'name' => { value => \&string },
'version' => { mandatory => 1, value => \&version },
'license' => { value => \&license },
'generated_by' => { value => \&string },
'license_uri' => { value => \&url },
'distribution_type' => { value => \&string },
'dynamic_config' => { value => \&boolean },
'requires' => $module_map1,
'recommends' => $module_map1,
'build_requires' => $module_map1,
'conflicts' => $module_map2,
# additional user defined key/value pairs
# note we can only validate the key name, as the structure is user defined
':key' => { name => \&string, value => \&anything },
},
);
#--------------------------------------------------------------------------#
# Code
#--------------------------------------------------------------------------#
#pod =method new
#pod
#pod my $cmv = CPAN::Meta::Validator->new( $struct )
#pod
#pod The constructor must be passed a metadata structure.
#pod
#pod =cut
sub new {
my ($class,$data) = @_;
# create an attributes hash
my $self = {
'data' => $data,
'spec' => eval { $data->{'meta-spec'}{'version'} } || "1.0",
'errors' => undef,
};
# create the object
return bless $self, $class;
}
#pod =method is_valid
#pod
#pod if ( $cmv->is_valid ) {
#pod ...
#pod }
#pod
#pod Returns a boolean value indicating whether the metadata provided
#pod is valid.
#pod
#pod =cut
sub is_valid {
my $self = shift;
my $data = $self->{data};
my $spec_version = $self->{spec};
$self->check_map($definitions{$spec_version},$data);
return ! $self->errors;
}
#pod =method errors
#pod
#pod warn( join "\n", $cmv->errors );
#pod
#pod Returns a list of errors seen during validation.
#pod
#pod =cut
sub errors {
my $self = shift;
return () unless(defined $self->{errors});
return @{$self->{errors}};
}
#pod =begin :internals
#pod
#pod =head2 Check Methods
#pod
#pod =over
#pod
#pod =item *
#pod
#pod check_map($spec,$data)
#pod
#pod Checks whether a map (or hash) part of the data structure conforms to the
#pod appropriate specification definition.
#pod
#pod =item *
#pod
#pod check_list($spec,$data)
#pod
#pod Checks whether a list (or array) part of the data structure conforms to
#pod the appropriate specification definition.
#pod
#pod =item *
#pod
#pod =back
#pod
#pod =cut
my $spec_error = "Missing validation action in specification. "
. "Must be one of 'map', 'list', or 'value'";
sub check_map {
my ($self,$spec,$data) = @_;
if(ref($spec) ne 'HASH') {
$self->_error( "Unknown META specification, cannot validate." );
return;
}
if(ref($data) ne 'HASH') {
$self->_error( "Expected a map structure from string or file." );
return;
}
for my $key (keys %$spec) {
next unless($spec->{$key}->{mandatory});
next if(defined $data->{$key});
push @{$self->{stack}}, $key;
$self->_error( "Missing mandatory field, '$key'" );
pop @{$self->{stack}};
}
for my $key (keys %$data) {
push @{$self->{stack}}, $key;
if($spec->{$key}) {
if($spec->{$key}{value}) {
$spec->{$key}{value}->($self,$key,$data->{$key});
} elsif($spec->{$key}{'map'}) {
$self->check_map($spec->{$key}{'map'},$data->{$key});
} elsif($spec->{$key}{'list'}) {
$self->check_list($spec->{$key}{'list'},$data->{$key});
} else {
$self->_error( "$spec_error for '$key'" );
}
} elsif ($spec->{':key'}) {
$spec->{':key'}{name}->($self,$key,$key);
if($spec->{':key'}{value}) {
$spec->{':key'}{value}->($self,$key,$data->{$key});
} elsif($spec->{':key'}{'map'}) {
$self->check_map($spec->{':key'}{'map'},$data->{$key});
} elsif($spec->{':key'}{'list'}) {
$self->check_list($spec->{':key'}{'list'},$data->{$key});
} else {
$self->_error( "$spec_error for ':key'" );
}
} else {
$self->_error( "Unknown key, '$key', found in map structure" );
}
pop @{$self->{stack}};
}
}
sub check_list {
my ($self,$spec,$data) = @_;
if(ref($data) ne 'ARRAY') {
$self->_error( "Expected a list structure" );
return;
}
if(defined $spec->{mandatory}) {
if(!defined $data->[0]) {
$self->_error( "Missing entries from mandatory list" );
}
}
for my $value (@$data) {
push @{$self->{stack}}, $value || "";
if(defined $spec->{value}) {
$spec->{value}->($self,'list',$value);
} elsif(defined $spec->{'map'}) {
$self->check_map($spec->{'map'},$value);
} elsif(defined $spec->{'list'}) {
$self->check_list($spec->{'list'},$value);
} elsif ($spec->{':key'}) {
$self->check_map($spec,$value);
} else {
$self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
}
pop @{$self->{stack}};
}
}
#pod =head2 Validator Methods
#pod
#pod =over
#pod
#pod =item *
#pod
#pod header($self,$key,$value)
#pod
#pod Validates that the header is valid.
#pod
#pod Note: No longer used as we now read the data structure, not the file.
#pod
#pod =item *
#pod
#pod url($self,$key,$value)
#pod
#pod Validates that a given value is in an acceptable URL format
#pod
#pod =item *
#pod
#pod urlspec($self,$key,$value)
#pod
#pod Validates that the URL to a META specification is a known one.
#pod
#pod =item *
#pod
#pod string_or_undef($self,$key,$value)
#pod
#pod Validates that the value is either a string or an undef value. Bit of a
#pod catchall function for parts of the data structure that are completely user
#pod defined.
#pod
#pod =item *
#pod
#pod string($self,$key,$value)
#pod
#pod Validates that a string exists for the given key.
#pod
#pod =item *
#pod
#pod file($self,$key,$value)
#pod
#pod Validate that a file is passed for the given key. This may be made more
#pod thorough in the future. For now it acts like \&string.
#pod
#pod =item *
#pod
#pod exversion($self,$key,$value)
#pod
#pod Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
#pod
#pod =item *
#pod
#pod version($self,$key,$value)
#pod
#pod Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
#pod are both valid. A leading 'v' like 'v1.2.3' is also valid.
#pod
#pod =item *
#pod
#pod boolean($self,$key,$value)
#pod
#pod Validates for a boolean value: a defined value that is either "1" or "0" or
#pod stringifies to those values.
#pod
#pod =item *
#pod
#pod license($self,$key,$value)
#pod
#pod Validates that a value is given for the license. Returns 1 if an known license
#pod type, or 2 if a value is given but the license type is not a recommended one.
#pod
#pod =item *
#pod
#pod custom_1($self,$key,$value)
#pod
#pod Validates that the given key is in CamelCase, to indicate a user defined
#pod keyword and only has characters in the class [-_a-zA-Z]. In version 1.X
#pod of the spec, this was only explicitly stated for 'resources'.
#pod
#pod =item *
#pod
#pod custom_2($self,$key,$value)
#pod
#pod Validates that the given key begins with 'x_' or 'X_', to indicate a user
#pod defined keyword and only has characters in the class [-_a-zA-Z]
#pod
#pod =item *
#pod
#pod identifier($self,$key,$value)
#pod
#pod Validates that key is in an acceptable format for the META specification,
#pod for an identifier, i.e. any that matches the regular expression
#pod qr/[a-z][a-z_]/i.
#pod
#pod =item *
#pod
#pod module($self,$key,$value)
#pod
#pod Validates that a given key is in an acceptable module name format, e.g.
#pod 'Test::CPAN::Meta::Version'.
#pod
#pod =back
#pod
#pod =end :internals
#pod
#pod =cut
sub header {
my ($self,$key,$value) = @_;
if(defined $value) {
return 1 if($value && $value =~ /^--- #YAML:1.0/);
}
$self->_error( "file does not have a valid YAML header." );
return 0;
}
sub release_status {
my ($self,$key,$value) = @_;
if(defined $value) {
my $version = $self->{data}{version} || '';
if ( $version =~ /_/ ) {
return 1 if ( $value =~ /\A(?:testing|unstable)\z/ );
$self->_error( "'$value' for '$key' is invalid for version '$version'" );
}
else {
return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ );
$self->_error( "'$value' for '$key' is invalid" );
}
}
else {
$self->_error( "'$key' is not defined" );
}
return 0;
}
# _uri_split taken from URI::Split by Gisle Aas, Copyright 2003
sub _uri_split {
return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
}
sub url {
my ($self,$key,$value) = @_;
if(defined $value) {
my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
unless ( defined $scheme && length $scheme ) {
$self->_error( "'$value' for '$key' does not have a URL scheme" );
return 0;
}
unless ( defined $auth && length $auth ) {
$self->_error( "'$value' for '$key' does not have a URL authority" );
return 0;
}
return 1;
}
$value ||= '';
$self->_error( "'$value' for '$key' is not a valid URL." );
return 0;
}
sub urlspec {
my ($self,$key,$value) = @_;
if(defined $value) {
return 1 if($value && $known_specs{$self->{spec}} eq $value);
if($value && $known_urls{$value}) {
$self->_error( 'META specification URL does not match version' );
return 0;
}
}
$self->_error( 'Unknown META specification' );
return 0;
}
sub anything { return 1 }
sub string {
my ($self,$key,$value) = @_;
if(defined $value) {
return 1 if($value || $value =~ /^0$/);
}
$self->_error( "value is an undefined string" );
return 0;
}
sub string_or_undef {
my ($self,$key,$value) = @_;
return 1 unless(defined $value);
return 1 if($value || $value =~ /^0$/);
$self->_error( "No string defined for '$key'" );
return 0;
}
sub file {
my ($self,$key,$value) = @_;
return 1 if(defined $value);
$self->_error( "No file defined for '$key'" );
return 0;
}
sub exversion {
my ($self,$key,$value) = @_;
if(defined $value && ($value || $value =~ /0/)) {
my $pass = 1;
for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
return $pass;
}
$value = '' unless(defined $value);
$self->_error( "'$value' for '$key' is not a valid version." );
return 0;
}
sub version {
my ($self,$key,$value) = @_;
if(defined $value) {
return 0 unless($value || $value =~ /0/);
return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
} else {
$value = '';
}
$self->_error( "'$value' for '$key' is not a valid version." );
return 0;
}
sub boolean {
my ($self,$key,$value) = @_;
if(defined $value) {
return 1 if($value =~ /^(0|1)$/);
} else {
$value = '';
}
$self->_error( "'$value' for '$key' is not a boolean value." );
return 0;
}
my %v1_licenses = (
'perl' => 'http://dev.perl.org/licenses/',
'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
'apache' => 'http://apache.org/licenses/LICENSE-2.0',
'artistic' => 'http://opensource.org/licenses/artistic-license.php',
'artistic_2' => 'http://opensource.org/licenses/artistic-license-2.0.php',
'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.php',
'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
'mit' => 'http://opensource.org/licenses/mit-license.php',
'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php',
'open_source' => undef,
'unrestricted' => undef,
'restrictive' => undef,
'unknown' => undef,
);
my %v2_licenses = map { $_ => 1 } qw(
agpl_3
apache_1_1
apache_2_0
artistic_1
artistic_2
bsd
freebsd
gfdl_1_2
gfdl_1_3
gpl_1
gpl_2
gpl_3
lgpl_2_1
lgpl_3_0
mit
mozilla_1_0
mozilla_1_1
openssl
perl_5
qpl_1_0
ssleay
sun
zlib
open_source
restricted
unrestricted
unknown
);
sub license {
my ($self,$key,$value) = @_;
my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
if(defined $value) {
return 1 if($value && exists $licenses->{$value});
} else {
$value = '';
}
$self->_error( "License '$value' is invalid" );
return 0;
}
sub custom_1 {
my ($self,$key) = @_;
if(defined $key) {
# a valid user defined key should be alphabetic
# and contain at least one capital case letter.
return 1 if($key && $key =~ /^[_a-z]+$/i && $key =~ /[A-Z]/);
} else {
$key = '';
}
$self->_error( "Custom resource '$key' must be in CamelCase." );
return 0;
}
sub custom_2 {
my ($self,$key) = @_;
if(defined $key) {
return 1 if($key && $key =~ /^x_/i); # user defined
} else {
$key = '';
}
$self->_error( "Custom key '$key' must begin with 'x_' or 'X_'." );
return 0;
}
sub identifier {
my ($self,$key) = @_;
if(defined $key) {
return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
} else {
$key = '';
}
$self->_error( "Key '$key' is not a legal identifier." );
return 0;
}
sub module {
my ($self,$key) = @_;
if(defined $key) {
return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
} else {
$key = '';
}
$self->_error( "Key '$key' is not a legal module name." );
return 0;
}
my @valid_phases = qw/ configure build test runtime develop /;
sub phase {
my ($self,$key) = @_;
if(defined $key) {
return 1 if( length $key && grep { $key eq $_ } @valid_phases );
return 1 if $key =~ /x_/i;
} else {
$key = '';
}
$self->_error( "Key '$key' is not a legal phase." );
return 0;
}
my @valid_relations = qw/ requires recommends suggests conflicts /;
sub relation {
my ($self,$key) = @_;
if(defined $key) {
return 1 if( length $key && grep { $key eq $_ } @valid_relations );
return 1 if $key =~ /x_/i;
} else {
$key = '';
}
$self->_error( "Key '$key' is not a legal prereq relationship." );
return 0;
}
sub _error {
my $self = shift;
my $mess = shift;
$mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
$mess .= " [Validation: $self->{spec}]";
push @{$self->{errors}}, $mess;
}
1;
# ABSTRACT: validate CPAN distribution metadata structures
=pod
=encoding UTF-8
=head1 NAME
CPAN::Meta::Validator - validate CPAN distribution metadata structures
=head1 VERSION
version 2.150010
=head1 SYNOPSIS
my $struct = decode_json_file('META.json');
my $cmv = CPAN::Meta::Validator->new( $struct );
unless ( $cmv->is_valid ) {
my $msg = "Invalid META structure. Errors found:\n";
$msg .= join( "\n", $cmv->errors );
die $msg;
}
=head1 DESCRIPTION
This module validates a CPAN Meta structure against the version of the
the specification claimed in the C field of the structure.
=head1 METHODS
=head2 new
my $cmv = CPAN::Meta::Validator->new( $struct )
The constructor must be passed a metadata structure.
=head2 is_valid
if ( $cmv->is_valid ) {
...
}
Returns a boolean value indicating whether the metadata provided
is valid.
=head2 errors
warn( join "\n", $cmv->errors );
Returns a list of errors seen during validation.
=begin :internals
=head2 Check Methods
=over
=item *
check_map($spec,$data)
Checks whether a map (or hash) part of the data structure conforms to the
appropriate specification definition.
=item *
check_list($spec,$data)
Checks whether a list (or array) part of the data structure conforms to
the appropriate specification definition.
=item *
=back
=head2 Validator Methods
=over
=item *
header($self,$key,$value)
Validates that the header is valid.
Note: No longer used as we now read the data structure, not the file.
=item *
url($self,$key,$value)
Validates that a given value is in an acceptable URL format
=item *
urlspec($self,$key,$value)
Validates that the URL to a META specification is a known one.
=item *
string_or_undef($self,$key,$value)
Validates that the value is either a string or an undef value. Bit of a
catchall function for parts of the data structure that are completely user
defined.
=item *
string($self,$key,$value)
Validates that a string exists for the given key.
=item *
file($self,$key,$value)
Validate that a file is passed for the given key. This may be made more
thorough in the future. For now it acts like \&string.
=item *
exversion($self,$key,$value)
Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
=item *
version($self,$key,$value)
Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
are both valid. A leading 'v' like 'v1.2.3' is also valid.
=item *
boolean($self,$key,$value)
Validates for a boolean value: a defined value that is either "1" or "0" or
stringifies to those values.
=item *
license($self,$key,$value)
Validates that a value is given for the license. Returns 1 if an known license
type, or 2 if a value is given but the license type is not a recommended one.
=item *
custom_1($self,$key,$value)
Validates that the given key is in CamelCase, to indicate a user defined
keyword and only has characters in the class [-_a-zA-Z]. In version 1.X
of the spec, this was only explicitly stated for 'resources'.
=item *
custom_2($self,$key,$value)
Validates that the given key begins with 'x_' or 'X_', to indicate a user
defined keyword and only has characters in the class [-_a-zA-Z]
=item *
identifier($self,$key,$value)
Validates that key is in an acceptable format for the META specification,
for an identifier, i.e. any that matches the regular expression
qr/[a-z][a-z_]/i.
=item *
module($self,$key,$value)
Validates that a given key is in an acceptable module name format, e.g.
'Test::CPAN::Meta::Version'.
=back
=end :internals
=for Pod::Coverage anything boolean check_list custom_1 custom_2 exversion file
identifier license module phase relation release_status string string_or_undef
url urlspec version header check_map
=head1 BUGS
Please report any bugs or feature using the CPAN Request Tracker.
Bugs can be submitted through the web interface at
L
When submitting a bug or request, please include a test-file or a patch to an
existing test-file that illustrates the bug or desired feature.
=head1 AUTHORS
=over 4
=item *
David Golden
=item *
Ricardo Signes
=item *
Adam Kennedy
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by David Golden, Ricardo Signes, Adam Kennedy and Contributors.
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
__END__
# vim: ts=2 sts=2 sw=2 et :
PK ! Y| | Requirements.pmnu [ use 5.006; # keep at v5.6 for CPAN.pm
use strict;
use warnings;
package CPAN::Meta::Requirements;
# ABSTRACT: a set of version requirements for a CPAN dist
our $VERSION = '2.140';
#pod =head1 SYNOPSIS
#pod
#pod use CPAN::Meta::Requirements;
#pod
#pod my $build_requires = CPAN::Meta::Requirements->new;
#pod
#pod $build_requires->add_minimum('Library::Foo' => 1.208);
#pod
#pod $build_requires->add_minimum('Library::Foo' => 2.602);
#pod
#pod $build_requires->add_minimum('Module::Bar' => 'v1.2.3');
#pod
#pod $METAyml->{build_requires} = $build_requires->as_string_hash;
#pod
#pod =head1 DESCRIPTION
#pod
#pod A CPAN::Meta::Requirements object models a set of version constraints like
#pod those specified in the F or F files in CPAN distributions,
#pod and as defined by L;
#pod It can be built up by adding more and more constraints, and it will reduce them
#pod to the simplest representation.
#pod
#pod Logically impossible constraints will be identified immediately by thrown
#pod exceptions.
#pod
#pod =cut
use Carp ();
# To help ExtUtils::MakeMaker bootstrap CPAN::Meta::Requirements on perls
# before 5.10, we fall back to the EUMM bundled compatibility version module if
# that's the only thing available. This shouldn't ever happen in a normal CPAN
# install of CPAN::Meta::Requirements, as version.pm will be picked up from
# prereqs and be available at runtime.
BEGIN {
eval "use version ()"; ## no critic
if ( my $err = $@ ) {
eval "use ExtUtils::MakeMaker::version" or die $err; ## no critic
}
}
# Perl 5.10.0 didn't have "is_qv" in version.pm
*_is_qv = version->can('is_qv') ? sub { $_[0]->is_qv } : sub { exists $_[0]->{qv} };
# construct once, reuse many times
my $V0 = version->new(0);
#pod =method new
#pod
#pod my $req = CPAN::Meta::Requirements->new;
#pod
#pod This returns a new CPAN::Meta::Requirements object. It takes an optional
#pod hash reference argument. Currently, only one key is supported:
#pod
#pod =for :list
#pod * C -- if provided, when a version cannot be parsed into
#pod a version object, this code reference will be called with the invalid
#pod version string as first argument, and the module name as second
#pod argument. It must return a valid version object.
#pod
#pod All other keys are ignored.
#pod
#pod =cut
my @valid_options = qw( bad_version_hook );
sub new {
my ($class, $options) = @_;
$options ||= {};
Carp::croak "Argument to $class\->new() must be a hash reference"
unless ref $options eq 'HASH';
my %self = map {; $_ => $options->{$_}} @valid_options;
return bless \%self => $class;
}
# from version::vpp
sub _find_magic_vstring {
my $value = shift;
my $tvalue = '';
require B;
my $sv = B::svref_2object(\$value);
my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
while ( $magic ) {
if ( $magic->TYPE eq 'V' ) {
$tvalue = $magic->PTR;
$tvalue =~ s/^v?(.+)$/v$1/;
last;
}
else {
$magic = $magic->MOREMAGIC;
}
}
return $tvalue;
}
# safe if given an unblessed reference
sub _isa_version {
UNIVERSAL::isa( $_[0], 'UNIVERSAL' ) && $_[0]->isa('version')
}
sub _version_object {
my ($self, $module, $version) = @_;
my ($vobj, $err);
if (not defined $version or (!ref($version) && $version eq '0')) {
return $V0;
}
elsif ( ref($version) eq 'version' || ( ref($version) && _isa_version($version) ) ) {
$vobj = $version;
}
else {
# hack around version::vpp not handling <3 character vstring literals
if ( $INC{'version/vpp.pm'} || $INC{'ExtUtils/MakeMaker/version/vpp.pm'} ) {
my $magic = _find_magic_vstring( $version );
$version = $magic if length $magic;
}
# pad to 3 characters if before 5.8.1 and appears to be a v-string
if ( $] < 5.008001 && $version !~ /\A[0-9]/ && substr($version,0,1) ne 'v' && length($version) < 3 ) {
$version .= "\0" x (3 - length($version));
}
eval {
local $SIG{__WARN__} = sub { die "Invalid version: $_[0]" };
# avoid specific segfault on some older version.pm versions
die "Invalid version: $version" if $version eq 'version';
$vobj = version->new($version);
};
if ( my $err = $@ ) {
my $hook = $self->{bad_version_hook};
$vobj = eval { $hook->($version, $module) }
if ref $hook eq 'CODE';
unless (eval { $vobj->isa("version") }) {
$err =~ s{ at .* line \d+.*$}{};
die "Can't convert '$version': $err";
}
}
}
# ensure no leading '.'
if ( $vobj =~ m{\A\.} ) {
$vobj = version->new("0$vobj");
}
# ensure normal v-string form
if ( _is_qv($vobj) ) {
$vobj = version->new($vobj->normal);
}
return $vobj;
}
#pod =method add_minimum
#pod
#pod $req->add_minimum( $module => $version );
#pod
#pod This adds a new minimum version requirement. If the new requirement is
#pod redundant to the existing specification, this has no effect.
#pod
#pod Minimum requirements are inclusive. C<$version> is required, along with any
#pod greater version number.
#pod
#pod This method returns the requirements object.
#pod
#pod =method add_maximum
#pod
#pod $req->add_maximum( $module => $version );
#pod
#pod This adds a new maximum version requirement. If the new requirement is
#pod redundant to the existing specification, this has no effect.
#pod
#pod Maximum requirements are inclusive. No version strictly greater than the given
#pod version is allowed.
#pod
#pod This method returns the requirements object.
#pod
#pod =method add_exclusion
#pod
#pod $req->add_exclusion( $module => $version );
#pod
#pod This adds a new excluded version. For example, you might use these three
#pod method calls:
#pod
#pod $req->add_minimum( $module => '1.00' );
#pod $req->add_maximum( $module => '1.82' );
#pod
#pod $req->add_exclusion( $module => '1.75' );
#pod
#pod Any version between 1.00 and 1.82 inclusive would be acceptable, except for
#pod 1.75.
#pod
#pod This method returns the requirements object.
#pod
#pod =method exact_version
#pod
#pod $req->exact_version( $module => $version );
#pod
#pod This sets the version required for the given module to I the given
#pod version. No other version would be considered acceptable.
#pod
#pod This method returns the requirements object.
#pod
#pod =cut
BEGIN {
for my $type (qw(maximum exclusion exact_version)) {
my $method = "with_$type";
my $to_add = $type eq 'exact_version' ? $type : "add_$type";
my $code = sub {
my ($self, $name, $version) = @_;
$version = $self->_version_object( $name, $version );
$self->__modify_entry_for($name, $method, $version);
return $self;
};
no strict 'refs';
*$to_add = $code;
}
}
# add_minimum is optimized compared to generated subs above because
# it is called frequently and with "0" or equivalent input
sub add_minimum {
my ($self, $name, $version) = @_;
# stringify $version so that version->new("0.00")->stringify ne "0"
# which preserves the user's choice of "0.00" as the requirement
if (not defined $version or "$version" eq '0') {
return $self if $self->__entry_for($name);
Carp::confess("can't add new requirements to finalized requirements")
if $self->is_finalized;
$self->{requirements}{ $name } =
CPAN::Meta::Requirements::_Range::Range->with_minimum($V0, $name);
}
else {
$version = $self->_version_object( $name, $version );
$self->__modify_entry_for($name, 'with_minimum', $version);
}
return $self;
}
#pod =method add_requirements
#pod
#pod $req->add_requirements( $another_req_object );
#pod
#pod This method adds all the requirements in the given CPAN::Meta::Requirements
#pod object to the requirements object on which it was called. If there are any
#pod conflicts, an exception is thrown.
#pod
#pod This method returns the requirements object.
#pod
#pod =cut
sub add_requirements {
my ($self, $req) = @_;
for my $module ($req->required_modules) {
my $modifiers = $req->__entry_for($module)->as_modifiers;
for my $modifier (@$modifiers) {
my ($method, @args) = @$modifier;
$self->$method($module => @args);
};
}
return $self;
}
#pod =method accepts_module
#pod
#pod my $bool = $req->accepts_module($module => $version);
#pod
#pod Given an module and version, this method returns true if the version
#pod specification for the module accepts the provided version. In other words,
#pod given:
#pod
#pod Module => '>= 1.00, < 2.00'
#pod
#pod We will accept 1.00 and 1.75 but not 0.50 or 2.00.
#pod
#pod For modules that do not appear in the requirements, this method will return
#pod true.
#pod
#pod =cut
sub accepts_module {
my ($self, $module, $version) = @_;
$version = $self->_version_object( $module, $version );
return 1 unless my $range = $self->__entry_for($module);
return $range->_accepts($version);
}
#pod =method clear_requirement
#pod
#pod $req->clear_requirement( $module );
#pod
#pod This removes the requirement for a given module from the object.
#pod
#pod This method returns the requirements object.
#pod
#pod =cut
sub clear_requirement {
my ($self, $module) = @_;
return $self unless $self->__entry_for($module);
Carp::confess("can't clear requirements on finalized requirements")
if $self->is_finalized;
delete $self->{requirements}{ $module };
return $self;
}
#pod =method requirements_for_module
#pod
#pod $req->requirements_for_module( $module );
#pod
#pod This returns a string containing the version requirements for a given module in
#pod the format described in L or undef if the given module has no
#pod requirements. This should only be used for informational purposes such as error
#pod messages and should not be interpreted or used for comparison (see
#pod L instead).
#pod
#pod =cut
sub requirements_for_module {
my ($self, $module) = @_;
my $entry = $self->__entry_for($module);
return unless $entry;
return $entry->as_string;
}
#pod =method structured_requirements_for_module
#pod
#pod $req->structured_requirements_for_module( $module );
#pod
#pod This returns a data structure containing the version requirements for a given
#pod module or undef if the given module has no requirements. This should
#pod not be used for version checks (see L instead).
#pod
#pod Added in version 2.134.
#pod
#pod =cut
sub structured_requirements_for_module {
my ($self, $module) = @_;
my $entry = $self->__entry_for($module);
return unless $entry;
return $entry->as_struct;
}
#pod =method required_modules
#pod
#pod This method returns a list of all the modules for which requirements have been
#pod specified.
#pod
#pod =cut
sub required_modules { keys %{ $_[0]{requirements} } }
#pod =method clone
#pod
#pod $req->clone;
#pod
#pod This method returns a clone of the invocant. The clone and the original object
#pod can then be changed independent of one another.
#pod
#pod =cut
sub clone {
my ($self) = @_;
my $new = (ref $self)->new;
return $new->add_requirements($self);
}
sub __entry_for { $_[0]{requirements}{ $_[1] } }
sub __modify_entry_for {
my ($self, $name, $method, $version) = @_;
my $fin = $self->is_finalized;
my $old = $self->__entry_for($name);
Carp::confess("can't add new requirements to finalized requirements")
if $fin and not $old;
my $new = ($old || 'CPAN::Meta::Requirements::_Range::Range')
->$method($version, $name);
Carp::confess("can't modify finalized requirements")
if $fin and $old->as_string ne $new->as_string;
$self->{requirements}{ $name } = $new;
}
#pod =method is_simple
#pod
#pod This method returns true if and only if all requirements are inclusive minimums
#pod -- that is, if their string expression is just the version number.
#pod
#pod =cut
sub is_simple {
my ($self) = @_;
for my $module ($self->required_modules) {
# XXX: This is a complete hack, but also entirely correct.
return if $self->__entry_for($module)->as_string =~ /\s/;
}
return 1;
}
#pod =method is_finalized
#pod
#pod This method returns true if the requirements have been finalized by having the
#pod C method called on them.
#pod
#pod =cut
sub is_finalized { $_[0]{finalized} }
#pod =method finalize
#pod
#pod This method marks the requirements finalized. Subsequent attempts to change
#pod the requirements will be fatal, I they would result in a change. If they
#pod would not alter the requirements, they have no effect.
#pod
#pod If a finalized set of requirements is cloned, the cloned requirements are not
#pod also finalized.
#pod
#pod =cut
sub finalize { $_[0]{finalized} = 1 }
#pod =method as_string_hash
#pod
#pod This returns a reference to a hash describing the requirements using the
#pod strings in the L specification.
#pod
#pod For example after the following program:
#pod
#pod my $req = CPAN::Meta::Requirements->new;
#pod
#pod $req->add_minimum('CPAN::Meta::Requirements' => 0.102);
#pod
#pod $req->add_minimum('Library::Foo' => 1.208);
#pod
#pod $req->add_maximum('Library::Foo' => 2.602);
#pod
#pod $req->add_minimum('Module::Bar' => 'v1.2.3');
#pod
#pod $req->add_exclusion('Module::Bar' => 'v1.2.8');
#pod
#pod $req->exact_version('Xyzzy' => '6.01');
#pod
#pod my $hashref = $req->as_string_hash;
#pod
#pod C<$hashref> would contain:
#pod
#pod {
#pod 'CPAN::Meta::Requirements' => '0.102',
#pod 'Library::Foo' => '>= 1.208, <= 2.206',
#pod 'Module::Bar' => '>= v1.2.3, != v1.2.8',
#pod 'Xyzzy' => '== 6.01',
#pod }
#pod
#pod =cut
sub as_string_hash {
my ($self) = @_;
my %hash = map {; $_ => $self->{requirements}{$_}->as_string }
$self->required_modules;
return \%hash;
}
#pod =method add_string_requirement
#pod
#pod $req->add_string_requirement('Library::Foo' => '>= 1.208, <= 2.206');
#pod $req->add_string_requirement('Library::Foo' => v1.208);
#pod
#pod This method parses the passed in string and adds the appropriate requirement
#pod for the given module. A version can be a Perl "v-string". It understands
#pod version ranges as described in the L. For
#pod example:
#pod
#pod =over 4
#pod
#pod =item 1.3
#pod
#pod =item >= 1.3
#pod
#pod =item <= 1.3
#pod
#pod =item == 1.3
#pod
#pod =item != 1.3
#pod
#pod =item > 1.3
#pod
#pod =item < 1.3
#pod
#pod =item >= 1.3, != 1.5, <= 2.0
#pod
#pod A version number without an operator is equivalent to specifying a minimum
#pod (C=>). Extra whitespace is allowed.
#pod
#pod =back
#pod
#pod =cut
my %methods_for_op = (
'==' => [ qw(exact_version) ],
'!=' => [ qw(add_exclusion) ],
'>=' => [ qw(add_minimum) ],
'<=' => [ qw(add_maximum) ],
'>' => [ qw(add_minimum add_exclusion) ],
'<' => [ qw(add_maximum add_exclusion) ],
);
sub add_string_requirement {
my ($self, $module, $req) = @_;
unless ( defined $req && length $req ) {
$req = 0;
$self->_blank_carp($module);
}
my $magic = _find_magic_vstring( $req );
if (length $magic) {
$self->add_minimum($module => $magic);
return;
}
my @parts = split qr{\s*,\s*}, $req;
for my $part (@parts) {
my ($op, $ver) = $part =~ m{\A\s*(==|>=|>|<=|<|!=)\s*(.*)\z};
if (! defined $op) {
$self->add_minimum($module => $part);
} else {
Carp::confess("illegal requirement string: $req")
unless my $methods = $methods_for_op{ $op };
$self->$_($module => $ver) for @$methods;
}
}
}
#pod =method from_string_hash
#pod
#pod my $req = CPAN::Meta::Requirements->from_string_hash( \%hash );
#pod my $req = CPAN::Meta::Requirements->from_string_hash( \%hash, \%opts );
#pod
#pod This is an alternate constructor for a CPAN::Meta::Requirements
#pod object. It takes a hash of module names and version requirement
#pod strings and returns a new CPAN::Meta::Requirements object. As with
#pod add_string_requirement, a version can be a Perl "v-string". Optionally,
#pod you can supply a hash-reference of options, exactly as with the L
#pod method.
#pod
#pod =cut
sub _blank_carp {
my ($self, $module) = @_;
Carp::carp("Undefined requirement for $module treated as '0'");
}
sub from_string_hash {
my ($class, $hash, $options) = @_;
my $self = $class->new($options);
for my $module (keys %$hash) {
my $req = $hash->{$module};
unless ( defined $req && length $req ) {
$req = 0;
$class->_blank_carp($module);
}
$self->add_string_requirement($module, $req);
}
return $self;
}
##############################################################
{
package
CPAN::Meta::Requirements::_Range::Exact;
sub _new { bless { version => $_[1] } => $_[0] }
sub _accepts { return $_[0]{version} == $_[1] }
sub as_string { return "== $_[0]{version}" }
sub as_struct { return [ [ '==', "$_[0]{version}" ] ] }
sub as_modifiers { return [ [ exact_version => $_[0]{version} ] ] }
sub _reject_requirements {
my ($self, $module, $error) = @_;
Carp::confess("illegal requirements for $module: $error")
}
sub _clone {
(ref $_[0])->_new( version->new( $_[0]{version} ) )
}
sub with_exact_version {
my ($self, $version, $module) = @_;
$module = 'module' unless defined $module;
return $self->_clone if $self->_accepts($version);
$self->_reject_requirements(
$module,
"can't be exactly $version when exact requirement is already $self->{version}",
);
}
sub with_minimum {
my ($self, $minimum, $module) = @_;
$module = 'module' unless defined $module;
return $self->_clone if $self->{version} >= $minimum;
$self->_reject_requirements(
$module,
"minimum $minimum exceeds exact specification $self->{version}",
);
}
sub with_maximum {
my ($self, $maximum, $module) = @_;
$module = 'module' unless defined $module;
return $self->_clone if $self->{version} <= $maximum;
$self->_reject_requirements(
$module,
"maximum $maximum below exact specification $self->{version}",
);
}
sub with_exclusion {
my ($self, $exclusion, $module) = @_;
$module = 'module' unless defined $module;
return $self->_clone unless $exclusion == $self->{version};
$self->_reject_requirements(
$module,
"tried to exclude $exclusion, which is already exactly specified",
);
}
}
##############################################################
{
package
CPAN::Meta::Requirements::_Range::Range;
sub _self { ref($_[0]) ? $_[0] : (bless { } => $_[0]) }
sub _clone {
return (bless { } => $_[0]) unless ref $_[0];
my ($s) = @_;
my %guts = (
(exists $s->{minimum} ? (minimum => version->new($s->{minimum})) : ()),
(exists $s->{maximum} ? (maximum => version->new($s->{maximum})) : ()),
(exists $s->{exclusions}
? (exclusions => [ map { version->new($_) } @{ $s->{exclusions} } ])
: ()),
);
bless \%guts => ref($s);
}
sub as_modifiers {
my ($self) = @_;
my @mods;
push @mods, [ add_minimum => $self->{minimum} ] if exists $self->{minimum};
push @mods, [ add_maximum => $self->{maximum} ] if exists $self->{maximum};
push @mods, map {; [ add_exclusion => $_ ] } @{$self->{exclusions} || []};
return \@mods;
}
sub as_struct {
my ($self) = @_;
return 0 if ! keys %$self;
my @exclusions = @{ $self->{exclusions} || [] };
my @parts;
for my $tuple (
[ qw( >= > minimum ) ],
[ qw( <= < maximum ) ],
) {
my ($op, $e_op, $k) = @$tuple;
if (exists $self->{$k}) {
my @new_exclusions = grep { $_ != $self->{ $k } } @exclusions;
if (@new_exclusions == @exclusions) {
push @parts, [ $op, "$self->{ $k }" ];
} else {
push @parts, [ $e_op, "$self->{ $k }" ];
@exclusions = @new_exclusions;
}
}
}
push @parts, map {; [ "!=", "$_" ] } @exclusions;
return \@parts;
}
sub as_string {
my ($self) = @_;
my @parts = @{ $self->as_struct };
return $parts[0][1] if @parts == 1 and $parts[0][0] eq '>=';
return join q{, }, map {; join q{ }, @$_ } @parts;
}
sub _reject_requirements {
my ($self, $module, $error) = @_;
Carp::confess("illegal requirements for $module: $error")
}
sub with_exact_version {
my ($self, $version, $module) = @_;
$module = 'module' unless defined $module;
$self = $self->_clone;
unless ($self->_accepts($version)) {
$self->_reject_requirements(
$module,
"exact specification $version outside of range " . $self->as_string
);
}
return CPAN::Meta::Requirements::_Range::Exact->_new($version);
}
sub _simplify {
my ($self, $module) = @_;
if (defined $self->{minimum} and defined $self->{maximum}) {
if ($self->{minimum} == $self->{maximum}) {
if (grep { $_ == $self->{minimum} } @{ $self->{exclusions} || [] }) {
$self->_reject_requirements(
$module,
"minimum and maximum are both $self->{minimum}, which is excluded",
);
}
return CPAN::Meta::Requirements::_Range::Exact->_new($self->{minimum})
}
if ($self->{minimum} > $self->{maximum}) {
$self->_reject_requirements(
$module,
"minimum $self->{minimum} exceeds maximum $self->{maximum}",
);
}
}
# eliminate irrelevant exclusions
if ($self->{exclusions}) {
my %seen;
@{ $self->{exclusions} } = grep {
(! defined $self->{minimum} or $_ >= $self->{minimum})
and
(! defined $self->{maximum} or $_ <= $self->{maximum})
and
! $seen{$_}++
} @{ $self->{exclusions} };
}
return $self;
}
sub with_minimum {
my ($self, $minimum, $module) = @_;
$module = 'module' unless defined $module;
$self = $self->_clone;
if (defined (my $old_min = $self->{minimum})) {
$self->{minimum} = (sort { $b cmp $a } ($minimum, $old_min))[0];
} else {
$self->{minimum} = $minimum;
}
return $self->_simplify($module);
}
sub with_maximum {
my ($self, $maximum, $module) = @_;
$module = 'module' unless defined $module;
$self = $self->_clone;
if (defined (my $old_max = $self->{maximum})) {
$self->{maximum} = (sort { $a cmp $b } ($maximum, $old_max))[0];
} else {
$self->{maximum} = $maximum;
}
return $self->_simplify($module);
}
sub with_exclusion {
my ($self, $exclusion, $module) = @_;
$module = 'module' unless defined $module;
$self = $self->_clone;
push @{ $self->{exclusions} ||= [] }, $exclusion;
return $self->_simplify($module);
}
sub _accepts {
my ($self, $version) = @_;
return if defined $self->{minimum} and $version < $self->{minimum};
return if defined $self->{maximum} and $version > $self->{maximum};
return if defined $self->{exclusions}
and grep { $version == $_ } @{ $self->{exclusions} };
return 1;
}
}
1;
# vim: ts=2 sts=2 sw=2 et:
__END__
=pod
=encoding UTF-8
=head1 NAME
CPAN::Meta::Requirements - a set of version requirements for a CPAN dist
=head1 VERSION
version 2.140
=head1 SYNOPSIS
use CPAN::Meta::Requirements;
my $build_requires = CPAN::Meta::Requirements->new;
$build_requires->add_minimum('Library::Foo' => 1.208);
$build_requires->add_minimum('Library::Foo' => 2.602);
$build_requires->add_minimum('Module::Bar' => 'v1.2.3');
$METAyml->{build_requires} = $build_requires->as_string_hash;
=head1 DESCRIPTION
A CPAN::Meta::Requirements object models a set of version constraints like
those specified in the F or F files in CPAN distributions,
and as defined by L;
It can be built up by adding more and more constraints, and it will reduce them
to the simplest representation.
Logically impossible constraints will be identified immediately by thrown
exceptions.
=head1 METHODS
=head2 new
my $req = CPAN::Meta::Requirements->new;
This returns a new CPAN::Meta::Requirements object. It takes an optional
hash reference argument. Currently, only one key is supported:
=over 4
=item *
C -- if provided, when a version cannot be parsed into a version object, this code reference will be called with the invalid version string as first argument, and the module name as second argument. It must return a valid version object.
=back
All other keys are ignored.
=head2 add_minimum
$req->add_minimum( $module => $version );
This adds a new minimum version requirement. If the new requirement is
redundant to the existing specification, this has no effect.
Minimum requirements are inclusive. C<$version> is required, along with any
greater version number.
This method returns the requirements object.
=head2 add_maximum
$req->add_maximum( $module => $version );
This adds a new maximum version requirement. If the new requirement is
redundant to the existing specification, this has no effect.
Maximum requirements are inclusive. No version strictly greater than the given
version is allowed.
This method returns the requirements object.
=head2 add_exclusion
$req->add_exclusion( $module => $version );
This adds a new excluded version. For example, you might use these three
method calls:
$req->add_minimum( $module => '1.00' );
$req->add_maximum( $module => '1.82' );
$req->add_exclusion( $module => '1.75' );
Any version between 1.00 and 1.82 inclusive would be acceptable, except for
1.75.
This method returns the requirements object.
=head2 exact_version
$req->exact_version( $module => $version );
This sets the version required for the given module to I the given
version. No other version would be considered acceptable.
This method returns the requirements object.
=head2 add_requirements
$req->add_requirements( $another_req_object );
This method adds all the requirements in the given CPAN::Meta::Requirements
object to the requirements object on which it was called. If there are any
conflicts, an exception is thrown.
This method returns the requirements object.
=head2 accepts_module
my $bool = $req->accepts_module($module => $version);
Given an module and version, this method returns true if the version
specification for the module accepts the provided version. In other words,
given:
Module => '>= 1.00, < 2.00'
We will accept 1.00 and 1.75 but not 0.50 or 2.00.
For modules that do not appear in the requirements, this method will return
true.
=head2 clear_requirement
$req->clear_requirement( $module );
This removes the requirement for a given module from the object.
This method returns the requirements object.
=head2 requirements_for_module
$req->requirements_for_module( $module );
This returns a string containing the version requirements for a given module in
the format described in L or undef if the given module has no
requirements. This should only be used for informational purposes such as error
messages and should not be interpreted or used for comparison (see
L instead).
=head2 structured_requirements_for_module
$req->structured_requirements_for_module( $module );
This returns a data structure containing the version requirements for a given
module or undef if the given module has no requirements. This should
not be used for version checks (see L instead).
Added in version 2.134.
=head2 required_modules
This method returns a list of all the modules for which requirements have been
specified.
=head2 clone
$req->clone;
This method returns a clone of the invocant. The clone and the original object
can then be changed independent of one another.
=head2 is_simple
This method returns true if and only if all requirements are inclusive minimums
-- that is, if their string expression is just the version number.
=head2 is_finalized
This method returns true if the requirements have been finalized by having the
C method called on them.
=head2 finalize
This method marks the requirements finalized. Subsequent attempts to change
the requirements will be fatal, I they would result in a change. If they
would not alter the requirements, they have no effect.
If a finalized set of requirements is cloned, the cloned requirements are not
also finalized.
=head2 as_string_hash
This returns a reference to a hash describing the requirements using the
strings in the L specification.
For example after the following program:
my $req = CPAN::Meta::Requirements->new;
$req->add_minimum('CPAN::Meta::Requirements' => 0.102);
$req->add_minimum('Library::Foo' => 1.208);
$req->add_maximum('Library::Foo' => 2.602);
$req->add_minimum('Module::Bar' => 'v1.2.3');
$req->add_exclusion('Module::Bar' => 'v1.2.8');
$req->exact_version('Xyzzy' => '6.01');
my $hashref = $req->as_string_hash;
C<$hashref> would contain:
{
'CPAN::Meta::Requirements' => '0.102',
'Library::Foo' => '>= 1.208, <= 2.206',
'Module::Bar' => '>= v1.2.3, != v1.2.8',
'Xyzzy' => '== 6.01',
}
=head2 add_string_requirement
$req->add_string_requirement('Library::Foo' => '>= 1.208, <= 2.206');
$req->add_string_requirement('Library::Foo' => v1.208);
This method parses the passed in string and adds the appropriate requirement
for the given module. A version can be a Perl "v-string". It understands
version ranges as described in the L. For
example:
=over 4
=item 1.3
=item >= 1.3
=item <= 1.3
=item == 1.3
=item != 1.3
=item > 1.3
=item < 1.3
=item >= 1.3, != 1.5, <= 2.0
A version number without an operator is equivalent to specifying a minimum
(C=>). Extra whitespace is allowed.
=back
=head2 from_string_hash
my $req = CPAN::Meta::Requirements->from_string_hash( \%hash );
my $req = CPAN::Meta::Requirements->from_string_hash( \%hash, \%opts );
This is an alternate constructor for a CPAN::Meta::Requirements
object. It takes a hash of module names and version requirement
strings and returns a new CPAN::Meta::Requirements object. As with
add_string_requirement, a version can be a Perl "v-string". Optionally,
you can supply a hash-reference of options, exactly as with the L
method.
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
=head1 SUPPORT
=head2 Bugs / Feature Requests
Please report any bugs or feature requests through the issue tracker
at L.
You will be notified automatically of any progress on your issue.
=head2 Source Code
This is open source software. The code repository is available for
public review and contribution under the terms of the license.
L
git clone https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements.git
=head1 AUTHORS
=over 4
=item *
David Golden
=item *
Ricardo Signes
=back
=head1 CONTRIBUTORS
=for stopwords Ed J Karen Etheridge Leon Timmermans robario
=over 4
=item *
Ed J
=item *
Karen Etheridge
=item *
Leon Timmermans
=item *
robario
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by David Golden and Ricardo Signes.
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
PK ! Ps, History/Meta_1_0.podnu [ =for :stopwords DOAP RDF
=head1 NAME
CPAN::Meta::History::Meta_1_0 - Version 1.0 metadata specification for META.yml
=head1 PREFACE
This is a historical copy of the version 1.0 specification for F
files, copyright by Ken Williams and licensed under the same terms as Perl
itself.
Modifications from the original:
=over
=item *
Conversion from the original HTML to POD format
=item *
Include list of valid licenses from L 0.17 rather than
linking to the module, with minor updates to text and links to reflect
versions at the time of publication.
=item *
Fixed some dead links to point to active resources.
=back
=head1 DESCRIPTION
This document describes version 1.0 of the F specification.
The META.yml file describes important properties of contributed Perl
distributions such as the ones found on L. It is
typically created by tools like L and L.
The fields in the F file are meant to be helpful to people
maintaining module collections (like CPAN), for people writing
installation tools (like L or L), or just people who want to
know some stuff about a distribution before downloading it and starting to
install it.
=head1 Format
F files are written in the L format. The
reasons we chose YAML instead of, say, XML or Data::Dumper are discussed in
L
on the MakeMaker mailing list.
The first line of a F file should be a valid
L
like C<"--- #YAML:1.0">
=head1 Fields
The rest of the META.yml file is one big YAML
L,
whose keys are described here.
=over 4
=item name
Example: C
The name of the distribution. Often created by taking the "main
module" in the distribution and changing "::" to "-". Sometimes it's
completely different, however, as in the case of the
L distribution.
=item version
Example: C<0.16>
The version of the distribution to which the META.yml file refers.
=item license
Example: C
The license under which this distribution may be used and
redistributed.
Must be one of the following licenses:
=over 4
=item perl
The distribution may be copied and redistributed under the same terms as perl
itself (this is by far the most common licensing option for modules on CPAN).
This is a dual license, in which the user may choose between either the GPL
version 1 or the Artistic version 1 license.
=item gpl
The distribution is distributed under the terms of the GNU General Public
License version 2 (L).
=item lgpl
The distribution is distributed under the terms of the GNU Lesser General
Public License version 2 (L).
=item artistic
The distribution is licensed under the Artistic License version 1, as specified
by the Artistic file in the standard perl distribution
(L).
=item bsd
The distribution is licensed under the BSD 3-Clause License
(L).
=item open_source
The distribution is licensed under some other Open Source Initiative-approved
license listed at L.
=item unrestricted
The distribution is licensed under a license that is B approved by
L but that allows distribution
without restrictions.
=item restrictive
The distribution may not be redistributed without special permission from the
author and/or copyright holder.
=back
=item distribution_type
Example: C
What kind of stuff is contained in this distribution. Most things on
CPAN are Cs (which can also mean a collection of
modules), but some things are C