summaryrefslogtreecommitdiffstats
path: root/private/refresh-perl-provides
blob: 4d70eff163eeab4a0aaae598bfd5fd4a03322ef8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/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 <http://www.gnu.org/licenses/>.

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