]> git.donarmstrong.com Git - wannabuild.git/blob - bin/wanna-build
cleanup $arch in call_edos_depcheck
[wannabuild.git] / bin / wanna-build
1 #!/usr/bin/perl
2
3 # wanna-build: coordination script for Debian buildds
4 # Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5 # Copyright (C) 2005-2008 Ryan Murray <rmurray@debian.org>
6 # Copyright (C) 2010,2011 Andreas Barth <aba@not.so.argh.org>
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License as
10 # published by the Free Software Foundation; either version 2 of the
11 # License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 #
22 use strict;
23 use warnings;
24 use 5.010;
25
26 package conf;
27
28 use vars qw< $basedir $dbbase $transactlog $mailprog $buildd_domain >;
29 # defaults
30 $basedir ||= "/var/lib/debbuild";
31 $dbbase ||= "build-db";
32 $transactlog ||= "transactions.log";
33 $mailprog ||= "/usr/sbin/sendmail";
34 require "/org/wanna-build/etc/wanna-build.conf";
35 die "$conf::basedir is not a directory\n" if ! -d $conf::basedir;
36 die "dbbase is empty\n" if ! $dbbase;
37 die "transactlog is empty\n" if ! $transactlog;
38 die "mailprog binary $conf::mailprog does not exist or isn't executable\n"
39         if !-x $conf::mailprog;
40 package main;
41
42 use POSIX;
43 use FileHandle;
44 use File::Copy;
45 use DBI;
46 use Getopt::Long qw ( :config gnu_getopt );
47 use lib '/org/wanna-build/lib';
48 #use lib 'lib';
49 use WannaBuild;
50 use YAML::Tiny;
51 use Data::Dumper;
52 use Hash::Merge qw ( merge );
53 use String::Format;
54 use Date::Parse;
55 use List::Util qw[max];
56 use Dpkg::Version (); # import nothing
57 if ( defined $Dpkg::Version::VERSION ) {
58     *vercmp = \&Dpkg::Version::version_compare;
59 } else {
60     *vercmp = \&Dpkg::Version::vercmp;
61 }
62
63 use Dpkg::Deps; # TODO: same
64
65 our ($verbose, $mail_logs, $list_order, $list_state,
66     $curr_date, $op_mode, $user, $real_user, $distribution,
67     $fail_reason, $opt_override, $import_from, $export_to,
68     %prioval, %sectval,
69     $info_all_dists, $arch,
70     $short_date, $list_min_age, $list_max_age, $dbbase, @curr_time,
71     $build_priority, %new_vers, $binNMUver, %merge_srcvers, %merge_binsrc,
72     $printformat, $ownprintformat, $privmode, $extra_depends, $extra_conflicts,
73     %distributions, %distribution_aliases, $actions,
74     $sshwrapper,
75     );
76 our $Pas = '/org/buildd.debian.org/etc/packages-arch-specific/Packages-arch-specific';
77 our $simulate = 0;
78 our $simulate_edos = 0;
79 our $api = undef; # allow buildds to specify an different api
80 our $recorduser = undef;
81
82 # global vars
83 $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:/org/wanna-build/bin/";
84 $ENV{'LC_ALL'} = 'C';
85 $verbose = 0;
86 $mail_logs = "";
87 @curr_time = gmtime;
88 $curr_date = strftime("%Y %b %d %H:%M:%S",@curr_time);
89 $short_date = strftime("%m/%d/%y",@curr_time);
90 $| = 1;
91
92 # set mode of operation based on command line switch. Should be used
93 # by GetOptions below.
94 sub _set_mode_set { $op_mode = "set-$_[0]" }
95 sub _set_mode { $op_mode = "$_[0]" }
96
97 sub _option_deprecated { warn "Option $_[0] is deprecated" }
98
99 my @wannabuildoptions = (
100     # this is not supported by all operations (yet)!
101     'simulate'      => \$simulate,
102     'simulate-edos' => \$simulate_edos,
103     'simulate-all'  => sub { $simulate = 1; $simulate_edos = 1; },
104     'api=i'         => sub {
105         $api = $_[1];
106         die "$api too large" unless $api <= 1;
107     },
108     'verbose|v'       => \$verbose,
109     'override|o'      => \$opt_override,
110     'correct-compare' => \$WannaBuild::opt_correct_version_cmp,
111
112     # TODO: remove after buildds no longer pass to wanna-build
113     'no-propagation|N'      => \&_option_deprecated,
114     'no-down-propagation|D' => \&_option_deprecated,
115
116     # normal actions
117     'building|take'         => \&_set_mode_set,
118     'failed|f'              => \&_set_mode_set,
119     'uploaded|u'            => \&_set_mode_set,
120     'not-for-us|no-build|n' => \&_set_mode_set,
121     'built'                 => \&_set_mode_set,
122     'attempted'             => \&_set_mode_set,
123     'needs-build|give-back' => \&_set_mode_set,
124     'dep-wait'              => \&_set_mode_set,
125     'update'                => \&_set_mode_set,
126     'forget'                => \&_set_mode,
127     'forget-user'           => \&_set_mode,
128     'merge-v3'              => \&_set_mode,
129     'info|i'                => \&_set_mode,
130     'binary-nmu|binNMU=i'   => sub {
131         _set_mode_set(@_);
132         $binNMUver = $_[1];
133     },
134     'permanent-build-priority|perm-build-priority=i' => sub {
135         _set_mode_set(@_);
136         $build_priority = $_[1];
137     },
138     'build-priority=i' => sub {
139         _set_mode_set(@_);
140         $build_priority = $_[1];
141     },
142     'list|l=s' => sub {
143         _set_mode(@_);
144         $list_state = $_[1];
145         die "Unknown state to list: $list_state\n"
146           if not $list_state ~~ [
147               qw( needs-build building uploaded built
148                   build-attempted failed installed
149                   dep-wait not-for-us auto-not-for-us
150                   all failed-removed install-wait
151                   reupload-wait bd-uninstallable ) ];
152     },
153     'dist|d=s' => sub {
154         $distribution = $_[1];
155         given ( $_[1] ) {
156             when ( [qw< a all >] ) {
157                 $info_all_dists = 1;
158                 $distribution   = '';
159             }
160             when ('o') { $distribution = 'oldstable'; }
161             when ('s') { $distribution = 'stable'; }
162             when ('t') { $distribution = 'testing'; }
163             when ('u') { $distribution = 'unstable'; }
164
165             if ($distribution eq 'any-priv') {
166                 $privmode = 1;
167                 $distribution = 'any';
168             }
169             if ($distribution eq 'any-unpriv') {
170                 $privmode = 0;
171                 $distribution = 'any';
172             }
173         }
174     },
175     'order|O=s' => sub {
176         $list_order = $_[1];
177         die "Bad ordering character\n"
178           if $list_order !~ /^[PSpsncbCWT]+$/;
179     },
180     'message|m=s'  => \$fail_reason,
181     'database|b=s' => sub {
182         # If they didn't specify an arch, try to get it from database name which
183         # is in the form of $arch/build-db
184         # This is for backwards compatibity with older versions that didn't
185         # specify the arch yet.
186         warn "database is deprecated, please use 'arch' instead.\n";
187         $_[1] =~ m#^([^/]+)#;
188         $arch ||= $1;
189     },
190     'arch|A=s'     => \$arch,
191     'user|U=s'     => \$user,
192     'min-age|a=i'       => \$list_min_age,
193     'max-age=i'         => sub { $list_min_age = -1 * ($_[1]); },
194     'format=s'          => \$printformat,
195     'own-format=s'      => \$ownprintformat,
196     'Pas=s'             => \$Pas,
197     'extra-depends=s'   => \$extra_depends,
198     'extra-conflicts=s' => \$extra_conflicts,
199
200     # special actions
201     'export=s' => sub { _set_mode(@_); $export_to   = $_[1]; },
202     'import=s' => sub { _set_mode(@_); $import_from = $_[1]; },
203     'manual-edit'                => \&_set_mode,
204     'distribution-architectures' => \&_set_mode,
205     'distribution-aliases'       => \&_set_mode,
206
207     'ssh-wrapper'       => \$sshwrapper,
208     'recorduser'        => \$recorduser,
209     );
210
211 GetOptions(@wannabuildoptions) or usage();
212
213 my $dbh;
214
215 END {
216         if (defined $dbh)
217         {
218                 $dbh->disconnect or warn $dbh->errstr;
219         }
220 }
221
222 $distribution ||= "sid";
223
224 my $schema_suffix = '';
225 if ((isin( $op_mode, qw(list info distribution-architectures distribution-aliases)) && $distribution !~ /security/ && !$recorduser && !($privmode)) || $simulate) {
226         $dbh = DBI->connect("DBI:Pg:service=wanna-build") || 
227                 die "FATAL: Cannot open database: $DBI::errstr\n";
228         $schema_suffix = '_public';
229 }
230 else
231 {
232         $dbh = DBI->connect("DBI:Pg:service=wanna-build-privileged") || 
233                 die "FATAL: Cannot open database: $DBI::errstr\n";
234 }
235
236 # TODO: This shouldn't be needed, file a bug.
237 $dbh->{pg_server_prepare} = 0;
238
239 $dbh->begin_work or die $dbh->errstr;
240
241 my $q = 'SELECT distribution, public, auto_dep_wait, build_dep_resolver, suppress_successful_logs, archive FROM distributions';
242 my $rows = $dbh->selectall_hashref($q, 'distribution');
243 foreach my $name (keys %$rows) {
244         $distributions{$name} = {};
245         $distributions{$name}->{'noadw'} = 1 if !($rows->{$name}->{'auto_dep_wait'});
246         $distributions{$name}->{'hidden'} = 1 if !($rows->{$name}->{'public'});
247         $distributions{$name}->{'build_dep_resolver'} = $rows->{$name}->{'build_dep_resolver'} if $rows->{$name}->{'build_dep_resolver'};
248         $distributions{$name}->{'suppress_successful_logs'} = $rows->{$name}->{'suppress_successful_logs'} if $rows->{$name}->{'suppress_successful_logs'};
249         $distributions{$name}->{'archive'} = $rows->{$name}->{'archive'} if $rows->{$name}->{'archive'};
250 }
251
252 $q = 'SELECT alias, distribution FROM distribution_aliases';
253 $rows = $dbh->selectall_hashref($q, 'alias');
254 foreach my $name (keys %$rows) {
255         $distribution_aliases{$name} = $rows->{$name}->{'distribution'};
256 }
257 $distribution = $distribution_aliases{$distribution} if (isin($distribution, keys %distribution_aliases));
258
259 $op_mode ||= "set-building";
260 undef $distribution if $distribution eq 'any';
261 if ($distribution) {
262     my @dists = split(/[, ]+/, $distribution);
263     foreach my $dist (@dists) {
264         die "Bad distribution '$distribution'\n"
265             if !isin($dist, keys %distributions);
266     }
267 }
268 if (!isin ( $op_mode, qw(list) ) && ( !$distribution || $distribution =~ /[ ,]/)) {
269     die "multiple distributions are only allowed for list";
270 }
271
272 # TODO: Check that it's an known arch (for that dist), and give
273 # a proper error.
274
275 if ($verbose) {
276         my $version = '$Revision: db181a534e9d $ $Date: 2008/03/26 06:20:22 $ $Author: rmurray $';
277         $version =~ s/(^\$| \$ .*$)//g;
278         print "wanna-build $version for $distribution on $arch\n";
279 }
280
281 if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export
282                                   merge-packages manual-edit
283                                   merge-sources distribution-architectures
284                                   distribution-aliases))) {
285         warn "No packages given.\n";
286         usage();
287 }
288
289 $real_user = (getpwuid($<))[0];
290 die "Can't determine your user name\n"
291         if $op_mode ne "list" && !$user &&
292            !($user = $real_user);
293
294 if (!$fail_reason) {
295         if ($op_mode eq "set-failed" ) {
296                 print "Enter reason for failing (end with '.' alone on ".
297                       "its line):\n";
298                 my $line;
299                 while(!eof(STDIN)) {
300                         $line = <STDIN>;
301                         last if $line eq ".\n";
302                         $fail_reason .= $line;
303                 }
304                 chomp( $fail_reason );
305         } elsif ($op_mode eq "set-dep-wait") {
306                 print "Enter dependencies (one line):\n";
307                 my $line;
308                 while( !$line && !eof(STDIN) ) {
309                         chomp( $line = <STDIN> );
310                 }
311                 die "No dependencies given\n" if !$line;
312                 $fail_reason = $line;
313         } elsif ($op_mode eq "set-binary-nmu" and $binNMUver > 0) {
314                 print "Enter changelog entry (one line):\n";
315                 my $line;
316                 while( !$line && !eof(STDIN) ) {
317                         chomp( $line = <STDIN> );
318                 }
319                 die "No changelog entry given\n" if !$line;
320                 $fail_reason = $line;
321         }
322 }
323
324 my $yamlmap = ();
325 my $yamldir = "/org/wanna-build/etc/yaml";
326 my @files = ('wanna-build.yaml');
327 if ((getpwuid($>))[7]) { push (@files, ((getpwuid($>))[7])."/.wanna-build.yaml"); }
328 if ($user && $user =~ /(buildd.*)-/) { push (@files, "$1.yaml") };
329 if ($user) { push ( @files, "$user.yaml"); }
330 foreach my $file (@files) {
331         my $cfile = File::Spec->rel2abs( $file, $yamldir );
332         if ($verbose >= 2) { print "Trying to read $file ($cfile) ...\n"; }
333         next unless -f $cfile;
334         if ($verbose >= 2) { print "Read $file ($cfile) ...\n"; }
335         my $m = YAML::Tiny->read( $cfile )->[0];
336         $yamlmap = merge($m, $yamlmap);
337 }
338 if (not $yamlmap) {
339         die "FATAL: no configuration found\n";
340 }
341 $list_order = $yamlmap->{"list-order"}{$list_state} if !$list_order and $list_state;
342 $list_order ||= $yamlmap->{"list-order"}{'default'};
343 $api //= $yamlmap->{"api"};
344 $api //= 0;
345
346 if (isin($op_mode, qw<forget-user merge-v3 import>) && defined @conf::admin_users && !isin( $real_user, @conf::admin_users) && !$simulate ) {
347     die "This operation is restricted to admin users";
348 }
349 if (!isin($op_mode, qw<distribution-architectures distribution-aliases>)) {
350     die "need an architecture" unless $arch;
351 }
352
353         SWITCH: foreach ($op_mode) {
354                 /^set-(.+)/ && do {
355                         add_packages( $1, @ARGV );
356                         last SWITCH;
357                 };
358                 /^list/ && do {
359                         list_packages( $list_state );
360                         last SWITCH;
361                 };
362                 /^info/ && do {
363                         info_packages( @ARGV );
364                         last SWITCH;
365                 };
366                 /^forget-user/ && do {
367                         forget_users( @ARGV );
368                         last SWITCH;
369                 };
370                 /^forget/ && do {
371                         forget_packages( @ARGV );
372                         last SWITCH;
373                 };
374                 /^merge-v3/ && do {
375                         # call with installed-packages+ . installed-sources+ [ . available-for-build-packages* [ . consider-as-installed-source* ]  ]
376                         # in case available-for-build-packages is not specified, installed-packages are used
377                         lock_table() unless $simulate;
378                         my $replacemap = { '%ARCH%' => $arch, '%SUITE%' => $distribution };
379                         map { my $k = $_; grep { $k =~ s,$_,$replacemap->{$_}, } keys %{$replacemap}; $_ = $k; } @ARGV;
380                         my @ipkgs = &parse_argv( \@ARGV, '.');
381                         my @isrcs = &parse_argv( \@ARGV, '.');
382                         my @bpkgs = &parse_argv( \@ARGV, '.');
383                         my @psrcs = &parse_argv( \@ARGV, '.');
384                         use WB::QD;
385                         my $srcs = WB::QD::readsourcebins($arch, $Pas, \@isrcs, \@ipkgs);
386                         if (@psrcs) {
387                             my $psrcs = WB::QD::readsourcebins($arch, $Pas, \@psrcs, []);
388                             foreach my $k (keys %$$psrcs) {
389                                 next if $$srcs->{$k};
390                                 my $pkg = $$psrcs->{$k};
391                                 $pkg->{'status'} = 'related';
392                                 $$srcs->{$k} = $pkg;
393                             }
394                         }
395                         parse_all_v3($$srcs, {'arch' => $arch, 'suite' => $distribution, 'time' => $curr_date});
396                         @bpkgs = @ipkgs unless @bpkgs;
397                         call_edos_depcheck( {'arch' => $arch, 'pkgs' => \@bpkgs, 'srcs' => $$srcs, 'depwait' => 1 });
398                         last SWITCH;
399                 };
400                 /^import/ && do {
401                         $dbh->do("DELETE from ".table_name()." WHERE distribution = ?", undef, $distribution)
402                                 or die $dbh->errstr;
403                         forget_users();
404                         read_db( $import_from );
405                         last SWITCH;
406                 };
407                 /^export/ && do {
408                         export_db( $export_to );
409                         last SWITCH;
410                 };
411                 /^distribution-architectures/ && do {
412                         show_distribution_architectures();
413                         last SWITCH;
414                 };
415                 /^distribution-aliases/ && do {
416                         show_distribution_aliases();
417                         last SWITCH;
418                 };
419
420                 die "Unexpected operation mode $op_mode\n";
421         }
422         if ($recorduser) {
423                 my $userinfo = get_user_info($user);
424                 if (!defined $userinfo)
425                 {
426                         add_user_info($user);
427                 }
428                 else
429                 {
430                         update_user_info($user);
431                 }
432         }
433
434
435 $dbh->commit unless $simulate;
436 $dbh->disconnect;
437
438 if ($mail_logs && $conf::log_mail) {
439         send_mail( $conf::log_mail,
440                            "wanna-build $distribution state changes $curr_date",
441                            "State changes at $curr_date for distribution ".
442                            "$distribution:\n\n$mail_logs\n" );
443 }
444
445 exit 0;
446
447
448 BEGIN {
449     $actions = {
450         'set-building'  => { 'noversion' => 1, 'nopkgdef' => 1, },
451         'set-built'     => { 'builder' => 1, to => 'Built', action => 'built', 'from' => [qw<Building Build-Attempted>]},
452         'set-attempted' => { 'builder' => 1, to => 'Build-Attempted', action => 'attempted', 'from' => [qw<Building Build-Attempted>]},
453         'set-uploaded'  => { 'builder' => 1, to => 'Uploaded', action => 'uploaded', 'from' => [qw<Building Built Build-Attempted>], binversion => 1, },
454         'set-failed'    => { 'builder' => 1, to => 'Failed', action => 'failed', from => [qw<Building Built Build-Attempted Dep-Wait Failed>], warnfrom => [qw<Needs-Build Uploaded Dep-Wait BD-Uninstallable>], },
455         'set-dep-wait'  => { 'builder' => 1, warnfrom => [qw<Needs-Build Failed BD-Uninstallable>], },
456         'set-update'    => { 'noversion' => 1, },
457         'set-needs-build' => { builder => 1, to => 'BD-Uninstallable', action => 'give-back'},
458     };
459 }
460
461 sub add_packages {
462     my $newstate = shift;
463     my( $package, $name, $version, $ok, $reason );
464
465     foreach $package (@_) {
466         $package =~ s,^.*/,,; # strip path
467         $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
468         $package =~ s/_[a-zA-Z\d-]+\.changes$//; # strip extension
469         if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
470             ($name,$version) = ($1,$2);
471         } else {
472             warn "$package: can't extract package name and version (bad format)\n";
473             next;
474         }
475
476         my $pkg = get_source_info($name);
477         if (!($actions->{$op_mode}) || !($actions->{$op_mode}->{'nopkgdef'})) {
478             if (!defined($pkg)) {
479                 print "$name: not registered yet.\n";
480                 next;
481             }
482         }
483         if ($actions->{$op_mode} && $actions->{$op_mode}->{'builder'}) {
484             if (($pkg->{'builder'} && $user ne $pkg->{'builder'}) &&
485                 !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user) &&
486                 !$opt_override) {
487                 print "$pkg->{'package'}: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
488                 next;
489             }
490         }
491         if (!($actions->{$op_mode}) || !($actions->{$op_mode}->{'noversion'})) {
492             my $nmuver = binNMU_version($pkg->{version}, $pkg->{'binary_nmu_version'});
493             if ((!pkg_version_eq($pkg,$version) || $actions->{$op_mode}->{'binversion'}) && !version_eq( $nmuver, $version )) {
494                 print "$pkg->{package}: version mismatch ($nmuver";
495                 print " by $pkg->{'builder'}" if $pkg->{'builder'};
496                 print ")\n";
497                 next;
498             }
499         }
500
501         if ($actions->{$op_mode} && $actions->{$op_mode}->{'from'}) {
502             if (!isin($pkg->{'state'}, @{$actions->{$op_mode}->{'from'}}, @{$actions->{$op_mode}->{'warnfrom'}})) {
503                 print "$name: skiping: state is $pkg->{'state'}, not in ".join(", ",@{$actions->{$op_mode}->{'from'}}, @{$actions->{$op_mode}->{'warnfrom'}})."\n";
504                 next;
505             }
506         }
507         if ($actions->{$op_mode} && $actions->{$op_mode}->{'warnfrom'}) {
508             if (isin($pkg->{'state'}, @{$actions->{$op_mode}->{'warnfrom'}})) {
509                 print "$name: warning: state is $pkg->{'state'}, processing anyways.\n";
510             }
511         }
512
513         if ($op_mode eq "set-building") {
514             add_one_building( $name, $version, $pkg );
515         }
516         elsif ($op_mode eq "set-failed") {
517             print "$pkg->{'package'}: already registered as failed; will append new message\n" if $pkg->{'state'} eq "Failed";
518             $pkg->{'builder'} = $user;
519             $pkg->{'failed'} .= "\n" if $pkg->{'failed'};
520             $pkg->{'failed'} .= $fail_reason;
521         }
522         elsif ($op_mode eq "set-not-for-us") {
523             add_one_notforus( $pkg );
524         }
525         elsif ($op_mode eq "set-needs-build") {
526             my $state = $pkg->{'state'};
527
528             if ($state eq "BD-Uninstallable") {
529                 if ($opt_override) {
530                         print "$name: Forcing uninstallability mark to be removed. This is not permanent and might be reset with the next trigger run\n";
531
532                         change_state( \$pkg, 'Needs-Build' );
533                         delete $pkg->{'builder'};
534                         delete $pkg->{'depends'};
535                         log_ta( $pkg, "--give-back" );
536                         update_source_info($pkg);
537                         print "$name: given back\n" if $verbose;
538                         next;
539                 }
540                 else {
541                         print "$name: has uninstallable build-dependencies. Skipping\n  (use --override to clear dependency list and give back anyway)\n";
542                         next;
543                 }
544             }
545             elsif ($state eq "Dep-Wait") {
546                 if ($opt_override) {
547                         print "$name: Forcing source dependency list to be cleared\n";
548                 }
549                 else {
550                         print "$name: waiting for source dependencies. Skipping\n  (use --override to clear dependency list and give back anyway)\n";
551                         next;
552                 }
553             }
554             elsif (!isin( $state, qw(Building Built Build-Attempted))) {
555                 print "$name: not taken for building (state is $state).";
556                 if ($opt_override) {
557                         print "\n$name: Forcing give-back\n";
558                 }
559                 else {
560                         print " Skipping.\n";
561                         next;
562                 }
563             }
564             $pkg->{'builder'} = undef;
565             $pkg->{'depends'} = undef;
566         }
567         elsif ($op_mode eq "set-dep-wait") {
568             add_one_depwait( $pkg );
569         }
570         elsif ($op_mode eq "set-build-priority") {
571             set_one_buildpri( 'buildpri', $pkg );
572         }
573         elsif ($op_mode eq "set-permanent-build-priority") {
574             set_one_buildpri( 'permbuildpri', $pkg );
575         }
576         elsif ($op_mode eq "set-binary-nmu") {
577             set_one_binnmu( $name, $version, $pkg );
578         }
579         elsif ($op_mode eq "set-update") {
580             $pkg->{'version'} =~ s/\+b[0-9]+$//;
581
582             log_ta( $pkg, "--update" );
583             update_source_info($pkg);
584         }
585
586         if ($actions->{$op_mode} && $actions->{$op_mode}->{'action'} && $actions->{$op_mode}->{'to'}) {
587             change_state( \$pkg, $actions->{$op_mode}->{'to'} );
588             log_ta( $pkg, "--".$actions->{$op_mode}->{'action'} );
589             update_source_info($pkg);
590             print "$name: registered as ".$actions->{$op_mode}->{'action'}."\n" if $verbose;
591         }
592     }
593 }
594
595 sub add_one_building {
596         my $name = shift;
597         my $version = shift;
598         my( $ok, $reason );
599
600         $ok = 1;
601         my $pkg = shift;
602         if (defined($pkg)) {
603             my $pkgnack = {
604                 'Not-For-Us' => 'not suitable for this architecture',
605                 'Dep-Wait' => 'not all source dependencies available yet',
606                 'BD-Uninstallable' => 'source dependencies are not installable',
607             };
608                 if ($pkgnack->{$pkg->{'state'}}) {
609                         $ok = 0;
610                         $reason = $pkgnack->{$pkg->{'state'}};
611                 }
612                 elsif ($pkg->{'state'} eq "Uploaded" &&
613                            (version_lesseq($version, $pkg->{'version'}))) {
614                         $ok = 0;
615                         $reason = "already uploaded by $pkg->{'builder'}";
616                         $reason .= " (in newer version $pkg->{'version'})"
617                                 if !version_eq($pkg, $version);
618                 }
619                 elsif ($pkg->{'state'} eq "Installed" &&
620                            version_less($version,$pkg->{'version'})) {
621                         if ($opt_override) {
622                                 print "$name: Warning: newer version $pkg->{'version'} ".
623                                           "already installed, but overridden.\n";
624                         }
625                         else {
626                                 $ok = 0;
627                                 $reason = "newer version $pkg->{'version'} already in ".
628                                                   "archive; doesn't need rebuilding";
629                                 print "$name: Note: If the following is due to an epoch ",
630                                           " change, use --override\n";
631                         }
632                 }
633                 elsif ($pkg->{'state'} eq "Installed" &&
634                            pkg_version_eq($pkg,$version)) {
635                         $ok = 0;
636                         $reason = "is up-to-date in the archive; doesn't need rebuilding";
637                 }
638                 elsif ($pkg->{'state'} eq "Needs-Build" &&
639                            version_less($version,$pkg->{'version'})) {
640                         if ($opt_override) {
641                                 print "$name: Warning: newer version $pkg->{'version'} ".
642                                           "needs building, but overridden.";
643                         }
644                         else {
645                                 $ok = 0;
646                                 $reason = "newer version $pkg->{'version'} needs building, ".
647                                                   "not $version";
648                         }
649                 }
650                 elsif (isin($pkg->{'state'},qw(Building Built Build-Attempted))) {
651                         if (version_less($pkg->{'version'},$version)) {
652                                 print "$name: Warning: Older version $pkg->{'version'} ",
653                                       "is being built by $pkg->{'builder'}\n";
654                                 if ($pkg->{'builder'} ne $user) {
655                                         send_mail( $pkg->{'builder'},
656                                                            "package takeover in newer version",
657                                                            "You are building package '$name' in ".
658                                                            "version $version\n".
659                                                            "(as far as I'm informed).\n".
660                                                            "$user now has taken the newer ".
661                                                            "version $version for building.".
662                                                            "You can abort the build if you like.\n" );
663                                 }
664                         }
665                         else {
666                                 if ($opt_override) {
667                                         print "User $pkg->{'builder'} had already ",
668                                               "taken the following package,\n",
669                                                   "but overriding this as you request:\n";
670                                         send_mail( $pkg->{'builder'}, "package takeover",
671                                                            "The package '$name' (version $version) that ".
672                                                            "was taken by you\n".
673                                                            "has been taken over by $user\n" );
674                                 }
675                                 elsif ($pkg->{'builder'} eq $user) {
676                                         print "$name: Note: already taken by you.\n";
677                                         print "$name: ok\n" if $verbose;
678                                         return;
679                                 }
680                                 else {
681                                         $ok = 0;
682                                         $reason = "already taken by $pkg->{'builder'}";
683                                         $reason .= " (in newer version $pkg->{'version'})"
684                                                 if !version_eq($pkg->{'version'}, $version);
685                                 }
686                         }
687                 }
688                 elsif ($pkg->{'state'} =~ /^Failed/ &&
689                            pkg_version_eq($pkg, $version)) {
690                         if ($opt_override) {
691                                 print "The following package previously failed ",
692                                           "(by $pkg->{'builder'})\n",
693                                           "but overriding this as you request:\n";
694                                 send_mail( $pkg->{'builder'}, "failed package takeover",
695                                                    "The package '$name' (version $version) that ".
696                                                    "is taken by you\n".
697                                                    "and has failed previously has been taken over ".
698                                                    "by $user\n" )
699                                         if $pkg->{'builder'} ne $user;
700                         }
701                         else {
702                                 $ok = 0;
703                                 $reason = "build of $version failed previously:\n    ";
704                                 $reason .= join( "\n    ", split( "\n", $pkg->{'failed'} ));
705                                 $reason .= "\nalso the package doesn't need builing"
706                                         if $pkg->{'state'} eq 'Failed-Removed';
707                         }
708                 }
709         }
710         if ($ok) {
711             if ($api < 1) {
712                 my $ok = 'ok';
713                 if ($pkg->{'binary_nmu_version'}) {
714                         print "$name: Warning: needs binary NMU $pkg->{'binary_nmu_version'}\n" .
715                               "$pkg->{'binary_nmu_changelog'}\n";
716                         $ok = 'aok';
717                 } else {
718                         print "$name: Warning: Previous version failed!\n"
719                                 if $pkg->{'previous_state'} =~ /^Failed/ ||
720                                    $pkg->{'state'} =~ /^Failed/;
721                 }
722                 print "$name: $ok\n" if $verbose;
723             } else {
724                 print  "- $name:\n";
725                 print  "    - status: ok\n";
726                 printf "    - pkg-ver: %s_%s\n", $name, $version;
727                 print  "    - binNMU: $pkg->{'binary_nmu_version'}\n" if $pkg->{'binary_nmu_version'};
728                 print  "    - extra-changelog: $pkg->{'binary_nmu_changelog'}\n" if $pkg->{'binary_nmu_changelog'} && $pkg->{'binary_nmu_version'};
729                 print  "    - extra-depends: $pkg->{'extra_depends'}\n" if $pkg->{'extra_depends'};
730                 print  "    - extra-conflicts: $pkg->{'extra_conflicts'}\n" if $pkg->{'extra_conflicts'};
731                 print  "    - archive: $distributions{$distribution}->{'archive'}\n" if $distributions{$distribution}->{'archive'};
732                 print  "    - build_dep_resolver: $distributions{$distribution}->{'build_dep_resolver'}\n" if $distributions{$distribution}->{'build_dep_resolver'};
733                 print  "    - arch_all: $pkg->{'build_arch_all'}\n" if $pkg->{'build_arch_all'};
734                 print  "    - suppress_successful_logs: $distributions{$distribution}->{'suppress_successful_logs'}\n" if $distributions{$distribution}->{'suppress_successful_logs'};
735             }
736                 change_state( \$pkg, 'Building' );
737                 $pkg->{'package'} = $name;
738                 $pkg->{'version'} = $version;
739                 $pkg->{'builder'} = $user;
740                 log_ta( $pkg, "--take" );
741                 update_source_info($pkg);
742         }
743         else {
744             if ($api < 1) {
745                 print "$name: NOT OK!\n  $reason\n";
746             } else {
747                 print "- $name:\n    - status: not ok\n    - reason: \"$reason\"\n";
748             }
749         }
750 }
751
752
753 sub add_one_notforus {
754         my $pkg = shift;
755         my $state = $pkg->{'state'};
756         my $name = $pkg->{'package'};
757
758         if ($pkg->{'state'} eq 'Not-For-Us') {
759                 # reset Not-For-Us state in case it's called twice; this is
760                 # the only way to get a package out of this state...
761                 # There is no really good state in which such packages should
762                 # be put :-( So use Failed for now.
763                 change_state( \$pkg, 'Failed' );
764                 $pkg->{'package'} = $name;
765                 $pkg->{'failed'} = "Was Not-For-Us previously";
766                 delete $pkg->{'builder'};
767                 delete $pkg->{'depends'};
768                 log_ta( $pkg, "--no-build(rev)" );
769                 print "$name: now not unsuitable anymore\n";
770
771                 send_mail( $conf::notforus_maint,
772                                    "$name moved out of Not-For-Us state",
773                                    "The package '$name' has been moved out of the Not-For-Us ".
774                                    "state by $user.\n".
775                                    "It should probably also be removed from ".
776                                    "Packages-arch-specific or\n".
777                                    "the action was wrong.\n" )
778                         if $conf::notforus_maint;
779         }
780         else {
781                 change_state( \$pkg, 'Not-For-Us' );
782                 $pkg->{'package'} = $name;
783                 delete $pkg->{'builder'};
784                 delete $pkg->{'depends'};
785                 delete $pkg->{'binary_nmu_version'};
786                 delete $pkg->{'binary_nmu_changelog'};
787                 log_ta( $pkg, "--no-build" );
788                 print "$name: registered as unsuitable\n" if $verbose;
789
790                 send_mail( $conf::notforus_maint,
791                                    "$name set to Not-For-Us",
792                                    "The package '$name' has been set to state Not-For-Us ".
793                                    "by $user.\n".
794                                    "It should probably also be added to ".
795                                    "Packages-arch-specific or\n".
796                                    "the Not-For-Us state is wrong.\n" )
797                         if $conf::notforus_maint;
798         }
799         update_source_info($pkg);
800 }
801
802 sub set_one_binnmu {
803         my $name = shift;
804         my $version = shift;
805         my $pkg = shift;
806         my $state = $pkg->{'state'};
807
808         if (defined $pkg->{'binary_nmu_version'}) {
809                 if ($binNMUver == 0) {
810                         change_state( \$pkg, 'Installed' );
811                         delete $pkg->{'builder'};
812                         delete $pkg->{'depends'};
813                         delete $pkg->{'binary_nmu_version'};
814                         delete $pkg->{'binary_nmu_changelog'};
815                         delete $pkg->{'buildpri'};
816                 } elsif ($binNMUver <= $pkg->{'binary_nmu_version'}) {
817                         print "$name: already building binNMU $pkg->{'binary_nmu_version'}\n";
818                         return;
819                 } else {
820                         $pkg->{'binary_nmu_version'} = $binNMUver;
821                         $pkg->{'binary_nmu_changelog'} = $fail_reason;
822                         $pkg->{'notes'} = 'out-of-date';
823                         delete $pkg->{'buildpri'};
824                         change_state( \$pkg, 'BD-Uninstallable' );
825                 }
826                 log_ta( $pkg, "--binNMU" );
827                 update_source_info($pkg);
828                 return;
829         } elsif ($binNMUver == 0) {
830                 print "${name}_$version: no scheduled binNMU to cancel.\n";
831                 return;
832         }
833
834         if ($state ne 'Installed') {
835                 print "${name}_$version: not installed; can't register for binNMU.\n";
836                 return;
837         }
838
839         my $fullver = binNMU_version($version,$binNMUver);
840         if ( version_lesseq( $fullver, $pkg->{'installed_version'} ) )
841         {
842                 print "$name: binNMU $fullver is not newer than current version $pkg->{'installed_version'}\n";
843                 return;
844         }
845
846         change_state( \$pkg, 'BD-Uninstallable' );
847         delete $pkg->{'builder'};
848         delete $pkg->{'depends'};
849         $pkg->{'binary_nmu_version'} = $binNMUver;
850         $pkg->{'binary_nmu_changelog'} = $fail_reason;
851         $pkg->{'notes'} = 'out-of-date';
852         delete $pkg->{'buildpri'};
853         log_ta( $pkg, "--binNMU" );
854         update_source_info($pkg);
855         print "${name}: registered for binNMU $fullver\n" if $verbose;
856 }
857
858 sub set_one_buildpri {
859         my $key = shift;
860         my $pkg = shift;
861         my $name = $pkg->{'package'};
862
863         if ( $build_priority ) {
864                 $pkg->{$key} = $build_priority;
865         } else {
866                 delete $pkg->{$key};
867         }
868         update_source_info($pkg);
869         print "$name: set to build priority $build_priority\n" if $verbose;
870 }
871
872 sub add_one_depwait {
873         my $pkg = shift;
874         my $state = $pkg->{'state'};
875         my $name = $pkg->{'package'};
876
877         if ($state eq "Dep-Wait") {
878                 print "$name: merging with previously registered dependencies\n";
879         }
880         
881         if (isin( $state, qw<Installed Not-For-Us>)) {
882             print "add_one_depwait: $name: skiping in state $state\n";
883             return;
884         }
885         
886         if ($fail_reason =~ /^\s*$/ ||
887                    !parse_deplist( $fail_reason, 1 )) {
888                 print "$name: Bad dependency list\n";
889                 return;
890         }
891         change_state( \$pkg, 'Dep-Wait' );
892         $pkg->{'builder'} = $user;
893         my $deplist = parse_deplist( $pkg->{'depends'} );
894         my $new_deplist = parse_deplist( $fail_reason );
895         # add new dependencies, maybe overwriting old entries
896         foreach (keys %$new_deplist) {
897                 $deplist->{$_} = $new_deplist->{$_};
898         }
899         $pkg->{'depends'} = build_deplist($deplist);
900         log_ta( $pkg, "--dep-wait" ) unless $simulate;
901         update_source_info($pkg) unless $simulate;
902         print "$name: registered as waiting for dependencies\n" if $verbose || $simulate;
903 }
904
905
906 # for sorting priorities and sections
907 BEGIN {
908         %prioval = ( required             => -5,
909                                  important            => -4,
910                                  standard             => -3,
911                                  optional             => -2,
912                                  extra                => -1,
913                                  unknown              => -1 );
914         %sectval = ( 
915                                  libs                   => -200,
916                                  'debian-installer'     => -199,
917                                  base                   => -198,
918                                  devel                  => -197,
919                                  kernel                 => -196,
920                                  shells                 => -195,
921                                  perl                   => -194,
922                                  python                 => -193,
923                                  graphics               => -192,
924                                  admin                  => -191,
925                                  utils                  => -190,
926                                  x11                    => -189,
927                                  editors                => -188,
928                                  net                    => -187,
929                                  httpd                  => -186,
930                                  mail                   => -185,
931                                  news                   => -184,
932                                  tex                    => -183,
933                                  text                   => -182,
934                                  web                    => -181,
935                                  vcs                    => -180,
936                                  doc                    => -179,
937                                  localizations          => -178,
938                                  interpreters           => -177,
939                                  ruby                   => -176,
940                                  java                   => -175,
941                                  ocaml                  => -174,
942                                  lisp                   => -173,
943                                  haskell                => -172,
944                                  'cli-mono'             => -171,
945                                  gnome                  => -170,
946                                  kde                    => -169,
947                                  xfce                   => -168,
948                                  gnustep                => -167,
949                                  database               => -166,
950                                  video                  => -165,
951                                  debug                  => -164,
952                                  games                  => -163,
953                                  misc                   => -162,
954                                  fonts                  => -161,
955                                  otherosfs              => -160,
956                                  oldlibs                => -159,
957                                  libdevel               => -158,
958                                  sound                  => -157,
959                                  math                   => -156,
960                                  'gnu-r'                => -155,
961                                  science                => -154,
962                                  comm                   => -153,
963                                  electronics            => -152,
964                                  hamradio               => -151,
965                                  embedded               => -150,
966                                  php                    => -149,
967                                  zope                   => -148,
968         );
969         foreach my $i (keys %sectval) {
970                 $sectval{"contrib/$i"} = $sectval{$i}+40;
971                 $sectval{"non-free/$i"} = $sectval{$i}+80;
972         }
973         $sectval{'unknown'}     = -165;
974
975 }
976
977 sub sort_list_func {
978     my $map_funcs = {
979         'C' => ['<->', sub { return $_[0]->{'calprio'}; }],
980         'W' => ['<->', sub { return $_[0]->{'state_days'}; }],
981         'P' => ['<->', sub { return ($_[0]->{'buildpri'}//0) + ($_[0]->{'permbuildpri'}//0); }],
982         'p' => ['<=>', sub { return $prioval{$_[0]->{'priority'}//""}//0; }],
983         's' => ['<=>', sub { return $sectval{$_[0]->{'section'}//""}//0; }],
984         'n' => ['cmp', sub { return $_[0]->{'package'}; }],
985         'b' => ['cmp', sub { return $_[0]->{'builder'}; }],
986         'c' => ['<=>', sub { return ($_[0]->{'notes'}//"" =~ /^(out-of-date|partial)/) ? 0: ($_[0]->{'notes'}//"" =~ /^uncompiled/) ? 2 : 1; }],
987         'S' => ['<->', sub { return isin($_[0]->{'priority'}, qw(required important standard)); }],
988         'T' => ['<->', sub { return $_[0]->{'state_time'} % 86400;} ], # Fractions of a day
989     };
990
991         foreach my $letter (split( //, $list_order )) {
992             my $r;
993             $r = (&{$map_funcs->{$letter}[1]}($b)//0 ) <=> (&{$map_funcs->{$letter}[1]}($a)//0 ) if $map_funcs->{$letter}[0] eq '<->';
994             $r = (&{$map_funcs->{$letter}[1]}($a)//0 ) <=> (&{$map_funcs->{$letter}[1]}($b)//0 ) if $map_funcs->{$letter}[0] eq '<=>';
995             $r = (&{$map_funcs->{$letter}[1]}($a)//"") cmp (&{$map_funcs->{$letter}[1]}($b)//"") if $map_funcs->{$letter}[0] eq 'cmp';
996             return $r if $r != 0;
997         }
998         return 0;
999 }
1000
1001 sub calculate_prio {
1002         my $priomap = $yamlmap->{priority};
1003         my $pkg = shift;
1004         my @s=split("/", $pkg->{'section'}//"");
1005         $pkg->{'component'} = $s[0] if $s[1];
1006         $pkg->{'component'} ||= 'main';
1007         $pkg->{'calprio'} = 0;
1008         foreach my $k (keys %{$priomap->{keys}}) {
1009                 $pkg->{'calprio'} += $priomap->{keys}->{$k}{$pkg->{$k}} if $pkg->{$k} and $priomap->{keys}->{$k}{$pkg->{$k}};
1010         }
1011
1012         my $days = $pkg->{'state_days'};
1013         $days = $priomap->{'waitingdays'}->{'min'} if $priomap->{'waitingdays'}->{'min'} and $days < $priomap->{'waitingdays'}->{'min'};
1014         $days = $priomap->{'waitingdays'}->{'max'} if $priomap->{'waitingdays'}->{'max'} and $days > $priomap->{'waitingdays'}->{'max'};
1015         my $scale = $priomap->{'waitingdays'}->{'scale'} || 1;
1016         $pkg->{'calprio'} += $days * $scale;
1017
1018         my $btime = max($pkg->{'anytime'}//0, $pkg->{'successtime'}//0);
1019         my $bhours = $btime ? int($btime/3600) : ($priomap->{'buildhours'}->{'default'} || 2);
1020         $bhours = $priomap->{'buildhours'}->{'min'} if $priomap->{'buildhours'}->{'min'} and $bhours < $priomap->{'buildhours'}->{'min'};
1021         $bhours = $priomap->{'buildhours'}->{'max'} if $priomap->{'buildhours'}->{'max'} and $bhours > $priomap->{'buildhours'}->{'max'};
1022         $scale = $priomap->{'buildhours'}->{'scale'} || 1;
1023         $pkg->{'calprio'} -= $bhours * $scale;
1024
1025         $pkg->{'calprio'} += $pkg->{'permbuildpri'} if  $pkg->{'permbuildpri'};
1026         $pkg->{'calprio'} += $pkg->{'buildpri'} if  $pkg->{'buildpri'};
1027
1028         return $pkg;
1029 }
1030
1031
1032 sub seconds2time {
1033     my $t = shift;
1034     return "" unless $t;
1035     my $sec = $t % 60;
1036     my $min = int($t/60) % 60;
1037     my $hours = int($t / 3600);
1038     return sprintf("%d:%02d:%02d", $hours, $min, $sec) if $hours;
1039     return sprintf("%d:%02d", $min, $sec);
1040 }
1041
1042
1043 sub use_fmt {
1044     my $r;
1045
1046     if (ref($_[0]) eq 'CODE') {
1047         $r = &{$_[0]};
1048     } else {
1049         $r = $_[0];
1050     }
1051
1052     shift;
1053     my $t = shift;
1054
1055     $r ||= "";
1056     return $r unless $t;
1057
1058     my $pkg = shift;
1059     my $var = shift;
1060     if (substr($t,0,1) eq '!') {
1061         $t = substr($t,1);
1062         return "" if $r;
1063     } else {
1064         return "" unless $r;
1065     }
1066     if ($t =~ /%/) {
1067         return print_format($t, $pkg, $var);
1068     }
1069     return $t;
1070 }
1071 sub make_fmt { my $c = shift; my $pkg = shift; my $var = shift; return sub { use_fmt($c, $_[0], $pkg, $var); } };
1072
1073 sub print_format {
1074     my $printfmt = shift;
1075     my $pkg = shift;
1076     my $var = shift;
1077
1078 =pod
1079
1080 Within an format string, the following values are allowed (need to be preceded by %).
1081 This can be combined to e.g.
1082 wanna-build --format='wanna-build -A %a --give-back %p_%v' -A mipsel --list=failed
1083
1084 a Architecture
1085 c section (e.g. libs or utils)
1086 D in case of BD-Uninstallable the reason for the uninstallability
1087 d distribution
1088 E in case of Dep-Wait the packages being waited on, in case of Needs-Build the number in the queue
1089 F in case of Failed the fail reason
1090 n newline
1091 o time of last successful build (seconds)
1092 O time of last successful build (formated)
1093 P previous state
1094 p Package name
1095 q time of last build (seconds)
1096 Q time of last build (formated)
1097 r max time of last (successful) build (seconds)
1098 R max time of last (successful) build (formated)
1099 S Package state
1100 s Time in this state in full seconds since epoch
1101 t time of state change
1102 T time since state change
1103 u Builder (e.g. buildd_mipsel-rem)
1104 v Package version
1105 V full Package version (i.e. with +b.., = %v%{+b}B%B
1106 X the string normally between [], e.g. optional:out-of-date:calprio{61}:days{25}
1107
1108 %{Text}?  print Text in case ? is not empty; ? is never printed
1109 %{!Text}? print Text in case ? is empty; ? is never printed
1110 Text could contain further %. To start with !, use %!
1111
1112 =cut
1113
1114     return stringf($printfmt, (
1115         'p' => make_fmt( $pkg->{'package'}, $pkg, $var),
1116         'a' => make_fmt( $arch, $pkg, $var),
1117         's' => make_fmt( sub { return floor(str2time($pkg->{'state_change'})); }, $pkg, $var),
1118         'v' => make_fmt( $pkg->{'version'}, $pkg, $var),
1119         'V' => make_fmt( sub { $pkg->{'binary_nmu_version'} ? $pkg->{'version'}."+b".$pkg->{'binary_nmu_version'} : $pkg->{'version'} }, $pkg, $var),
1120         'S' => make_fmt( $pkg->{'state'}, $pkg, $var),
1121         'u' => make_fmt( $pkg->{'builder'}, $pkg, $var),
1122         'X' => make_fmt( sub {
1123             no warnings;
1124             my $c = "$pkg->{'priority'}:$pkg->{'notes'}";
1125             $c .= ":PREV-FAILED" if $pkg->{'previous_state'} && $pkg->{'previous_state'} =~ /^Failed/;
1126             $c .= ":bp{" . (($pkg->{'buildpri'}//0)+($pkg->{'permbuildpri'}//0)) . "}" if (($pkg->{'buildpri'}//0)+($pkg->{'permbuildpri'}//0));
1127             $c .= ":binNMU{" . $pkg->{'binary_nmu_version'} . "}" if defined $pkg->{'binary_nmu_version'};
1128             $c .= ":calprio{". $pkg->{'calprio'}."}";
1129             $c .= ":days{". $pkg->{'state_days'}."}";
1130             return $c;
1131             }, $pkg, $var),
1132         'c' => make_fmt( $pkg->{'section'}, $pkg, $var),
1133         'P' => make_fmt( $pkg->{'previous_state'} || "unknwon", $pkg, $var),
1134         'E' => make_fmt( sub { return $pkg->{'depends'} if $pkg->{'state'} eq "Dep-Wait";
1135             return $var->{scnt}{'Needs-Build'} + 1 if $pkg->{'state'} eq 'Needs-Build';
1136             return ""; }, $pkg, $var),
1137         'F' => make_fmt( sub { return "" unless $pkg->{'failed'};
1138             my $failed = $pkg->{'failed'};
1139             $failed =~ s/\\/\\\\/g;
1140             return $pkg->{'package'}."#".$arch."-failure\n ".
1141             join("\\0a",split("\n",$failed))."\\0a\n"; }, $pkg, $var),
1142         'D' => make_fmt( sub { return "" unless $pkg->{'bd_problem'};
1143             return $pkg->{'package'}."#".$arch."-bd-problem\n".
1144             join("\\0a",split("\n",$pkg->{'bd_problem'}))."\\0a\n"; }, $pkg, $var),
1145         'B' => make_fmt( sub { return $pkg->{'binary_nmu_version'} if defined $pkg->{'binary_nmu_version'}; }, $pkg, $var),
1146         'd' => make_fmt( $pkg->{'distribution'}, $pkg, $var),
1147         't' => make_fmt( $pkg->{'state_change'}, $pkg, $var),
1148         'T' => make_fmt( sub { return seconds2time(time() - floor(str2time($pkg->{'state_change'}))); }, $pkg, $var),
1149         'o' => make_fmt( $pkg->{'successtime'}, $pkg, $var),
1150         'O' => make_fmt( sub { return seconds2time ( $pkg->{'successtime'}); }, $pkg, $var),
1151         'q' => make_fmt( $pkg->{'anytime'}, $pkg, $var),
1152         'Q' => make_fmt( sub { return seconds2time ( $pkg->{'anytime'}); }, $pkg, $var),
1153         'r' => make_fmt( sub { my $c = max($pkg->{'successtime'}//0, $pkg->{'anytime'}//0); return $c if $c; return; }, $pkg, $var),
1154         'R' => make_fmt( sub { return seconds2time ( max($pkg->{'successtime'}//0, $pkg->{'anytime'}//0)); }, $pkg, $var),
1155     ));
1156 }
1157
1158 sub list_packages {
1159         my $state = shift;
1160         my @list;
1161         my $cnt = 0;
1162         my %scnt;
1163         my $ctime = time;
1164
1165         my $db = get_all_source_info(state => $state, user => $user, list_min_age => $list_min_age, multisuite => 1);
1166         foreach my $key (keys %$db) {
1167                 next if $key =~ /^_/;
1168                 push @list, calculate_prio($db->{$key});
1169         }
1170
1171         # filter components
1172         @list = grep { my $i = $_->{'component'}; grep { $i eq $_ } split /[, ]+/, $yamlmap->{"restrict"}{'component'} } @list;
1173         # extra depends / conflicts only from api 1 on
1174         @list = grep { !$_->{'extra_depends'} and !$_->{'extra_conflicts'} } @list if $api < 1 ;
1175
1176         # first adjust ownprintformat, then set printformat accordingly
1177         $printformat ||= $yamlmap->{"format"}{$ownprintformat} if $ownprintformat;
1178         $printformat ||= $yamlmap->{"format"}{"default"}{$state};
1179         $printformat ||= $yamlmap->{"format"}{"default"}{"default"};
1180         undef $printformat if ($ownprintformat && $ownprintformat eq 'none');
1181
1182         foreach my $pkg (sort sort_list_func @list) {
1183                 no warnings;
1184                 if ($printformat) {
1185                     print print_format($printformat, $pkg, {'cnt' => $cnt, 'scnt' => \%scnt})."\n";
1186                     ++$cnt;
1187                     $scnt{$pkg->{'state'}}++;
1188                     next;
1189                 }
1190                 print print_format("%c/%p_%v", $pkg, {});
1191                 print print_format(": %S", $pkg, {})
1192                         if $state eq "all";
1193                 print print_format("%{ by }u%u", $pkg, {})
1194                         if $pkg->{'state'} ne "Needs-Build";
1195                 print print_format(" [%X]\n", $pkg, {});
1196                 print "  Reasons for failing:\n",
1197                           join("\n    ",split("\n",$pkg->{'failed'})), "\n"
1198                         if $pkg->{'state'} =~ /^Failed/;
1199                 print "  Dependencies: $pkg->{'depends'}\n"
1200                         if $pkg->{'state'} eq "Dep-Wait";
1201                 print "  Reasons for BD-Uninstallable:\n    ",
1202                           join("\n    ",split("\n",$pkg->{'bd_problem'})), "\n"
1203                         if $pkg->{'state'} eq "BD-Uninstallable";
1204                 print "  Previous state was $pkg->{'previous_state'}\n"
1205                         if $verbose && $pkg->{'previous_state'};
1206                 print "  No previous state recorded\n"
1207                         if $verbose && !$pkg->{'previous_state'};
1208                 print "  State changed at $pkg->{'state_change'}\n"
1209                         if $verbose && $pkg->{'state_change'};
1210                 print "  Previous state $pkg->{'previous_state'} left $pkg->{'state_time'} ago\n"
1211                         if $verbose && $pkg->{'previous_state'};
1212                 print "  Previous failing reasons:\n    ",
1213                       join("\n    ",split("\n",$pkg->{'old_failed'})), "\n"
1214                         if $verbose && $pkg->{'old_failed'};
1215                 ++$cnt;
1216                 $scnt{$pkg->{'state'}}++ if $state eq "all";
1217         }
1218         if ($state eq "all" && !$printformat) {
1219                 foreach (sort keys %scnt) {
1220                         print "Total $scnt{$_} package(s) in state $_.\n";
1221                 }
1222         }
1223         print "Total $cnt package(s)\n" unless $printformat;
1224         
1225 }
1226
1227 sub info_packages {
1228         my( $name, $pkg, $key, $dist );
1229         my @firstkeys = qw(package version builder state section priority
1230                                            installed_version previous_state state_change);
1231         my @dists = $info_all_dists ? keys %distributions : ($distribution);
1232         my %beautykeys = ( 'package' => 'Package', 'version' => 'Version', 'builder' => 'Builder',
1233                 'state' => 'State', 'section' => 'Section', 'priority' => 'Priority',
1234                 'installed_version' => 'Installed-Version', 'previous_state' => 'Previous-State',
1235                 'state_change' => 'State-Change',
1236                 'bd_problem' => 'BD-Problem', 
1237                 'binary_nmu_changelog' => 'Binary-NMU-Changelog', 'binary_nmu_version' => 'Binary-NMU-Version',
1238                 'buildpri' => 'BuildPri', 'depends' => 'Depends', 'failed' => 'Failed',
1239                 'notes' => 'Notes',
1240                 'distribution' => 'Distribution', 'old_failed' => 'Old-Failed',
1241                 'permbuildpri' => 'PermBuildPri', 'rel' => 'Rel',
1242                 'calprio' => 'CalculatedPri', 'state_days' => 'State-Days', 'state_time' => 'State-Time',
1243                 'successtime' => 'Success-build-time',
1244                 'anytime' => 'Build-time',
1245                 'extra_depends' => 'Extra-Dependencies',
1246                 'extra_conflicts' => 'Extra-Conflicts',
1247                 'build_arch_all' => 'Build-Arch-All',
1248                          );
1249         
1250         foreach $name (@_) {
1251                 $name =~ s/_.*$//; # strip version
1252                 foreach $dist (@dists) {
1253                         my $pname = "$name" . ($info_all_dists ? "($dist)" : "");
1254                         
1255                         $pkg = get_readonly_source_info($name);
1256                         if (!defined( $pkg )) {
1257                                 print "$pname: not registered\n";
1258                                 next;
1259                         }
1260                         $pkg = calculate_prio($pkg);
1261
1262                         print "$pname:\n";
1263                         foreach $key (@firstkeys) {
1264                                 next if !defined $pkg->{$key};
1265                                 my $val = $pkg->{$key};
1266                                 chomp( $val );
1267                                 $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
1268                                 $val =~ s/\n/\n    /g;
1269                                 my $print_key = $key;
1270                                 $print_key = $beautykeys{$print_key} if $beautykeys{$print_key};
1271                                 printf "  %-20s: %s\n", $print_key, $val;
1272                         }
1273                         foreach $key (sort keys %$pkg) {
1274                                 next if isin( $key, @firstkeys );
1275                                 my $val = $pkg->{$key};
1276                                 next if !defined($val);
1277                                 chomp( $val );
1278                                 $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
1279                                 $val =~ s/\n/\n    /g;
1280                                 my $print_key = $key;
1281                                 $print_key = $beautykeys{$print_key} if $beautykeys{$print_key};
1282                                 printf "  %-20s: %s\n", $print_key, $val;
1283                         }
1284                 }
1285         }
1286 }
1287
1288 sub forget_packages {
1289         no warnings;
1290         my( $name, $pkg, $key, $data );
1291         
1292         foreach $name (@_) {
1293                 $name =~ s/_.*$//; # strip version
1294                 $pkg = get_source_info($name);
1295                 if (!defined( $pkg )) {
1296                         print "$name: not registered\n";
1297                         next;
1298                 }
1299
1300                 $data = "";
1301                 foreach $key (sort keys %$pkg) {
1302                         my $val = $pkg->{$key};
1303                         chomp( $val );
1304                         $val =~ s/\n/\n /g;
1305                         $data .= sprintf "  %-20s: %s\n", $key, $val;
1306                 }
1307                 send_mail( $conf::db_maint,
1308                                    "$name deleted from DB " . table_name() . " " . $distribution,
1309                                    "The package '$name' has been deleted from the database ".
1310                                    "by $user.\n\n".
1311                                    "Data registered about the deleted package:\n".
1312                                    "$data\n" ) if $conf::db_maint;
1313                 change_state( \$pkg, 'deleted' );
1314                 log_ta( $pkg, "--forget" );
1315                 del_source_info($name);
1316                 print "$name: deleted from database\n" if $verbose;
1317         }
1318 }
1319
1320 sub forget_users {
1321         $dbh->do("DELETE from " . user_table_name() . 
1322                 " WHERE distribution = ?", undef, $distribution) or die $dbh->errstr;
1323 }
1324
1325 sub read_db {
1326         my $file = shift;
1327
1328         print "Reading ASCII database from $file..." if $verbose >= 1;
1329         open( my $fh, '<', $file ) or
1330                 die "Can't open database $file: $!\n";
1331
1332         local($/) = ""; # read in paragraph mode
1333         while( <$fh> ) {
1334                 my( %thispkg, $name );
1335                 s/[\s\n]+$//;
1336                 s/\n[ \t]+/\376\377/g;  # fix continuation lines
1337                 s/\376\377\s*\376\377/\376\377/og;
1338   
1339                 while( /^(\S+):[ \t]*(.*)[ \t]*$/mg ) {
1340                         my ($key, $val) = ($1, $2);
1341                         $key =~ s/-/_/g;
1342                         $key =~ tr/A-Z/a-z/;
1343                         $val =~ s/\376\377/\n/g;
1344                         $thispkg{$key} = $val;
1345                 }
1346                 check_entry( \%thispkg );
1347                 # add to db
1348                 if (exists($thispkg{'package'})) {
1349                         update_source_info(\%thispkg);
1350                 }
1351                 elsif(exists($thispkg{'user'})) {
1352                         # user in import, username in database.
1353                         $dbh->do('INSERT INTO ' . user_table_name() .
1354                                         ' (username, distribution, last_seen)' .
1355                                         ' values (?, ?, ?)',
1356                                 undef, $thispkg{'user'}, $distribution,
1357                                 $thispkg{'last_seen'})
1358                                 or die $dbh->errstr;
1359                  }
1360         }
1361         close( $fh );
1362         print "done\n" if $verbose >= 1;
1363 }
1364
1365 sub check_entry {
1366         my $pkg = shift;
1367         my $field;
1368
1369         return if $op_mode eq "manual-edit"; # no checks then
1370         
1371         # check for required fields
1372         if (exists $pkg->{'user'}) {
1373                 return;
1374         }
1375         if (!exists $pkg->{'package'}) {
1376                 print STDERR "Bad entry: ",
1377                           join( "\n", map { "$_: $pkg->{$_}" } keys %$pkg ), "\n";
1378                 die "Database entry lacks package or username field\n";
1379         }
1380         # if no State: field, generate one (for old db compat)
1381         if (!exists($pkg->{'state'})) {
1382                 $pkg->{'state'} =
1383                         exists $pkg->{'failed'} ? 'Failed' : 'Building';
1384         }
1385         if (!exists $pkg->{'version'} and $pkg->{'state'} ne 'Not-For-Us') {
1386                 die "Database entry for $pkg->{'package'} lacks Version: field\n";
1387         }
1388         # check state field
1389         die "Bad state $pkg->{'state'} of package $pkg->{Package}\n"
1390                 if !isin( $pkg->{'state'},
1391                                   qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait Dep-Wait-Removed
1392                                          Failed Failed-Removed Not-For-Us BD-Uninstallable Auto-Not-For-Us
1393                                          ) );
1394 }
1395
1396 sub export_db {
1397         my $file = shift;
1398         my($name,$pkg,$key);
1399
1400         print "Writing ASCII database to $file..." if $verbose >= 1;
1401         open( my $fh, '>', $file ) or
1402                 die "Can't open export $file: $!\n";
1403
1404         my $db = get_all_source_info();
1405         foreach $name (keys %$db) {
1406                 next if $name =~ /^_/;
1407                 my $pkg = $db->{$name};
1408                 foreach $key (keys %{$pkg}) {
1409                         my $val = $pkg->{$key};
1410                         next if !defined($val);
1411                         $val =~ s/\n*$//;
1412                         $val =~ s/^/ /mg;
1413                         $val =~ s/^ +$/ ./mg;
1414                         print $fh "$key: $val\n";
1415                 }
1416                 print $fh "\n";
1417        }
1418        close( $fh );
1419        print "done\n" if $verbose >= 1;
1420 }
1421
1422 sub change_state {
1423         my $pkgr = shift;
1424         my $pkg = $$pkgr;
1425         my $newstate = shift;
1426         my $state = \$pkg->{'state'};
1427         
1428         $newstate = 'Needs-Build' if $newstate eq 'BD-Uninstallable' && $distributions{$distribution}{noadw};
1429         return if defined($$state) and $$state eq $newstate;
1430         $pkg->{'previous_state'} = $$state if defined($$state);
1431         $pkg->{'state_change'} = $curr_date;
1432         $pkg->{'do_state_change'} = 1;
1433
1434         if (defined($$state) and $$state eq 'Failed') {
1435                 $pkg->{'old_failed'} =
1436                         "-"x20 . " $pkg->{'version'} " . "-"x20 . "\n" .
1437                         ($pkg->{'failed'} // ""). "\n" .
1438                         ($pkg->{'old_failed'} // "");
1439                 delete $pkg->{'failed'};
1440         }
1441         delete $pkg->{'bd_problem'} if ($$state//"") eq 'BD-Uninstallable';
1442         $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet" if $newstate eq 'BD-Uninstallable';
1443         $$state = $newstate;
1444 }
1445
1446 sub log_ta {
1447         my $pkg = shift;
1448         my $action = shift;
1449         my $dist = $distribution;
1450         my $str;
1451         my $prevstate;
1452
1453         $prevstate = $pkg->{'previous_state'};
1454         $str = "$action($dist): $pkg->{'package'}_$pkg->{'version'} ".
1455                    "changed from $prevstate to $pkg->{'state'} ".
1456                    "by $real_user as $user";
1457         
1458         if ($simulate) {
1459             printf "update transactions: %s %s %s %s %s %s %s %s\n",
1460                 $pkg->{'package'}, $distribution,
1461                 $pkg->{'version'}, $action, $prevstate, $pkg->{'state'},
1462                 $real_user, $user;
1463             return;
1464         }
1465         $dbh->do('INSERT INTO ' . transactions_table_name() .
1466                         ' (package, distribution, version, action, ' .
1467                         ' prevstate, state, real_user, set_user, time) ' .
1468                         ' values (?, ?, ?, ?, ?, ?, ?, ?, ?)',
1469                 undef, $pkg->{'package'}, $distribution,
1470                 $pkg->{'version'}, $action, $prevstate, $pkg->{'state'},
1471                 $real_user, $user, 'now()') or die $dbh->errstr;
1472
1473         if (!($prevstate eq 'Failed' && $pkg->{'state'} eq 'Failed')) {
1474                 $str .= " (with --override)"
1475                         if $opt_override;
1476                 $mail_logs .= "$str\n";
1477         }
1478 }
1479
1480
1481 sub send_mail {
1482         my $to = shift;
1483         my $subject = shift;
1484         my $text = shift;
1485
1486         my $from = $conf::db_maint;
1487         my $domain = $conf::buildd_domain;
1488
1489         $from .= "\@$domain" if $from !~ /\@/;
1490
1491         $to .= '@' . $domain if $to !~ /\@/;
1492         $text =~ s/^\.$/../mg;
1493         local $SIG{'PIPE'} = 'IGNORE';
1494         open( my $pipe,  '|-', "$conf::mailprog -oem $to" )
1495                 or die "Can't open pipe to $conf::mailprog: $!\n";
1496         chomp $text;
1497         print $pipe "From: $from\n";
1498         print $pipe "Subject: $subject\n\n";
1499         print $pipe "$text\n";
1500         close( $pipe );
1501 }
1502
1503 # for parsing input to dep-wait
1504 sub parse_deplist {
1505     my $deps = shift;
1506     my $verify = shift;
1507     my %result;
1508     
1509     return $verify ? 0 : \%result unless $deps;
1510     foreach (split( /\s*,\s*/, $deps )) {
1511         if ($verify) {
1512             # verification requires > starting prompts, no | crap
1513             if (!/^(\S+)\s*(\(\s*(>(?:[>=])?)\s*(\S+)\s*\))?\s*$/) {
1514                 return 0;
1515             }
1516             next;
1517         }
1518         my @alts = split( /\s*\|\s*/, $_ );
1519         # Anything with an | is ignored, as it can be configured on a
1520         # per-buildd basis what will be installed
1521         next if $#alts != 0;
1522         $_ = shift @alts;
1523
1524         if (!/^(\S+)\s*(\(\s*(>=|=|==|>|>>|<<|<=)\s*(\S+)\s*\))?\s*$/) {
1525             warn( "parse_deplist: bad dependency $_\n" );
1526             next;
1527         }
1528         my($dep, $rel, $relv) = ($1, $3, $4);
1529         $rel = ">>" if defined($rel) and $rel eq ">";
1530         $result{$dep}->{'package'} = $dep;
1531         if ($rel && $relv) {
1532             $result{$dep}->{'rel'} = $rel;
1533             $result{$dep}->{'version'} = $relv;
1534         }
1535     }
1536     return 1 if $verify;
1537     return \%result;
1538 }
1539
1540 sub build_deplist {
1541         my $list = shift;
1542         my($key, $result);
1543         
1544         foreach $key (keys %$list) {
1545                 $result .= ", " if $result;
1546                 $result .= $key;
1547                 $result .= " ($list->{$key}->{'rel'} $list->{$key}->{'version'})"
1548                         if $list->{$key}->{'rel'} && $list->{$key}->{'version'};
1549         }
1550         return $result;
1551 }
1552
1553
1554 sub filterarch {
1555     return "" unless $_[0];
1556     return Dpkg::Deps::parse($_[0], ("reduce_arch" => 1, "host_arch" => $_[1]))->dump();
1557 }
1558
1559 sub wb_edos_builddebcheck {
1560 # Copyright (C) 2008 Ralf Treinen <treinen@debian.org>
1561 # This program is free software: you can redistribute it and/or modify it under
1562 # the terms of the GNU General Public License as published by the Free Software
1563 # Foundation, version 2 of the License.
1564 # integrated into wanna-builds code by Andreas Barth 2010
1565
1566     my $args = shift;
1567     my $sourceprefix="source---";
1568     my $architecture=$args->{'arch'};
1569     my $edosoptions = "-failures -explain -quiet";
1570     my $packagefiles = $args->{'pkgs'};
1571     my $sourcesfile = $args->{'src'};
1572
1573     my $packagearch="";
1574     foreach my $packagefile (@$packagefiles) {
1575         open(my $fh,'<', $packagefile);
1576         while (<$fh>) {
1577             next unless /^Architecture/;
1578             next if /^Architecture:\s*all/;
1579             /Architecture:\s*([^\s]*)/;
1580             if ($packagearch eq "") {
1581                 $packagearch = $1;
1582             } elsif ( $packagearch ne $1) {
1583                 return "Package file contains different architectures: $packagearch, $1";
1584             }
1585         }
1586         close $fh;
1587     }
1588
1589     if ( $architecture eq "" ) {
1590         if ( $packagearch eq "" ) {
1591         return "No architecture option given, " .
1592             "and no non-all architecture found in the Packages file";
1593         } else {
1594             $architecture = $packagearch;
1595         }
1596     } else {
1597         if ( $packagearch ne "" & $architecture ne $packagearch) {
1598             return "Architecture option is $architecture ".
1599             "but the package file contains architecture $packagearch";
1600         }   
1601     }
1602
1603     print "calling: edos-debcheck $edosoptions < $sourcesfile ".join('', map {" '-base FILE' ".$_ } @$packagefiles)."\n";
1604     open(my $result_cmd, '-|',
1605         "edos-debcheck $edosoptions < $sourcesfile ".join('', map {" '-base FILE' ".$_ } @$packagefiles));
1606
1607     my $explanation="";
1608     my $result={};
1609     my $binpkg="";
1610
1611     while (<$result_cmd>) {
1612 # source---pulseaudio (= 0.9.15-4.1~bpo50+1): FAILED
1613 #   source---pulseaudio (= 0.9.15-4.1~bpo50+1) depends on missing:
1614 #   - libltdl-dev (>= 2.2.6a-2)
1615 # source---libcanberra (= 0.22-1~bpo50+1): FAILED
1616 #   source---libcanberra (= 0.22-1~bpo50+1) depends on missing:
1617 #   - libltdl-dev
1618 #   - libltdl7-dev (>= 2.2.6)
1619
1620         if (/^\s+/) {
1621             s/^(\s*)$sourceprefix(.*)depends on/$1$2build-depends on/o;
1622             s/^(\s*)$sourceprefix(.*) and (.*) conflict/$1$2 build-conflicts with $3/o;
1623             $explanation .= $_;
1624         } else {
1625             if (/^$sourceprefix(.*) \(.*\): FAILED/o) {
1626                 $result->{$binpkg} = $explanation if $binpkg;
1627                 $explanation = "";
1628                 $binpkg = $1;
1629             } elsif (/^(depwait---.*) \(.*\): FAILED/o) {
1630                 $result->{$binpkg} = $explanation if $binpkg;
1631                 $explanation = "";
1632                 $binpkg = $1;
1633             } else { # else something broken is happening
1634                 #print "ignoring $_\n";
1635                 1;
1636             }
1637         }
1638     }
1639
1640     close $result_cmd;
1641     $result->{$binpkg} = $explanation if $binpkg;
1642     return $result;
1643
1644 }
1645
1646
1647 sub call_edos_depcheck {
1648     return if $simulate_edos;
1649     my $args = shift;
1650     my $srcs = $args->{'srcs'};
1651     my $key;
1652     
1653     return if defined ($distributions{$distribution}{noadw}) && not defined $args->{'depwait'};
1654
1655     # We need to check all of needs-build, as any new upload could make
1656     # something in needs-build have uninstallable deps
1657     # We also check everything in bd-uninstallable, as any new upload could
1658     # make that work again
1659     my (%interesting_packages, %interesting_packages_depwait);
1660     my $db = get_all_source_info();
1661     foreach $key (keys %$db) {
1662         my $pkg = $db->{$key};
1663         if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/) and not defined ($distributions{$distribution}{noadw})) {
1664                 $interesting_packages{$key} = undef;
1665         }
1666         if (defined $pkg and isin($pkg->{'state'}, qw/Dep-Wait/) and defined $args->{'depwait'}) {
1667                 $interesting_packages_depwait{$key} = undef;
1668                 # we always check for BD-Uninstallability in depwait - could be that depwait is satisfied but package is uninstallable
1669                 $interesting_packages{$key} = undef unless defined ($distributions{$distribution}{noadw});
1670         }
1671     }
1672     
1673     #print "I would look at these sources with edos-depcheck:\n";
1674     #print join " ", keys %interesting_packages,"\n";
1675     return unless %interesting_packages || %interesting_packages_depwait;
1676
1677     my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-XXXXX";
1678     use File::Temp qw/ tempfile /;
1679     my ($SOURCES, $tmpfile) = tempfile( $tmpfile_pattern, UNLINK => 1 );
1680     for my $key (keys %interesting_packages) {
1681         my $pkg = $db->{$key};
1682         # we print the source files as binary ones (with "source---"-prefixed),
1683         # so we can try if these "binary" packages are installable.
1684         # If such a "binary" package is installable, the corresponding source package is buildable.
1685         print $SOURCES "Package: source---$key\n";
1686         print $SOURCES "Version: $pkg->{'version'}\n";
1687         my $t = &filterarch($srcs->{$key}{'dep'} || $srcs->{$key}{'depends'}, $args->{'arch'});
1688         my $tt = &filterarch($pkg->{'extra_depends'}, $args->{'arch'});
1689         $t = $t ? ($tt ? "$t, $tt" : $t) : $tt;
1690         print $SOURCES "Depends: $t\n" if $t;
1691         my $u = &filterarch($srcs->{$key}{'conf'} || $srcs->{$key}{'conflicts'}, $args->{'arch'});
1692         my $uu = &filterarch($pkg->{'extra_conflicts'}, $args->{'arch'});
1693         $u = $u ? ($uu ? "$u, $uu" : $u) : $uu;
1694         print $SOURCES "Conflicts: $u\n" if $u;
1695         print $SOURCES "Architecture: all\n";
1696         print $SOURCES "\n";
1697     }
1698     for my $key (keys %interesting_packages_depwait) {
1699         my $pkg = $db->{$key};
1700         # we print the source files as binary ones (with "depwait---"-prefixed),
1701         # so we can try if these "binary" packages are installable.
1702         # If such a "binary" package is installable, the corresponding source package goes out of depwait
1703         print $SOURCES "Package: depwait---$key\n";
1704         print $SOURCES "Version: $pkg->{'version'}\n";
1705         print $SOURCES "Depends: $pkg->{'depends'}\n";
1706         print $SOURCES "Architecture: all\n";
1707         print $SOURCES "\n";
1708     }
1709     close $SOURCES;
1710
1711     my $edosresults = wb_edos_builddebcheck({'arch' => $args->{'arch'}, 'pkgs' => $args->{'pkgs'}, 'src' => $tmpfile});
1712     if (ref($edosresults) eq 'HASH') {
1713         foreach my $key (grep { $_ !~ /^depwait---/ } keys %$edosresults) {
1714                 if (exists $interesting_packages{$key}) {
1715                     $interesting_packages{$key} = $edosresults->{$key};
1716                 } else {
1717                     #print "TODO: edos reported a package we do not care about now\n" if $verbose;
1718                 }
1719         }
1720         foreach my $key (grep { $_ =~ /^depwait---/ } keys %$edosresults) {
1721                 $key =~ /^depwait---(.*)/ and $key = $1;
1722                 if (exists $interesting_packages_depwait{$key}) {
1723                     $interesting_packages_depwait{$key} = $edosresults->{"depwait---".$key};
1724                 } else {
1725                     #print "TODO: edos reported a package we do not care about now\n" if $verbose;
1726                 }
1727         }
1728     } else {
1729         # if $edosresults isn't an hash, then something went wrong and the string is the error message
1730         print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" .
1731              "all packages have installable build-dependencies."
1732     }
1733     
1734     unlink( $tmpfile );
1735
1736     for my $key (keys %interesting_packages) {
1737         next if defined $interesting_packages_depwait{$key};
1738         my $pkg = $db->{$key};
1739         my $change = 
1740             (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
1741             (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
1742         my $problemchange = ($interesting_packages{$key}//"") ne ($pkg->{'bd_problem'}//"");
1743         if ($change) {
1744             if (defined $interesting_packages{$key}) {
1745                     change_state( \$pkg, 'BD-Uninstallable' );
1746                     $pkg->{'bd_problem'} = $interesting_packages{$key};
1747             } else {
1748                     change_state( \$pkg, 'Needs-Build' );
1749             }
1750         }
1751         if ($problemchange) {
1752             if (defined $interesting_packages{$key}) {
1753                     $pkg->{'bd_problem'} = $interesting_packages{$key};
1754             }   
1755         }
1756         if ($change) {
1757             log_ta( $pkg, "--merge-all (edos)" ) unless $simulate;
1758             print "edos-builddebchange changed state of ${key}_$pkg->{'version'} ($args->{'arch'}) to $pkg->{'state'}\n" if $verbose || $simulate;
1759         }
1760         if ($change || $problemchange) {
1761             update_source_info($pkg) unless $simulate;
1762         }
1763     }
1764
1765     for my $key (keys %interesting_packages_depwait) {
1766         if ($interesting_packages_depwait{$key}) {
1767             print "dep-wait for $key ($args->{'arch'}) not fullfiled yet\n" if $verbose || $simulate;
1768             next;
1769         }
1770         my $pkg = $db->{$key};
1771             if (defined $interesting_packages{$key}) {
1772                     change_state( \$pkg, 'BD-Uninstallable' );
1773                     $pkg->{'bd_problem'} = $interesting_packages{$key};
1774             } else {
1775                     change_state( \$pkg, 'Needs-Build' );
1776             }
1777         log_ta( $pkg, "edos_depcheck: depwait" ) unless $simulate;
1778         update_source_info($pkg) unless $simulate;
1779         print "edos-builddebchange changed state of ${key}_$pkg->{'version'} ($args->{'arch'}) from dep-wait to $pkg->{'state'}\n" if $verbose || $simulate;
1780     }
1781 }
1782
1783 sub usage {
1784         my $prgname;
1785         ($prgname = $0) =~ s,^.*/,,;
1786         print <<"EOF";
1787 Usage: $prgname <options...> <package_version...>
1788 Options:
1789     -v, --verbose: Verbose execution.
1790     -A arch: Architecture this operation is for.
1791     --take: Take package for building [default operation]
1792     -f, --failed: Record in database that a build failed due to
1793         deficiencies in the package (that aren't fixable without a new
1794         source version).
1795     -u, --uploaded: Record in the database that the packages build
1796         correctly and were uploaded.
1797     -n, --no-build: Record in the database that the packages aren't
1798         desired for this architecture and shouldn't appear in listings even
1799         if they're out of date.
1800     --dep-wait: Record in the database that the packages are waiting
1801         for some source dependencies to become available
1802     --binNMU num: Schedule a re-build of the package with unchanged source, but
1803          a new version number (source-version + "+b<num>")
1804     --give-back: Mark a package as ready to build that is in state Building,
1805          Built or Build-Attempted. To give back a package in state Failed, use
1806          --override. This command will actually put the package in state
1807          BD-Uninstallable, until the installability of its Build-Dependencies
1808          were verified. This happens at each call of --merge-all, usually
1809          every 15 minutes.
1810     -i SRC_PKG, --info SRC_PKG: Show information for source package
1811     -l STATE, --list=STATE: List all packages in state STATE; can be
1812         combined with -U to restrict to a specific user; STATE can
1813         also be 'all'
1814     -m MESSAGE, --message=MESSAGE: Give reason why package failed or
1815         source dependency list
1816         (used with -f, --dep-wait, and --binNMU)
1817     -o, --override: Override another user's lock on a package, i.e.
1818         take it over; a notice mail will be sent to the other user
1819     -U USER, --user=USER: select user name for which listings should
1820         apply, if not given all users are listed.
1821         if -l is missing, set user name to be entered in db; usually
1822         automatically choosen
1823     --import FILE: Import database from a ASCII file FILE
1824     --export FILE: Export database to a ASCII file FILE
1825
1826 The remaining arguments (depending on operation) usually start with
1827 "name_version", the trailer is ignored. This allows to pass the names
1828 of .dsc files, for which file name completion can be used.
1829 --merge-packages and --merge-quinn take Package/quin--diff file names
1830 on the command line or read stdin. --list needs nothing more on the
1831 command line. --info takes source package names (without version).
1832 EOF
1833         exit 1;
1834 }
1835
1836 sub pkg_version_eq {
1837         my $pkg = shift;
1838         my $version = shift;
1839
1840         return 1
1841                if (defined $pkg->{'binary_nmu_version'}) and 
1842                version_compare(binNMU_version($pkg->{'version'},
1843                         $pkg->{'binary_nmu_version'}),'=', $version);
1844         return version_compare( $pkg->{'version'}, "=", $version );
1845 }
1846
1847 sub table_name {
1848         return '"' . $arch . $schema_suffix . '".packages';
1849 }
1850
1851 sub user_table_name {
1852         return '"' . $arch . $schema_suffix . '".users';
1853 }
1854
1855 sub transactions_table_name {
1856         return '"' . $arch . $schema_suffix . '".transactions';
1857 }
1858
1859 sub pkg_history_table_name {
1860         return '"' . $arch . $schema_suffix . '".pkg_history';
1861 }
1862
1863 sub get_readonly_source_info {
1864         my $name = shift;
1865         # SELECT FLOOR(EXTRACT('epoch' FROM age(localtimestamp, '2010-01-22  23:45')) / 86400) -- change to that?
1866         my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time"
1867             . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution AND result = 'successful') AS successtime"
1868             . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution ) AS anytime"
1869             . ", extra_depends, extra_conflicts, build_arch_all"
1870             . " FROM " .  table_name()
1871             . ' WHERE package = ? AND distribution = ?';
1872         my $pkg = $dbh->selectrow_hashref( $q,
1873                 undef, $name, $distribution);
1874         return $pkg;
1875 }
1876
1877 sub get_source_info {
1878         my $name = shift;
1879         return get_readonly_source_info($name) if $simulate;
1880         my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time FROM ' . 
1881                 table_name() . ' WHERE package = ? AND distribution = ?' .
1882                 ' FOR UPDATE',
1883                 undef, $name, $distribution);
1884         return $pkg;
1885 }
1886
1887 sub get_all_source_info {
1888         my %options = @_;
1889
1890         my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time"
1891 #            . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution AND result = 'successful') AS successtime"
1892 #            . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution ) AS anytime"
1893             . ", successtime.build_time as successtime, anytime.build_time as anytime, extra_depends, extra_conflicts"
1894             . " FROM " .  table_name()
1895                 . " left join ( "
1896                   . "select distinct on (package, distribution) build_time, package, distribution from ".pkg_history_table_name()." where result = 'successful' order by package, distribution, timestamp "
1897                   . " ) as successtime using (package, distribution) "
1898                 . " left join ( "
1899                   . "select distinct on (package, distribution) build_time, package, distribution from ".pkg_history_table_name()." order by package, distribution, timestamp desc"
1900                   . " ) as anytime using (package, distribution) "
1901             . " WHERE TRUE ";
1902         my @args = ();
1903         if ($distribution) {
1904             my @dists = split(/[, ]+/, $distribution);
1905             $q .= ' AND ( distribution = ? '.(' OR distribution = ? ' x $#dists).' )';
1906             foreach my $d ( @dists ) {
1907                 push @args, ($d);
1908             }
1909         }
1910         if ($options{state} && uc($options{state}) ne "ALL") {
1911                 $q .= ' AND upper(state) = ? ';
1912                 push @args, uc($options{state});
1913         }
1914
1915         if ($options{user} && uc($options{state}) ne "NEEDS-BUILD") { # if it's NEEDS-BUILD, we don't look at users
1916                 #this basically means "this user, or no user at all":
1917                 $q .= " AND (builder = ? OR upper(state) = 'NEEDS-BUILD')";
1918                 push @args, $options{user};
1919         }
1920
1921         if ($options{list_min_age} && $options{list_min_age} > 0) {
1922                 $q .= ' AND age(state_change) > ? ';
1923                 push @args, $options{list_min_age} . " days";
1924         }
1925
1926         if ($options{list_min_age} && $options{list_min_age} < 0) {
1927                 $q .= ' AND age(state_change) < ? ';
1928                 push @args, -$options{list_min_age} . " days";
1929         }
1930
1931         my $db;
1932         if (($options{multisuite}) && (!$distribution || $distribution =~ / /)) {
1933             # return packages in multiple suites - only for those functions marked as clean for that api change
1934             $db = $dbh->selectall_hashref($q, [qw<package distribution>], undef, @args);
1935             my $dbk = {};
1936             foreach my $p ( keys %$db ) {
1937                 foreach my $d (keys %{$db->{$p}}) {
1938                     $dbk->{"$p/$d"} = $db->{$p}->{$d};
1939                 }
1940             }
1941             $db = $dbk;
1942         } else {
1943             $db = $dbh->selectall_hashref($q, [qw<package>], undef, @args);
1944         }
1945         return $db;
1946 }
1947
1948 sub show_distribution_architectures {
1949         my $q = 'SELECT distribution, spacecat_all(architecture) AS architectures '.
1950                 'FROM distribution_architectures '.
1951                 'GROUP BY distribution';
1952         my $rows = $dbh->selectall_hashref($q, 'distribution');
1953         foreach my $name (keys %$rows) {
1954                 print $name.': '.$rows->{$name}->{'architectures'}."\n";
1955         }
1956 }
1957
1958 sub show_distribution_aliases {
1959         foreach my $alias (keys %distribution_aliases) {
1960                 print $alias.': '.$distribution_aliases{$alias}."\n";
1961         }
1962 }
1963
1964 sub update_source_info {
1965         my $pkg = shift;
1966         $pkg->{'extra_depends'} = $extra_depends if defined $extra_depends;
1967         undef $pkg->{'extra_depends'} unless $pkg->{'extra_depends'};
1968         $pkg->{'extra_conflicts'} = $extra_conflicts if defined $extra_conflicts;
1969         undef $pkg->{'extra_conflicts'} unless $pkg->{'extra_conflicts'};
1970         print Dumper $pkg if $verbose and $simulate;
1971         return if $simulate;
1972
1973         my $pkg2 = get_source_info($pkg->{'package'});
1974         if (! defined $pkg2)
1975         {
1976                 add_source_info($pkg);
1977         }
1978
1979         $dbh->do('UPDATE ' . table_name() . ' SET ' .
1980                         'version = ?, ' .
1981                         'state = ?, ' .
1982                         'section = ?, ' .
1983                         'priority = ?, ' .
1984                         'installed_version = ?, ' .
1985                         'previous_state = ?, ' .
1986                         (($pkg->{'do_state_change'}) ? "state_change = now()," : "").
1987                         'notes = ?, ' .
1988                         'builder = ?, ' .
1989                         'failed = ?, ' .
1990                         'old_failed = ?, ' .
1991                         'binary_nmu_version = ?, ' .
1992                         'binary_nmu_changelog = ?, ' .
1993                         'permbuildpri = ?, ' .
1994                         'buildpri = ?, ' .
1995                         'depends = ?, ' .
1996                         'rel = ?, ' .
1997                         'extra_depends = ?, ' .
1998                         'extra_conflicts = ?, ' .
1999                         'bd_problem = ? ' .
2000                         'WHERE package = ? AND distribution = ?',
2001                 undef,
2002                 $pkg->{'version'},
2003                 $pkg->{'state'},
2004                 $pkg->{'section'},
2005                 $pkg->{'priority'},
2006                 $pkg->{'installed_version'},
2007                 $pkg->{'previous_state'},
2008                 $pkg->{'notes'},
2009                 $pkg->{'builder'},
2010                 $pkg->{'failed'},
2011                 $pkg->{'old_failed'},
2012                 $pkg->{'binary_nmu_version'},
2013                 $pkg->{'binary_nmu_changelog'},
2014                 $pkg->{'permbuildpri'},
2015                 $pkg->{'buildpri'},
2016                 $pkg->{'depends'},
2017                 $pkg->{'rel'},
2018                 $pkg->{'extra_depends'},
2019                 $pkg->{'extra_conflicts'},
2020                 $pkg->{'bd_problem'},
2021                 $pkg->{'package'},
2022                 $distribution) or die $dbh->errstr;
2023 }
2024
2025 sub add_source_info {
2026         return if $simulate;
2027         my $pkg = shift;
2028         $dbh->do('INSERT INTO ' . table_name() .
2029                         ' (package, distribution) values (?, ?)',
2030                 undef, $pkg->{'package'}, $distribution) or die $dbh->errstr;
2031 }
2032
2033 sub del_source_info {
2034         return if $simulate;
2035         my $name = shift;
2036         $dbh->do('DELETE FROM ' . table_name() .
2037                         ' WHERE package = ? AND distribution = ?',
2038                 undef, $name, $distribution) or die $dbh->errstr;
2039 }
2040
2041 sub get_user_info {
2042         my $name = shift;
2043         my $user = $dbh->selectrow_hashref('SELECT * FROM ' . 
2044                 user_table_name() . ' WHERE username = ? AND distribution = ?',
2045                 undef, $name, $distribution);
2046         return $user;
2047 }
2048
2049 sub update_user_info {
2050         return if $simulate;
2051         my $user = shift;
2052         $dbh->do('UPDATE ' . user_table_name() .
2053                         ' SET last_seen = now() WHERE username = ?' .
2054                         ' AND distribution = ?',
2055                 undef, $user, $distribution)
2056                 or die $dbh->errstr;
2057 }
2058
2059
2060 sub add_user_info {
2061         return if $simulate;
2062         my $user = shift;
2063         $dbh->do('INSERT INTO ' . user_table_name() .
2064                         ' (username, distribution, last_seen)' .
2065                         ' values (?, ?, now())',
2066                 undef, $user, $distribution)
2067                 or die $dbh->errstr;
2068 }
2069
2070 sub lock_table {
2071         return if $simulate;
2072         $dbh->do('LOCK TABLE ' . table_name() .
2073                 ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr;
2074 }
2075
2076 sub parse_argv {
2077 # parts the array $_[0] and $_[1] and returns the sub-array (modifies the original one)
2078     my @ret = ();
2079     my $args = shift;
2080     my $separator = shift;
2081     while($args->[0] && $args->[0] ne $separator) { 
2082         push @ret, shift @$args;
2083     }
2084     shift @$args if @$args;
2085     return @ret;
2086 }
2087
2088 sub parse_all_v3 {
2089     my $srcs = shift;
2090     my $vars = shift;
2091     my $db = get_all_source_info();
2092     my $binary = $srcs->{'_binary'};
2093
2094     SRCS:
2095     foreach my $name (keys %$srcs) {
2096         next if $name eq '_binary';
2097
2098         # state = installed, out-of-date, uncompiled, packages-arch-specific, overwritten-by-arch-all, arch-not-in-arch-list, arch-all-only
2099         my $pkgs = $srcs->{$name};
2100         next if isin($pkgs->{'status'}, qw <arch-all-only>);
2101         my $pkg = $db->{$name};
2102
2103         unless ($pkg) {
2104             next SRCS if $pkgs->{'status'} eq 'packages-arch-specific';
2105             my $logstr = sprintf("merge-v3 %s %s_%s (%s, %s):", $vars->{'time'}, $name, $pkgs->{'version'}, $vars->{'arch'}, $vars->{'suite'});
2106
2107             # does at least one binary exist in the database and is more recent - if so, we're probably just outdated, ignore the source package
2108             for my $bin (@{$pkgs->{'binary'}}) {
2109                 if ($binary->{$bin} and vercmp($pkgs->{'version'}, $binary->{$bin}->{'version'}) < 0) {
2110                     print "$logstr skipped because binaries (assumed to be) overwritten\n" if $verbose || $simulate;
2111                     next SRCS;
2112                 }
2113             }
2114             $pkg->{'package'}  = $name;
2115         }
2116         $pkg->{'version'} ||= "";
2117         $pkg->{'state'} ||= "";
2118         my $logstr = sprintf("merge-v3 %s %s_%s", $vars->{'time'}, $name, $pkgs->{'version'}).
2119             ($pkgs->{'binnmu'} ? ";b".$pkgs->{'binnmu'} : "").
2120             sprintf(" (%s, %s, previous: %s", $vars->{'arch'}, $vars->{'suite'}, $pkg->{'version'}//"").
2121             ($pkg->{'binary_nmu_version'} ? ";b".$pkg->{'binary_nmu_version'} : "").
2122             ", $pkg->{'state'}".($pkg->{'notes'} ? "/".$pkg->{'notes'} : "")."):";
2123
2124         if (isin($pkgs->{'status'}, qw (installed related)) && $pkgs->{'version'} eq $pkg->{'version'} && ($pkgs->{'binnmu'}//0) < int($pkg->{'binary_nmu_version'}//0)) {
2125                 $pkgs->{'status'} = 'out-of-date';
2126         }
2127         if (isin($pkgs->{'status'}, qw <installed related arch-not-in-arch-list packages-arch-specific overwritten-by-arch-all arch-all-only>)) {
2128             my $change = 0;
2129             my $tstate = {'installed' => 'Installed', 'related' => 'Installed', 
2130                 'arch-not-in-arch-list' => 'Auto-Not-For-Us', 'packages-arch-specific' => 'Auto-Not-For-Us', 'overwritten-by-arch-all' => 'Auto-Not-For-Us', 'arch-all-only' => 'Auto-Not-For-Us',
2131                 }->{$pkgs->{'status'}};
2132             next if isin( $pkg->{'state'}, qw<Not-For-Us Failed Failed-Removed Dep-Wait Dep-Wait-Removed>) && isin( $tstate, qw<Auto-Not-For-Us>);
2133             # if the package is currently current, the status is Installed, not not-for-us
2134             if ($pkg->{'state'} ne $tstate) {
2135                 change_state( \$pkg, $tstate);
2136                 if (isin( $tstate, qw<Installed>)) {
2137                     delete $pkg->{'depends'};
2138                     delete $pkg->{'extra_depends'};
2139                     delete $pkg->{'extra_conflicts'};
2140                 }
2141                 $change++;
2142             }
2143             my $attrs = { 'version' => 'version', 'installed_version' => 'version', 'binary_nmu_version' => 'binnmu', 'section' => 'section', 'priority' => 'priority' };
2144             foreach my $k (keys %$attrs) {
2145                 next if isin( $tstate, qw<Auto-Not-For-Us>) && isin( $k, qw<installed_version binary_nmu_version>);
2146                 if (($pkg->{$k}//"") ne ($pkgs->{$attrs->{$k}}//"")) {
2147                     $pkg->{$k} = $pkgs->{$attrs->{$k}};
2148                     $change++;
2149                 }
2150             }
2151             if (isin($pkgs->{'status'}, qw <related packages-arch-specific overwritten-by-arch-all arch-not-in-arch-list arch-all-only>)) {
2152                 my $tnotes = $pkgs->{'status'};
2153                 if (($pkg->{'notes'}//"") ne $tnotes) {
2154                     $pkg->{'notes'} = $tnotes;
2155                     $change++;
2156                 }
2157             }
2158             if ($change) {
2159                 print "$logstr set to $tstate/".($pkg->{'notes'}//"")."\n" if $verbose || $simulate;
2160                 log_ta( $pkg, "--merge-v3: $tstate" ) unless $simulate;
2161                 update_source_info($pkg) unless $simulate;
2162             }
2163             next;
2164         }
2165
2166         # only uncompiled / out-of-date are left, so check if anything new
2167         if (!(isin($pkgs->{'status'}, qw (uncompiled out-of-date)))) {
2168             print "$logstr package in unknown state: $pkgs->{'status'}\n";
2169             next SRCS;
2170         }
2171         next if $pkgs->{'version'} eq $pkg->{'version'} and $pkgs->{'binnmu'}//0 >= int($pkg->{'binary_nmu_version'}//0);
2172         next if $pkgs->{'version'} eq $pkg->{'version'} and !isin( $pkg->{'state'}, qw(Installed));
2173         next if isin( $pkg->{'state'}, qw(Not-For-Us Failed-Removed));
2174
2175         if (defined( $pkg->{'state'} ) && isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
2176             send_mail( $pkg->{'builder'},
2177                 "new version of $name (dist=$distribution)",
2178                 "As far as I'm informed, you're currently building the package $name\n".
2179                 "in version $pkg->{'version'}.\n\n".
2180                 "Now there's a new source version $pkgs->{'version'}. If you haven't finished\n".
2181                 "compiling $name yet, you can stop it to save some work.\n".
2182                 "Just to inform you...\n".
2183                 "(This is an automated message)\n" ) unless $simulate;
2184             print "$logstr new version while building $pkg->{'version'} -- sending mail to builder ($pkg->{'builder'})\n"
2185                                   if $verbose || $simulate;
2186             }
2187         change_state( \$pkg, 'Needs-Build');
2188         $pkg->{'notes'} = $pkgs->{'status'};
2189         $pkg->{'version'} = $pkgs->{'version'};
2190         $pkg->{'section'} = $pkgs->{'section'};
2191         $pkg->{'priority'} = $pkgs->{'priority'};
2192         $pkg->{'dep'} = $pkgs->{'depends'};
2193         $pkg->{'conf'} = $pkgs->{'conflicts'};
2194         delete $pkg->{'builder'};
2195         delete $pkg->{'binary_nmu_version'} unless $pkgs->{'binnmu'};
2196         delete $pkg->{'binary_nmu_changelog'} unless $pkgs->{'binnmu'};
2197         delete $pkg->{'buildpri'};
2198         log_ta( $pkg, "--merge-v3: needs-build" ) unless $simulate;
2199         update_source_info($pkg) unless $simulate;
2200         print "$logstr set to needs-builds\n" if $simulate || $verbose;
2201     }
2202
2203     foreach my $name (keys %$db) {
2204         next if $srcs->{$name};
2205         my $pkg = $db->{$name};
2206         my $logstr = "merge-v3 $vars->{'time'} ".$name."_$pkg->{'version'} ($vars->{'arch'}, $vars->{'suite'}, previous: $pkg->{'state'}):";
2207         # package disappeared - delete
2208         change_state( \$pkg, 'deleted' );
2209         log_ta( $pkg, "--merge-v3: deleted" ) unless $simulate;
2210         print "$logstr deleted from database\n" if $verbose || $simulate;
2211         del_source_info($name) unless $simulate;
2212         delete $db->{$name};
2213     }
2214 }