1 # This module is part of debbugs, and is released
2 # under the terms of the GPL version 2, or any later
3 # version at your option.
4 # See the file README and COPYING for more information.
6 # [Other people have contributed to this file; their copyrights should
8 # Copyright 2007 by Don Armstrong <don@donarmstrong.com>.
10 package Debbugs::Packages;
15 use Debbugs::Config qw(:config :globals);
17 use base qw(Exporter);
18 use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS @EXPORT);
24 %EXPORT_TAGS = (versions => [qw(getversions get_versions)],
25 mapping => [qw(getpkgsrc getpkgcomponent getsrcpkgs),
26 qw(binarytosource sourcetobinary makesourceversions)
30 Exporter::export_ok_tags(qw(versions mapping));
31 $EXPORT_TAGS{all} = [@EXPORT_OK];
34 use Fcntl qw(O_RDONLY);
35 use MLDBM qw(DB_File Storable);
36 use Storable qw(dclone);
37 use Params::Validate qw(validate_with :types);
38 use Debbugs::Common qw(make_list);
40 $MLDBM::DumpMeth = 'portable';
41 $MLDBM::RemoveTaint = 1;
45 Debbugs::Packages - debbugs binary/source package handling
49 The Debbugs::Packages module provides support functions to map binary
50 packages to their corresponding source packages and vice versa. (This makes
51 sense for software distributions, where developers may work on a single
52 source package which produces several binary packages for use by users; it
53 may not make sense in other contexts.)
61 Returns a reference to a hash of binary package names to their corresponding
70 return $_pkgsrc if $_pkgsrc;
71 return {} unless defined $Debbugs::Packages::gPackageSource;
76 open(MM,"$Debbugs::Packages::gPackageSource")
77 or die("open $Debbugs::Packages::gPackageSource: $!");
79 next unless m/^(\S+)\s+(\S+)\s+(\S.*\S)\s*$/;
80 my ($bin,$cmp,$src)=($1,$2,$3);
83 push @{$srcpkg{$src}}, $bin;
84 $pkgcomponent{$bin}= $cmp;
88 $_pkgcomponent = \%pkgcomponent;
95 Returns a reference to a hash of binary package names to the component of
96 the archive containing those binary packages (e.g. "main", "contrib",
101 sub getpkgcomponent {
102 return $_pkgcomponent if $_pkgcomponent;
104 return $_pkgcomponent;
109 Returns a list of the binary packages produced by a given source package.
115 getpkgsrc() if not defined $_srcpkg;
116 return () if not defined $src or not exists $_srcpkg->{$src};
117 return @{$_srcpkg->{$src}};
122 Returns a reference to the source package name and version pair
123 corresponding to a given binary package name, version, and architecture. If
124 undef is passed as the architecture, returns a list of references to all
125 possible pairs of source package names and versions for all architectures,
126 with any duplicates removed.
130 our %_binarytosource;
132 my ($binname, $binver, $binarch) = @_;
134 # TODO: This gets hit a lot, especially from buggyversion() - probably
135 # need an extra cache for speed here.
136 return () unless defined $gBinarySourceMap;
138 if (tied %_binarytosource or
139 tie %_binarytosource, 'MLDBM',
140 $gBinarySourceMap, O_RDONLY) {
141 # avoid autovivification
142 my $binary = $_binarytosource{$binname};
143 return () unless defined $binary;
144 my %binary = %{$binary};
145 if (exists $binary{$binver}) {
146 if (defined $binarch) {
147 my $src = $binary{$binver}{$binarch};
148 return () unless defined $src; # not on this arch
149 # Copy the data to avoid tiedness problems.
152 # Get (srcname, srcver) pairs for all architectures and
153 # remove any duplicates. This involves some slightly tricky
154 # multidimensional hashing; sorry. Fortunately there'll
155 # usually only be one pair returned.
157 for my $ar (keys %{$binary{$binver}}) {
158 my $src = $binary{$binver}{$ar};
159 next unless defined $src;
160 $uniq{$src->[0]}{$src->[1]} = 1;
163 for my $sn (sort keys %uniq) {
164 push @uniq, [$sn, $_] for sort keys %{$uniq{$sn}};
171 # No $gBinarySourceMap, or it didn't have an entry for this name and
178 Returns a list of references to triplets of binary package names, versions,
179 and architectures corresponding to a given source package name and version.
180 If the given source package name and version cannot be found in the database
181 but the source package name is in the unversioned package-to-source map
182 file, then a reference to a binary package name and version pair will be
183 returned, without the architecture.
187 our %_sourcetobinary;
189 my ($srcname, $srcver) = @_;
191 if (tied %_sourcetobinary or
192 tie %_sourcetobinary, 'MLDBM',
193 $gSourceBinaryMap, O_RDONLY) {
194 # avoid autovivification
195 my $source = $_sourcetobinary{$srcname};
196 return () unless defined $source;
197 my %source = %{$source};
198 if (exists $source{$srcver}) {
199 my $bin = $source{$srcver};
200 return () unless defined $bin;
205 # No $gSourceBinaryMap, or it didn't have an entry for this name and
206 # version. Try $gPackageSource (unversioned) instead.
207 my @srcpkgs = getsrcpkgs($srcname);
208 return map [$_, $srcver], @srcpkgs;
213 Returns versions of the package in a distribution at a specific
219 my ($pkg, $dist, $arch) = @_;
220 return get_versions(package=>$pkg,
222 defined $arch ? (arch => $arch):(),
230 get_version(package=>'foopkg',
235 Returns a list of the versions of package in the distributions and
236 architectures listed. This routine only returns unique values.
240 =item package -- package to return list of versions
242 =item dist -- distribution (unstable, stable, testing); can be an
245 =item arch -- architecture (i386, source, ...); can be an arrayref
247 =item time -- returns a version=>time hash at which the newest package
248 matching this version was uploaded
250 =item source -- returns source/version instead of just versions
260 my %param = validate_with(params => \@_,
261 spec => {package => {type => SCALAR,
263 dist => {type => SCALAR|ARRAYREF,
264 default => 'unstable',
266 arch => {type => SCALAR|ARRAYREF,
269 time => {type => BOOLEAN,
272 source => {type => BOOLEAN,
279 return () if not defined $gVersionTimeIndex;
280 unless (tied %_versions_time) {
281 tie %_versions_time, 'MLDBM', $gVersionTimeIndex, O_RDONLY
282 or die "can't open versions index $gVersionTimeIndex: $!";
284 $versions = \%_versions_time;
287 return () if not defined $gVersionIndex;
288 unless (tied %_versions) {
289 tie %_versions, 'MLDBM', $gVersionIndex, O_RDONLY
290 or die "can't open versions index $gVersionIndex: $!";
292 $versions = \%_versions;
295 for my $package (make_list($param{package})) {
296 my $version = $versions->{$package};
297 next unless defined $version;
298 for my $dist (make_list($param{dist})) {
299 for my $arch (exists $param{arch}?
300 make_list($param{arch}):
301 (keys %{$version->{$dist}})) {
302 next unless defined $version->{$dist}{$arch};
303 for my $ver (ref $version->{$dist}{$arch} ?
304 keys %{$version->{$dist}{$arch}} :
305 $version->{$dist}{$arch}
308 if ($param{source}) {
309 $f_ver = makesourceversions($package,$arch,$ver)
312 $versions{$f_ver} = max($versions{$f_ver}||0,$version->{$dist}{$arch}{$ver});
315 $versions{$f_ver} = 1;
324 return keys %versions;
328 =item makesourceversions
330 @{$cgi_var{found}} = makesourceversions($cgi_var{package},undef,@{$cgi_var{found}});
332 Canonicalize versions into source versions, which have an explicitly
333 named source package. This is used to cope with source packages whose
334 names have changed during their history, and with cases where source
335 version numbers differ from binary version numbers.
339 our %_sourceversioncache = ();
340 sub makesourceversions {
345 for my $version (@_) {
346 if ($version =~ m[/]) {
347 # Already a source version.
348 $sourceversions{$version} = 1;
350 my $cachearch = (defined $arch) ? $arch : '';
351 my $cachekey = "$pkg/$cachearch/$version";
352 if (exists($_sourceversioncache{$cachekey})) {
353 for my $v (@{$_sourceversioncache{$cachekey}}) {
354 $sourceversions{$v} = 1;
359 my @srcinfo = binarytosource($pkg, $version, $arch);
361 # We don't have explicit information about the
362 # binary-to-source mapping for this version (yet). Since
363 # this is a CGI script and our output is transient, we can
364 # get away with just looking in the unversioned map; if it's
365 # wrong (as it will be when binary and source package
366 # versions differ), too bad.
367 my $pkgsrc = getpkgsrc();
368 if (exists $pkgsrc->{$pkg}) {
369 @srcinfo = ([$pkgsrc->{$pkg}, $version]);
370 } elsif (getsrcpkgs($pkg)) {
371 # If we're looking at a source package that doesn't have
372 # a binary of the same name, just try the same version.
373 @srcinfo = ([$pkg, $version]);
378 $sourceversions{"$_->[0]/$_->[1]"} = 1 foreach @srcinfo;
379 $_sourceversioncache{$cachekey} = [ map { "$_->[0]/$_->[1]" } @srcinfo ];
383 return sort keys %sourceversions;