package Mkcd::List; my $VERSION = '2.4.2'; use strict; use File::NCopy qw(copy); use File::Path; use URPM qw(ranges_overlap); use Mkcd::Package qw(rpmVersionCompare); use Mkcd::Tools qw(log_); =head1 NAME List - mkcd module =head1 SYNOPSYS require Mkcd::List; =head1 DESCRIPTION C include the mkcd packages list functions. =head1 SEE ALSO mkcd =head1 COPYRIGHT Copyright (C) 2000 MandrakeSoft 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; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =cut my $config; sub new { my ($class, $conf) = @_; $config = $conf; bless { config => $config, }, $class; } sub processDiff { my ($class, $groups, $diff, $discsFiles) = @_; my (@cd, @action); my %new; my $prev = $diff->{previous_idx} || {}; foreach (@{$diff->{idx}}) { push @{$action[1]}, $_ if !$prev->{$_}; $new{$_} = 1 } foreach (keys %{$prev}) { push @{$action[2]}, $_ if !$new{$_} } foreach my $op (2,1) { foreach my $idx (@{$action[$op]}) { my $d = $diff->{data}[$idx]; log_("ERROR processDiff: THIS MUST NOT HAPPEN action is null ($d) op $op idx $idx\n", $config->{verbose}, $config->{LOG}) and next if !$d; my ($curdir, $grp, $list,undef,undef, $data) = @$d; my $cd = $curdir->[0]; foreach my $ent (@$data) { my $rpm = $ent->[0]; log_("LOG disc $cd group $grp: ($op) $rpm ($groups->[$grp]{size}{$rpm}{$list}[1])\n", $config->{verbose}, $config->{LOG},3); if (!$rpm) { foreach (@$ent) { if (ref $_) { log_("ERROR processDiff: @$_\n", $config->{verbose}, $config->{LOG},2) } else { log_("ERROR processDiff: $_\n", $config->{verbose}, $config->{LOG},2) } } } $rpm or next; my $source = $groups->[$grp]{size}{$rpm}{$list}[1]; push @{$cd[$cd]{$curdir->[1]}{$list}{$source}}, [$op, "$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}.rpm"]; if ($op == 1) { $discsFiles->[$cd]{$curdir->[1]}{$list}{$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}} = $source } elsif ($op == 2) { delete $discsFiles->[$cd]{$curdir->[1]}{$list}{$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}} } } } } my %new_diff; # clear diff foreach my $idx (@{$diff->{idx}}) { my $nidx = push @{$new_diff{data}}, $diff->{data}[$idx]; push @{$new_diff{idx}}, $nidx - 1; $new_diff{previous_idx}{$nidx - 1} = 1 } return (\@cd, \%new_diff) } sub getDoneList { my ($config, $group, $listnumber, $discsFiles) = @_; my $topdir = "$config->{topdir}/build/$config->{name}"; foreach my $type ('rpm', 'srpm') { foreach my $curdir (@{$group->{list}{$listnumber}{$type}}) { my ($cd, $rep) = @$curdir; my $path = $config->{disc}[$cd]{function}{data}{dir}{$rep}; my $dir = "$topdir/$cd/$path"; log_("getDoneList: $listnumber disc $cd rep $rep\n", $config->{verbose}, $config->{LOG},2); if ($config->{nolive}) { if ($config->{list}[$listnumber]{packages}) { foreach (@{$config->{list}[$listnumber]{packages}}) { log_("getDoneList: @$_\n",1, $config->{LOG},3); my ($dir, @srpms) = @$_; local *RPMS; opendir RPMS, $dir or log_("WARNING getGroupReps: cannot open $dir\n",1, $config->{LOG},0) and return; foreach (readdir RPMS) { /(.*)\.rpm$/ or next; /src\.rpm$/ and next; my $rpm = $group->{urpm}{rpmkey}{key}{$1}; $group->{done}{$rpm} = $group->{orderedrep}{rpm}{"$cd/$rep"}; $discsFiles->[$cd]{$rep}{$listnumber}{$1} = $dir } } } else { log_("ERROR getDoneList: could not find data for $listnumber disc $cd rep $rep\n", $config->{verbose}, $config->{LOG},0); } } elsif (-d $dir) { local *RPMS; opendir RPMS, $dir or log_("WARNING getGroupReps: cannot open $dir\n",1, $config->{LOG}) and return; foreach (readdir RPMS) { /(.*)\.rpm$/ or next; /src\.rpm$/ and next; my $rpm = $group->{urpm}{rpmkey}{key}{$1}; $group->{done}{$rpm} = $group->{orderedrep}{rpm}{"$cd/$rep"}; $discsFiles->[$cd]{$rep}{$listnumber}{$1} = $dir } } else { log_("ERROR getDoneList: could not find data for $listnumber disc $cd rep $rep\n", $config->{verbose}, $config->{LOG},0); next } $config->{list}[$listnumber]{disc}{$cd}{$rep}{done} = 1 } } foreach my $d (@{$config->{list}[$listnumber]{virtual}}) { my $cd = $d->{disc}; my $path = $d->{path}; my $rep = $d->{repname}; my $dir = "$topdir/$cd/$path"; log_("getDoneList: virtual disc $cd path $path in $dir\n",1, $config->{LOG},2); foreach my $list_rep (@{$group->{rep}{$listnumber}}) { $list_rep->{rpm}{$dir} or next; foreach (@{$list_rep->{rpm}{$dir}}) { my $rpm = $group->{urpm}{rpmkey}{key}{$_}; $group->{done}{$rpm} = $group->{orderedrep}{rpm}{"$cd/$rep"}; $discsFiles->[$cd]{$rep}{$listnumber}{$_} = $dir } } $config->{list}[$listnumber]{disc}{$cd}{$rep}{done} = 1; } } sub getList { my ($class, $group, $discsFiles) = @_; my $config = $class->{config}; my %filelist; my @norpmsrate; log_("getList: group list " . join(' ', (keys %{$group->{list}})) . "\n", $config->{verbose}, $config->{LOG},1); foreach my $listnumber (keys %{$group->{list}}) { my $done = $config->{list}[$listnumber]{done}; $done and getDoneList($config, $group, $listnumber, $discsFiles); log_("getList: FILE LIST listnumber $listnumber ($config->{list}[$listnumber]{filelist}) or ($config->{list}[$listnumber]{prelist})\n", $config->{verbose}, $config->{LOG},2); if ($config->{list}[$listnumber]{filelist} || $config->{list}[$listnumber]{prelist}) { foreach (@{$config->{list}[$listnumber]{filelist}}) { log_("getList: FILE LIST listnumber $listnumber ($_)\n", $config->{verbose}, $config->{LOG},2); open my $A, $_ or log_("ERROR: cannot open $_, ignoring\n",1, $config->{LOG}) and next; local $_; while (<$A>) { s/#.*//; next if !$_ || /^\s*$/; my ($name, $options) = /\s*(\S+)\s*(.*)/; my @options = split ',', $options; log_("FILESLIST: $_ -> $name options @options\n", $config->{verbose}, $config->{LOG},3); my %opt; foreach (@options) { s/^\s*//; /norpmsrate/ and push @norpmsrate, $name and next; /^(?:(?:nosrc|section|noalternatives|regexp|ignore|nodeps|force|limit|exclude)|(rate|notondisc|rpmsrate|needed|section) (\d+))$/ or log_("WARNING: getList: $_: unknown option\n",1, $config->{LOG}) and next; $_ = $1 || $_; $opt{$_} = $2 || 1; } log_("Adding $name -- " . join(' ', keys %opt) . "\n", $config->{verbose}, $config->{LOG},4); push @{$filelist{$listnumber}}, [ $name, \%opt ]; } close $A } foreach my $p (@{$config->{list}[$listnumber]{prelist}}) { log_("Prelist Adding $p->[0] -- " . join(' ', keys %{$p->[1]}) . "\n", $config->{verbose}, $config->{LOG},3); $p->[1]{norpmsrate} and push @norpmsrate, $_->[0] and next; push @{$filelist{$listnumber}}, $p } } else { if (!$done && $config->{list}[$listnumber]{auto}) { push @{$filelist{$listnumber}}, [ "INSTALL", { section =>1, force => 1 }]; push @{$filelist{$listnumber}}, [ "SYSTEM", { section =>1, force => 1 }]; push @{$filelist{$listnumber}}, [ "kernel-(smp-|)[0-9].*", { regexp => 1, force => 1 }]; push @{$filelist{$listnumber}}, [ "kernel.*linus", { regexp => 1, noalternatives => 1 }]; push @{$filelist{$listnumber}}, [ ".*", { regexp => 1 }] } else { log_("getList: FILE LIST listnumber $listnumber defaulting to .* regexp\n", $config->{verbose}, $config->{LOG},2); push @{$filelist{$listnumber}}, [ ".*" , { regexp => 1 } ] } } my $listdone = 1; foreach my $r (@{$group->{list}{$listnumber}{rpm}}) { my ($cd, $rep, $repopt, $opt) = @$r; log_("getList: searching for done rep ($cd/$rep)\n", $config->{verbose}, $config->{LOG},2); if ($config->{list}[$listnumber]{disc}{$cd}{$rep}{done}) { if (!$opt->{dup}) { foreach my $rpmkey (keys %{$discsFiles->[$cd]{$rep}{$listnumber}}) { my $rpm = $group->{urpm}{rpmkey}{key}{$rpmkey}; $group->{done}{$rpm} = $group->{orderedrep}{rpm}{"$cd/$rep"}; log_("getList: $rpm in $cd/$rep -> $group->{done}{$rpm}\n", $config->{verbose}, $config->{LOG},3); push @{$filelist{$listnumber}}, [$rpm, { done => $group->{done}{$rpm}, regexp => 1, udpate => $r->[2]{update} }]; } } } else { $listdone = 0 } } $listdone and log_("getList: setting list $listnumber as done\n", $config->{verbose}, $config->{LOG},2) and $config->{list}[$listnumber]{done} = 1; } (\%filelist, \@norpmsrate) } # # compute individual scoring (max_size*(rpmsrate+1)*rpmsrate_factor/(size*size_factor)) # then add dependencies sons score ( score + deps_factor*(sons_score) # # special rpmsrate groups score could be added in the rpmsrate value # # FIXME current scoring rules make size only significant for equaly dependent packages, # dependencies get far more importance for packages a lot of packages depend on. # # Size scoring could be added afterwards, but this will break the autodeps created with # this scheme. # # TODO # add scoring rules to include srpm size in score. # # sub scoreList { my ($class, $group) = @_; my $scoreweight = $group->{score}; my $urpm = $group->{urpm}; my $rpmsrate = $group->{rpmsrate}; my $maxsize = $group->{maxsize} || 1; $scoreweight ||= [1,1,1]; log_("scoreList: SCORE for group: @$scoreweight\n", $config->{verbose}, $config->{LOG},2); log_("scoreList: Individual scoring\n", $config->{verbose}, $config->{LOG},2); my ($sf, $i, $total); my (@min, @max); if ($scoreweight->[1]) { (@min,@max) = (($maxsize*$scoreweight->[0]*6/($scoreweight->[1]*1),0), (0,0)) } else { (@min,@max) = (($maxsize*$scoreweight->[0]*6,0), (0,0)) } my @specialdeps; foreach (keys %{$urpm->{rpm}}) { # print "INFO KEYS $_\n"; my ($ratekey) = /(.*)-[^-]+-[^-]+\.[^.]+$/; # FIXME take the bigger size when package appears in multiple lists my $size; foreach my $list (keys %{$group->{size}{$_}}) { $size = $group->{size}{$_}{$list}[0] if $size < $group->{size}{$_}{$list}[0] } $size or log_("WARNING scoreList: $_ has zero size\n",1, $config->{LOG}); my $s; my $rate = $group->{brokendeps}{$_} ? 0 : (defined $group->{pkgrate}{$_} ? $group->{pkgrate}{$_} : $rpmsrate->[0]{$ratekey}); if ($scoreweight->[1]) { $sf = ($size*9)/$maxsize + 1; # from 1 to 10 $s = $scoreweight->[0]*($rate + 1)/($scoreweight->[1]*$sf); } else { $s = $scoreweight->[0]*($rate + 1); } $group->{scorelist}{$_} = $s; $s < $min[0] and @min = ($s, $_); $s > $max[0] and @max = ($s, $_); $total+=$s; $i++ } $i and log_("scoreList: minimal $min[0] ($min[1]), maximal $max[0] ($max[1]), average " . $total/$i . "\n", $config->{verbose}, $config->{LOG},3); 1 } sub autodeps { my ($class, $group, $rpmlist) = @_; my $scoredeps = $group->{score}[2]; $scoredeps or log_("autodeps: deps score is null, bypassing autodeps\n",1, $config->{LOG},1) and return 1; log_("autodeps: compute reversed depslist.ordered ($scoredeps)\n", $config->{verbose}, $config->{LOG},2); my $revDeps = $group->{revdeps}; my %rpm; foreach my $k (values %{$rpmlist}) { foreach (keys %{$k}) { $rpm{$_} = $k->{$_} } } # FIXME this algo is not correct ref $group->{urpm}{depslist} or return 0; for (my $i = @{$group->{urpm}{depslist}} - 1; $i >= 0; $i--) { my $rpm = $group->{depslistid}[$i]; if (!$rpm{$rpm}) { #log_("autodeps: ignoring $rpm\n",1, $config->{LOG}); #push @{$group->{rejected}{$rpm}}, [ "autodeps", $1 ]; next } if ($rpm{$rpm}{ignore}) { log_("autodeps: $rpm has ignore flag, do not add deps score\n", $config->{verbose}, $config->{LOG},3); next } foreach (@{$revDeps->[$i]}) { $group->{scorelist}{$rpm} += $scoredeps * $group->{scorelist}{$group->{depslistid}[$_]}; } } 1 } sub reverseDepslist { my ($class, $group) = @_; my $urpm = $group->{urpm}; my $depslist = $urpm->{depslist}; $depslist or return (); my $locales = $group->{lang}; my (@revdeps, %skip); log_("reverseDepslist\n", $config->{verbose}, $config->{LOG},2); for (my $i; $i < @$depslist; $i++) { my $d = $depslist->[$i]; my $rpm = sprintf "%s-%s-%s.%s", $d->name, $d->version, $d->release, $d->arch; $group->{depslistid}[$i] = $rpm; my %rev; # FIXME deps is deprecated, the correct function to use it packages->requires foreach (split(' ', $urpm->{deps}[$i])) { if (!$group->{options}{nodeps} && !$class->{config}{nodeps} && m/NOTFOUND_(\S*)/) { $skip{$i} = 1; $group->{brokendeps}{$rpm} = 2; push @{$group->{rejected}{$rpm}}, [ "deps", $1 ]; log_("WARNING reverseDepslist: $rpm has unresolved dependencies ($1)\n", $config->{verbose}, $config->{LOG}, 1); next } if (/\|/) { my $s = [split '\|', $_]; my (@tmp_s, $msg_tot); foreach (@$s) { my ($ok, $msg) = check_version($d, $depslist->[$_], $rpm); if ($ok) { push @tmp_s, $_ } else { $msg_tot .= $msg } $skip{$_} or push @{$revdeps[$_]}, $i } if (@tmp_s) { push @{$group->{pkgdeps}{$rpm}}, \@tmp_s } else { $skip{$i} = 1; $group->{brokendeps}{$rpm} = 2; log_("WARNING reverseDepslist: rejecting $rpm on $_\n", $config->{verbose}, $config->{LOG}, 4); push @{$group->{rejected}{$rpm}}, [ "deps", $msg_tot ] } } else { if ($locales && $group->{depslistid}[$_] =~ /locales-([^-]+)-[^-]+-[^-]+\.[^.]+/) { if (!$locales->{$1}) { log_("reverseDepslist: locale $1 ($group->{depslistid}[$_]) skipped for $rpm\n", $config->{verbose}, $config->{LOG}, 2) and $skip{$i} = 1; !$group->{brokendeps}{$rpm} and $group->{brokendeps}{$rpm} = 1 } } my ($ok, $msg) = check_version($d, $depslist->[$_], $rpm); if ($ok) { push @{$group->{pkgdeps}{$rpm}}, $_ } else { $skip{$i} = 1; $group->{brokendeps}{$rpm} = 2; log_("WARNING reverseDepslist: rejecting $rpm on $_\n", $config->{verbose}, $config->{LOG}, 4); push @{$group->{rejected}{$rpm}}, [ "deps", $msg ] } $skip{$_} or push @{$revdeps[$_]}, $i } } } return \@revdeps } sub check_version { my ($d, $deps_d, $rpm) = @_; my $ok = 1; my $msg; my $deps_rpm = sprintf "%s-%s-%s.%s", $deps_d->name, $deps_d->version, $deps_d->release, $deps_d->arch; $rpm ||= sprintf "%s-%s-%s.%s", $d->name, $d->version, $d->release, $d->arch; foreach my $req ($d->requires) { my ($n, $s) = $req =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/; # perl needs major checking $n =~ /perl/ and next; # Some deps are broken and as a consequence using the major rejects some packages that shouldn't #$s and ($s =~ /:/ or $s =~ s/([>=<]+) /$1 0:/); $s =~ /:/ and $s =~ s/ \d+:/ /; if ($n && $s) { # nok is usefull in case the package provides several times the require, each time with a different version my ($tok,$m); foreach my $prov ($deps_d->provides) { my ($deps_n, $deps_s) = $prov =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/; #$deps_s and ($deps_s =~ /:/ or $deps_s =~ s/([>=<]+) /$1 0:/); $deps_s =~ /:/ and $deps_s =~ s/ \d+:/ /; if ($deps_n && $deps_s) { if ($deps_n eq $n){ $m = 1; if (URPM::ranges_overlap($deps_s, $s)) { $tok = 1; } else { my $t = "$deps_rpm provides $deps_n $deps_s but $rpm needs $n $s"; $msg .= " $t"; log_("ERROR check_version: $t\n", $config->{verbose}, $config->{LOG}, 0) } } } } $ok = $tok if $m } } ($ok, $msg) } sub closeRpmsList { my ($group, $rpmfile) = @_; my $n = 1; my %done; my %doneName; my %alternatives; while ($n) { $n = 0; foreach my $listnumber (@{$group->{orderedlist}{rpm}}) { foreach my $rpm (keys %{$rpmfile->{$listnumber}}) { # FIXME if the different packages have different deps, old packages may be better. # this should be done in buildDisc, or better packages have to be selected here. if (!$group->{options}{dup}) { my ($name, $version, $release, $arch) = $rpm =~ /^(.*)-([^-]+)-([^-]+)\.([^.]+)$/; if ($doneName{$name}) { if (!($doneName{$name}[0] eq "$version-$release.$arch")) { log_("WARNING closeRpmsList: $name-$version-$release.$arch duplicated with $doneName{$name}[0]\n", $config->{verbose}, $config->{LOG}, 1); my ($v, $r, $a) = @{$doneName{$name}[1]}; my $todel; my $vers; my $ret = rpmVersionCompare($rpm, "$name-$v-$r.$a"); if ($ret < 0) { $todel = $rpm; $vers = [$v, $r, $a] } elsif ($ret > 0) { $todel = "$name-$v-$r.$a"; $vers = [$version, $release, $arch] } else { log_("ERROR closeRpmsList: oops, something not possible happened in duplicate version comparaison ($rpm and $name-$v-$r.$a)\n",1, $config->{LOG}); } if ($todel) { log_("closeRpmsList: deleting $todel\n", $config->{verbose}, $config->{LOG},2); $doneName{$name} = [ "$vers->[0]-$vers->[1].$vers->[2]", $vers ]; #$group->{brokendeps}{$todel} = 3; #push @{$group->{rejected}{$todel}}, [ "old_version", "$name-$vers->[0]-$vers->[1].$vers->[2]"]; delete $rpmfile->{$listnumber}{$todel}; $todel eq $rpm and next } $n = 1 } } else { $doneName{$name} = [ "$version-$release.$arch", [$version, $release, $arch] ] } } if ($group->{brokendeps}{$rpm} == 2 || $group->{brokendeps}{$rpm} == 3) { log_("closeRpmsList: deleting $rpm (list $listnumber)\n", $config->{verbose}, $config->{LOG},2); delete $rpmfile->{$listnumber}{$rpm}; $n = 1; next } $done{$rpm} and next; $rpmfile->{$listnumber}{$rpm}{nodeps} and next; my $needed; # FIXME the right thing to do would be to put the require in the rep just before the one of the $rpm, and not to force it. $needed = 1 if $rpmfile->{$listnumber}{$rpm}{force}; $needed ||= $rpmfile->{$listnumber}{$rpm}{done}; $needed ||= $rpmfile->{$listnumber}{$rpm}{needed}; # $needed and log_("closeRpmsList: $rpm needed set to $needed (force $rpmfile->{$listnumber}{$rpm}{force} done $rpmfile->{$listnumber}{$rpm}{done} needed $rpmfile->{$listnumber}{$rpm}{needed})\n", $config->{verbose}, $config->{LOG}); foreach (@{$group->{pkgdeps}{$rpm}}) { m/NOTFOUND_(.*)/ and log_("ERROR closeRpmsList: $1 not provided\n", $config->{verbose}, $config->{LOG},1) and next; my $rpmdep; my $rpmdeplist; my $specialrpmdep; if (ref $_) { if ($alternatives{"@$_"}) { ($rpmdep, $rpmdeplist) = @{$alternatives{"@$_"}}; log_("closeRpmsList: $rpm taking alternatives from already selected package ($rpmdep $rpmdeplist)\n", $config->{verbose}, $config->{LOG}, 4) } if (! ref $rpmfile->{$rpmdeplist}{$rpmdep}) { foreach my $testalt (1,0) { ($rpmdep, $rpmdeplist) = (undef,undef); # FIXME this is wrong, package can come from any list my @score = ($group->{maxlist}{rpm}, int @{$group->{list}{$listnumber}{rpm}}, $group->{maxsize}); my @specialscore = (int @{$group->{list}{$listnumber}{rpm}}, $group->{maxsize}); log_("closeRpmsList: $rpm @$_ (maxscore @score) alternative\n", $config->{verbose}, $config->{LOG}, 4); foreach (@$_) { my $pkg = $group->{depslistid}[$_]; log_("closeRpmsList: trying $pkg (brokendeps $group->{brokendeps}{$pkg})\n", $config->{verbose}, $config->{LOG}, 4); $group->{brokendeps}{$pkg} == 2 and next; $group->{brokendeps}{$pkg} == 3 and next; my $pkglist = find_list($group, $pkg, $listnumber); $pkglist or log_("closeRpmsList: $pkg list could not be used for $rpm dependencies\n", $config->{verbose}, $config->{LOG}, 4) and next; log_("closeRpmsList: list $pkglist\n", $config->{verbose}, $config->{LOG}, 4); if ($rpmfile->{$pkglist}{$pkg}) { $rpmfile->{$pkglist}{$pkg}{limit} and next; $testalt and $rpmfile->{$pkglist}{$pkg}{noalternatives} and next; } my $rep = $group->{size}{$pkg}{$pkglist}[2]; my $s = $group->{size}{$pkg}{$pkglist}[0]; my $l = $group->{listsort}{rpm}{$pkglist}; log_("\t$pkg ($l, $rep, $s) (@score)\n", $config->{verbose}, $config->{LOG}, 5); # also put an alternative from this list if ($pkglist == $listnumber) { if ($rep < $specialscore[1]) { @specialscore = ($rep, $s); $specialrpmdep = $pkg; } elsif ($rep == $specialscore[1] && $s < $specialscore[2]) { @specialscore = ($rep, $s); $specialrpmdep = $pkg; } } if ($l < $score[0]) { @score = ($l, $rep, $s); $rpmdep = $pkg; $rpmdeplist = $pkglist; log_("1 $rpmdep -- $rpmdeplist -- $l, $rep, $s\n", $config->{verbose}, $config->{LOG}, 6); } elsif ($l == $score[0]) { if ($pkglist == $listnumber) { if ($rep < $score[1]) { @score = ($l, $rep, $s); $rpmdep = $pkg; $rpmdeplist = $pkglist; log_("2 $rpmdep -- $rpmdeplist -- $l, $rep, $s\n", $config->{verbose}, $config->{LOG}, 6); } elsif ($rep == $score[1] && $s < $score[2]) { @score = ($l, $rep, $s); $rpmdep = $pkg; $rpmdeplist = $pkglist; log_("3 $rpmdep -- $rpmdeplist -- $l, $rep, $s\n", $config->{verbose}, $config->{LOG}, 6); } } elsif ($s < $score[2]) { @score = ($l, $rep, $s); $rpmdep = $pkg; $rpmdeplist = $pkglist; log_("4 $rpmdep -- $rpmdeplist -- $l, $rep, $s\n", $config->{verbose}, $config->{LOG}, 6); } } } last if $rpmdep && $rpmdeplist } if ($rpmdep && $rpmdeplist) { log_("\tResult:\t$rpmdep\n", $config->{verbose}, $config->{LOG}, 4); $alternatives{"@$_"} = [ $rpmdep, $rpmdeplist ] } else { log_("WARNING: $rpm has unresolved or excluded dependencies, removed\n", $config->{verbose}, $config->{LOG}, 1); log_("closeRpmsList: deleting $rpm (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 2); delete $rpmfile->{$listnumber}{$rpm}; $n = 1; $group->{brokendeps}{$rpm} = 2; push @{$group->{rejected}{$rpm}}, ["deps_rejected", (join ' ', map { $group->{depslistid}[$_] } @$_)]; } } } else { # TODO verify that there is no need to do $rpmfile->{$pkglist}{$rpmdep} or brokendeps; $rpmdep = $group->{depslistid}[$_]; $rpmdeplist = find_list($group, $rpmdep, $listnumber); } # log_("rpmdep $rpmdep rpmdeplist $rpmdeplist rpm $rpm\n",1, $config->{LOG}); if ($rpmdep) { if (!$rpmdeplist || $group->{brokendeps}{$rpmdep} == 2 || $group->{brokendeps}{$rpmdep} == 3) { $group->{brokendeps}{$rpm} = $group->{brokendeps}{$rpmdep}; push @{$group->{rejected}{$rpm}}, [ "deps_rejected", $rpmdep ]; $n = 1; log_("WARNING closeRpmsList: $rpm has unresolved or excluded dependencies ($rpmdep list $rpmdeplist), removed\n", $config->{verbose}, $config->{LOG}, 1); log_("closeRpmsList: deleting $rpm (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 2); delete $rpmfile->{$listnumber}{$rpm}; next } if (! ref $rpmfile->{$rpmdeplist}{$rpmdep}) { $n = 1; log_("closeRpmsList: ADDED $rpmdep (list $rpmdeplist) needed $needed\n", $config->{verbose}, $config->{LOG}, 3); $rpmfile->{$rpmdeplist}{$rpmdep} = { needed => $needed } } elsif ($needed) { my $n = $rpmfile->{$rpmdeplist}{$rpmdep}{needed}; $rpmfile->{$rpmdeplist}{$rpmdep}{needed} = !$n || $needed < $n ? $needed : $n; log_("closeRpmsList: setting $rpmdep needed option to $rpmfile->{$rpmdeplist}{$rpmdep}{needed}\n", $config->{verbose}, $config->{LOG}, 4); } } if ($specialrpmdep) { if (! ref $rpmfile->{$listnumber}{$specialrpmdep}) { $n = 1; log_("closeRpmsList: ADDED $specialrpmdep (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 3); $rpmfile->{$listnumber}{$specialrpmdep} = { } } } } $done{$rpm} = 1; } log_("closeRpmsList: $listnumber [$n]\n", $config->{verbose}, $config->{LOG}, 2); } } } sub addRPMToList { my ($group, $listnumber, $rpmfile, $done, $rpms, $fentry, $name) = @_; my $exclude = sub { my ($rpm, $name) = @_; log_("addRPMToList: excluding $_\n",1, $config->{LOG}, 1); $group->{brokendeps}{$rpm} = 3; push @{$group->{rejected}{$rpm}}, ["excluded", $name]; }; $name =~ s/\+/\\+/g; my @toadd; if ($fentry->{regexp}) { @toadd = grep { /$name/ } @$rpms } else { @toadd = grep { /^$name-[^-]+-[^-]+\.[^.]*$/ } @$rpms } log_("addRPMToList: toadd $name (regexp $fentry->{regexp}) (@toadd)\n", $config->{verbose}, $config->{LOG}, 3); if ($fentry->{done}) { foreach (@toadd) { my ($pkg) = /^(.*)-[^-]+-[^-]+\.[^.]*$/; my %ht; foreach my $k (keys %$fentry) { $ht{$k} = $fentry->{$k} } $rpmfile->{$listnumber}{$_} = \%ht; $done->{$pkg} = [ $_, $group->{size}{$_}{$listnumber}[2], \%ht, $listnumber ]; log_("addRPMToList: ADDED done $_ (list $listnumber) options " . (join '', keys %ht) . "\n", $config->{verbose}, $config->{LOG}, 4) } return } my %pkg; if ($fentry->{regexp}) { foreach (@toadd) { $rpmfile->{$listnumber}{$_} and log_("WARNING addRPMToList: do not replace $_ entry in rpmfile\n", $config->{verbose}, $config->{LOG}, 1) and next; $_ or log_("ERROR addRPMToList: empty rpm\n", $config->{verbose}, $config->{LOG}, 2) and next; $group->{size}{$_}{$listnumber} or next; $group->{brokendeps}{$_} == 2 and next; $group->{brokendeps}{$_} == 3 and next; my ($pkgname) = /^(.*)-[^-]+-[^-]+\.[^.]*$/; $done->{$_} and next; my $rep = $group->{size}{$_}{$listnumber}[2]; if ($fentry->{exclude}) { $exclude->($_, $name); next } if ($done->{$pkgname} && $done->{$pkgname}[3] == $listnumber) { if (!$fentry->{update} || !$done->{$pkgname}[2]{done}) { if ($rep < $done->{$pkgname}[1]) { $pkg{$done->{$pkgname}[0]} = 0; log_("REPLACING $done->{$pkgname}[0] with $_ (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4); $pkg{$_} = 1; $done->{$pkgname} = [ $_, $rep, $fentry, $listnumber ]; $done->{$_} = 1 } elsif ($done->{$pkgname}[1] == $rep) { if (rpmVersionCompare($done->{$pkgname}[0], $_) < 0) { $pkg{$done->{$pkgname}[0]} = 0; log_("REPLACING $done->{$pkgname}[0] with $_ (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4); $pkg{$_} = 1; $done->{$pkgname} = [ $_, $rep, $fentry, $listnumber ]; $done->{$_} = 1 } } } } else { $pkg{$_} = 1; $done->{$pkgname} = [ $_, $rep, $fentry, $listnumber ]; $done->{$_} = 1 } } } else { my $rep; my $pkg; # FIXME present algorythm selects only one package per version, and choose the one in the list declared first. # Maybe adding all the version and letting closeRRPMsList choose the right one is better. foreach (@toadd) { $_ or log_("ERROR addRPMToList: empty rpm\n",1, $config->{LOG}, 2) and next; $rpmfile->{$listnumber}{$_} and log_("WARNING addRPMToList: do not replace $_ entry in rpmfile\n", $config->{verbose}, $config->{LOG}, 1) and next; $group->{size}{$_}{$listnumber} or next; $group->{brokendeps}{$_} == 2 and next; $group->{brokendeps}{$_} == 3 and next; if ($fentry->{exclude}) { $exclude->($_, $name); next; } if ($group->{size}{$_}{$listnumber}[2] < $rep || !$rep) { $rep = $group->{size}{$_}{$listnumber}[2]; log_("addRPMToList: choosing $_ (rep $rep)\n", $config->{verbose}, $config->{LOG}, 2); $pkg = $_ } elsif ($group->{size}{$_}{$listnumber}[2] == $rep) { if (rpmVersionCompare($pkg, $_) < 0) { $rep = $group->{size}{$_}{$listnumber}[2]; log_("addRPMToList: choosing $_ (rep $rep)\n", $config->{verbose}, $config->{LOG}, 2); $pkg = $_ } } } my ($pkgname) = $pkg =~ /^(.*)-[^-]+-[^-]+\.[^.]*$/; if (!$done->{$pkgname}) { $pkg{$pkg} = 1; $done->{$pkgname} = [ $pkg, $rep, $fentry, $listnumber ]; $done->{$pkg} = 1 } } $fentry->{exclude} and return 1; foreach (keys %pkg) { $rpmfile->{$listnumber}{$_} and log_("WARNING addRPMToList: do not replace $_ entry in rpmfile\n", $config->{verbose}, $config->{LOG}, 1) and next; $pkg{$_} or next; defined $fentry->{rate} and $group->{pkgrate}{$_} = $fentry->{rate} and log_("addRPMToList: setting $_ rate to $fentry->{rate}\n",1, $config->{LOG}, 2); my %ht; foreach my $k (keys %$fentry) { $ht{$k} = $fentry->{$k} } $rpmfile->{$listnumber}{$_} = \%ht; log_("addRPMToList: ADDED $_ (list $listnumber) options " . join(" ", keys %ht) . "\n", $config->{verbose}, $config->{LOG}, 4) } } sub build_list { my ($class, $group) = @_; my %rpmfile; my $filelist = $group->{filelist}; my @fullrpm = (keys %{$group->{urpm}{rpm}}); my @section = (keys %{$group->{rpmsrate}[1]}); my %done; foreach my $listnumber (@{$group->{orderedlist}{rpm}}) { log_("build_list: list $listnumber ($group->{listrpm}{$listnumber})\n", $config->{verbose}, $config->{LOG}, 2); my $rpms = $group->{listrpm}{$listnumber}; if (ref $rpms) { log_("$listnumber -- $group->{filelist} -- " . keys(%{$group->{filelist}}) . "\n", $config->{verbose}, $config->{LOG}, 3); ref $filelist->{$listnumber} or log_("WARNING: list $listnumber has an empty file list\n", $config->{verbose}, $config->{LOG}, 1) and next; log_("build_list: FILE LIST $listnumber (" . int @{$filelist->{$listnumber}} . ")\n", $config->{verbose}, $config->{LOG}, 4); foreach my $fentry (@{$filelist->{$listnumber}}) { my $name = $fentry->[0]; my $opt = $fentry->[1]; log_("build_list: processing $name " . join(' ', keys %{$opt}) . "\n", $config->{verbose}, $config->{LOG}, 5); my @toadd; if ($opt->{section}) { my $level = $opt->{section}; log_("build_list: selecting rpmsrate package of section $name with score higher than $level\n", $config->{verbose}, $config->{LOG}, 2); $opt->{section} = 0; if ($opt->{regexp}) { $opt->{regexp} = 0; @toadd = grep { /$name/ } @section; log_("$name (@section) -> @toadd\n", $config->{verbose}, $config->{LOG}, 4); foreach (@toadd) { foreach (@{$group->{rpmsrate}[1]{$_}}) { log_("$_ -> $group->{rpmsrate}[0]{$_}\n", $config->{verbose}, $config->{LOG}, 4); if ($group->{rpmsrate}[0]{$_} >= $level) { addRPMToList($group, $listnumber, \%rpmfile, \%done, $rpms, $opt, $_); } } } } else { my $rpmlist = $group->{rpmsrate}[1]{$name} or log_("ERROR build_list: $name unknown rpmsrate section\n", $config->{verbose}, $config->{LOG}, 0) and next; foreach (@$rpmlist) { if ($group->{rpmsrate}[0]{$_} >= $level) { addRPMToList($group, $listnumber, \%rpmfile, \%done, $rpms, $opt, $_) } } } } else { addRPMToList($group, $listnumber, \%rpmfile, \%done, $rpms, $opt, $name); } } } else { log_("WARNING: List $listnumber is empty, ignoring\n", $config->{verbose}, $config->{LOG}, 0); $class->{config}{list}[$listnumber]{empty} = 1; } } if (!$class->{config}{nodeps} && !$group->{options}{nodeps}) { my @toadd = grep { /^basesystem-[^-]+-[^-]+\.[^.]*$/ } @fullrpm; my $pkg; my $listnumber; foreach (@toadd) { my $l = find_list($group, $_); if ($group->{listmatrix}{rpm}{$listnumber}{$l} || !$listnumber) { $pkg = $_; $listnumber = $l } } if ($pkg) { $rpmfile{$listnumber}{$pkg} = {}; log_("build_list ADDED $pkg (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4); } else { log_("ERROR: basesystem package is not available.\n",1, $config->{LOG}) } # add deps closeRpmsList($group, \%rpmfile) } \%rpmfile } sub optimize_space { my ($groups, $diff, $size, $cdsize, $cdnum, $gain, $cdlists, $special, $grp, $list, $type, $all_rpmsize) = @_; $config->{optimize_space} or return 0; log_("optimize_space: cdnum $cdnum gain $gain cdlists $cdlists grp $grp list $list\n", $config->{verbose}, $config->{LOG}, 4); my @cd_to_test; if (!$special) { if (defined $size->{optimize_space}{disc}{$cdnum} && $gain > $size->{optimize_space}{disc}{$cdnum}) { log_("WARNING optimize_space: last time only get $size->{optimize_space}{disc}{$cdnum} on disc $cdnum, does not try again\n", $config->{verbose}, $config->{LOG}, 4); return 0 } my $maxSpace; for (my $i; $i < @$cdsize; $i++) { $cdlists->{$i} or next; $groups->[$grp]{disc_impacted}{$i} or next; my $space = $cdsize->[$i] - $size->{disc}[$i]; push @cd_to_test, [ $i, $space ]; $maxSpace += $space } $maxSpace -= $all_rpmsize; if ($maxSpace < $gain) { log_("WARNING optimize_space: could not get $gain on disc $cdnum (only $maxSpace available)\n", $config->{verbose}, $config->{LOG}, 4); return 0 } else { log_("optimize_space: $maxSpace available, try to move packages to get $gain free space on disc $cdnum\n", $config->{verbose}, $config->{LOG}, 4) } } my $realgain; my @cd_sorted = sort { $b->[1] <=> $a->[1] } @cd_to_test; my $check_diff_deps = sub { my ($rpms, $deps_rpms, $rep_num, $g) = @_; my @rpmsd; log_("optimize_space check_diff_deps\n",$config->{verbose}, $config->{LOG}, 4); foreach (@$deps_rpms) { push @rpmsd, @{$_->[6]} if $_->[6] } my $deps = get_pkgs_deps(\@rpmsd, $groups->[$g]); my %deps; foreach my $d_ids (@$deps) { if (ref $d_ids) { foreach (@$d_ids) { $deps{alt}{$_} = $d_ids } } else { $deps{std}{$_} = 1 } } foreach my $d (@$rpms) { # log_("optimize_space check_diff_deps: data $d\n", $config->{verbose}, $config->{LOG}); foreach my $data (@{$d->[5]}) { my $rpm = $data->[0]; # log_("optimize_space check_diff_deps: $rpm ($groups->[$g]{urpm}{rpm}{$rpm})\n", $config->{verbose}, $config->{LOG}); my $id = $groups->[$g]{urpm}{rpm}{$rpm}->id; if ($deps{std}{$id}) { log_("optimize_space check_diff_deps: $rpm depends on these packages\n", $config->{verbose}, $config->{LOG}, 5); return 0 } elsif ($deps{alt}{$id}) { log_("optimize_space check_diff_deps: $rpm depends on these packages as alternatives, trying to find another alternative\n", $config->{verbose}, $config->{LOG}, 5); my $n_pres = 1; foreach (@{$deps{alt}{$id}}) { my $alt_rpm = $groups->[$g]{depslistid}[$_]; if ($groups->[$g]{done}{$alt_rpm} <= $rep_num) { $n_pres = 0 } } return 0 if $n_pres } } } 1 }; my $check_previous_deps = sub { my ($rpms1, $rpms2, $g, $src_rep, $dest_rep) = @_; log_("optimize_space check_previous_deps: rpms1 $rpms1 rpms2 $rpms2 g $g src_rep $src_rep dest_rep $dest_rep\n", $config->{verbose}, $config->{LOG}, 6); my ($src, $dest); if ($src_rep > $dest_rep) { $src = $src_rep; $dest = $dest_rep; } elsif ($src_rep < $dest_rep) { $dest = $src_rep; $src = $dest_rep; } else { return 1; } if ($src > $dest + 1) { foreach my $rep ($dest+1 .. $src-1) { foreach (@{$diff->{idx}}) { my $d = $diff->{data}[$_]; push @$rpms1, $d if $d->[3] == $rep } } } return $check_diff_deps->($rpms2, $rpms1, $dest, $g); }; my $exchange = sub { my ($elt, $src_cd, $src_rep, $cd, $rep, $dest_curdir, $idx, $tot_size, $group, $l) = @_; my $rpms; foreach (@{$elt->[5]}) { $groups->[$group]{done}{$_->[0]} = $rep; $rpms .= " $_->[0]" } log_("optimize_space exchange: moving $rpms (grp $group list $l) from disc $src_cd rep $src_rep to disc $cd rep $rep\n", $config->{verbose}, $config->{LOG}, 5); # TODO data is not duplicated, must check to see if this is a pb my @new_elt = @$elt; $new_elt[0] = $dest_curdir; $new_elt[3] = $rep; my $b_idx = push @{$diff->{data}}, \@new_elt; $diff->{idx}[$idx] = $b_idx-1; $size->{disc}[$src_cd] -= $tot_size; my $src_repname = $groups->[$group]{replist}{srpm}[$src_rep-1][1]; $size->{rep}{$src_cd}{$src_repname}{$l} -= $tot_size; $size->{disc}[$cd] += $tot_size; my $dest_repname = $groups->[$group]{replist}{srpm}[$rep-1][1]; $size->{rep}{$cd}{$dest_repname}{$l} += $tot_size; log_("optimize_space exchange: moving $rpms (grp $group list $l) from disc $src_cd rep $src_rep (size $size->{disc}[$src_cd]) to disc $cd rep $rep (size $size->{disc}[$cd])\n", $config->{verbose}, $config->{LOG}, 5); if ($src_cd == $cdnum) { $realgain += $tot_size; log_("optimize_space exchange: current gain $realgain\n", $config->{verbose}, $config->{LOG}, 5); if ($realgain > $gain) { goto optimize_space_ok } } elsif ($cd == $cdnum) { $realgain -= $tot_size; log_("optimize_space exchange: current gain $realgain\n", $config->{verbose}, $config->{LOG}, 5); } 1 }; my $delete = sub { my ($elt, $src_cd, $src_rep, $idx, $tot_size, $group, $l) = @_; my $rpms; # FIXME must test no_src_fit mode foreach (@{$elt->[5]}) { $groups->[$group]{done}{$_->[0]} = 0; push @{$groups->[$group]{rejected}{$_->[0]}}, ["no_space"]; $rpms .= " $_->[0]" } # remove entry for (my $i = $idx; $i < @{$diff->{idx}} - 1; $i++) { $diff->{idx}[$i] = $diff->{idx}[$i+1]; } pop @{$diff->{idx}}; log_("optimize_space delete: new diff size " . (int @{$diff->{idx}}) . "\n", $config->{verbose}, $config->{LOG}); $size->{disc}[$src_cd] -= $tot_size; log_("optimize_space delete: deleting idx $idx packages $rpms (grp $group list $l) from disc $src_cd rep $src_rep (disc $src_cd size $size->{disc}[$src_cd])\n", $config->{verbose}, $config->{LOG}); my $src_repname = $groups->[$group]{replist}{srpm}[$src_rep-1][1]; $size->{rep}{$src_cd}{$src_repname}{$l} -= $tot_size; if ($src_cd == $cdnum) { $realgain += $tot_size; log_("optimize_space delete: current gain $realgain\n", $config->{verbose}, $config->{LOG}, 5); if ($realgain > $gain) { goto optimize_space_ok } } 1 }; my $delete_and_check = sub { my ($to_del_d, $i, $prev) = @_; my ($group, $list) = ($to_del_d->[1], $to_del_d->[2]); my ($dn, $needed); my @pkg_to_del; my ($bin_d, $j, $bin_dcd, $bin_d_curdir, $bin_drepnum, $bin_dsize); for ($j = $i; $j >= 0; $j--) { $bin_d = $diff->{data}[$diff->{idx}[$j]]; ($bin_d_curdir, undef, undef, $bin_drepnum) = @$bin_d; ($bin_dcd) = @$bin_d_curdir; $bin_dsize = $bin_d->[7]; ($bin_d->[4] == 1 || $config->{nosrcfit} || $config->{nosrc} || $groups->[$group]{options}{nosrcfit}) and last; my $bin_srpms; foreach (@{$bin_d->[5]}) { $bin_srpms .= " $_->[0]" } log_("optimize_space delete_and_check: strict source mode, deleting also srpms $bin_srpms associated from disc $bin_dcd\n", $config->{verbose}, $config->{LOG}, 5); push @pkg_to_del, [$bin_d, $bin_dcd, $bin_drepnum, $j, $bin_dsize, $bin_d->[1], $bin_d->[2]] } log_("optimize_space delete_and_check: deleting rpms from disc $bin_dcd\n", $config->{verbose}, $config->{LOG}, 5); push @pkg_to_del, [$bin_d, $bin_dcd, $bin_drepnum, $j, $bin_dsize, $bin_d->[1], $bin_d->[2]]; my $ok = 1; foreach my $pkg (@pkg_to_del) { my ($d, $dcd, $drepnum, $i, $dsize, $g, $l) = @$pkg; if ($d->[1] != $group || $d->[2] != $list) { log_("ERROR optimize_space delete_and_check: this must not happen, in strict source mode sources must follow rpms in diff indexes (group $bin_d->[1] list $bin_d->[1] instead of group $g list $l)\n", $config->{verbose}, $config->{LOG}, 4); $ok = 0; } if ($d->[4] == 1) { foreach (@{$d->[5]}) { if ($_->[2][1]{force} || $_->[2][1]{needed} || $_->[2][1]{done}) { $needed = 1; log_("optimize_space delete_and_check: could not delete $_->[0] needed $_->[2][1]{needed} ($drepnum)\n", $config->{verbose}, $config->{LOG}, 5); last } } if ($needed || @$prev && !$check_diff_deps->($prev, [$d], $drepnum, $g)) { push @$prev, $d; $ok = 0; } } } my $mid = @pkg_to_del; if ($ok) { foreach my $pkg (@pkg_to_del) { my ($d, $dcd, $drepnum, $i, $dsize, $g, $l) = @$pkg; $delete->($d, $dcd, $drepnum, $i, $dsize, $g, $l); } return ($mid, $mid) } return (0, $mid) }; my $move_bin = sub { my ($g, $l, $special, $reverse) = @_; $special && $reverse and return 0; my @test; my @ordered_bin_list_rep = sort { $reverse ? $a->[2] <=> $b->[2] : $b->[2] <=> $a->[2] } grep { $_->[3]{$l} } @{$groups->[$g]{replist}{rpm}}; my $dn; my $pdn = -1; my $spec = $special; log_("optimize_space: moving binaries from group $g list $l (reverse $reverse)\n", $config->{verbose}, $config->{LOG}, 5); while ($dn > $pdn) { move_bin_while: $pdn = $dn; for (my $bin_idx; $bin_idx < $#ordered_bin_list_rep; $bin_idx++) { my @bin_r = @{$ordered_bin_list_rep[$bin_idx]}; my @src_bin_r = @{$ordered_bin_list_rep[$bin_idx+1]}; my ($bin_cd, undef, $bin_num, $hashlist) = @bin_r; next if $bin_cd == $cdnum; my ($src_bin_cd,undef, $src_bin_num) = @src_bin_r; my @next_src_bin_r; my ($next_src_bin_cd, $next_src_bin_num); if ($bin_idx+2 < @ordered_bin_list_rep) { @next_src_bin_r = @{$ordered_bin_list_rep[$bin_idx+2]}; ($next_src_bin_cd,undef, $next_src_bin_num) = @src_bin_r; } next if $bin_cd == $src_bin_cd; my $end; log_("optimize_space: try to move binaries from disc $src_bin_cd rep $src_bin_num to disc $bin_cd rep $bin_num (spec $spec)\n", $config->{verbose}, $config->{LOG}, 5); my ($a, $b, $c) = $reverse ? (0, @{$diff->{idx}},1) : ($#{$diff->{idx}},0, -1); my $next_first_size; if (!$spec && $next_src_bin_num) { my $next_d; my $next_idx; for ($next_idx = $a; $c*$next_idx <= $b; $next_idx += $c) { $next_d = $diff->{data}[$diff->{idx}[$next_idx]]; ($next_d->[1] == $g && $next_d->[2] == $l && $next_d->[4] == 1 && $next_d->[3] == $next_src_bin_num) and last; } $next_first_size = $next_d->[7]; log_("optimize_space: next to move will be id $next_idx size $next_first_size on disc $next_src_bin_cd rep $next_src_bin_num\n", $config->{verbose}, $config->{LOG}, 5); if ($next_first_size + $size->{disc}[$src_bin_cd] < $cdsize->[$src_bin_cd]) { log_("optimize_space: no need to move from disc $src_bin_cd rep $src_bin_num, trying next\n", $config->{verbose}, $config->{LOG}, 5); next } } my (@previous, $check, $d); my $rep_diff = abs($src_bin_num - $bin_num); foreach my $mode (0,1) { for (my $idx = $a; $c*$idx <= $b; $idx += $c) { my $d = $diff->{data}[$diff->{idx}[$idx]]; ($d->[1] == $g && $d->[2] == $l && $d->[4] == 1 && $d->[3] == $src_bin_num) or next; my $first_size = $d->[7]; if ($spec) { log_("optimize_space: try to delete rpms in rep $bin_num\n", $config->{verbose}, $config->{LOG}, 5); foreach my $del_mode (0,1) { my ($to_del_idx, $to_del_d,@prev, $to_del_l); for ($to_del_idx = $#{$diff->{idx}}; $to_del_idx >= 0; $to_del_idx--) { $to_del_d = $diff->{data}[$diff->{idx}[$to_del_idx]]; my $del_bin_cd = $diff->{data}[$diff->{idx}[$to_del_idx]][0][0]; if (!$to_del_d->[5]) { log_("ERROR optimize_space move_bin: this must not happen, idx $to_del_idx is null in diff->{data} ($to_del_d)\n", $config->{verbose}, $config->{LOG}, 5); next } $to_del_d->[1] == $g or next; if ($del_mode == 0 && $to_del_d->[2] != $l && $to_del_d->[4] == 1) { push @prev, $to_del_d; next } $spec = 0; my ($nb, $mid) = $delete_and_check->($to_del_d, $to_del_idx, \@prev); $to_del_idx -= $mid - 1; $dn += $nb; if ($idx >= $to_del_idx) { $idx -= $nb } log_("optimize_space move_bin: first_size $first_size size_disc $size->{disc}[$bin_cd] cdsize $cdsize->[$bin_cd]\n", $config->{verbose}, $config->{LOG}, 5); next; goto move_bin_try_to_move if $first_size + $size->{disc}[$bin_cd] < $cdsize->[$bin_cd]; goto move_bin_while if $first_size < $cdsize->[$del_bin_cd] - $size->{disc}[$del_bin_cd] } } } move_bin_try_to_move: my $av_space = $cdsize->[$bin_cd] - $size->{disc}[$bin_cd]; my $needed; foreach (@{$d->[5]}) { if ($_->[2][1]{needed} || $_->[2][1]{done}) { $needed = 1; log_("optimize_space: could not move $_->[0] needed $_->[2][1]{needed} ($bin_num)\n", $config->{verbose}, $config->{LOG}, 5); last } } if ($needed || ((@previous || $rep_diff > 1) && !$check_previous_deps->(\@previous, [$d], $g, $src_bin_num, $bin_num))) { push @previous, $d; next } if ($first_size < $av_space) { my $dest_curdir = $hashlist->{$l}; $dn += $exchange->($d, $src_bin_cd, $src_bin_num, $bin_cd, $bin_num, $dest_curdir, $idx, $first_size); last if $next_first_size + $size->{disc}[$src_bin_cd] < $cdsize->[$src_bin_cd] } else { if ($mode == 0) { push @previous, $d } else { if ($special && !$spec) { $spec = $special; goto move_bin_while } last } } } } } } $dn }; my $try_to_move = sub { my ($grp, $list, $type, $g, $l, $opti_mode, $cd, $special) = @_; my $dn; if ($opti_mode || $grp != $g || $list != $l){ if ($groups->[$grp]{list_conflict}{$list}{$type}{$g}{$l}{rpm} && (!$cd || $groups->[$grp]{list_cd}{$list}{$cd})) { $dn += $move_bin->($g, $l); } else { log_("optimize_space try_to_move: group $grp list $list type $type has no common disc with group $g list $l type rpm\n", $config->{verbose}, $config->{LOG}, 5); } } if ($groups->[$grp]{list_conflict}{$list}{$type}{$g}{$l}{srpm}) { my $spec = 0; foreach my $s_mode (0,1,2) { my $loop_gain = -1; log_("optimize_space try_to_move: srpm mode $s_mode\n", $config->{verbose}, $config->{LOG}, 5); while ($realgain > $loop_gain) { move_src_while: $loop_gain = $realgain; my @max_size; my @prev; for (my $i = $#{$diff->{idx}}; $i >= 0; $i--) { my $d = $diff->{data}[$diff->{idx}[$i]]; $d->[4] != 2 || $d->[1] != $g and next; my ($d_curdir, undef, undef, $drepnum, undef, $ddata) = @$d; my $dcd = $d_curdir->[0]; if (!($spec || $s_mode == 2 || $dcd == $cdnum)) { next } my $dsize = $d->[7]; if (!$s_mode && $dsize > (1.5 * ($gain - $realgain))) { next } if ($max_size[$drepnum]) { next if $dsize > $max_size[$drepnum] } my $srpms; foreach (@$ddata) { $srpms .= " $_->[0]" } log_("optimize_space: trying to move $srpms (size $dsize) from disc $dcd srpm rep $drepnum\n", $config->{verbose}, $config->{LOG}, 5); my @ordered_src_list_rep = sort { $a->[2] <=> $b->[2] } grep { $_->[3]{$l} } @{$groups->[$g]{replist}{srpm}}; my $test_dn = $dn; for (my $k=0; $k < @ordered_src_list_rep; $k++) { my ($dest_cd, $dest_repname, $dest_repnum, $hash_list) = @{$ordered_src_list_rep[$k]}; log_("optimize_space: trying disc $dest_cd srpm rep $dest_repnum\n", $config->{verbose}, $config->{LOG}, 5); $dest_repnum == $drepnum and next; $dest_cd == $cdnum and next; my $dest_curdir = $hash_list->{$l}; log_("optimize_space: trying disc $dest_cd srpm rep $dest_repnum\n", $config->{verbose}, $config->{LOG}, 5); if ($d->[2] == $l && $dsize <= $cdsize->[$dest_cd] - $size->{disc}[$dest_cd]) { log_("optimize_space: moving srpm $srpms from disc $dcd to disc $dest_cd\n", $config->{verbose}, $config->{LOG}, 5); $dn += $exchange->($d, $dcd, $drepnum, $dest_cd, $dest_repnum, $dest_curdir, $i, $dsize); goto move_src_while if $cdnum == $dcd; #$dn += $move_bin->($g, $l) if (!$cd || $groups->[$grp]{list_cd}{$list}{$cd}) && $groups->[$grp]{list_conflict}{$list}{$type}{$g}{$l}{rpm} && $groups->[$g]{list_conflict}{$l}{srpm}{$g}{$l}{rpm}; $realgain > $loop_gain ? last : goto try_to_move_end } elsif (!$spec && $special) { $spec = 1; goto move_src_while } elsif ($special) { log_("optimize_space: deleting srpm $srpms from disc $dcd\n", $config->{verbose}, $config->{LOG}, 5); my ($nb, $mid) += $delete_and_check->($d, $i, \@prev); $i -= $mid - 1; $dn += $nb; next if $cdnum == $dcd; $spec = 0; #$dn += $move_bin->($g, $l) if (!$cd || $groups->[$grp]{list_cd}{$list}{$cd}) && $groups->[$grp]{list_conflict}{$list}{$type}{$g}{$l}{rpm} && $groups->[$g]{list_conflict}{$l}{srpm}{$g}{$l}{rpm}; $realgain > $loop_gain ? last : goto try_to_move_end } } $max_size[$drepnum] = $dsize if $dn == $test_dn } } } } else { log_("optimize_space: group $grp list $list type $type has no common disc with group $g list $l type srpm\n", $config->{verbose}, $config->{LOG}, 5); } if ($special && $groups->[$g]{disc_impacted}{$cdnum}) { $dn += $move_bin->($g, $l,1); } try_to_move_end: $dn }; my $opti = sub { my ($grp, $list, $type, $cd, $opti_mode, $special) = @_; my $i; $i++; log_("optimize_space: main loop $i (special $special)\n", $config->{verbose}, $config->{LOG}, 5); if (!$cd || $groups->[$grp]{list_cd}{$list}{$cd}) { $try_to_move->($grp, $list, $type, $grp, $list, $opti_mode, $cd, $special) } foreach my $g (0 .. $#{$groups}) { foreach my $l (keys %{$groups->[$g]{list}}) { if (!$l) { log_("ERROR optimize_space: list 0 must not be defined\n", $config->{verbose}, $config->{LOG}, 2); next } if ($cd || $groups->[$grp]{list_cd}{$list}{$cd}) { $try_to_move->($grp, $list, $type, $g, $l, $opti_mode, $cd, $special) } } } }; foreach my $opti_mode (0,1) { my $dn = 1; while ($dn) { $dn = 0; foreach my $cd_d (@cd_sorted,[0]) { my ($cd) = @$cd_d; log_("optimize_space: cd @$cd_d\n", $config->{verbose}, $config->{LOG}, 6); if (defined $grp && defined $list) { my @type_o = $type eq 'rpm' ? ('rpm', 'srpm') : ('srpm', 'rpm'); foreach my $t (@type_o) { $dn = $opti->($grp, $list, $t, $cd, $opti_mode) } if ($special) { foreach my $t (@type_o) { $dn = $opti->($grp, $list, $t, $cd, $opti_mode, 1) } } } else { foreach my $s (0 .. $special) { foreach my $grp (keys %{$config->{disc}[$cdnum]{group_list}}) { foreach my $lst (keys %{$config->{disc}[$cdnum]{group_list}{$grp}}) { if (!$lst) { log_("ERROR optimize_space: list 0 must not be defined\n", $config->{verbose}, $config->{LOG}, 2); next } foreach my $type (keys %{$config->{disc}[$cdnum]{group_list}{$grp}{$lst}}) { $dn = $opti->($grp, $lst, $type, $special) } $dn and goto try_another_time } } } } } try_another_time: @cd_to_test = (); for (my $i; $i < @$cdsize; $i++) { $cdlists->{$i} or next; $groups->[$grp]{disc_impacted}{$i} or next; push @cd_to_test, [ $i, $cdsize->[$i] - $size->{disc}[$i] ] } } } optimize_space_ok: log_("optimize_space: manage to gain $realgain\n", $config->{verbose}, $config->{LOG}, 2); $size->{optimize_space}{disc}{$cdnum} = $realgain if $realgain < $gain; return $realgain } sub addRPMToDiff { my ($rpm, $rpmd, $diff, $cdnum, $repnumber, $i, $list, $curdir, $size, $rpmsize, $totrpmsize, $repnum, $done, $packages) = @_; my @data; for (my $s; $s < @$rpm; $s++) { push @data, [$rpm->[$s],1, $rpmd->[$s], $rpmsize->[$s]]; log_("addRPMToDiff: $rpm->[$s] put in rep $repnumber\n", $config->{verbose}, $config->{LOG}, 4); $done->{$rpm->[$s]} = $repnumber; } my $idx = push @{$diff->{data}}, [ $curdir, $i, $list, $repnum, 1, \@data, $rpmd, $totrpmsize ]; push @{$diff->{idx}}, --$idx; $size->{disc}[$cdnum] += $totrpmsize; $size->{rep}{$cdnum}{$curdir->[1]}{$list} += $totrpmsize; log_("addRPMToDiff: SIZE disc $cdnum: $size->{disc}[$cdnum] (+ @$rpm $totrpmsize ID $idx) added rpmd $rpmd\n", $config->{verbose}, $config->{LOG}, 3); 1 } sub find_list { my ($group, $r, $list, $notdone) = @_; my $l; foreach (keys %{$group->{size}{$r}}) { # log_("find_list: for $r trying list $_ (listmatrix $l - $_ -> $group->{listmatrix}{rpm}{$l}{$_} listmatrix $list - $_ -> $group->{listmatrix}{rpm}{$list}{$_})\n",1, $config->{LOG}); $l = $_ if ( ($l && $group->{listmatrix}{rpm}{$l}{$_} || (!$l && ($group->{listmatrix}{rpm}{$list}{$_} || !$list))) && ($notdone && !$config->{list}[$_]{done} || !$notdone)) } return $l } sub processDeps { my ($r, $group, $done, $rpmlist, $topush, $intopush, $depsdisc, $rpmd, $list, $loop, $i, $tobedone, $buildlist, $rpm) = @_; log_("processDeps: deps $r\n", $config->{verbose}, $config->{LOG}, 3); my $tcd = $done->{$r}; my $l = find_list($group, $r, $list, !$tcd); if ($group->{rejected}{$r}) { log_("ERROR processDeps: deps $r rejected, rejecting @$rpm\n",1, $config->{LOG}, 1); log_("Rejecting @$rpm $r\n", $config->{verbose}, $config->{LOG},1); foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["deps_rejected", $r] } $$loop = 1; %$topush = (); return 0 } if ($tcd) { if ($tcd > $$depsdisc) { $$depsdisc = $tcd }; log_("processDeps: deps done $r on rep $tcd ($$depsdisc)\n", $config->{verbose}, $config->{LOG}, 4); return 2 } if ($tobedone->[$i]{$r}) { if ($l == $list) { log_("$r tobedone\n", $config->{verbose}, $config->{LOG},3); $intopush->{$r} and log_("WARNING processDeps: $r added twice\n", $config->{verbose}, $config->{LOG}, 3) and return 1; push @$rpmd, [$r, $rpmlist->[$i]{$l}{$r}]; $intopush->{$r} = 1; push @{$topush->{$l}}, $rpmd; log_("processDeps: adding looping deps $r ($_ -- $l) with @$rpm\n", $config->{verbose}, $config->{LOG},3) } else { if ($group->{listmatrix}{rpm}{$list}{$l}) { # FIXME tobedone may not mean dependencies loop in parallel mode for different list. log_("processDeps: $r is already scheduled on list $l, waiting.\n", $config->{verbose}, $config->{LOG},4); %$topush = (); push @{$buildlist->[$i]{$list}}, @$rpmd > 1 ? $rpmd : $rpmd->[0]; return 3 #$intopush{$r} and log_("ERROR: $r added twice\n",1, $config->{LOG}) and return 0; #$intopush{$r} = 1; #push @{$topush{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; #log_("DEPS $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG}) } else { log_("ERROR processDeps: deps $r could not be put in directory before packages @$rpm\n", $config->{verbose}, $config->{LOG},1); log_("Rejecting @$rpm $r\n", $config->{verbose}, $config->{LOG},1); push @{$group->{rejected}{$r}}, [ "order_pb", "@$rpm" ]; foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["order_pb", $r] } %$topush = (); $$loop = 1; return 0 } } } else { if ($l == $list) { $intopush->{$r} and log_("WARNING processDeps: $r added twice\n",1, $config->{LOG},2) and return 1; $intopush->{$r} = 1; push @{$topush->{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; log_("processDeps: adding normal deps $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG},3) } else { if ($group->{options}{sequential}) { log_("WARNING processDeps: could not add interlist deps in sequential mode\n",1, $config->{LOG},2); log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG},2); foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["sequential", $r] } %$topush = (); $$loop = 1; return 0 } else { if ($group->{listmatrix}{rpm}{$list}{$l}) { $intopush->{$r} and log_("WARNING processDeps: $r added twice\n",1, $config->{LOG},2) and return 1; $intopush->{$r} = 1; push @{$topush->{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; log_("processDeps: adding normal deps $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG},3) } else { log_("ERROR processDeps: deps $r could not be put in directory before packages @$rpm\n",1, $config->{LOG},1); log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG},1); foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["order_pb", $r] } %$topush = (); $$loop = 1; return 0 } } } } } sub updateGenericLimit { my ($groups, $cdsize) = @_; log_("updateGenericLimit\n", $config->{verbose}, $config->{LOG},2); for (my $i; $i < @$groups; $i++) { foreach my $type (keys %{$groups->[$i]{orderedlist}}) { foreach my $list (@{$groups->[$i]{orderedlist}{$type}}) { foreach my $r (@{$groups->[$i]{list}{$list}{$type}}) { my ($cd, $rep, $repopt) = @$r; #log_("trying to update disc $cd rep $rep list $list limit repopt $repopt (",1, $config->{LOG}),keys %$repopt,") opt $opt (",keys %$opt,")\n"; $config->{list}[$list]{disc}{$cd}{$rep}{done} and next; $repopt->{limit} or next; $repopt->{limit}{size} = $repopt->{limit}{value} * $cdsize->[$cd]; log_("updateGenericLimit: setting disc $cd rep $rep list $list limit to $repopt->{limit}{size} ($repopt->{limit}{value} * $cdsize->[$cd])\n", $config->{verbose}, $config->{LOG}, 3); } } } } } sub testSoftLimit { my ($opt, $cd, $groups, $buildlist) = @_; log_("testSoftLimit\n", $config->{verbose}, $config->{LOG}, 2); my $softnok = 1; # FIXME this code must be tested if ($opt->{limit} && $opt->{limit}{soft}) { foreach my $l (@{$config->{disc}[$cd]{fastgeneric}}) { my $lst = $l->[2]{list}; for (my $i; $i < @$groups; $i++) { $groups->[$i]{list}{$lst} or next; $softnok = 0 if (@{$buildlist->[$i]{$lst}} && !($lst->{limit} && $lst->{limit}{soft})) } } } return $softnok; } sub add_one_disc { my ($cdlists, $group, $cdsize, $list, $cds, $sources, $size, $g) = @_; my $ncd; foreach (keys %{$cdlists}) { $ncd = $_ + 1 if $ncd <= $_ } log_("add_one_disc: $config->{list}[$list]{cd} -- $ncd\n", $config->{verbose}, $config->{LOG}, 3); if (!$config->{list}[$list]{cd} || $config->{list}[$list]{cd} >= $ncd) { log_("add_one_disc: adding new disc $ncd\n", $config->{verbose}, $config->{LOG}, 4); $config->{disc}[$ncd]{size} = $config->{discsize}; my $functions = $config->{group}{disc}{functions}{functions}; $cdsize->[$ncd] = $config->{discsize}; $config->{disc}[$ncd]{name} = $ncd; $size->{optimize_space}{disc}{$ncd} = $cdsize->[$ncd]; $group->{disc_impacted}{$ncd} = 1; my ($curdir, $srpmcurdir); my $tmp = "$config->{tmp}/build/$config->{name}"; my $f = "$tmp/$ncd.list"; -f $f and unlink $f; if ($config->{nolive}) { log_("makeDisc: removing $tmp/$ncd\n", $config->{verbose}, $config->{LOG}, 3); rmtree "$tmp/$ncd"; mkpath "$tmp/$ncd"; } else { my $dir = "$config->{topdir}/build/$config->{name}"; rmtree "$dir/$ncd"; rmtree "$dir/first/$ncd"; mkpath "$dir/$ncd" } my $instcd = $group->{installDisc}; my ($rep, $src_rep); if ($sources && $config->{list}[$list]{sources} && $config->{list}[$list]{sources}{separate}) { $config->{disc}[$ncd]{serial} = "$config->{name}-disc$ncd-sources"; $config->{disc}[$ncd]{longname} = "MandrakeLinux $config->{name} sources"; $config->{disc}[$ncd]{appname} = "MandrakeLinux $config->{name} sources disc $ncd"; $config->{disc}[$ncd]{label} = substr "MandrakeLinux-$config->{name}-$ncd.src", 0, 32; $config->{disc}[$ncd]{group_list}{$g}{$list}{srpm} = 1; &{$functions->{dir}[0][5]}($ncd,3, "srpms", "Mandrake/SRPMS"); &{$functions->{generic}[0][5]}($ncd,4, "srpms",1); &{$functions->{generic}[1][5]}($ncd,6, { source => 1 }); push @{$config->{disc}[$instcd]{function}{data}{installation}[1]{srpmsdir}}, [ 0, $ncd, "srpms" ]; $srpmcurdir = [ $ncd, "srpms" ]; push @{$group->{list}{$list}{srpm}}, $srpmcurdir; $src_rep = $group->{maxrep}{srpm}; push @{$group->{replist}{srpm}}, [ $ncd, 'srpms', $group->{maxrep}{srpm}++, { $list => $srpmcurdir } ]; } else { $config->{disc}[$ncd]{serial} = "$config->{name}-disc$ncd"; $config->{disc}[$ncd]{longname} = "MandrakeLinux $config->{name}"; $config->{disc}[$ncd]{appname} = "MandrakeLinux $config->{name} disc $ncd"; $config->{disc}[$ncd]{label} = substr "MandrakeLinux-$config->{name}-$ncd.i586", 0, 32; $config->{disc}[$ncd]{group_list}{$g}{$list}{rpm} = 1; &{$functions->{dir}[0][5]}($ncd,1, "rpms", "Mandrake/RPMS$ncd"); &{$functions->{generic}[0][5]}($ncd,2, "rpms",1); $group->{orderedrep}{rpm}{"$ncd/rpms"} = $ncd; # # generic has no FIXED part, otherwize a call to generic with fixed=0 # would have been needed # $curdir = [$ncd, "rpms"]; push @{$group->{list}{$list}{rpm}}, $curdir; $rep = $group->{maxrep}{rpm}; if ($group->{replist}{rpm}[$group->{maxrep}{rpm}-1]) { die "FATAL add_one_disc: rep $group->{maxrep}{rpm} should not exist !\n" } else { $group->{replist}{rpm}[$group->{maxrep}{rpm}-1], [ $ncd, 'rpms', $group->{maxrep}{rpm}++, { $list => $curdir } ]; } push @{$config->{disc}[$instcd]{function}{data}{installation}[1]{rpmsdir}}, [ 0, $ncd, "rpms" ]; if ($config->{list}[$list]{sources}) { &{$functions->{dir}[0][5]}($ncd,3, "srpms", "Mandrake/SRPMS"); &{$functions->{generic}[0][5]}($ncd,4, "srpms",1); &{$functions->{generic}[1][5]}($ncd,6, { source => 1 }); push @{$config->{disc}[$instcd]{function}{data}{installation}[1]{srpmsdir}}, [ 0, $ncd, "srpms" ]; $srpmcurdir = [ $ncd, "srpms" ]; $src_rep = $group->{maxrep}{srpm}; if ($group->{replist}{srpm}[$group->{maxrep}{srpm}-1]) { die "FATAL add_one_disc: rep $group->{maxrep}{srpm} should not exist !\n" } else { $group->{replist}{srpm}[$group->{maxrep}{srpm}-1], [ $ncd, 'srpms', $group->{maxrep}{srpm}++, { $list => $srpmcurdir } ]; } push @{$group->{list}{$list}{srpm}}, $srpmcurdir } } push @$cds, $ncd; $cdlists->{$ncd} = 2; return ($curdir, $rep, $srpmcurdir, $src_rep) } else { return 0 } } sub addSRPMToDiff { my ($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, $srpm, $list, $i, $cdnum) = @_; for (my $s; $s < @$rpmd; $s++) { if (!$rpmd->[$s][1]{nosrc} && !$done->{$srpm->[$s]}) { my $srep = $srpmrep->{$srpm->[$s]}; my $idx = push @{$diff->{data}}, [ $srep->[2], $i, $list, $srep->[1], 2, [[$srpm->[$s],1, $rpmd->[$s], $srpmsize->[$s]]], 0, $srpmsize->[$s] ]; push @{$diff->{idx}}, $idx - 1; $size->{disc}[$srep->[0]] += $srpmsize->[$s]; $size->{rep}{$srep->[0]}{$srep->[2][1]}{$list} += $srpmsize->[$s]; log_("SIZE disc $srep->[0]: $size->{disc}[$srep->[0]] (+ $srpm->[$s] $srpmsize->[$s])\n", $config->{verbose}, $config->{LOG}, 2); } $done->{$srpm->[$s]}++; } 1 } sub sourcesSizeCheck { my ($done, $rpmd, $srpm, $group, $groups, $size, $cdsize, $list, $cdlists, $cdnum, $rpmsize, $buildlist, $cds, $i, $diff) = @_; my %srpmrep; my $srpmok = 1; my @srpmsize; for (my $s; $s < @$srpm; $s++) { $done->{$srpm->[$s]} and next; $rpmd->[$s][1]{nosrc} and next; my $srpmsize = $group->{size}{$srpm->[$s]}{$list}[0]; $srpmsize[$s] = $srpmsize; for (my $k; $k < @{$group->{list}{$list}{srpm}}; $k++) { my $srpmdir = $group->{list}{$list}{srpm}[$k]; my ($srccd, $srcrepname, $srcopt) = @$srpmdir; my $src_rep_num = $group->{orderedrep}{srpm}{"$srccd/$srcrepname"}; log_("trying source disc $srccd\n", $config->{verbose}, $config->{LOG}, 2); $cdlists->{$srccd} > 1 or next; my $currentrpm; $cdnum == $srccd and $currentrpm = $rpmsize; my $softnok = testSoftLimit($srcopt, $srccd, $groups, $buildlist); my $gain = $size->{disc}[$srccd] + $srpmsize + $currentrpm - $cdsize->[$srccd]; # FIXME this need to be tested if ($gain <= 0 && !($srcopt->{limit} && ($softnok || !$srcopt->{limit}{soft}) && $size->{rep}{$srccd}{$srcrepname}{$list} > $srcopt->{limit}{size})) { $srpmrep{$srpm->[$s]} = [$srccd, $src_rep_num, $srpmdir]; last } elsif ($k == $#{$group->{list}{$list}{srpm}}) { if (optimize_space($groups, $diff, $size, $cdsize, $srccd, $gain, $cdlists,0, $i, $list, 'srpm', $srpmsize + $currentrpm) < $gain) { $srpmok = 0 } else { $srpmrep{$srpm->[$s]} = [$srccd, $src_rep_num, $srpmdir]; } } } if (!$srpmrep{$srpm->[$s]}) { $srpmok = 0 # no last here because if in autoMode a CD will be added after and we will not retest for each srpm if it could be put on an existing CD. } } if (!$srpmok && $config->{list}[$list]{auto}) { my (undef,undef, $srpmdir, $repnum) = add_one_disc($cdlists, $group, $cdsize, $list, $cds,1, $size, $i); if ($srpmdir) { for (my $s; $s < @$srpm; $s++) { if (!$srpmrep{$srpm->[$s]}) { $srpmrep{$srpm->[$s]} = [$srpmdir->[0], $repnum, $srpmdir]; } } $srpmok = 1 } } return (\%srpmrep, \@srpmsize, $srpmok) } sub choose_alt { my ($deps, $rpmlist, $group, $cdnum, $repname, $list, $buildlist, $intopush) = @_; my $r = -1; my $score = [ 0, $group->{maxlist} ]; my $done = $group->{done}; foreach my $testalt (1,0) { foreach (@$deps) { # FIXME it may have a problem here, as depslistid are not erased when the # package is removed, that is to say that if the previous deps failed for # any reason, alternates deps may be added, although excluded before # however this _must_ not happen, and signify a bug somewhere else. my $pkg = $group->{depslistid}[$_]; my $l = find_list($group, $pkg, $list); $testalt and $rpmlist->{$l}{$pkg}{noalternatives} and log_("choose_alt: $pkg is not selected in first pass for alternatives\n", $config->{verbose}, $config->{LOG},6) and next; $intopush->{$pkg} and $r = $pkg and last; log_("choose_alt: alternatives deps $pkg\n", $config->{verbose}, $config->{LOG}, 6); $group->{rejected}{$pkg} and log_("choose_alt: $pkg is rejected, ignoring\n", $config->{verbose}, $config->{LOG}, 6) and next; my $tcd = $done->{$pkg}; if ($done->{$pkg} && $tcd <= $group->{orderedrep}{rpm}{"$cdnum/$repname"}) { log_("$pkg ($tcd) done\n", $config->{verbose}, $config->{LOG}, 6); $r = 0; last } my $s = $group->{scorelist}{$pkg}; my $pkgList = find_list($group, $pkg, $list, !$tcd); if ($list != $pkgList && $group->{options}{sequential} && !$config->{list}[$pkgList]{done} && @{$buildlist->{$pkgList}}) { next } log_("choose_alt: $pkg list $pkgList\n", $config->{verbose}, $config->{LOG}, 6); if (!$tcd && $group->{listmatrix}{rpm}{$list}{$pkgList}) { if ($group->{listsort}{rpm}{$pkgList} < $score->[1] || $group->{listsort}{rpm}{$pkgList} == $score->[1] && $s > $score->[0]) { log_("choose_alt: choosing $pkg ($s, $group->{listsort}{$pkgList})\n", $config->{verbose}, $config->{LOG}, 6); $score = [ $s, $group->{listsort}{rpm}{$pkgList} ]; $r = $pkg; } } } $r and last } return $r } sub get_pkgs_deps { my ($rpmd, $group) = @_; my (@tdeps, %curID); foreach (@$rpmd) { my $rpm = $_->[0]; $curID{$group->{urpm}{rpm}{$rpm}->id} = 1; $_->[1]{nodeps} and next; $group->{pkgdeps}{$rpm} and push @tdeps, @{$group->{pkgdeps}{$rpm}} } my (@deps, %depsdone); foreach (@tdeps) { if (ref $_) { my @toadd; my $key = join '|', @$_; $depsdone{$key}++ and next; foreach my $d (@$_) { if ($curID{$d}) { @toadd = (); last } push @toadd, $d } @toadd and push @deps, \@toadd } elsif (!$curID{$_}) { $depsdone{$_}++ and next; push @deps, $_ } } return \@deps } sub check_deps { my ($rpmd, $group, $done, $rpmlist, $list, $i, $tobedone, $buildlist, $rpm, $cdnum, $repname, $needed, $thisorderrep) = @_; my $deps = get_pkgs_deps($rpmd, $group); my $loop; if (@$deps) { my ($waiting, %topush, %intopush, $depsdisc); foreach (@$deps) { if (!ref $_) { my $a = processDeps($group->{depslistid}[$_], $group, $done, $rpmlist, \%topush, \%intopush, \$depsdisc, $rpmd, $list, \$loop, $i, $tobedone, $buildlist, $rpm); if ($a < 0) { return 0 } elsif ($a == 0) { last } elsif ($a == 2) { next } elsif ($a == 3) { $waiting = 1; last } } else { # must create a virtual package that install all of them in one loop log_("check_deps: alternatives deps @$_\n", $config->{verbose}, $config->{LOG}, 5); my $r = choose_alt($_, $rpmlist->[$i], $group, $cdnum, $repname, $list, $buildlist->[$i], \%intopush); $intopush{$r} and next; if ($r == -1) { log_("ERROR check_deps: alternatives deps (@$_) could not be put in directory before packages @$rpm\n", $config->{verbose}, $config->{LOG}, 2); log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG},2); foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, ["order_pb", "@$_"] } %topush = (); $loop = 1; last } if ($r) { my $a = processDeps($r, $group, $done, $rpmlist, \%topush, \%intopush, \$depsdisc, $rpmd, $list, \$loop, $i, $tobedone, $buildlist, $rpm); if ($a < 0) { return 0 } elsif ($a == 0) { last } elsif ($a == 2) { next } elsif ($a == 3) { $waiting = 1; last } } else { log_("Finding better alternatives rep (@$_ - $depsdisc)\n", $config->{verbose}, $config->{LOG}, 4); my $bestdisc = (keys %{$group->{orderedrep}{rpm}}); if ($bestdisc >= $depsdisc) { foreach (@$_) { my $pkg = $group->{depslistid}[$_]; $group->{rejected}{$pkg} and log_("$pkg rejected\n", $config->{verbose}, $config->{LOG}, 2) and next; my $tcd = $done->{$pkg} or next; log_("$pkg => rep $tcd\n", $config->{verbose}, $config->{LOG}, 4); if ($tcd < $bestdisc) { $bestdisc = $tcd } } $bestdisc > $depsdisc and $depsdisc = $bestdisc } log_("Finding better alternatives rep result $depsdisc\n", $config->{verbose}, $config->{LOG}, 4); } } } $waiting and next; if (keys %topush) { log_("Adding dependencies, looping\n", $config->{verbose}, $config->{LOG}, 3); $loop = 1; my $test = @$rpmd > 1 ? $rpmd : $rpmd->[0]; push @{$buildlist->[$i]{$list}}, @$rpmd > 1 ? $rpmd : $rpmd->[0]; foreach (keys %topush) { $list != $_ and push @{$needed->[$i]{$list}{asap}}, [ $_, int @{$buildlist->[$i]{$_}} ]; push @{$buildlist->[$i]{$_}}, @{$topush{$_}} } } elsif ($thisorderrep < $depsdisc) { if ($group->{listmaxrep}{rpm}{$list} >= $depsdisc) { # has a chance to put it after depsdic log_("Dependencies on further directories ($depsdisc)\n", $config->{verbose}, $config->{LOG}, 3); next } else { log_("check_deps: dependances are in further directories, rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2); foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["order_pb", ""] } $loop = 1 } } } return $loop; } sub put_in_rep { my ($i, $groups, $group, $size, $rpmsize, $all_rpmsize, $cdsize, $needed, $rpm, $rpmd, $list, $cdlists, $buildlist, $diff, $cds, $done, $tobedone, $rpmlist, $nosrcfit) = @_; my $loop; my $dn; log_("put_in_rep: @$rpm\n", $config->{verbose}, $config->{LOG}, 3); for (my $j; !$loop && !$dn && $j < @{$group->{list}{$list}{rpm}}; $j++) { $loop = 0; my $curdir = $group->{list}{$list}{rpm}[$j]; $config->{list}[$list]{disc}{$curdir->[0]}{$curdir->[1]}{done} and next; my ($cdnum, $repname, $repopt) = @$curdir; my $rep_num = $group->{orderedrep}{rpm}{"$cdnum/$repname"}; $cdlists->{$cdnum} > 1 or next; my $thisorderrep = $group->{orderedrep}{rpm}{"$cdnum/$repname"}; my $softnok = testSoftLimit($repopt, $cdnum, $groups, $buildlist); my $gain = $size->{disc}[$cdnum] + $all_rpmsize - $cdsize->[$cdnum]; log_("put_in_rep: curdir cd $cdnum (space $gain rpm size $all_rpmsize) rep $repname rep_num $rep_num softnok $softnok\n", $config->{verbose}, $config->{LOG}, 4); if ($gain > 0 || $repopt->{limit} && ($softnok || !$repopt->{limit}{soft}) && $size->{rep}{$cdnum}{$repname}{$list} + $all_rpmsize > $repopt->{limit}{size}) { if ($j == $#{$group->{list}{$list}{rpm}}) { if (!($repopt->{limit} && !$softnok && $repopt->{limit}{soft})) { if (optimize_space($groups, $diff, $size, $cdsize, $cdnum, $gain, $cdlists,0, $i, $list, 'rpm', $all_rpmsize) < $gain) { if ($config->{list}[$list]{auto}) { ($curdir, $rep_num) = add_one_disc($cdlists, $group, $cdsize, $list, $cds,0, $size, $i); if ($curdir) { $cdnum = $curdir->[0] } else { log_("Could not add more disc, rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2); foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, [ "no_disc", "" ] } next } } else { log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2); foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, ["no_space", ""] } next } } } else { foreach my $l (@{$config->{disc}[$cdnum]{fastgeneric}}) { my $lst = $l->[2]{list}; $list == $lst and next; for (my $i; $i < @$groups; $i++) { $groups->[$i]{list}{$lst}{rpm} or next; push @{$needed->[$i]{$list}{asap}}, [ $lst, 0 ] if (!($lst->{limit} && $lst->{limit}{soft})) } } } } else { next } } if (!$config->{nodeps} && !$group->{options}{nodeps}) { $loop = check_deps($rpmd, $group, $done, $rpmlist, $list, $i, $tobedone, $buildlist, $rpm, $cdnum, $repname, $needed, $thisorderrep) } $loop and next; log_("@$rpm deps ok\n", $config->{verbose}, $config->{LOG}, 4); my $nosrc = 1; my @srpm; my $donesrpm = 1; if (!$group->{options}{nosources} && @{$group->{list}{$list}{srpm}}) { for (my $s; $s < @$rpmd; $s++) { my $srpm = $group->{urpm}{sourcerpm}{$rpm->[$s]}; $srpm =~ s/\.rpm$//; if (!$group->{size}{$srpm}{$list}) { log_("put_in_rep ERROR: $srpm not available, trying alternatives => ", $config->{verbose}, $config->{LOG}, 5); my ($srpmname) = $srpm =~ /(.*)-[^-]+-[^-]+\.src/; $srpm = $group->{srpmname}{$srpmname}; if ($group->{size}{$srpm}{$list}) { log_(" $srpm\n", $config->{verbose}, $config->{LOG}) } else { if ($srpm) { log_("not found (but a srpm $srpm exist in another list)\n", $config->{verbose}, $config->{LOG}, 5); $srpm = 0 } else { log_("not found\n", $config->{verbose}, $config->{LOG}, 5) } } } if ($srpm) { $done->{$srpm} or $donesrpm = 0; $srpm[$s] = $srpm; $rpmd->[$s][1]{nosrc} or $nosrc = 0 } } } log_("put_in_rep: group $i list $list: @$rpm (@srpm) -- $curdir->[0] -- $curdir->[1] -- disc $cdnum\n", $config->{verbose}, $config->{LOG}, 4); if ($config->{nosrc} || $group->{options}{nosources} || !@{$group->{list}{$list}{srpm}} || $nosrc || $donesrpm) { ($dn) = addRPMToDiff($rpm, $rpmd, $diff, $cdnum, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $i, $list, $curdir, $size, $rpmsize, $all_rpmsize, $rep_num, $done) } else { if ($config->{nosrcfit} || $group->{options}{nosrcfit}) { $dn = addRPMToDiff($rpm, $rpmd, $diff, $cdnum, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $i, $list, $curdir, $size, $rpmsize, $all_rpmsize, $rep_num, $done); push @$nosrcfit, [$rpmd, \@srpm, $list, $i, $curdir, $cdnum] } else { my ($srpmrep, $srpmsize, $srpmok) = sourcesSizeCheck($done, $rpmd, \@srpm, $group, $groups, $size, $cdsize, $list, $cdlists, $cdnum, $all_rpmsize, $buildlist, $cds, $i, $diff); if ($srpmok) { addRPMToDiff($rpm, $rpmd, $diff, $cdnum, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $i, $list, $curdir, $size, $rpmsize, $all_rpmsize, $rep_num, $done); $dn = addSRPMToDiff($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, \@srpm, $list, $i, $cdnum); } else { log_("WARNING: @srpm does not fit on the discs\n",1, $config->{LOG}, 2) } } if (!$dn) { foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, ["no_space", ""] } log_("WARNING: @$rpm does not fit on the disc ($size->{disc}[$cdnum] + $all_rpmsize > $cdsize->[$cdnum]) \n", $config->{verbose}, $config->{LOG}, 1) } } } return $dn } sub loop_on_lists { my ($i, $groups, $group, $groupok, $needed, $buildlist, $tobedone, $diff, $nosrcfit, $size, $cdsize, $cds, $rpmlist, $cdlists, $ok) = @_; # # FIXME source rpms are not shared between group, it may be usefull for mutilple installation # with common source dir, so that the same source rpm is shared (but this is not so common). # my $done = $group->{done}; my $dn; log_("loop_on_lists: group $i (@{$group->{orderedlist}{rpm}})\n", $config->{verbose}, $config->{LOG}, 2); while (!$dn) { $groupok->[$i] = 1; foreach my $list (@{$group->{orderedlist}{rpm}}) { log_("loop_on_lists: list $list (empty $config->{list}[$list]{empty} done $config->{list}[$list]{done})\n", $config->{verbose}, $config->{LOG}, 3); do { $config->{list}[$list]{done} and goto end; $config->{list}[$list]{empty} and goto end; my $next; foreach (@{$needed->[$i]{$list}{asap}}) { my $nb_elt = @{$buildlist->[$i]{$_->[0]}}; log_("List $list need list $_->[0] to be <= $_->[1] ($nb_elt)\n", $config->{verbose}, $config->{LOG}, 4); $nb_elt <= $_->[1] or $next = 1 } $next and log_("List $list waiting\n",1, $config->{LOG},4) and goto end; $needed->[$i]{$list}{asap} = []; my ($trpmd, $k, $goon, @rpmd); do { $trpmd = pop @{$buildlist->[$i]{$list}} or goto end; if (ref $trpmd->[0]) { foreach (@$trpmd) { !$done->{$_->[0]} and push @rpmd, $_ } } else { !$done->{$trpmd->[0]} and push @rpmd, $trpmd } } until @rpmd; $groupok->[$i] = 0; $ok = 0; my @rpm; my $all_rpmsize; my @rpmsize; foreach (@rpmd) { my $r = $_->[0]; !$r and log_("ERROR loop_on_lists: empty package @$_\n", $config->{verbose}, $config->{LOG}, 2); push @rpm, $r; log_("RPM $r (group $i list $list)\n", $config->{verbose}, $config->{LOG},6); $tobedone->[$i]{$r} = 1; $all_rpmsize += $group->{size}{$r}{$list}[0]; push @rpmsize, $group->{size}{$r}{$list}[0] } $dn = put_in_rep($i, $groups, $group, $size, \@rpmsize, $all_rpmsize, $cdsize, $needed, \@rpm, \@rpmd, $list, $cdlists, $buildlist, $diff, $cds, $done, $tobedone, $rpmlist, $nosrcfit); } while $group->{options}{sequential} && @{$buildlist->[$i]{$list}}; end: last if $group->{options}{sequential} && @{$buildlist->[$i]{$list}} && !$config->{list}[$list]{done} } $groupok->[$i] and $dn = 1 } return $ok } sub calc_needed_size { my ($group, $i, $needed, $needed_size, $buildlist, $rpmlist) = @_; my ($msg, %local_done); $msg = "calc_needed_size\n"; my $done = $group->{done}; foreach my $rep (@{$group->{replist}{rpm}}) { my ($cd, $repname, $num, $l) = @$rep; $msg .= "calc_needed_size: rep $num\n"; foreach my $list (keys %{$needed}) { $l->{$list} or next; $config->{list}[$list]{disc}{$cd}{$repname}{done} and log_("calc_needed_size: rep $cd/$repname for list $list is done, ignoring\n", $config->{verbose}, $config->{LOG}, 5) and next; foreach my $elt (@{$needed->{$list}{alap}[$num]}) { my $rpm = $elt->[0]; if ($done->{$rpm} || $local_done{$rpm}) { next } if ($group->{rejected}{$rpm}) { $msg .= "ERROR: $rpm is rejected, ignoring\n"; next } $msg .= "calc_needed_size: list $list rpm $rpm size $group->{size}{$rpm}{$list}[0]\n"; $needed_size->[$num]{fix} += $group->{size}{$rpm}{$list}[0]; $local_done{$rpm} = 1; # FIXME This following code is a simplified version of check_deps. It may be overkill to use # full check_deps as anyway check_deps will be used to put the package at the end. foreach (@{$group->{pkgdeps}{$rpm}}) { if (ref $_) { $local_done{"@$_"} and next; $local_done{"@$_"} = 1; my $r = choose_alt($_, $rpmlist->[$i], $group, $cd, $num, $list, $buildlist); if ($r != -1 && $r != 0) { next if $local_done{$r}; $needed_size->[$num]{var} += $group->{size}{$r}{$list}[0]; } } else { my $pkg = $group->{depslistid}[$_]; next if $done->{$pkg} || $local_done{$pkg}; $local_done{$pkg} = 1; $needed_size->[$num]{var} += $group->{size}{$pkg}{$list}[0]; } } } } } log_($msg, $config->{verbose}, $config->{LOG}, 3) } sub revert_to { my ($groups, $i, $p2r, $diff, $size, $buildlist) = @_; log_("revert_to: try to find $p2r\n", $config->{verbose}, $config->{LOG}, 3); foreach (@{$diff->{data}}) { $_ or next; foreach (@{$_->[5]}) { goto revert_to_ok if $_->[0] eq $p2r } } log_("ERROR revert_to: $p2r is not present in movement history\n", $config->{verbose}, $config->{LOG}, 2); return 0; revert_to_ok: log_("revert_to: $p2r found\n", $config->{verbose}, $config->{LOG}, 5); my @keep; my $idx; do { $idx = pop @{$diff->{idx}}; goto revert_to_endloop if grep { $_->[0] eq $p2r } @{$diff->{data}[$idx][5]}; my $step = $diff->{data}[$idx]; if ($groups->[$i]{conflict}{$step->[1]}) { my ($curdir, $g, $list) = @$step; my $cdnum = $curdir->[0]; foreach (@{$step->[5]}) { my ($rpm,undef,undef, $rpmsize) = @$_; delete $groups->[$g]{done}{$rpm}; $diff->{data}[$idx] = 0; $size->{disc}[$cdnum] -= $rpmsize; $size->{rep}{$cdnum}{$curdir->[1]}{$list} -= $rpmsize; log_("revert_to: reverting $rpm ID $idx for $p2r on cd $cdnum group $g list $list (cd size $size->{disc}[$cdnum])\n", $config->{verbose}, $config->{LOG}, 3) } push @{$buildlist->{$list}}, $step->[6] if $step->[4] == 1 } else { unshift @keep, $idx; } } while @{$diff->{idx}}; revert_to_endloop: die "FATAL revert_to: diff data are empty\n" if ! @{$diff->{data}}; push @{$diff->{idx}}, $idx, @keep; 1 } sub mark_and_check_lists { my ($groups, $i, $needed, $diff, $buildlist, $rpmlist, $mark, $size, $cdsize, $ok) = @_; my @needed_size; my $group = $groups->[$i]; my $need_to_calc; foreach my $list (@{$group->{orderedlist}{rpm}}) { ref $buildlist->{$list} or next; log_("mark_and_check_list: list $list\n", $config->{verbose}, $config->{LOG}, 3); $config->{list}[$list]{done} and log_("mark_and_check_lists: list $list is done, ignoring\n", $config->{verbose}, $config->{LOG}, 5) and next; if (defined $mark->{cur}{$list}) { $need_to_calc = 1; my $m = $mark->{cur}{$list}; my $m_rpm = $m->[0]; if ($group->{done}{$m_rpm}) { log_("mark_and_check_list: $m_rpm done, deleting mark for list $list\n", $config->{verbose}, $config->{LOG},4); push @{$mark->{his}}, $m; delete $mark->{cur}{$list} } elsif ($group->{rejected}{$m_rpm}) { log_("mark_and_check_list: $m_rpm rejected, deleting mark for list $list\n", $config->{verbose}, $config->{LOG},4); delete $mark->{cur}{$list} } elsif (!@{$buildlist->{$list}}) { log_("mark_and_check_list: list $list finished and $m_rpm not done or rejected\n",1, $config->{LOG},4) } } if (!defined $mark->{cur}{$list} && @{$buildlist->{$list}}) { my $rpm; for (my $i = $#{$buildlist->{$list}}; $i >= 0; $i--) { my $t = $buildlist->{$list}[$i]; $rpm = (ref $t->[0]) ? $t->[0] : $t; if (!$group->{done}{$rpm->[0]}) { last } # this is not necessary, but when we are at it... pop @{$buildlist->{$list}} } $mark->{cur}{$list} = $rpm; log_("mark_and_check_list: marking $rpm->[0] for $list\n", $config->{verbose}, $config->{LOG},3); } } if ($need_to_calc || $ok) { calc_needed_size($group, $i, $needed, \@needed_size, $buildlist, $rpmlist); my ($need_in_rep, $av_in_rep, %done_disc); # # First impression would have been to check needed in reverse order, because we could imagine # that, in the current configuration, if needed 2 does not fit, for exemple, one package # is removed, needed 2 is put. But if needed does not fit at this moment, needed 2 is removed, # and needed 3 put, then needed 2 does not fit, and it is needed to revert more to make both of # them fit. # # If fact this could not happen, because if needed 3 does not fit when needed 2 has just been # put, this mean that the calc_needed_size if bogus. # foreach my $rep (@{$group->{replist}{rpm}}) { my ($cd,undef, $num) = @$rep; $need_in_rep += $needed_size[$num]{var} + $needed_size[$num]{fix}; if (! $done_disc{$num}) { $av_in_rep += $cdsize->[$cd] - $size->{disc}[$cd]; $done_disc{$num} = 1 } log_("mark_and_check_list: rep $num need_in_rep $need_in_rep av_in_rep $av_in_rep needed_size $needed_size[$num]{fix} disc_av_space ($cdsize->[$cd] - $size->{disc}[$cd])\n", $config->{verbose}, $config->{LOG}, 4); if ($need_in_rep && $need_in_rep > $av_in_rep || $needed_size[$num]{fix} > $cdsize->[$cd] - $size->{disc}[$cd]) { log_("mark_and_check_list: not enough space for needed in rep $num on disc $cd\n", $config->{verbose}, $config->{LOG}, 3); pop @{$mark->{his}}; my $p2r = pop @{$mark->{his}}; log_("mark_and_check_list: trying to revert $p2r->[0]\n", $config->{verbose}, $config->{LOG}, 4); if (revert_to($groups, $i, $p2r->[0], $diff, $size, $buildlist)) { log_("mark_and_check_list: $p2r->[0] reverted\n", $config->{verbose}, $config->{LOG}, 3); foreach my $idx (0 .. @{$group->{orderedlist}{rpm}}) { my $list = $group->{orderedlist}{rpm}[$idx]; $needed->{$list} or next; my $elt; foreach my $tr (1 .. $num) { foreach my $elt (@{$needed->{$list}{alap}[$tr]}) { my $rpm = $elt->[0]; if ($group->{rejected}{$rpm}) { log_("ERROR: $rpm is rejected, ignoring\n", $config->{verbose}, $config->{LOG}, 3); next } push @{$buildlist->{$list}}, $elt; } } $mark->{cur}{$list} = $elt->[0]; my $l_idx = $#{$buildlist->{$list}}; next if $l_idx < 0; foreach my $tidx ($idx + 1 .. @{$group->{orderedlist}{rpm}}) { my $l = $group->{orderedlist}{rpm}[$tidx]; push @{$needed->{$l}{asap}}, [ $list, $l_idx ] } } } else { log_("ERROR mark_and_check_list: reverting to $p2r->[0] failed\n", $config->{verbose}, $config->{LOG}, 4) } } } } return $ok } # TODO the algo is not as beautiful as it should be # ... but it is getting better # ... and better sub buildDiscs { my ($class, $groups, $buildlist, $rpmlist, $groupok, $size, $cdsize, $cdlists, $cds, $needed, $diff, $n) = @_; log_("buildDiscs\n", $config->{verbose}, $config->{LOG}, 3); $config = $class->{config}; if ($n > 1) { foreach my $i (reverse @$cds) { $size->{optimize_space}{disc}{$i} = $size->{disc}[$i]; if ($size->{disc}[$i] > $cdsize->[$i]) { my $gain = ($size->{disc}[$i] - $cdsize->[$i])/2; next if $gain < 0; optimize_space($groups, $diff, $size, $cdsize, $i, $gain, $cdlists,1) } else { log_("buildDiscs: disc $i size OK $size->{disc}[$i] ($cdsize->[$i])\n", $config->{verbose}, $config->{LOG},2) } } } my ($ok, $iti); my @groupok; my (@tobedone, @nosrcfit); my @mark = ({}) x @$groups; updateGenericLimit($groups, $cdsize); while (!$ok) { log_("iti: " . $iti++ . "\n", $config->{verbose}, $config->{LOG},4); $ok = 1; for (my $i = 0; $i < @$groups; $i++) { my $group = $groups->[$i]; if (!$config->{fast}) { $groupok[$i] = mark_and_check_lists($groups, $i, $needed->[$i], $diff, $buildlist->[$i], $rpmlist, $mark[$i], $size, $cdsize, $groupok[$i]); $groupok[$i] and next; } $ok = loop_on_lists($i, $groups, $group, \@groupok, $needed, $buildlist, \@tobedone, $diff, \@nosrcfit, $size, $cdsize, $cds, $rpmlist, $cdlists, $ok); } } foreach (@nosrcfit) { my ($rpmd, $srpm, $list, $i, $curdir, $cdnum) = @$_; my $group = $groups->[$i]; my $done = $group->{done}; my ($srpmrep, $srpmsize, $srpmok) = sourcesSizeCheck($done, $rpmd, $srpm, $group, $groups, $size, $cdsize, $list, $cdlists,0,0, $buildlist, $cds, $i, $diff); if ($srpmok) { addSRPMToDiff($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, $srpm, $list, $i, $cdnum); } else { log_("WARNING: @$srpm does not fit on the discs\n",1, $config->{LOG},2) } } my $is_rejected; log_("buildDiscs: rejected packages\n", $config->{verbose}, $config->{LOG},2); for (my $i; $i < @$groups; $i++) { $groups->[$i]{rejected} or next; my $gh = $groups->[$i]{rejected}; foreach (keys %$gh) { if (!$is_rejected) { $is_rejected = 1 if grep { $_->[0] =~ /no_disc/ || $_->[0] =~ /no_space/ } @{$gh->{$_}}; } log_("WARNING buildDisc: group $i REJECTED $_ (",1, $config->{LOG},2); ref $groups->[$i]{rejected}{$_} and log_((join ',', map { "$config->{rejected_options}{$_->[0]}: $_->[1]" } @{$groups->[$i]{rejected}{$_}}),1, $config->{LOG},2); log_(")\n",1, $config->{LOG}); } } ($is_rejected) } 1 # Changelog # # 2002 02 21 # change false $j comparaison to $depsdisc in buildDisc to new $thisorderrep value. # # 2002 03 03 # new limit option handling. # add updateGenericSoft function # add testSoftLimit function # update size to check rep size # # 2002 03 08 # fix autoMode CD adding # # 2002 03 13 # better selection of alternatives in multi-list to take the one in the first lists. # # 2002 03 14 # add sources new sources handling method # in nosrcfit mode sources are added afterwards # # 2002 03 19 # add prelist in geList for cdcom, will be useful for oem too I guess. # # 2002 05 02 # add_one_disc: add separate mode for sources mode # # 2002 05 09 # add graft structure for md5 and graft point handling # # 2002 05 13 # fix a tricky bugs in build_list about fentry shared and not recreated for each packages. # # 2002 06 01 # use perl-URPM # # 2002 06 15 # new diff mode, global, shared between disc and group, only one table. # # 2002 08 16 # new diff_idx table to sort diff data # # 2002 08 24 # optimize_space first version, still need to handle correctly needed and more advanced optimization methods. # # 2002 09 18 # optimize_space work, fixes and updates. # # 2002 10 25 # fix needed assignation pb in closeRPMslist