# Copyright (C) 2001-2023 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.010; 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;