Spaces:
Running
Running
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<META.yml> or F<META.json> files in CPAN distributions, | |
#pod and as defined by L<CPAN::Meta::Spec>; | |
#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<bad_version_hook> -- 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<exactly> 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<CPAN::Meta::Spec> 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</accepts_module> 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</accepts_module> 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<finalize> 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<if> 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<CPAN::Meta::Spec> 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<CPAN::Meta::Spec/Version Ranges>. 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<E<gt>=>). 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</new> | |
#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<META.yml> or F<META.json> files in CPAN distributions, | |
and as defined by L<CPAN::Meta::Spec>; | |
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<bad_version_hook> -- 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<exactly> 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<CPAN::Meta::Spec> 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</accepts_module> 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</accepts_module> 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<finalize> method called on them. | |
=head2 finalize | |
This method marks the requirements finalized. Subsequent attempts to change | |
the requirements will be fatal, I<if> 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<CPAN::Meta::Spec> 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<CPAN::Meta::Spec/Version Ranges>. 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<E<gt>=>). 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</new> | |
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<https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements/issues>. | |
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<https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements> | |
git clone https://github.com/Perl-Toolchain-Gang/CPAN-Meta-Requirements.git | |
=head1 AUTHORS | |
=over 4 | |
=item * | |
David Golden <[email protected]> | |
=item * | |
Ricardo Signes <[email protected]> | |
=back | |
=head1 CONTRIBUTORS | |
=for stopwords Ed J Karen Etheridge Leon Timmermans robario | |
=over 4 | |
=item * | |
Ed J <[email protected]> | |
=item * | |
Karen Etheridge <[email protected]> | |
=item * | |
Leon Timmermans <[email protected]> | |
=item * | |
robario <[email protected]> | |
=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 | |