X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=bin%2Fwanna-build;h=232b0b5b98d70dc8259f81d4bcf0d398e885b948;hb=cd5b747d02fd709968f8f122fbe3fa755a60bade;hp=d5813c063db69f72928a9c6bc5194f083403d4f8;hpb=59b2b19a196b5c0f4d24634320b78e38c0114faa;p=wannabuild.git diff --git a/bin/wanna-build b/bin/wanna-build index d5813c0..232b0b5 100755 --- a/bin/wanna-build +++ b/bin/wanna-build @@ -40,12 +40,15 @@ use FileHandle; use File::Copy; use DBI; use lib '/org/wanna-build/bin'; +use lib '/org/wanna-build/lib'; use WannaBuild; use YAML::Tiny; use Data::Dumper; use Hash::Merge qw ( merge ); use String::Format; use Date::Parse; +use List::Util qw[max]; +use Dpkg::Version qw(vercmp); # TODO: change this for running with squeeze dpkg our ($verbose, $mail_logs, $list_order, $list_state, $curr_date, $op_mode, $user, $real_user, $distribution, @@ -55,8 +58,10 @@ our ($verbose, $mail_logs, $list_order, $list_state, $category, %catval, %short_category, $short_date, $list_min_age, $dbbase, @curr_time, $build_priority, %new_vers, $binNMUver, %merge_srcvers, %merge_binsrc, - $printformat + $printformat, $ownprintformat, $privmode ); +our $Pas = '/org/buildd.debian.org/etc/packages-arch-specific/Packages-arch-specific'; +our $simulate = 0; # global vars $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:/org/wanna-build/bin/"; @@ -127,6 +132,7 @@ my %options = "pretend-avail" => { short => "p", mode => "pretend-avail" }, "merge-all" => { mode => "merge-all" }, "merge-all-secondary" => { mode => "merge-all-secondary" }, + "merge-v3" => { mode => "merge-v3" }, info => { short => "i", mode => "info" }, 'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver, code => sub { die "Invalid binNMU version: $binNMUver\n" @@ -190,6 +196,8 @@ my %options = $list_min_age *= -1; } }, "format" => { arg => \$printformat }, + "own-format" => { arg => \$ownprintformat }, + "Pas" => { arg => \$Pas }, # special actions export => { arg => \$export_to, mode => "export" }, import => { arg => \$import_from, mode => "import" }, @@ -239,11 +247,26 @@ while( @ARGV && $ARGV[0] =~ /^-/ ) { $op_mode = $category ? "set-failed" : "set-building" if !$op_mode; # default operation -$list_order = $list_state eq "failed" ? 'fPcpsn' : 'PScpsn' - if !$list_order and $list_state; $distribution ||= "unstable"; -die "Bad distribution '$distribution'\n" - if !isin($distribution, keys %conf::distributions); +if ($distribution eq 'any-priv') { + $privmode = 'yes'; + $distribution = 'any'; +} +if ($distribution eq 'any-unpriv') { + $privmode = 'no'; + $distribution = 'any'; +} +undef $distribution if $distribution eq 'any'; +if ($distribution) { + my @dists = split(/[, ]+/, $distribution); + foreach my $dist (@dists) { + die "Bad distribution '$distribution'\n" + if !isin($dist, keys %conf::distributions); + } +} +if (!isin ( $op_mode, qw(list) ) && ( !$distribution || $distribution =~ /[ ,]/)) { + die "multiple distributions are only allowed for list"; +} # If they didn't specify an arch, try to get it from database name which # is in the form of $arch/build-db @@ -306,18 +329,22 @@ if (!$fail_reason) { my $yamlmap = (); my $yamldir = "/org/wanna-build/etc/yaml"; my @files = ('wanna-build.yaml'); +if ((getpwuid($>))[7]) { push (@files, ((getpwuid($>))[7])."/.wanna-build.yaml"); } if ($user =~ /(buildd.*)-/) { push (@files, "$1.yaml") }; -push ( @files, "$user.yaml"); +if ($user) { push ( @files, "$user.yaml"); } foreach my $file (@files) { - if ($verbose >= 2) { print "Trying to read $file ...\n"; } - next unless -f $yamldir."/".$file; - if ($verbose >= 2) { print "Read $file ...\n"; } - my $m = YAML::Tiny->read( $yamldir."/".$file )->[0]; + my $cfile = File::Spec->rel2abs( $file, $yamldir ); + if ($verbose >= 2) { print "Trying to read $file ($cfile) ...\n"; } + next unless -f $cfile; + if ($verbose >= 2) { print "Read $file ($cfile) ...\n"; } + my $m = YAML::Tiny->read( $cfile )->[0]; $yamlmap = merge($m, $yamlmap); } if (not $yamlmap) { die "FATAL: no configuration found\n"; } +$list_order = $yamlmap->{"list-order"}{$list_state} if !$list_order and $list_state; +$list_order ||= $yamlmap->{"list-order"}{'default'}; my $dbh; @@ -329,7 +356,7 @@ END { } my $schema_suffix = ''; -if (isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !(not -t and $user =~ /-/)) { +if (isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !(not -t and $user =~ /-/) && !($privmode eq 'yes')) { $dbh = DBI->connect("DBI:Pg:service=wanna-build") || die "FATAL: Cannot open database: $DBI::errstr\n"; $schema_suffix = '_public'; @@ -434,7 +461,7 @@ sub process { parse_quinn_diff(0); @ARGV = ( $ARGS[2] ); my $srcs = parse_sources(1); - call_edos_depcheck( $ARGS[0], $srcs ); + call_edos_depcheck( {'arch' => $arch, 'pkgs' => ($ARGS[0]), 'srcs' => $srcs }); last SWITCH; }; /^merge-all-secondary/ && do { @@ -457,9 +484,25 @@ sub process { parse_quinn_diff(0); @ARGV = ( $ARGS[2] ); my $srcs = parse_sources(1); - call_edos_depcheck( $ARGS[3], $srcs ); + call_edos_depcheck( {'arch' => $arch, 'pkgs' => ($ARGS[3]), 'srcs' => $srcs }); last SWITCH; }; + /^merge-v3/ && do { + die "This operation is restricted to admin users\n" + if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users) and !$simulate); + # call with installed-packages+ . installed-sources+ [ . available-for-build-packages+ ] + # in case available-for-build-packages is not specified, installed-packages are used + lock_table() unless $simulate; + my @ipkgs = &parse_argv( \@ARGV, '.'); + my @isrcs = &parse_argv( \@ARGV, '.'); + my @bpkgs = &parse_argv( \@ARGV, '.'); + use WB::QD; + my $srcs = WB::QD::readsourcebins($arch, $Pas, \@isrcs, \@ipkgs); + parse_all_v3($$srcs); + @bpkgs = @ipkgs unless @bpkgs; + call_edos_depcheck( {'arch' => $arch, 'pkgs' => @bpkgs, 'srcs' => $$srcs, 'depwait' => 1 }); + last SWITCH; + }; /^import/ && do { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and @@ -1765,73 +1808,24 @@ BEGIN { } sub sort_list_func { - my( $letter, $x, $ax, $bx ); - - foreach $letter (split( "", $list_order )) { - SWITCH: foreach ($letter) { - /C/ && do { - $x = $b->{'calprio'} <=> $a->{'calprio'}; - return $x if $x != 0; - last SWITCH; - }; - /W/ && do { - $x = $b->{'state_days'} <=> $a->{'state_days'}; - return $x if $x != 0; - last SWITCH; - }; - /P/ && do { - $x = $b->{'buildpri'} <=> $a->{'buildpri'}; - return $x if $x != 0; - last SWITCH; - }; - /p/ && do { - $x = $prioval{$a->{'priority'}} <=> $prioval{$b->{'priority'}}; - return $x if $x != 0; - last SWITCH; - }; - /s/ && do { - $x = $sectval{$a->{'section'}} <=> $sectval{$b->{'section'}}; - return $x if $x != 0; - last SWITCH; - }; - /n/ && do { - $x = $a->{'package'} cmp $b->{'package'}; - return $x if $x != 0; - last SWITCH; - }; - /b/ && do { - $x = $a->{'builder'} cmp $b->{'builder'}; - return $x if $x != 0; - last SWITCH; - }; - /c/ && do { - $ax = ($a->{'notes'} =~ /^(out-of-date|partial)/) ? 0 : - ($a->{'notes'} =~ /^uncompiled/) ? 2 : 1; - $bx = ($b->{'notes'} =~ /^(out-of-date|partial)/) ? 0 : - ($b->{'notes'} =~ /^uncompiled/) ? 2 : 1; - $x = $ax <=> $bx; - return $x if $x != 0; - last SWITCH; - }; - /f/ && do { - my $ca = defined $a->{'failed_category'} ? - $a->{'failed_category'} : "none"; - my $cb = defined $b->{'failed_category'} ? - $b->{'failed_category'} : "none"; - $x = $catval{$ca} <=> $catval{$cb}; - return $x if $x != 0; - last SWITCH; - }; - /S/ && do { - my $pa = $prioval{$a->{'priority'}} > - $prioval{'standard'}; - my $pb = $prioval{$b->{'priority'}} > - $prioval{'standard'}; - $x = $pa <=> $pb; - return $x if $x != 0; - last SWITCH; - }; - } + my $map_funcs = { + 'C' => ['<=>', sub { return (-1) * $_[0]->{'calprio'}; }], + 'W' => ['<=>', sub { return (-1) * $_[0]->{'state_days'}; }], + 'P' => ['<=>', sub { return (-1) * $_[0]->{'buildpri'}; }], + 'p' => ['<=>', sub { return $prioval{$_[0]->{'priority'}}; }], + 's' => ['<=>', sub { return $sectval{$_[0]->{'section'}}; }], + 'n' => ['cmp', sub { return $_[0]->{'package'}; }], + 'b' => ['cmp', sub { return $_[0]->{'builder'}; }], + 'c' => ['<=>', sub { return ($_[0]->{'notes'} =~ /^(out-of-date|partial)/) ? 0: ($_[0]->{'notes'} =~ /^uncompiled/) ? 2 : 1; }], + 'f' => ['<=>', sub { return $catval{ $_[0]->{'failed_category'} ? $_[0]->{'failed_category'}: "none" }; }], + 'S' => ['<=>', sub { return $prioval{$_[0]->{'priority'}} > $prioval{'standard'}; }], + }; + + foreach my $letter (split( //, $list_order )) { + my $r; + $r = &{$map_funcs->{$letter}[1]}($a) <=> &{$map_funcs->{$letter}[1]}($b) if $map_funcs->{$letter}[0] eq '<=>'; + $r = &{$map_funcs->{$letter}[1]}($a) cmp &{$map_funcs->{$letter}[1]}($b) if $map_funcs->{$letter}[0] eq 'cmp'; + return $r if $r != 0; } return 0; } @@ -1839,6 +1833,9 @@ sub sort_list_func { sub calculate_prio { my $priomap = $yamlmap->{priority}; my $pkg = shift; + my @s=split("/", $pkg->{'section'}); + $pkg->{'component'} = $s[0] if $s[1]; + $pkg->{'component'} ||= 'main'; $pkg->{'calprio'} = 0; foreach my $k (keys %{$priomap->{keys}}) { $pkg->{'calprio'} += $priomap->{keys}->{$k}{$pkg->{$k}} if $pkg->{$k} and $priomap->{keys}->{$k}{$pkg->{$k}}; @@ -1857,6 +1854,17 @@ sub calculate_prio { } +sub seconds2time { + my $t = shift; + return "" unless $t; + my $sec = $t % 60; + my $min = int($t/60) % 60; + my $hours = int($t / 3600); + return sprintf("%d:%02d:%02d", $hours, $min, $sec) if $hours; + return sprintf("%d:%02d", $min, $sec); +} + + sub use_fmt { my $r; @@ -1896,20 +1904,32 @@ Within an format string, the following values are allowed (need to be preceded b This can be combined to e.g. wanna-build --format='wanna-build -A %a --give-back %p_%v' -A mipsel --list=failed -p Package name a Architecture -s Time in this state in full seconds since epoch -v Package version +c section (e.g. libs or utils) +D in case of BD-Uninstallable the reason for the uninstallability +d distribution +E in case of Dep-Wait the packages being waited on, in case of Needs-Build the number in the queue +F in case of Failed the fail reason +n newline +o time of last successful build (seconds) +O time of last successful build (formated) +P previous state +p Package name +q time of last build (seconds) +Q time of last build (formated) +r max time of last (successful) build (seconds) +R max time of last (successful) build (formated) S Package state +s Time in this state in full seconds since epoch +t time of state change +T time since state change u Builder (e.g. buildd_mipsel-rem) +v Package version +V full Package version (i.e. with +b.., = %v%{+b}B%B X the string normally between [], e.g. optional:out-of-date:calprio{61}:days{25} -c section (e.g. libs or utils) -P previous state -E in case of Dep-Wait the packages being waited on, in case of Needs-Build the number in the queue -D in case of BD-Uninstallable the reason for the uninstallability -%{Text}? print text in case ? is not empty, don't print ? -%{!Text}? print text in case ? is empty, don't print ? +%{Text}? print Text in case ? is not empty; ? is never printed +%{!Text}? print Text in case ? is empty; ? is never printed Text could contain further %. To start with !, use %! =cut @@ -1918,6 +1938,7 @@ Text could contain further %. To start with !, use %! 'a' => make_fmt( $arch, $pkg, $var), 's' => make_fmt( sub { return floor(str2time($pkg->{'state_change'})); }, $pkg, $var), 'v' => make_fmt( $pkg->{'version'}, $pkg, $var), + 'V' => make_fmt( sub { $pkg->{'binary_nmu_version'} ? $pkg->{'version'}."+b".$pkg->{'binary_nmu_version'} : $pkg->{'version'} }, $pkg, $var), 'S' => make_fmt( $pkg->{'state'}, $pkg, $var), 'u' => make_fmt( $pkg->{'builder'}, $pkg, $var), 'X' => make_fmt( sub { @@ -1934,10 +1955,24 @@ Text could contain further %. To start with !, use %! 'E' => make_fmt( sub { return $pkg->{'depends'} if $pkg->{'state'} eq "Dep-Wait"; return $var->{scnt}{'Needs-Build'} + 1 if $pkg->{'state'} eq 'Needs-Build'; return ""; }, $pkg, $var), + 'F' => make_fmt( sub { return "" unless $pkg->{'failed'}; + my $failed = $pkg->{'failed'}; + $failed =~ s/\\/\\\\/g; + return $pkg->{'package'}."#".$arch."-failure\n ". + join("\\0a",split("\n",$failed))."\\0a\n"; }, $pkg, $var), 'D' => make_fmt( sub { return "" unless $pkg->{'bd_problem'}; return $pkg->{'package'}."#".$arch."-bd-problem\n". join("\\0a",split("\n",$pkg->{'bd_problem'}))."\\0a\n"; }, $pkg, $var), 'B' => make_fmt( sub { return $pkg->{'binary_nmu_version'} if defined $pkg->{'binary_nmu_version'}; }, $pkg, $var), + 'd' => make_fmt( $pkg->{'distribution'}, $pkg, $var), + 't' => make_fmt( $pkg->{'state_change'}, $pkg, $var), + 'T' => make_fmt( sub { return seconds2time(time() - floor(str2time($pkg->{'state_change'}))); }, $pkg, $var), + 'o' => make_fmt( $pkg->{'successtime'}, $pkg, $var), + 'O' => make_fmt( sub { return seconds2time ( $pkg->{'successtime'}); }, $pkg, $var), + 'q' => make_fmt( $pkg->{'anytime'}, $pkg, $var), + 'Q' => make_fmt( sub { return seconds2time ( $pkg->{'anytime'}); }, $pkg, $var), + 'r' => make_fmt( sub { return max($pkg->{'successtime'}, $pkg->{'anytime'}); }, $pkg, $var), + 'R' => make_fmt( sub { return seconds2time ( max($pkg->{'successtime'}, $pkg->{'anytime'})); }, $pkg, $var), )); } @@ -1954,6 +1989,15 @@ sub list_packages { push @list, calculate_prio($db->{$name}); } + # filter components + @list = grep { my $i = $_->{'component'}; grep { $i eq $_ } split /[, ]+/, $yamlmap->{"restrict"}{'component'} } @list; + + # first adjust ownprintformat, then set printformat accordingly + $printformat ||= $yamlmap->{"format"}{$ownprintformat}; + $printformat ||= $yamlmap->{"format"}{"default"}{$state}; + $printformat ||= $yamlmap->{"format"}{"default"}{"default"}; + undef $printformat if ($ownprintformat eq 'none'); + foreach $pkg (sort sort_list_func @list) { if ($printformat) { print print_format($printformat, $pkg, {'cnt' => $cnt, 'scnt' => \%scnt})."\n"; @@ -1961,21 +2005,12 @@ sub list_packages { $scnt{$pkg->{'state'}}++; next; } - print "$pkg->{'section'}/$pkg->{'package'}_$pkg->{'version'}"; - print ": $pkg->{'state'}" + print print_format("%c/%p_%v", $pkg, {}); + print print_format(": %S", $pkg, {}) if $state eq "all"; - print " by $pkg->{'builder'}" - if $pkg->{'state'} ne "Needs-Build" && $pkg->{'builder'}; - print " [$pkg->{'priority'}:$pkg->{'notes'}"; - print ":PREV-FAILED" - if $pkg->{'previous_state'} =~ /^Failed/; - print ":bp{" . $pkg->{'buildpri'} . "}" - if defined $pkg->{'buildpri'}; - print ":binNMU{" . $pkg->{'binary_nmu_version'} . "}" - if defined $pkg->{'binary_nmu_version'}; - print ":calprio{". $pkg->{'calprio'}."}"; - print ":days{". $pkg->{'state_days'}."}"; - print "]\n"; + print print_format("%{ by }u%u", $pkg, {}) + if $pkg->{'state'} ne "Needs-Build"; + print print_format(" [%X]\n", $pkg, {}); print " Reasons for failing:\n", " [Category: ", defined $pkg->{'failed_category'} ? $pkg->{'failed_category'} : "none", @@ -2025,7 +2060,9 @@ sub info_packages { 'failed_category' => 'Failed-Category', 'notes' => 'Notes', 'distribution' => 'Distribution', 'old_failed' => 'Old-Failed', 'permbuildpri' => 'PermBuildPri', 'rel' => 'Rel', - 'calprio' => 'CalculatedPri', 'state_days' => 'State-Days' + 'calprio' => 'CalculatedPri', 'state_days' => 'State-Days', + 'successtime' => 'Success-build-time', + 'anytime' => 'Build-time' ); foreach $name (@_) { @@ -2323,74 +2360,201 @@ sub build_deplist { return $result; } + +sub greparch { + my ($gapkglong, $gaarch) = @_; + my ($gapkg, $gaarchs) = split(/ \[/, $gapkglong); + if ($gaarchs) { + $_ = $gapkg; + chop($gaarchs); # take away the ] + my @gaarches = split(/ /, $gaarchs); + if (substr($gaarches[0], 0, 1) eq '!') { + return 0 if grep /^!$gaarch$/, @gaarches; + } else { # positive case + return 0 unless grep /^$gaarch$/, @gaarches; + } + }; + return 1; +} +sub filterarch { + my $faarch = $_[1]; + return join(', ', grep { &greparch($_, $faarch) } split(/, ?/, $_[0])); +} + +sub wb_edos_builddebcheck { +# Copyright (C) 2008 Ralf Treinen +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, version 2 of the License. +# integrated into wanna-builds code by Andreas Barth 2010 + + my $args = shift; + my $sourceprefix="source---"; + my $architecture=$args->{'arch'}; + my $edosoptions = "-failures -explain -quiet"; + my @packagefiles = $args->{'pkgs'}; + my $sourcesfile = $args->{'src'}; + + my $packagearch=""; + foreach my $packagefile (@packagefiles) { + open(P,$packagefile); + while (

) { + next unless /^Architecture/; + next if /^Architecture:\s*all/; + /Architecture:\s*([^\s]*)/; + if ($packagearch eq "") { + $packagearch = $1; + } elsif ( $packagearch ne $1) { + return "Package file contains different architectures: $packagearch, $1"; + } + } + close P; + } + + if ( $architecture eq "" ) { + if ( $packagearch eq "" ) { + return "No architecture option given, " . + "and no non-all architecture found in the Packages file"; + } else { + $architecture = $packagearch; + } + } else { + if ( $packagearch ne "" & $architecture ne $packagearch) { + return "Architecture option is $architecture ". + "but the package file contains architecture $packagearch"; + } + } + + print "calling: edos-debcheck $edosoptions < $sourcesfile ".join('', map {" '-base FILE' ".$_ } @packagefiles)."\n"; + open(RESULT, '-|', + "edos-debcheck $edosoptions < $sourcesfile ".join('', map {" '-base FILE' ".$_ } @packagefiles)); + + my $explanation=""; + my $result={}; + my $binpkg=""; + + while () { +# source---pulseaudio (= 0.9.15-4.1~bpo50+1): FAILED +# source---pulseaudio (= 0.9.15-4.1~bpo50+1) depends on missing: +# - libltdl-dev (>= 2.2.6a-2) +# source---libcanberra (= 0.22-1~bpo50+1): FAILED +# source---libcanberra (= 0.22-1~bpo50+1) depends on missing: +# - libltdl-dev +# - libltdl7-dev (>= 2.2.6) + + if (/^\s+/) { + s/^(\s*)$sourceprefix(.*)depends on/$1$2build-depends on/o; + s/^(\s*)$sourceprefix(.*) and (.*) conflict/$1$2 build-conflicts with $3/o; + $explanation .= $_; + } else { + if (/^$sourceprefix(.*) \(.*\): FAILED/o) { + $result->{$binpkg} = $explanation if $binpkg; + $explanation = ""; + $binpkg = $1; + } elsif (/^(depwait---.*) \(.*\): FAILED/o) { + $result->{$binpkg} = $explanation if $binpkg; + $explanation = ""; + $binpkg = $1; + } else { # else something broken is happening + #print "ignoring $_\n"; + 1; + } + } + } + + close RESULT; + $result->{$binpkg} = $explanation if $binpkg; + return $result; + +} + + sub call_edos_depcheck { - my $packagesfile = shift; - my $srcs = shift; + my $args = shift; + my $srcs = $args->{'srcs'}; my $key; - return if defined ($conf::distributions{$distribution}{noadw}); + return if defined ($conf::distributions{$distribution}{noadw}) && not defined $args->{'depwait'}; # We need to check all of needs-build, as any new upload could make # something in needs-build have uninstallable deps # We also check everything in bd-uninstallable, as any new upload could # make that work again - my %interesting_packages; + my (%interesting_packages, %interesting_packages_depwait); my $db = get_all_source_info(); foreach $key (keys %$db) { my $pkg = $db->{$key}; - if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/)) { + if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/) and not defined ($conf::distributions{$distribution}{noadw})) { $interesting_packages{$key} = undef; } + if (defined $pkg and isin($pkg->{'state'}, qw/Dep-Wait/) and defined $args->{'depwait'}) { + $interesting_packages_depwait{$key} = undef; + # we always check for BD-Uninstallability in depwait - could be that depwait is satisfied but package is uninstallable + $interesting_packages{$key} = undef unless defined ($conf::distributions{$distribution}{noadw}); + } } #print "I would look at these sources with edos-depcheck:\n"; #print join " ", keys %interesting_packages,"\n"; + return unless %interesting_packages || %interesting_packages_depwait; - my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-"; - my ($tmpfile, $i); - for( $i = 0;; ++$i ) { - $tmpfile = $tmpfile_pattern . $i; - last if ! -e $tmpfile; - } - - open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n"; + my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-XXXXX"; + use File::Temp qw/ tempfile /; + my ($SOURCES, $tmpfile) = tempfile( $tmpfile_pattern, UNLINK => 1 ); for my $key (keys %interesting_packages) { my $pkg = $db->{$key}; - print SOURCES "Package: $key\n"; - print SOURCES "Version: $pkg->{'version'}\n"; - print SOURCES "Build-Depends: $srcs->{$key}{'dep'}\n" if $srcs->{$key}{'dep'}; - print SOURCES "Build-Conflicts: $srcs->{$key}{'conf'}\n" if $srcs->{$key}{'conf'}; - print SOURCES "Architecture: all\n"; - print SOURCES "\n"; + # we print the source files as binary ones (with "source---"-prefixed), + # so we can try if these "binary" packages are installable. + # If such a "binary" package is installable, the corresponding source package is buildable. + print $SOURCES "Package: source---$key\n"; + print $SOURCES "Version: $pkg->{'version'}\n"; + my $t = &filterarch($srcs->{$key}{'dep'} || $srcs->{$key}{'depends'}, $arch); + print $SOURCES "Depends: $t\n" if $t; + my $u = &filterarch($srcs->{$key}{'conf'} || $srcs->{$key}{'conflicts'}, $arch); + print $SOURCES "Conflicts: $u\n" if $u; + print $SOURCES "Architecture: all\n"; + print $SOURCES "\n"; } - close SOURCES; - - if (open(EDOS,"-|","wb-edos-builddebcheck", "-a", $arch, $packagesfile, $tmpfile)) - { - local($/) = ""; # read in paragraph mode - while( ) { - my( $key, $reason ) ; - s/\s*$//m; - /^Package:\s*(\S+)$/mi and $key = $1; - /^Failed-Why:(([^\n]|\n ([^\n]|\.))*)$/msi and $reason = $1; - $reason =~ s/^\s*//mg; - $reason ||= 'No reason given by edos-debcheck'; + for my $key (keys %interesting_packages_depwait) { + my $pkg = $db->{$key}; + # we print the source files as binary ones (with "depwait---"-prefixed), + # so we can try if these "binary" packages are installable. + # If such a "binary" package is installable, the corresponding source package goes out of depwait + print $SOURCES "Package: depwait---$key\n"; + print $SOURCES "Version: $pkg->{'version'}\n"; + print $SOURCES "Depends: $pkg->{'depends'}\n"; + print $SOURCES "Architecture: all\n"; + print $SOURCES "\n"; + } + close $SOURCES; + my $edosresults = wb_edos_builddebcheck({'arch' => $args->{'arch'}, 'pkgs' => $args->{'pkgs'}, 'src' => $tmpfile}); + if (ref($edosresults) eq 'HASH') { + foreach my $key (grep { $_ !~ /^depwait---/ } keys %$edosresults) { if (exists $interesting_packages{$key}) { - $interesting_packages{$key} = $reason; + $interesting_packages{$key} = $edosresults->{$key}; } else { #print "TODO: edos reported a package we do not care about now\n" if $verbose; } - } - close EDOS; + } + foreach my $key (grep { $_ =~ /^depwait---/ } keys %$edosresults) { + $key =~ /^depwait---(.*)/ and $key = $1; + if (exists $interesting_packages_depwait{$key}) { + $interesting_packages_depwait{$key} = $edosresults->{"depwait---".$key}; + } else { + #print "TODO: edos reported a package we do not care about now\n" if $verbose; + } + } } else { - print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" . - "all packages have installable build-dependencies." + # if $edosresults isn't an hash, then something went wrong and the string is the error message + print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" . + "all packages have installable build-dependencies." } unlink( $tmpfile ); for my $key (keys %interesting_packages) { + next if defined $interesting_packages_depwait{$key}; my $pkg = $db->{$key}; my $change = (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') || @@ -2410,13 +2574,30 @@ sub call_edos_depcheck { } } if ($change) { - log_ta( $pkg, "--merge-all" ); - print "edos-builddebchange changed state of ${key}_$pkg->{'version'} to $pkg->{'state'}\n" if $verbose; + log_ta( $pkg, "--merge-all (edos)" ) unless $simulate; + print "edos-builddebchange changed state of ${key}_$pkg->{'version'} to $pkg->{'state'}\n" if $verbose || $simulate; } if ($change || $problemchange) { - update_source_info($pkg); + update_source_info($pkg) unless $simulate; } } + + for my $key (keys %interesting_packages_depwait) { + if ($interesting_packages_depwait{$key}) { + print "dependency on $key not fullfiled yet\n" if $verbose || $simulate; + next; + } + my $pkg = $db->{$key}; + if (defined $interesting_packages{$key}) { + change_state( \$pkg, 'BD-Uninstallable' ); + $pkg->{'bd_problem'} = $interesting_packages{$key}; + } else { + change_state( \$pkg, 'Needs-Build' ); + } + log_ta( $pkg, "edos_depcheck: depwait" ) unless $simulate; + update_source_info($pkg) unless $simulate; + print "edos-builddebchange changed state of ${key}_$pkg->{'version'} to $pkg->{'state'}\n" if $verbose || $simulate; + } } sub usage { @@ -2499,18 +2680,26 @@ sub transactions_table_name { return '"' . $arch . $schema_suffix . '".transactions'; } +sub pkg_history_table_name { + return '"' . $arch . $schema_suffix . '".pkg_history'; +} + sub get_readonly_source_info { my $name = shift; # SELECT FLOOR(EXTRACT('epoch' FROM age(localtimestamp, '2010-01-22 23:45')) / 86400) -- change to that? - my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change::timestamp)) as state_days FROM ' . - table_name() . ' WHERE package = ? AND distribution = ?', + my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, failed_category, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days" + . ", (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" + . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution ) AS anytime" + . " FROM " . table_name() + . ' WHERE package = ? AND distribution = ?'; + my $pkg = $dbh->selectrow_hashref( $q, undef, $name, $distribution); return $pkg; } sub get_source_info { my $name = shift; - my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change::timestamp)) as state_days FROM ' . + my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change)) as state_days FROM ' . table_name() . ' WHERE package = ? AND distribution = ?' . ' FOR UPDATE', undef, $name, $distribution); @@ -2520,22 +2709,35 @@ sub get_source_info { sub get_all_source_info { my %options = @_; - my $q = 'SELECT *, '. - 'extract(days from date_trunc(\'days\', now() - state_change::timestamp)) as state_days, '. - 'date_trunc(\'seconds\', now() - state_change::timestamp) as state_time'. - ' FROM ' . table_name() - . ' WHERE distribution = ? '; - my @args = ($distribution); + my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, failed_category, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days" +# . ", (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" +# . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution ) AS anytime" + . ", successtime.build_time as successtime, anytime.build_time as anytime" + . " FROM " . table_name() + . " left join ( " + . "select distinct on (package, distribution) build_time, package, distribution from ".pkg_history_table_name()." where result = 'successful' order by package, distribution, timestamp " + . " ) as successtime using (package, distribution) " + . " left join ( " + . "select distinct on (package, distribution) build_time, package, distribution from ".pkg_history_table_name()." order by package, distribution, timestamp desc" + . " ) as anytime using (package, distribution) " + . " WHERE TRUE "; + my @args = (); + if ($distribution) { + my @dists = split(/[, ]+/, $distribution); + $q .= ' AND ( distribution = ? '.(' OR distribution = ? ' x $#dists).' )'; + foreach my $d ( @dists ) { + push @args, ($d); + } + } if ($options{state} && uc($options{state}) ne "ALL") { $q .= ' AND upper(state) = ? '; push @args, uc($options{state}); } - if ($options{user}) { + if ($options{user} && uc($options{state}) ne "NEEDS-BUILD") { # if it's NEEDS-BUILD, we don't look at users #this basically means "this user, or no user at all": - $q .= ' AND (builder = ? OR upper(state) = ?)'; + $q .= " AND (builder = ? OR upper(state) = 'NEEDS-BUILD')"; push @args, $options{user}; - push @args, "NEEDS-BUILD"; } if ($options{category}) { @@ -2545,12 +2747,12 @@ sub get_all_source_info { } if ($options{list_min_age} > 0) { - $q .= ' AND age(state_change::timestamp) > ? '; + $q .= ' AND age(state_change) > ? '; push @args, $options{list_min_age} . " days"; } if ($options{list_min_age} < 0) { - $q .= ' AND age(state_change::timestamp) < ? '; + $q .= ' AND age(state_change) < ? '; push @args, -$options{list_min_age} . " days"; } @@ -2658,3 +2860,115 @@ sub lock_table() ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr; } +sub parse_argv() { +# parts the array $_[0] and $_[1] and returns the sub-array (modifies the original one) + my @ret = (); + my $args = shift; + my $separator = shift; + while($args->[0] && $args->[0] ne $separator) { + push @ret, shift @$args; + } + shift @$args if @$args; + return @ret; +} + +sub parse_all_v3() { + my $srcs = shift; + my $db = get_all_source_info(); + my $binary = $srcs->{'_binary'}; + + SRCS: + foreach my $name (keys %$srcs) { + next if $name eq '_binaries'; + + # state = installed, out-of-date, uncompiled, not-for-us + my $pkgs = $srcs->{$name}; + my $pkg = $db->{$name}; + + unless ($pkg) { + next SRCS if $pkgs->{'status'} eq 'not-for-us'; + + # does at least one binary exist in the database and is more recent - if so, we're probably just outdated, ignore the source package + for my $bin (@{$pkgs->{'binary'}}) { + if ($binary->{$bin} and vercmp($pkgs->{'version'}, $binary->{$bin}) < 0) { + print "merge-v3: skiping $name ($arch)\n" if $verbose || $simulate; + next SRCS; + } + } + } + + if ($pkgs->{'status'} eq 'installed' && $pkg->{'binary_nmu_version'} && $pkgs->{'binnmu'} < $pkg->{'binary_nmu_version'}) { + $pkgs->{'status'} = 'out-of-date'; + } + if ($pkgs->{'status'} eq 'installed') { + if ($pkg->{'state'} ne 'Installed') { + change_state( \$pkg, 'Installed'); + $pkg->{'version'} = $pkgs->{'version'}; + $pkg->{'installed_version'} = $pkgs->{'version'}; + $pkg->{'binary_nmu_version'} = $pkgs->{'binnmu'}; + $pkg->{'section'} = $pkgs->{'section'}; + $pkg->{'priority'} = $pkgs->{'priority'}; + print "merge-v3: set $name ($arch) to installed\n" if $verbose || $simulate; + log_ta( $pkg, "--merge-v3: installed" ) unless $simulate; + update_source_info($pkg) unless $simulate; + } + next; + } + + if ($pkgs->{'status'} eq 'not-for-us') { + next if isin( $pkg->{'state'}, qw(Not-For-Us Installed Failed-Removed)); + + if (isin( $pkg->{'state'}, qw(Failed Build-Attempted Built))) { + change_state( \$pkg, "Failed-Removed" ); + log_ta( $pkg, "--merge-v3: Failed-Removed" ) unless $simulate; + update_source_info($pkg) unless $simulate; + print "$name ($pkg->{'version'}): (virtually) deleted from database\n" if $verbose || $simulate; + next; + } + + print "should delete $name (not-for-us)\n" if $verbose || $simulate || 1; # not implemented yet on purpose + next; + } + + # only uncompiled / out-of-date are left, so check if anything new + next if $pkgs->{'version'} eq $pkg->{'version'}; + + print "should set $name to needs-builds\n" if $simulate; + if (defined( $pkg->{'state'} ) && isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) { + send_mail( $pkg->{'builder'}, + "new version of $name (dist=$distribution)", + "As far as I'm informed, you're currently building the package $name\n". + "in version $pkg->{'version'}.\n\n". + "Now there's a new source version $pkgs->{'version'}. If you haven't finished\n". + "compiling $name yet, you can stop it to save some work.\n". + "Just to inform you...\n". + "(This is an automated message)\n" ) unless $simulate; + print "$name: new version ($pkgs->{'version'}) while building $pkg->{'version'} -- sending mail to builder ($pkg->{'builder'})\n" + if $verbose || $simulate; + } + change_state( \$pkg, 'Needs-Build'); + $pkg->{'notes'} = $pkgs->{'status'}; + $pkg->{'version'} = $pkgs->{'version'}; + $pkg->{'section'} = $pkgs->{'section'}; + $pkg->{'priority'} = $pkgs->{'priority'}; + $pkg->{'dep'} = $pkgs->{'depends'}; + $pkg->{'conf'} = $pkgs->{'conflicts'}; + delete $pkg->{'builder'}; + delete $pkg->{'binary_nmu_version'}; + delete $pkg->{'binary_nmu_changelog'}; + log_ta( $pkg, "--merge-v3: needs-build" ) unless $simulate; + update_source_info($pkg) unless $simulate; + print "$name ($pkgs->{'version'} / $arch) needs rebuilding now.\n" if $verbose || $simulate; + } + + foreach my $name (keys %$db) { + next if $srcs->{$name}; + my $pkg = $db->{$name}; + # package disappeared - delete + change_state( \$pkg, 'deleted' ); + log_ta( $pkg, "--merge-v3: deleted" ) unless $simulate; + print "$name ($pkg->{'version'} / $arch) deleted from database\n" if $verbose || $simulate; + del_source_info($name) unless $simulate; + delete $db->{$name}; + } +}