# Copyright (C) 2001-2021 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
package Automake::Version;
use 5.006;
use strict;
use warnings FATAL => 'all';
use Automake::ChannelDefs;
=head1 NAME
Automake::Version - version comparison
=head1 SYNOPSIS
use Automake::Version;
print "Version $version is older than required version $required\n"
if Automake::Version::check ($version, $required);
=head1 DESCRIPTION
This module provides support for comparing versions string
as they are used in Automake.
A version is a string that looks like
C where C, C, and
C are digits, C is a character, and C any
alphanumeric word.
Usually, C is used to label alpha releases or intermediate
snapshots, C is used for git branches or patched releases, and
C is used for bug fixes releases on the C branch.
For the purpose of ordering, C<1.4> is the same as C<1.4.0>, but
C<1.4g> is the same as C<1.4.99g>. The C identifier is ignored
in the ordering, except when it looks like C<-pMINOR[ALPHA]>: some
versions were labeled like C<1.4-p3a>, this is the same as an alpha
release labeled C<1.4.3a>. Yes, it's horrible, but Automake did not
support two-dot versions in the past.
=head2 FUNCTIONS
=over 4
=item C
Split the string C<$version> into the corresponding C<(MAJOR, MINOR,
MICRO, ALPHA, FORK)> tuple. For instance C<'1.4g'> would be split
into C<(1, 4, 99, 'g', '')>. Return C<()> on error.
=cut
sub split ($)
{
my ($ver) = @_;
# Special case for versions like 1.4-p2a.
if ($ver =~ /^(\d+)\.(\d+)(?:-p(\d+)([a-z]+)?)$/)
{
return ($1, $2, $3, $4 || '', '');
}
# Common case.
elsif ($ver =~ /^(\d+)\.(\d+)(?:\.(\d+))?([a-z])?(?:-([A-Za-z0-9]+))?$/)
{
return ($1, $2, $3 || (defined $4 ? 99 : 0), $4 || '', $5 || '');
}
return ();
}
=item C
Compare two version tuples, as returned by C.
Return 1, 0, or -1, if C is found to be respectively
greater than, equal to, or less than C.
=cut
sub compare (\@\@)
{
my @l = @{$_[0]};
my @r = @{$_[1]};
for my $i (0, 1, 2)
{
return 1 if ($l[$i] > $r[$i]);
return -1 if ($l[$i] < $r[$i]);
}
for my $i (3, 4)
{
return 1 if ($l[$i] gt $r[$i]);
return -1 if ($l[$i] lt $r[$i]);
}
return 0;
}
=item C
Handles the logic of requiring a version number in Automake.
C<$VERSION> should be Automake's version, while C<$REQUIRED>
is the version required by the user input.
Return 0 if the required version is satisfied, 1 otherwise.
=cut
sub check ($$)
{
my ($version, $required) = @_;
my @version = Automake::Version::split ($version);
my @required = Automake::Version::split ($required);
prog_error "version is incorrect: $version"
if $#version == -1;
# This should not happen, because process_option_list and split_version
# use similar regexes.
prog_error "required version is incorrect: $required"
if $#required == -1;
# If we require 3.4n-foo then we require something
# >= 3.4n, with the 'foo' fork identifier.
return 1
if ($required[4] ne '' && $required[4] ne $version[4]);
return 0 > compare (@version, @required);
}
1;