X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=bin%2Fwanna-build;h=3f11f6568636de4b843738a95aa8812f8a409921;hb=d60797ad26cad0923ca3ac1288dfa369a5bb8398;hp=4ba644ebbdb4149adb1e30f1c8a496a79d389ab5;hpb=80adc1ea160f423e6cfbb348bf1efefbac89d332;p=wannabuild.git diff --git a/bin/wanna-build b/bin/wanna-build index 4ba644e..3f11f65 100755 --- a/bin/wanna-build +++ b/bin/wanna-build @@ -44,7 +44,7 @@ use WannaBuild; our ($verbose, $mail_logs, $list_order, $list_state, $curr_date, $op_mode, $user, $real_user, $distribution, - $fail_reason, $opt_override, $import_from, $opt_create_db, + $fail_reason, $opt_override, $import_from, $export_to, $opt_create_db, %prioval, %sectval, $info_all_dists, $arch, $category, %catval, %short_category, @@ -118,6 +118,7 @@ my %options = "merge-sources" => { mode => "merge-sources" }, "pretend-avail" => { short => "p", mode => "pretend-avail" }, "merge-all" => { mode => "merge-all" }, + "merge-all-secondary" => { mode => "merge-all-secondary" }, info => { short => "i", mode => "info" }, 'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver, code => sub { die "Invalid binNMU version: $binNMUver\n" @@ -173,15 +174,15 @@ my %options = code => sub { die "Argument of --min-age must be a non-zero number\n" if $list_min_age == 0; - $list_min_age *= 24*60*60; } }, "max-age" => { arg => \$list_min_age, code => sub { die "Argument of --max-age must be a non-zero number\n" if $list_min_age == 0; - $list_min_age *= -24*60*60; + $list_min_age *= -1; } }, # special actions + export => { arg => \$export_to, mode => "export" }, import => { arg => \$import_from, mode => "import" }, "manual-edit" => { mode => "manual-edit" }, ); @@ -251,7 +252,7 @@ if ($verbose) { print "wanna-build $version for $distribution on $arch\n"; } -if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import +if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export merge-packages manual-edit merge-sources))) { warn "No packages given.\n"; @@ -303,7 +304,7 @@ END { } my $schema_suffix = ''; -if (isin( $op_mode, qw(list info)) && $distribution !~ /security/) { +if (isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !(not -t and $user =~ /-/)) { $dbh = DBI->connect("DBI:Pg:service=wanna-build") || die "FATAL: Cannot open database: $DBI::errstr\n"; $schema_suffix = '_public'; @@ -364,6 +365,7 @@ sub process { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users)); + lock_table(); parse_quinn_diff(1); last SWITCH; }; @@ -371,6 +373,7 @@ sub process { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users)); + lock_table(); parse_quinn_diff(0); last SWITCH; }; @@ -378,13 +381,15 @@ sub process { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users)); - parse_packages(); + lock_table(); + parse_packages(0); last SWITCH; }; /^merge-sources/ && do { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users)); + lock_table(); parse_sources(0); last SWITCH; }; @@ -392,13 +397,14 @@ sub process { pretend_avail( @ARGV ); last SWITCH; }; - /^merge-all/ && do { + /^merge-all$/ && do { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users)); + lock_table(); my @ARGS = @ARGV; @ARGV = ( $ARGS[0] ); - my $pkgs = parse_packages(); + my $pkgs = parse_packages(0); @ARGV = ( $ARGS[1] ); parse_quinn_diff(0); @ARGV = ( $ARGS[2] ); @@ -406,6 +412,29 @@ sub process { call_edos_depcheck( $ARGS[0], $srcs ); last SWITCH; }; + /^merge-all-secondary/ && do { + die "This operation is restricted to admin users\n" + if (defined @conf::admin_users and + !isin( $real_user, @conf::admin_users)); + # This is in case the chroot has multiple unrelated + # dist, for instance unstable and experimental. + # This is not for stable and proposed-updates. + # The second packages file contains a combination + # of all Packages files known to the buildd, the + # first only for the current dist. + lock_table(); + my @ARGS = @ARGV; + @ARGV = ( $ARGS[0] ); + my $pkgs = parse_packages(0); + @ARGV = ( $ARGS[3] ); + my $pkgs = parse_packages(1); + @ARGV = ( $ARGS[1] ); + parse_quinn_diff(0); + @ARGV = ( $ARGS[2] ); + my $srcs = parse_sources(1); + call_edos_depcheck( $ARGS[3], $srcs ); + last SWITCH; + }; /^import/ && do { die "This operation is restricted to admin users\n" if (defined @conf::admin_users and @@ -418,6 +447,10 @@ sub process { read_db( $import_from ); last SWITCH; }; + /^export/ && do { + export_db( $export_to ); + last SWITCH; + }; die "Unexpected operation mode $op_mode\n"; } @@ -1017,8 +1050,14 @@ sub set_one_binnmu { return; } - change_state( \$pkg, 'BD-Uninstallable' ); - $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet"; + if ($distribution eq "unstable") { + change_state( \$pkg, 'BD-Uninstallable' ); + $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet"; + } + else + { + change_state( \$pkg, 'Needs-Build' ); + } delete $pkg->{'builder'}; delete $pkg->{'depends'}; $pkg->{'binary_nmu_version'} = $binNMUver; @@ -1147,6 +1186,8 @@ sub parse_sources { my $name; my $full = shift; + my $db = get_all_source_info(); + local($/) = ""; # read in paragraph mode while( <> ) { my( $version, $arch, $section, $priority, $builddep, $buildconf, $binaries ); @@ -1167,7 +1208,7 @@ sub parse_sources { $pkgs{$name}{'bin'} = $binaries; $pkgs{$name}{'dep'} = $builddep; $pkgs{$name}{'conf'} = $buildconf; - my $pkg = get_source_info($name); + my $pkg = $db->{$name}; if (defined $pkg) { my $change = 0; @@ -1180,6 +1221,7 @@ sub parse_sources { "from database, because now Arch: all\n" if $verbose; del_source_info($name); + delete $db->{$name}; next; } @@ -1207,7 +1249,6 @@ sub parse_sources { } # remove installed packages that no longer have source available # or binaries installed - my $db = get_all_source_info(); foreach $name (keys %$db) { next if $name =~ /^_/; my $pkg = $db->{$name}; @@ -1219,6 +1260,7 @@ sub parse_sources { "not in Sources anymore\n" if $verbose; del_source_info($name); + delete $db->{$name}; } else { next if !isin( $pkg->{'state'}, qw(Installed) ); if ($full && not defined $merge_srcvers{$name}) { @@ -1229,6 +1271,7 @@ sub parse_sources { "binaries don't exist anymore\n" if $verbose; del_source_info($name); + delete $db->{$name}; } elsif ($full && version_less( $merge_srcvers{$name}, $pkg->{'version'})) { print "$name ($pkg->{'version'}): ". "package is Installed but binaries are from ". @@ -1243,8 +1286,10 @@ sub parse_sources { # This function looks through a Packages file and sets the state of # packages to 'Installed' sub parse_packages { + my $depwait_only = shift; my $installed; + my $pkgs = get_all_source_info(); local($/) = ""; # read in paragraph mode while( <> ) { my( $name, $version, $depends, $source, $sourcev, $architecture, $provides, $binaryv, $binnmu ); @@ -1260,6 +1305,7 @@ sub parse_packages { next if (defined ($installed->{$name}) and $installed->{$name}{'version'} ne "" and version_lesseq( $version, $installed->{$name}{'version'} )); $installed->{$name}{'version'} = $version; + next if $depwait_only; $installed->{$name}{'depends'} = $depends; $installed->{$name}{'all'} = 1 if $architecture eq "all"; undef $installed->{$name}{'Provider'}; @@ -1286,7 +1332,7 @@ sub parse_packages { next if defined($merge_srcvers{$name}) and $merge_srcvers{$name} eq $version; $merge_srcvers{$name} = $version; - my $pkg = get_source_info($name); + my $pkg = $pkgs->{$name}; if (defined $pkg) { if (isin( $pkg->{'state'}, qw(Not-For-Us)) || @@ -1435,6 +1481,8 @@ sub parse_quinn_diff { my %quinn_pkgs; my $dubious = ""; + my $pkgs = get_all_source_info(); + while( <> ) { my $change = 0; next if !m,^([-\w\d/]*)/ # section @@ -1449,7 +1497,7 @@ sub parse_quinn_diff { $priority = "unknown" if $priority eq "-"; $priority = "standard" if ($name eq "debian-installer"); - my $pkg = get_source_info($name); + my $pkg = $pkgs->{$name}; # Always update section and priority. if (defined($pkg)) { @@ -1760,19 +1808,10 @@ sub list_packages { my %scnt; my $ctime = time; - my $db = get_all_source_info(); + my $db = get_all_source_info(state => $state, user => $user, category => $category, list_min_age => $list_min_age); foreach $name (keys %$db) { next if $name =~ /^_/; - $pkg = $db->{$name}; - next if $state ne "all" && $pkg->{'state'} !~ /^\Q$state\E$/i; - next if $user && (lc($state) ne 'needs-build' and $pkg->{'builder'} ne $user); - next if $category && $pkg->{'state'} eq "Failed" && - $pkg->{'failed_category'} ne $category; - next if ($list_min_age > 0 && - ($ctime-parse_date($pkg->{'state_change'})) < $list_min_age)|| - ($list_min_age < 0 && - ($ctime-parse_date($pkg->{'state_change'})) > -$list_min_age); - push( @list, $pkg ); + push @list, $db->{$name}; } foreach $pkg (sort sort_list_func @list) { @@ -1831,7 +1870,7 @@ sub info_packages { foreach $dist (@dists) { my $pname = "$name" . ($info_all_dists ? "($dist)" : ""); - $pkg = get_source_info($name); + $pkg = get_readonly_source_info($name); if (!defined( $pkg )) { print "$pname: not registered\n"; next; @@ -1989,6 +2028,32 @@ sub check_entry { ) ); } +sub export_db { + my $file = shift; + my($name,$pkg,$key); + + print "Writing ASCII database to $file..." if $verbose >= 1; + open( F, ">$file" ) or + die "Can't open export $file: $!\n"; + + my $db = get_all_source_info(); + foreach $name (keys %$db) { + next if $name =~ /^_/; + my $pkg = $db->{$name}; + foreach $key (keys %{$pkg}) { + my $val = $pkg->{$key}; + next if !defined($val); + $val =~ s/\n*$//; + $val =~ s/^/ /mg; + $val =~ s/^ +$/ ./mg; + print F "$key: $val\n"; + } + print F "\n"; + } + close( F ); + print "done\n" if $verbose >= 1; +} + sub change_state { my $pkgr = shift; my $pkg = $$pkgr; @@ -2025,13 +2090,13 @@ sub log_ta { "changed from $prevstate to $pkg->{'state'} ". "by $real_user as $user"; - my $transactlog = db_transactlog(); - if (!open( LOG, ">>$transactlog" )) { - warn "Can't open log file $transactlog: $!\n"; - return; - } - print LOG "$curr_date: $str\n"; - close( LOG ); + $dbh->do('INSERT INTO ' . transactions_table_name() . + ' (package, distribution, version, action, ' . + ' prevstate, state, real_user, set_user, time) ' . + ' values (?, ?, ?, ?, ?, ?, ?, ?, ?)', + undef, $pkg->{'package'}, $distribution, + $pkg->{'version'}, $action, $prevstate, $pkg->{'state'}, + $real_user, $user, 'now()') or die $dbh->errstr; if (!($prevstate eq 'Failed' && $pkg->{'state'} eq 'Failed')) { $str .= " (with --override)" @@ -2063,10 +2128,6 @@ sub send_mail { close( PIPE ); } -sub db_transactlog { - return "$conf::basedir/$arch/$conf::transactlog"; -} - # for parsing input to dep-wait sub parse_deplist { my $deps = shift; @@ -2103,59 +2164,6 @@ sub parse_deplist { return \%result; } -# for parsing Build-Depends from Sources -sub parse_srcdeplist { - my $pkg = shift; - my $deps = shift; - my $arch = shift; - my $dep; - my @results; - - foreach $dep (split( /\s*,\s*/, $deps )) { - my @alts = split( /\s*\|\s*/, $dep ); - # Anything with an | is ignored, as it can be configured on a - # per-buildd basis what will be installed - next if $#alts != 0; - $_ = shift @alts; - if (!/^([^\s([]+)\s*(\(\s*([<=>]+)\s*(\S+)\s*\))?(\s*\[([^]]+)\])?/) { - warn( "parse_srcdeplist: bad dependency $_\n" ); - next; - } - my($dep, $rel, $relv, $archlist) = ($1, $3, $4, $6); - if ($archlist) { - $archlist =~ s/^\s*(.*)\s*$/$1/; - my @archs = split( /\s+/, $archlist ); - my ($use_it, $ignore_it, $include) = (0, 0, 0); - foreach (@archs) { - if (/^!/) { - $ignore_it = 1 if substr($_, 1) eq $arch; - } else { - $use_it = 1 if $_ eq $arch; - $include = 1; - } - } - warn "Warning: inconsistent arch restriction on ", - "$pkg: $dep depedency\n" - if $ignore_it && $use_it; - next if $ignore_it || ($include && !$use_it); - } - my $neg = 0; - if ($dep =~ /^!/) { - $dep =~ s/^!\s*//; - $neg = 1; - } - my $result; - $result->{'package'} = $dep; - $result->{'Neg'} = $neg; - if ($rel && $relv) { - $result->{'rel'} = $rel; - $result->{'version'} = $relv; - } - push @results, $result; - } - return \@results; -} - sub build_deplist { my $list = shift; my($key, $result); @@ -2169,83 +2177,6 @@ sub build_deplist { return $result; } -sub get_unsatisfied_dep { - my $bd = shift; - my $pkgs = shift; - my $dep = shift; - my $savedep = shift; - - my $pkgname = $dep->{'package'}; - - if (defined $pkgs->{$pkgname}{'Provider'}) { - # provides. leave them for buildd/sbuild. - return ""; - } - - # check cache - return $pkgs->{$pkgname}{'Unsatisfied'} if $savedep and defined($pkgs->{$pkgname}{'Unsatisfied'}); - - # Return unsatisfied deps to a higher caller to process - if ((!defined($pkgs->{$pkgname})) or - (defined($dep->{'rel'}) and !version_compare( $pkgs->{$pkgname}{'version'}, $dep->{'rel'}, $dep->{'Version'} ) ) ) { - my %deplist; - $deplist{$pkgname} = $dep; - my $deps = build_deplist(\%deplist); - $pkgs->{$pkgname}{'Unsatisfied'} = $deps if $savedep; - return $deps; - } - - # set cache to "" to avoid infinite recursion - $pkgs->{$pkgname}{'Unsatisfied'} = "" if $savedep; - - if (defined $pkgs->{$dep->{'package'}}{'depends'}) { - my $deps = parse_deplist( $pkgs->{$dep->{'package'}}{'depends'} ); - foreach (keys %$deps) { - $dep = $$deps{$_}; - # recur on dep. - my $ret = get_unsatisfied_dep($bd,$pkgs,$dep,1); - if ($ret ne "") { - my $retdep = parse_deplist( $ret ); - foreach (keys %$retdep) { - $dep = $$retdep{$_}; - - $dep->{'rel'} = '>=' if defined($dep->{'rel'}) and $dep->{'rel'} =~ '^='; - - if (defined($dep->{'rel'}) and $dep->{'rel'} =~ '^>' and defined ($pkgs->{$dep->{'package'}}) and - version_compare($bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'ver'},'>>',$pkgs->{$dep->{'package'}}{'Sourcev'})) { - if (not defined($merge_binsrc{$dep->{'package'}})) { - # the uninstallable package doesn't exist in the new source; look for something else that does. - delete $$retdep{$dep->{'package'}}; - foreach (sort (split( /\s*,\s*/, $bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'bin'}))) { - next if ($pkgs->{$_}{'all'} or not defined $pkgs->{$_}{'version'}); - $dep->{'package'} = $_; - $dep->{'rel'} = '>>'; - $dep->{'version'} = $pkgs->{$_}{'Version'}; - $$retdep{$_} = $dep; - last; - } - } - } else { - # sanity check to make sure the depending binary still exists, and the depended binary exists and dep-wait on a new version of it - if ( defined($merge_binsrc{$pkgname}) and defined($pkgs->{$dep->{'package'}}{'version'}) ) { - delete $$retdep{$dep->{'package'}}; - $dep->{'package'} = $pkgname; - $dep->{'rel'} = '>>'; - $dep->{'version'} = $pkgs->{$pkgname}{'Version'}; - $$retdep{$pkgname} = $dep; - } - delete $$retdep{$dep->{'package'}} if (defined ($dep->{'rel'}) and $dep->{'rel'} =~ '^>'); - } - } - $ret = build_deplist($retdep); - $pkgs->{$pkgname}{'Unsatisfied'} = $ret if $savedep; - return $ret; - } - } - } - return ""; -} - sub call_edos_depcheck { my $packagesfile = shift; my $srcs = shift; @@ -2278,7 +2209,7 @@ sub call_edos_depcheck { open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n"; for my $key (keys %interesting_packages) { - my $pkg = get_source_info($key); + 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'}; @@ -2314,7 +2245,7 @@ sub call_edos_depcheck { unlink( $tmpfile ); for my $key (keys %interesting_packages) { - my $pkg = get_source_info($key); + my $pkg = $db->{$key}; my $change = (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') || (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable'); @@ -2387,6 +2318,7 @@ Options: if -l is missing, set user name to be entered in db; usually automatically choosen --import FILE: Import database from a ASCII file FILE + --export FILE: Export database to a ASCII file FILE The remaining arguments (depending on operation) usually start with "name_version", the trailer is ignored. This allows to pass the names @@ -2417,19 +2349,62 @@ sub user_table_name { return '"' . $arch . $schema_suffix . '".users'; } +sub transactions_table_name { + return '"' . $arch . $schema_suffix . '".transactions'; +} + +sub get_readonly_source_info { + my $name = shift; + my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' . + table_name() . ' WHERE package = ? AND distribution = ?', + undef, $name, $distribution); + return $pkg; +} + sub get_source_info { my $name = shift; my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' . table_name() . ' WHERE package = ? AND distribution = ?' . - 'FOR UPDATE', + ' FOR UPDATE', undef, $name, $distribution); return $pkg; } sub get_all_source_info { - my $db = $dbh->selectall_hashref('SELECT * FROM ' . table_name() . - ' WHERE distribution = ?', - 'package', undef, $distribution); + my %options = @_; + + my $q = 'SELECT * FROM ' . table_name() + . ' WHERE distribution = ? '; + my @args = ($distribution); + if ($options{state} && uc($options{state}) ne "ALL") { + $q .= ' AND upper(state) = ? '; + push @args, uc($options{state}); + } + + if ($options{user}) { + #this basically means "this user, or no user at all": + $q .= ' AND (builder = ? OR upper(state) = ?)'; + push @args, $options{user}; + push @args, "NEEDS-BUILD"; + } + + if ($options{category}) { + $q .= ' AND failed_category <> ? AND upper(state) = ? '; + push @args, $options{category}; + push @args, "FAILED"; + } + + if ($options{list_min_age} > 0) { + $q .= ' AND age(state_change::timestamp) > ? '; + push @args, $options{list_min_age} . " days"; + } + + if ($options{list_min_age} < 0) { + $q .= ' AND age(state_change::timestamp) < ? '; + push @args, -$options{list_min_age} . " days"; + } + + my $db = $dbh->selectall_hashref($q, 'package', undef, @args); return $db; } @@ -2528,3 +2503,9 @@ sub add_user_info { or die $dbh->errstr; } +sub lock_table() +{ + $dbh->do('LOCK TABLE ' . table_name() . + ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr; +} +