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 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
#!/usr/bin/perl
# build-versions-db builds the versions mldmb database
# and is released under the terms of the GNU GPL version 3, or any
# later version, at your option. See the file README and COPYING for
# more information.
# Copyright 2016 by Don Armstrong <don@donarmstrong.com>.
use warnings;
use strict;
use Getopt::Long;
use Pod::Usage;
=head1 NAME
build-versions-db -- builds source and source maintainers file
=head1 SYNOPSIS
build-versions-db [options] versions.idx.new versions.idx.new \
/srv/bugs.debian.org/versions/indices/ftp
Options:
--debug, -d debugging level (Default 0)
--help, -h display this help
--man, -m display manual
=head1 OPTIONS
=over
=item B<--update>
Update an existing database; the default. B<--no-update> will regenerate an
existing database from scratch.
=item B<--debug, -d>
Debug verbosity. (Default 0)
=item B<--help, -h>
Display brief usage information.
=item B<--man, -m>
Display this manual.
=back
=head1 EXAMPLES
build-versions-db versions.idx.new versions.idx.new \
/srv/bugs.debian.org/versions/indices/ftp \
stable
=cut
use vars qw($DEBUG);
use Debbugs::Versions::Dpkg;
use Debbugs::Config qw(:config);
use File::Copy;
use MLDBM qw(DB_File Storable);
use Fcntl;
my %options = (debug => 0,
help => 0,
man => 0,
update => 1,
);
GetOptions(\%options,
'update!',
'debug|d+','help|h|?','man|m');
pod2usage() if $options{help};
pod2usage({verbose=>2}) if $options{man};
$DEBUG = $options{debug};
my @USAGE_ERRORS;
if (not @ARGV >= 4) {
push @USAGE_ERRORS,
"You must provide at least four arguments, two databases, ".
"a top level directory and at least one suite";
}
pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
my $versions = shift @ARGV;
my $versions_time = shift @ARGV;
my $versions_new = $versions."_".$$."_".time;
my $versions_time_new = $versions_time."_".$$."_".time;
my $toplevel = shift @ARGV;
my @suites = @ARGV;
$MLDBM::DumpMeth=q(portable);
my $time = time;
my %db;
my %db2;
if ($options{update}) {
copy($versions_time,$versions_time_new);
}
tie %db, "MLDBM", $versions_new, O_CREAT|O_RDWR, 0664
or die "tie $versions: $!";
tie %db2, "MLDBM", $versions_time_new,O_CREAT|O_RDWR, 0664
or die "tie $versions_time failed: $!";
update_versions_suites(\%db,\%db2,\@suites);
versions_time_cleanup(\%db2) if $options{update};
move($versions_new,$versions);
move($versions_time_new,$versions_time);
sub open_compressed_file {
my ($file) = @_;
my $fh;
my $mode = '<:encoding(UTF-8)';
my @opts;
if ($file =~ /\.gz$/) {
$mode = '-|:encoding(UTF-8)';
push @opts,'gzip','-dc';
}
if ($file =~ /\.xz$/) {
$mode = '-|:encoding(UTF-8)';
push @opts,'xz','-dc';
}
if ($file =~ /\.bz2$/) {
$mode = '-|:encoding(UTF-8)';
push @opts,'bzip2','-dc';
}
open($fh,$mode,@opts,$file);
return $fh;
}
# Read Package, Version, and Source fields from a Packages.gz file.
sub read_packages {
my ($db,$db2,$packages, $component,$arch,$dist) = @_;
my $PACKAGES = open_compressed_file($packages) or
die "Unable to open $packages for reading: $!";
local $_;
local $/ = ''; # paragraph mode
print STDERR "reading packages $packages\n" if $DEBUG;
for (<$PACKAGES>) {
/^Package: (.+)/im or next;
my $pkg = $1;
/^Version: (.+)/im or next;
my $ver = $1;
my $extra_source_only = 0;
if (/^Extra-Source-Only: yes/im) {
$extra_source_only = 1;
}
update_package_version($db,$db2,$dist,$arch,$pkg,$ver,$time) unless
$extra_source_only;
}
close($PACKAGES) or
die "Error while closing ${packages}: $!";
}
sub update_package_version {
my ($db,$db2,$d,$a,$p,$v,$t) = @_;
# see MLDBM(3pm)/BUGS
my $tmp = $db->{$p};
# we allow multiple versions in an architecture now; this
# should really only happen in the case of source, however.
push @{$tmp->{$d}{$a}}, $v;
$db->{$p} = $tmp;
$tmp = $db2->{$p};
$tmp->{$d}{$a}{$v} = $time if not exists
$tmp->{$d}{$a}{$v};
$db2->{$p} = $tmp;
}
sub update_versions_suites {
my ($db,$db2,$suites) = @_;
# Iterate through all Packages and Sources files.
for my $suite (@{$suites}) {
my $suitedir = "$toplevel/$suite";
for my $component ('main', 'main/debian-installer',
'contrib', 'contrib/debian-installer',
'non-free', 'non-free/debian-installer',
'non-free-firmware', 'non-free-firmware/debian-installer',
) {
my $componentdir = "$suitedir/$component";
if (not -d $componentdir) {
print STDERR "No directory $suitedir/$component\n" if $DEBUG;
next;
}
my $COMPONENT;
opendir $COMPONENT, $componentdir or die "opendir $componentdir: $!";
# debian-installer is really a section rather than a component
# (ugh).
my $viscomponent = $component;
$viscomponent =~ s[/.*][];
my $sources = (grep { -f $_ } glob "$componentdir/source/Sources.*")[0];
if (not defined $sources) {
print STDERR "No sources matching $componentdir/source/Sources.*\n" if $DEBUG;
} else {
read_packages($db,$db2,$sources, $viscomponent,'source',$suite);
}
for my $arch (readdir $COMPONENT) {
next unless $arch =~ s/^binary-//;
my $archdir = "$componentdir/binary-$arch";
my $packages = (grep { -f $_ } glob("$archdir/Packages.*"))[0];
if (not defined $packages) {
print STDERR "No binary packages matching $archdir/Packages.*\n" if $DEBUG;
next;
}
read_packages($db,$db2,$packages, $viscomponent,$arch,$suite);
}
closedir $COMPONENT or
die "Unable to closedir $componentdir: $!";
}
}
}
sub versions_time_cleanup {
my ($db) = @_;
my $time = time;
for my $package (keys %{$db}) {
my $temp = $db->{$package};
for my $dist (keys %{$temp}) {
for my $arch (keys %{$temp->{$dist}}) {
my @versions = (sort {$temp->{$dist}{$arch}{$a} <=>
$temp->{$dist}{$arch}{$b}
}
keys %{$temp->{$dist}{$arch}});
next unless @versions > 1;
for my $i (0 .. ($#versions-1)) {
last if $temp->{$dist}{$arch}{$versions[$i+1]} >
($time - $config{remove_age}*60*60*24);
last if keys %{$temp->{$dist}{$arch}} <= 1;
delete $temp->{$dist}{$arch}{$versions[$i]};
}
}
}
$db->{$package} = $temp;
}
}
|