December 6, 2011

1

Progamatically Comparing Debian Package Versions

perl

Continuing on with this project it became necessary to look a little deeper on how apt based software repositories handle version numbers. At first I thought this was the silliest overly complicated mess I could imaging. For instance, how does one compare package versions that look like this ‘2:1.0~rc3++svn20100804-0.2squeeze1′ with versions that look like this ‘2:1.0~rc3++final.dfsg1-1′ to determine which is newer? Luckily this is actually well documented, makes perfect sense when you stop to think about it, and (through the power of open source) easy to accomplish programatically.

 Dissecting Debian Package Version

For starters reading the deb-version man page is very enlightening. I won’t reprint it here but will try to sum it up using the above examples.

For starters a deb version is composed of the following:

<epoch>:<upstream version>-<debian version>

So using our above example the epoch would be ‘2’, the upstream version would be ‘1.0~rc3++svn20100804′ and ‘1.0~rc3++final.dfsg1′ and the debian version would be either ‘0.2squeeze1′ or ‘1.’ So why so complicated? Well it appears to go back to some fundamental open source philosophy. Projects like Debian are not here to dictate how software developers handle their version numbering. It is important that Debian based communities to not create additional confusion by changing the version numbers from what upstream contributors have determined. However, sometimes the many different software developers use version schemes which are just impossible to  track. For that reason we have the OPTIONAL epoch number. This way the repository administrators can assign an integer to help deconflict obfusticated package version schemes. Lastly, if the community had to modify the original sources when creating a deb package (relocate default files, modify startup scripts, etc.) it would be dishonest to advertise this package as being the exact same version as the originators. For this reason it is appropriate to include a debian version section.

The Comparison

This is all fine and dandy, but how do we go about making comparisons between packages to determine which ones are newer? Luckily this is spelled out in the deb-version man page as well:

First the initial part of each string consisting entirely of  non-digit characters  is determined.  These two parts (one of which may be empty) are compared lexically.  If a difference is found it is returned.   The lexical comparison is a comparison of ASCII values modified so that all the letters sort earlier than all the non-letters and so that  a  tilde sorts  before  anything, even the end of a part.  For example, the following parts are in sorted order: ‘~~’, ‘~~a’,  ‘~’,  the  empty  part,
‘a’.
Then  the  initial  part of the remainder of each string which consists entirely of digit characters is determined.  The  numerical  values  of these  two  parts are compared, and any difference found is returned as the result of the comparison.   For  these  purposes  an  empty  string (which  can  only occur at the end of one or both version strings being compared) counts as zero.
These two steps (comparing and removing initial non-digit  strings and initial digit strings) are repeated until a difference is found or both  strings are exhausted.

WOW! ROUGH!

The Perl Solution

Luckily the developers of dpkg have provided us with a solution. After downloading the sources for dpkg I noticed a ‘scripts’ directory which was chalk full of Perl goodies. Specifically there is a module called Dpkg::Version which automates this comparison for us. If you follow down the dependencies you will see that the file Version.pm is dependent upon ErrorHandling.pm, Gettext.pm, and Dpkg.pm. So I created a folder called perl_modules. I copied Dpkg.pm into this directory. Also, in this directory I created the folder named Dpkg and placed the rest of the files within. What follows is a small example of how to access these (now portable and small) Perl modules and do the comparison:

#!/usr/bin/perl
 
# Here we include the 3rd party perl modules
use lib “./perl_modules”;
use Dpkg::Version;
 
# Here we plug each of our versions into the perl module
$v1 = Dpkg::Version->new(‘2:1.0~rc3++svn20100804-0.2squeeze1‘);
$v2 = Dpkg::Version->new(‘2:1.0~rc3++final.dfsg1-1‘);
 
# Here we do simple mathematical comparison
if ( $v1 > $v2 ) { print “Ver. 1 is Newer\n”; }
if ( $v1 < $v2 ) { print “Ver. 2 is Newer\n”; }
 
# As it turns out Ver. 1 is newer which is true.

Of course when we use code developed by someone else in a project make sure to adhere to the License agreement and give correct and appropriate attribution.

 

1 Comment Post a comment
  1. Dec 6 2011

    One small edit – forgot to mention that if you have libdpkg-perl installed in Debian or Ubuntu you should also be able to access the man page for Dpkg::Version.

Share your thoughts, post a comment.

You must be logged in to post a comment.