#!/usr/bin/perl use v5.20; use warnings; use utf8; # Generate a list of packages that are provided by the Perl core packages # and also packaged separately at a (hopefully) newer version. # The list will have the package name and the upstream version of the # corresponding module integrated in the currently installed Perl version. # Copyright (C) 2008 Niko Tyni # # 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 of the License, 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 . use Const::Fast; use List::SomeUtils qw(none); use Unicode::UTF8 qw(encode_utf8); # from /usr/share/doc/libapt-pkg-perl/examples/apt-cache use AptPkg::Config '$_config'; use AptPkg::System '$_system'; use AptPkg::Cache; const my $EMPTY => q{}; const my $LAST_ITEM => -1; (my $self = $0) =~ s{.*/}{}; # initialise the global config object with the default values and # setup the $_system object $_config->init; $_system = $_config->system; # suppress cache building messages $_config->{quiet} = 2; # set up the cache my $cache = AptPkg::Cache->new; # end from /usr/share/doc/libapt-pkg-perl/examples/apt-cache # special cases when libfoo-bar-perl => Foo::Bar doesn't work my %module_name = ( 'libio-compress-perl' => 'IO::Compress::Gzip', 'libio-compress-zlib-perl' => 'IO::Compress::Gzip', ); # special cases for where the code gets the prefix wrong my %manual_split = ('libautodie-perl' => qr/\A (\d++\.) (\d{2}) (\d{2})? \Z/xsmo,); use Module::CoreList; my $versioning = $_system->versioning; my $perl_version = $]; # Map 5.022002 into 5.22 $perl_version =~ s/^(5)\.0*([1-9][0-9])\d+/$1.$2/; # we look at packages provided by these my @core_packages = (qw(perl-base perl), "perl-modules-$perl_version"); # check we have a cache of Debian sid packages available warn encode_utf8( join(q{ }, 'Warning: this list should only be updated on a system', 'with an up to date APT cache of the Debian unstable distribution') ) if ( none { defined $_->{Origin} && defined $_->{Archive} && $_->{Origin} eq 'Debian' && $_->{Archive} eq 'unstable'; }@{$cache->files} ); print encode_utf8(<<"EOF"); # virtual packages provided by the Perl core packages that also have a # separate binary package available # # the listed version is the one included in the Perl core # # regenerate by running # debian/rules refresh-perl-provides # in the lintian source tree # # last updated for PERL_VERSION=$] EOF for my $pkg (@core_packages) { my $cached_versions = $cache->{$pkg} or die encode_utf8("no such binary package found in the APT cache: $pkg"); my $latest = bin_latest($cached_versions); for my $provides (@{$latest->{ProvidesList}}) { my $name = $provides->{Name}; # skip virtual-only packages next if (!$cache->{$name}{VersionList}); my $cpan_version = find_core_version($name); next if !$cpan_version; # the number of digits is a pain # we use the current version in the Debian archive to determine # how many we need # the epoch is easier, we just copy it my ($epoch, $digits) = epoch_and_digits($name); my $debian_version = cpan_version_to_deb($name, $cpan_version, $epoch, $digits); next if !$debian_version; print encode_utf8("$name $debian_version\n"); } } # look up the CPAN version of a package in the core sub find_core_version { my $module = shift; my $ret; return undef if $module =~ /^perl(5|api)/; if (exists $module_name{$module}) { $module = $module_name{$module}; } else { # mangle the package name into the module name $module =~ s/^lib//; $module =~ s/-perl$//; $module =~ s/-/::/g; } for (Module::CoreList->find_modules(qr/^\Q$module\E$/i, 0+$])) { $ret = $Module::CoreList::version{0+$]}{$_}; last; } return $ret; } sub cpan_version_to_deb { my ($pkg, $cpan_version, $epoch, $digits) = @_; $epoch ||= $EMPTY; # cpan_version # digits # result # 1.15_02, 2 => 1.15.02 # 1.15_02, 4 => 1.1502 # 1.15_02, 0 => 1.15.02 # # 1.15_021, 2 => 1.15.021 # 1.15_021, 4 => 1.1500.021 # 1.15_021, 0 => 1.15.021 # # 1.15, 1 => 1.15 # 1.15, 2 => 1.15 # 1.15, 4 => 1.1500 # 1.15, 0 => 1.15 # split 1.15_02 to (1, 15, 02) my $regex = qr/^(\d+\.)(\d+)(?:_(\d+))?$/; $regex = $manual_split{$pkg} if exists $manual_split{$pkg}; my ($major, $prefix, $suffix) = ($cpan_version =~ $regex); die encode_utf8("no match with $cpan_version?") if !$major; $suffix ||= $EMPTY; if (length($suffix) + length($prefix) == $digits) { $prefix .= $suffix; $suffix = $EMPTY; } if (length($suffix) + length($prefix) < $digits) { $prefix .= '0' while length($prefix) < $digits; } $suffix = ".$suffix" if $suffix ne $EMPTY; return $epoch.$major.$prefix.$suffix; } # Given a Debian binary package name, look up its latest version # and return its epoch (including the colon) if available, and # the number of digits in its decimal part sub epoch_and_digits { my $p = shift; return (0, 0) if !exists $cache->{$p}; return (0, 0) if !exists $cache->{$p}{VersionList}; # virtual package my $latest = bin_latest($cache->{$p}); my $v = $latest->{VerStr}; $v =~ s/\+dfsg//; my ($epoch, $major, $prefix, $suffix, $revision) = ($v =~ /^(?:(\d+:))?((?:\d+\.))+(\d+)(?:_(\d+))?(-[^-]+)$/); return ($epoch, length $prefix); } sub bin_latest { my $p = shift; return (sort bin_byversion @{$p->{VersionList}})[$LAST_ITEM]; } sub bin_byversion { return $versioning->compare($a->{VerStr}, $b->{VerStr}); } # Local Variables: # indent-tabs-mode: nil # cperl-indent-level: 4 # End: # vim: syntax=perl sw=4 sts=4 sr et