chiark / gitweb /
devscripts (2.10.69+squeeze4) stable-security; urgency=high
[devscripts.git] / scripts / build-rdeps.pl
1 #!/usr/bin/perl
2 #   Copyright (C) Patrick Schoenfeld
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 =head1 NAME
19
20 build-rdeps - find packages that depend on a specific package to build (reverse build depends)
21
22 =head1 SYNOPSIS
23
24 B<build-rdeps> I<package>
25
26 =head1 DESCRIPTION
27
28 B<build-rdeps> searches for all packages that build-depend on the specified package.
29
30 =head1 OPTIONS
31
32 =over 4
33
34 =item B<-u> B<--update>
35
36 Run apt-get update before searching for build-depends.
37
38 =item B<-s> B<--sudo>
39
40 Use sudo when running apt-get update. Has no effect if -u is omitted.
41
42 =item B<--distribution>
43
44 Select another distribution, which is searched for build-depends.
45
46 =item B<--only-main>
47
48 Ignore contrib and non-free
49
50 =item B<--exclude-component>
51
52 Ignore the given component (e.g. main, contrib, non-free).
53
54 =item B<--origin>
55
56 Restrict the search to only the specified origin (such as "Debian").
57
58 =item B<-m> B<--print-maintainer>
59
60 Print the value of the maintainer field for each package.
61
62 =item B<-d> B<--debug>
63
64 Run the debug mode
65
66 =item B<--help>
67
68 Show the usage information.
69
70 =item B<--version>
71
72 Show the version information.
73
74 =back
75
76 =head1 REQUIREMENTS
77
78 The tool requires apt Sources files to be around for the checked components.
79 In the default case this means that in /var/lib/apt/lists files need to be
80 around for main, contrib and non-free.
81
82 In practice this means one needs to add one deb-src line for each component,
83 e.g.
84
85 deb-src http://<mirror>/debian <dist> main contrib non-free
86
87 and run apt-get update afterwards or use the update option of this tool.
88
89 =cut
90
91 use warnings;
92 use strict;
93 use File::Basename;
94 use File::Find;
95 use Getopt::Long;
96 use Pod::Usage;
97 use Data::Dumper;
98 my $progname = basename($0);
99 my $version = '1.0';
100 my $dctrl = "/usr/bin/grep-dctrl";
101 my $sources_path = "/var/lib/apt/lists/";
102 my $release_pattern = '(.*_dists_(sid|unstable))_Release$';
103 my %seen_origins;
104 my @source_files;
105 my $opt_debug;
106 my $opt_update;
107 my $opt_sudo;
108 my $opt_maintainer;
109 my $opt_mainonly;
110 my $opt_distribution;
111 my $opt_origin = 'Debian';
112 my @opt_exclude_components;
113
114 if (!(-x $dctrl)) {
115         die "$progname: Fatal error. grep-dctrl is not available.\nPlease install the 'dctrl-tools' package.\n";
116 }
117
118 sub version {
119         print <<"EOT";
120 This is $progname $version, from the Debian devscripts package, v. ###VERSION###
121 This code is copyright by Patrick Schoenfeld, all rights reserved.
122 It comes with ABSOLUTELY NO WARRANTY. You are free to redistribute this code
123 under the terms of the GNU General Public License, version 2 or later.
124 EOT
125 exit (0);
126 }
127
128 sub usage {
129         print <<"EOT";
130 usage: $progname packagename
131        $progname --help
132        $progname --version
133
134 Searches for all packages that build-depend on the specified package.
135
136 Options:
137    -u, --update                   Run apt-get update before searching for build-depends.
138                                   (needs root privileges)
139    -s, --sudo                     Use sudo when running apt-get update
140                                   (has no effect when -u is ommitted)
141    -d, --debug                    Enable the debug mode
142    -m, --print-maintainer         Print the maintainer information (experimental)
143    --distribution distribution    Select a distribution to search for build-depends
144                                   (Default: unstable)
145    --origin origin                Select an origin to search for build-depends
146                                   (Default: Debian)
147    --only-main                    Ignore contrib and non-free
148    --exclude-component COMPONENT  Ignore the specified component (can be given multiple times)
149
150 EOT
151 version;
152 }
153
154 # Sub to test if a given section shall be included in the result
155 sub test_for_valid_component {
156     my $filebase = shift;
157
158     if ($opt_mainonly and $filebase =~ /(contrib|non-free)/) {
159         return -1;
160     }
161     foreach my $component (@opt_exclude_components) {
162         if ($filebase =~ /$component/) {
163             return -1;
164         }
165     }
166
167     if (! -e "$sources_path/$filebase") {
168         print STDERR "Warning: Ignoring missing sources file $filebase. (Missing component in sources.list?)\n";
169         return -1;
170     }
171
172     print STDERR "DEBUG: Component ($_) may not be excluded.\n" if ($opt_debug);
173     return 0;
174 }
175
176 # Scan Release files and add appropriate Sources files
177 sub readrelease {
178     my ($file, $base) = @_;
179     open(RELEASE, '<', "$sources_path/$file");
180     while (<RELEASE>) {
181         if (/^Origin:\s*(.+)\s*$/) {
182             my $origin = $1;
183             # skip undesired (non-specified or already seen) origins
184             if (($opt_origin && $origin !~ /^\s*\Q$opt_origin\E\s*$/)
185                 || $seen_origins{$origin}) {
186                 last;
187             }
188             $seen_origins{$origin} = 1;
189         }
190         elsif (/^(?:MD5|SHA)\w+:/) {
191             # from a list of checksums, grab names of Sources files
192             while (<RELEASE>) {
193                 last unless /^ /;
194                 if (/([^ ]+\/Sources)$/) {
195                     addsources($base, $1);
196                 }
197             }
198             last;
199         }
200     }
201     close(RELEASE);
202 }
203
204 # Add a *_Sources file if test_for_valid_component likes it
205 sub addsources {
206     my ($base, $filename) = @_;
207     # main/source/Sources
208     $filename =~ s/\//_/g;
209     # -> ftp.debian.org_..._main_source_Sources
210     $filename = "${base}_${filename}";
211     if (test_for_valid_component($filename) == 0) {
212         push(@source_files, $filename);
213         print STDERR "DEBUG: Added source file: $_\n" if ($opt_debug);
214     }
215 }
216
217 sub findreversebuilddeps {
218         my ($package, $source_file) = @_;
219         my %packages;
220         my $depending_package;
221         my $count=0;
222         my $maintainer_info='';
223
224         open(PACKAGES, "$dctrl -F Build-Depends,Build-Depends-Indep $package -s Package,Build-Depends,Build-Depends-Indep,Maintainer $source_file|");
225
226         while(<PACKAGES>) {
227                 chomp;
228                 print STDERR "$_\n" if ($opt_debug);
229                 if (/Package: (.*)$/) {
230                         $depending_package = $1;
231                         $packages{$depending_package}->{'Build-Depends'} = 0;
232                 }
233                 elsif (/Maintainer: (.*)$/) {
234                         if ($depending_package) {
235                                 $packages{$depending_package}->{'Maintainer'} = $1;
236                         }
237                 }
238                 elsif (/Build-Depends: (.*)$/ or /Build-Depends-Indep: (.*)$/) {
239                         if ($depending_package) {
240                                 print STDERR "$1\n" if ($opt_debug);
241                                 if ($1 =~ /^(.*\s)?\Q$package\E([\s,]|$)/) {
242                                         $packages{$depending_package}->{'Build-Depends'} = 1;
243                                 }
244                         }
245
246                 }
247         }
248
249         while($depending_package = each(%packages)) {
250                 if ($packages{$depending_package}->{'Build-Depends'} != 1) {
251                         print STDERR "Ignoring package $depending_package because its not really build depending on $package.\n" if ($opt_debug);
252                         next;
253                 }
254                 if ($opt_maintainer) {
255                         $maintainer_info = "($packages{$depending_package}->{'Maintainer'})";
256                 }
257
258                 $count+=1;
259                 print "$depending_package $maintainer_info \n";
260
261         }
262
263         if ($count == 0) {
264                 print "No reverse build-depends found for $package.\n\n"
265         }
266         else {
267                 print "\nFound a total of $count reverse build-depend(s) for $package.\n\n";
268         }
269 }
270
271 if ($#ARGV < 0) { usage; exit(0); }
272
273
274 Getopt::Long::Configure('bundling');
275 GetOptions(
276         "u|update" => \$opt_update,
277         "s|sudo" => \$opt_sudo,
278         "m|print-maintainer" => \$opt_maintainer,
279         "distribution=s" => \$opt_distribution,
280         "only-main" => \$opt_mainonly,
281         "exclude-component=s" => \@opt_exclude_components,
282         "origin=s" => \$opt_origin,
283         "d|debug" => \$opt_debug,
284         "h|help" => sub { usage; },
285         "v|version" => sub { version; }
286 );
287
288 my $package = shift;
289
290 if (!$package) {
291         die "$progname: missing argument. expecting packagename\n";
292 }
293
294 print STDERR "DEBUG: Package => $package\n" if ($opt_debug);
295
296 if ($opt_update) {
297         print STDERR "DEBUG: Updating apt-cache before search\n" if ($opt_debug);
298         my @cmd;
299         if ($opt_sudo) {
300                 print STDERR "DEBUG: Using sudo to become root\n" if ($opt_debug);
301                 push(@cmd, 'sudo');
302         }
303         push(@cmd, 'apt-get', 'update');
304         system @cmd;
305 }
306
307 if ($opt_distribution) {
308         print STDERR "DEBUG: Setting distribution to $opt_distribution\n" if ($opt_debug);
309         $release_pattern = '(.*_dists_' . $opt_distribution . ')_Release$';
310 }
311
312 # Find sources files
313 find(sub { readrelease($_, $1) if /$release_pattern/ }, $sources_path);
314
315 if (!@source_files) {
316         die "$progname: unable to find sources files.\nDid you forget to run apt-get update (or add --update to this command)?";
317 }
318
319 foreach my $source_file (@source_files) {
320         if ($source_file =~ /main/) {
321                 print "Reverse Build-depends in main:\n";
322                 print "------------------------------\n\n";
323                 findreversebuilddeps($package, "$sources_path/$source_file");
324         }
325
326         if ($source_file =~ /contrib/) {
327                 print "Reverse Build-depends in contrib:\n";
328                 print "---------------------------------\n\n";
329                 findreversebuilddeps($package, "$sources_path/$source_file");
330         }
331
332         if ($source_file =~ /non-free/) {
333                 print "Reverse Build-depends in non-free:\n";
334                 print "----------------------------------\n\n";
335                 findreversebuilddeps($package, "$sources_path/$source_file");
336         }
337 }
338
339 =head1 LICENSE
340
341 This code is copyright by Patrick Schoenfeld
342 <schoenfeld@debian.org>, all rights reserved.
343 This program comes with ABSOLUTELEY NO WARRANTY.
344 You are free to redistribute this code under the terms of the
345 GNU General Public License, version 2 or later.
346
347 =head1 AUTHOR
348
349 Patrick Schoenfeld <schoenfeld@debian.org>
350
351 =cut