source: soft/build_system/build_system/mkcd/trunk/pm/Mkcd/Build.pm @ 1

Last change on this file since 1 was 1, checked in by fasma, 12 years ago

Initial Import from Mandriva's soft revision 224062 and package revision 45733

File size: 71.2 KB
Line 
1package Mkcd::Build;
2
3my $VERSION = '3.0.0';
4
5use strict;
6use File::NCopy qw(copy);
7use File::Path;
8use URPM qw(ranges_overlap);
9use Mkcd::Package qw(rpmVersionCompare);
10use Mkcd::Tools qw(log_ find_list);
11use Mkcd::Optimize qw(optimize_space get_pkgs_deps);
12use MDK::Common qw(any if_);
13
14my $MIN_CHUNK = 0.0001;
15
16=head1 NAME
17
18Build - mkcd module
19
20=head1 SYNOPSYS
21
22    require Mkcd::Build;
23
24=head1 DESCRIPTION
25
26C<mkcd::Build> include the mkcd packages list functions.
27
28=head1 SEE ALSO
29
30mkcd
31
32=head1 COPYRIGHT
33
34Copyright (C) 2000-2004 Mandriva <warly@mandriva.com>
35
36This program is free software; you can redistribute it and/or modify
37it under the terms of the GNU General Public License as published by
38the Free Software Foundation; either version 2, or (at your option)
39any later version.
40
41This program is distributed in the hope that it will be useful,
42but WITHOUT ANY WARRANTY; without even the implied warranty of
43MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44GNU General Public License for more details.
45
46You should have received a copy of the GNU General Public License
47along with this program; if not, write to the Free Software
48Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
49
50=cut
51
52my $config;
53
54sub new {
55    my ($class, $conf) = @_;
56    $config = $conf;
57    bless {
58           config       => $config,
59          }, $class;
60}
61
62sub addRPMToDiff {
63    my ($rpm, $rpmd, $diff, $cdnum, $repnumber, $i, $list, $curdir, $size, $rpmsize, $totrpmsize, $repnum, $done, $reallist, $realrepnum, $group, $tobedone) = @_;
64    my @data;
65    for (my $s; $s < @$rpm; $s++) {
66        my ($rpm_size, $dev, $inode) = @{$rpmsize->[$s]};
67        push @data, [$rpm->[$s], 1, $rpmd->[$s], $rpm_size, $dev, $inode];
68        log_("addRPMToDiff: $rpm->[$s] put in rep $repnumber ($done->{rep} size $rpm_size, dev $dev inode $inode)\n", $config->{verbose}, $config->{LOG}, 4);
69        $done->{rep}{$rpm->[$s]} = $repnumber;
70        $done->{list}{$rpm->[$s]} = $list;
71        $size->{inode}[$cdnum]{$dev}{$inode}++;
72        $tobedone->{$rpm->[$s]}{$list} = 0
73    }
74    my $diff_data = [ $curdir, $i, $list, $repnum, 1, \@data, $rpmd, $totrpmsize, $reallist ];
75    my $idx = push @{$diff->{data}}, $diff_data;
76    push @{$diff->{idx}}, --$idx;
77    push @{$group->{reploc}{$realrepnum}{diff}{$repnum}}, $diff_data if $realrepnum != $repnum;
78    $size->{disc}[$cdnum] += $totrpmsize;
79    $size->{rep}{$cdnum}{$curdir->[1]}{$list} += $totrpmsize;
80    log_("addRPMToDiff: SIZE disc $cdnum: $size->{disc}[$cdnum] (+ @$rpm $totrpmsize ID $idx) added rpmd $rpmd\n", $config->{verbose}, $config->{LOG}, 3);
81    1
82}
83
84sub check_last_relocatable {
85    my ($group, $cd, $repname, $repnum, $list, $curdir_old, $old) = @_;
86    my ($curdir, $new_repnum, $newlist);
87    if (defined $group->{reploc}{$repnum}{last}{list} && $group->{reploc}{$repnum}{last}{list} == $list) {
88        $new_repnum = $group->{reploc}{$repnum}{last}{rep};
89        log_("check_last_relocatable: using previous relocatable dir $new_repnum\n",1 , $config->{LOG}, 1);
90        $curdir = $group->{reploc}{$repnum}{last}{curdir};
91        $newlist = $group->{reploc}{$repnum}{last}{newlist}
92    } else {
93        my $last = $group->{reploc}{$repnum}{last}{rep} || $repnum - 1;
94        $new_repnum = $last+$MIN_CHUNK;
95        log_("check_last_relocatable: using new relocatable dir $new_repnum cd $cd repname $repname repnum $repnum\n",1 , $config->{LOG}, 1);
96        $group->{reploc}{$repnum}{last}{rep} = $new_repnum;
97        $group->{reploc}{$repnum}{last}{list} = $list;
98        $newlist = @{$config->{list}};
99        $config->{list}[$newlist] = {};
100        push @{$group->{reploc}{$repnum}{list}}, [ $new_repnum, $cd, $repname, $newlist ];
101        $curdir = [ $cd, $repname ];
102        push @$curdir, $curdir_old->[2], $curdir_old->[3] if ref $curdir_old;
103        $group->{reploc}{$repnum}{last}{newlist} = $newlist;
104        $group->{reploc}{$repnum}{last}{curdir} = $curdir;
105        $group->{reploc}{$repnum}{curdir}{$new_repnum} = $curdir;
106        $group->{reploc}{$repnum}{newlist}{$new_repnum} = $newlist;
107        $group->{reploc}{$repnum}{reallist}{$new_repnum} = $list;
108        $group->{reploc}{$repnum}{old}{$new_repnum} = $old 
109    }
110    return $new_repnum, $curdir, $newlist
111}
112
113sub add_relocatable_package {
114    my ($group, $tcd, $diff, $r, $list, $i, $repnum, $repname) = @_;
115    my $done = $group->{done};
116    log_("add_relocatable_package: going in relocatable mode for deps $r before $repnum\n",1 , $config->{LOG}, 1);
117    my ($new_repnum, $curdir, $newlist) = check_last_relocatable($group, @{$group->{reverse_rep}{rpm}{$tcd}}{'cd', 'name'}, $repnum, $list, 0, 1);
118    log_("add_relocatable_package: $r put in rep $new_repnum list $list\n", $config->{verbose}, $config->{LOG}, 4);
119    $done->{rep}{$r} = $new_repnum;
120    $done->{list}{$r} = $newlist;
121
122    my $totrpmsize = $group->{size}{$r}{$list}[0];
123    $group->{reploc}{$repnum}{size}{$new_repnum} += $totrpmsize;
124    my $rpmd = [[ $r, 0, 0 ]];
125    my $data = [[ $r, 1, 0, 0 ]];
126    my $diff_data = [ $curdir, $i, $newlist, $new_repnum, 1, $data, $rpmd, $totrpmsize, $list ];
127    my $idx = push @{$diff->{data}}, $diff_data;
128    push @{$diff->{idx}}, --$idx;
129    push @{$group->{reploc}{$repnum}{diff}{$new_repnum}}, $diff_data;
130    $new_repnum
131}
132
133sub all_rejected {
134    my ($all_l, $r) = @_;
135    foreach my $l (@$all_l) {
136        return 0 if !$r->{$l}
137    }
138    1
139}
140
141sub processDeps {
142    my ($r, $group, $cdnum, $repname, $done, $rpmlist, $topush, $intopush, $depsdisc, $rpmd, $list, $loop, $i, $tobedone, $buildlist, $rpm, $needed, $diff) = @_;
143    # FIXME I think that this is wrong, but not really sure
144    # It may happen that package get rejected because the non-done deps could not,
145    # for any reason, be used, and this package is not put after the done deps.
146    # However I guess that the put_in_rep loop over directories may save the
147    # stuff.
148   
149    # FIXME This is not correct, should iterate on the lists to find if an entry exist (not important)
150    #if (! defined $done->{rep}{$r} && ! defined $rpmlist->[$i]{$done->{list}{$r}}{$r}) {
151    #   log_("WARNING THIS SHOULD NOT HAPPEN processDeps: adding a non programmed deps $r\n", $config->{verbose}, $config->{LOG}, 1);
152    #}
153    my $repnum = $group->{orderedrep}{rpm}{"$cdnum/$repname"};
154    my $tcd = $done->{rep}{$r} if !$rpmlist->[$i]{$done->{list}{$r}}{$r}{noprovide} || $done->{rep}{$r} <= $repnum;
155    log_("processDeps: deps $r (tcd $tcd done $done->{rep}{$r})\n", $config->{verbose}, $config->{LOG}, 3);
156    if ($tcd > $repnum && $rpmlist->[$i]{$done->{list}{$r}}{$r}{relocatable}) {
157        $tcd = add_relocatable_package($group, $tcd, $diff, $r, $done->{list}{$r}, $i, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $repname)
158    }
159    my ($r_l, $all_l) = find_list($config, $group, $r, $list, !$tcd);
160    if (all_rejected($all_l, $group->{rejected}{$r})) { 
161        log_("ERROR processDeps: deps $r rejected, rejecting @$rpm (list @$all_l)\n",1 , $config->{LOG}, 1);
162        log_("Rejecting @$rpm $r\n", $config->{verbose}, $config->{LOG},1);
163        foreach my $t (@$rpmd) { 
164            my $p = $t->[0];
165            my (undef, $p_list) = find_list($config, $group, $p, $list, !$tcd);
166            foreach my $t_l (@$p_list) { reject_rpm([$t], $t_l, $group, 'deps_rejected', $r, $loop, $topush) }
167        }
168        return
169    }
170    if ($tcd) {
171        if ($tcd > $$depsdisc) { $$depsdisc = $tcd };
172        log_("processDeps: deps done $r on rep $tcd ($$depsdisc) list $done->{list}{$r}\n", $config->{verbose}, $config->{LOG}, 4);
173        return 2 
174    }
175    foreach my $l (@$all_l) {
176        if ($tobedone->{$r}{$l}) {
177            if ($l == $list) {
178                log_("$r tobedone\n", $config->{verbose}, $config->{LOG},3);
179                if ($intopush->{$r}) { log_("WARNING processDeps: $r added twice\n", $config->{verbose}, $config->{LOG}, 3); return 1 }
180                push @$rpmd, [$r, $rpmlist->[$i]{$l}{$r}];
181                $intopush->{$r} = 1;
182                push @{$topush->{$l}}, $rpmd; 
183                log_("processDeps: adding looping deps $r ($l) with @$rpm\n", $config->{verbose}, $config->{LOG},3);
184                return
185            } else {
186                if ($group->{listmatrix}{rpm}{$list}{$l} && !$group->{rejected}{$r}{$l}) {
187                    # FIXME tobedone may not mean dependencies loop in parallel mode for different list.
188                    log_("processDeps: $r is already scheduled on list $l, waiting.\n", $config->{verbose}, $config->{LOG},4);
189                    %$topush = ();
190                    push @{$buildlist->[$i]{$list}}, @$rpmd > 1 ? $rpmd : $rpmd->[0];
191                    return 3
192                    #$intopush{$r} and log_("ERROR: $r added twice\n",1, $config->{LOG}) and return 0;
193                    #$intopush{$r} = 1;
194                    #push @{$topush{$l}}, [$r, $rpmlist->[$i]{$l}{$r}];
195                    #log_("DEPS $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG})
196                } else {
197                    log_("ERROR processDeps: deps $r could not be put in directory before packages @$rpm\n", $config->{verbose}, $config->{LOG},1);
198                    reject_rpm($rpmd, $list, $group, 'order_pb', $r, $loop, $topush);
199                    next
200                }
201            }
202        } else {
203            if ($l == $list) {
204                if ($intopush->{$r}) { log_("WARNING processDeps: $r added twice\n", $config->{verbose}, $config->{LOG}, 2); return 1 }
205                $intopush->{$r} = 1;
206                push @{$topush->{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; 
207                log_("processDeps: adding normal deps $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG},3);
208                return
209            } else {
210                if ($config->{list}[$l]{done}) {
211                    log_("processDeps: list $l of deps $r is done, doing nothing.\n", $config->{verbose}, $config->{LOG},6);
212                    return
213                }
214                if ($group->{options}{sequential}) {
215                    log_("WARNING processDeps: could not add interlist deps in sequential mode\n",$config->{verbose}, $config->{LOG},2);
216                    reject_rpm($rpmd, $list, $group, 'sequential', $r, $loop, $topush);
217                    next
218                } else {
219                    if (defined $needed->{$list}{asap} && grep { $l == $_->[0] } @{$needed->{$list}{asap}}) {
220                        log_("ERROR processDeps: list of deps $r is already waiting for @$rpm list\n",1, $config->{LOG},1);
221                        reject_rpm($rpmd, $list, $group, 'order_pb', $r, $loop, $topush);
222                        next
223                    } elsif (defined $needed->{$l}{asap} && grep { $list == $_->[0] } @{$needed->{$l}{asap}}) {
224                        log_("ERROR processDeps: list of @$rpm  is already waiting for $r list\n",1, $config->{LOG},1);
225                        reject_rpm($rpmd, $list, $group, 'order_pb', $r, $loop, $topush);
226                        next
227                    } else {
228                        if ($group->{listmatrix}{rpm}{$list}{$l}) {
229                            if ($intopush->{$r}) { log_("WARNING processDeps: $r added twice\n",$config->{verbose}, $config->{LOG},2); return 1 }
230                            $intopush->{$r} = 1;
231                            push @{$topush->{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; 
232                            log_("processDeps: adding normal deps $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG},3);
233                            return
234                        } else {
235                            log_("ERROR processDeps: deps $r could not be put in directory before packages @$rpm\n",$config->{verbose}, $config->{LOG},1);
236                            reject_rpm($rpmd, $list, $group, 'order_pb', $r, $loop, $topush);
237                            next
238                        } 
239                    }
240                }
241            }
242        }
243    }
244}
245
246sub reject_rpm {
247    my ($rpmd, $list, $group, $reason, $r, $loop, $topush) = @_;
248    foreach (@$rpmd) { 
249        log_("Rejecting $_->[0] list $list ($reason)\n", $config->{verbose}, $config->{LOG},1);
250        push @{$group->{rejected}{$_->[0]}{$list}}, [ $reason, $r ]
251    }
252    %$topush = ();
253    $$loop = 1
254}
255
256sub updateGenericLimit {
257    my ($groups, $cdsize) = @_;
258    log_("updateGenericLimit\n", $config->{verbose}, $config->{LOG},2);
259    for (my $i; $i < @$groups; $i++) {
260        foreach my $type (keys %{$groups->[$i]{orderedlist}}) {
261            foreach my $list (@{$groups->[$i]{orderedlist}{$type}}) {
262                foreach my $r (@{$groups->[$i]{list}{$list}{$type}}) {
263                    my ($cd, $rep, $repopt) = @$r;
264                    #log_("trying to update disc $cd rep $rep list $list limit repopt $repopt (",1, $config->{LOG}),keys %$repopt,") opt $opt (",keys %$opt,")\n";
265                    $config->{list}[$list]{disc}{$cd}{$rep}{done} and next;
266                    ref $repopt or next;
267                    defined $repopt->{limit}{value} or next;
268                    $repopt->{limit}{size} = $repopt->{limit}{value} * $cdsize->[$cd];
269                    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);
270                }
271            }
272        }
273    }
274}
275
276sub testSoftLimit {
277    my ($opt, $cd, $list, $groups, $buildlist) = @_;
278    log_("testSoftLimit\n", $config->{verbose}, $config->{LOG}, 2);
279    my $softnok;
280    # FIXME this code must be tested
281    if ($opt->{limit} && defined $opt->{limit}{soft}) {
282        log_("testSoftLimit: testing limit for disk $cd\n", $config->{verbose}, $config->{LOG}, 2);
283        foreach my $l (@{$config->{disc}[$cd]{fastgeneric}}) {
284            log_("testSoftLimit: testing limit for disk $cd list $l\n", $config->{verbose}, $config->{LOG}, 2);
285            my $lst = $l->[1]{lists};
286            foreach my $ls (@$lst) {
287                for (my $i; $i < @$groups; $i++) {
288                    $groups->[$i]{list}{$ls} or next;   
289                    next if $ls == $list;
290                    my $nb = int @{$buildlist->[$i]{$ls}};
291                    log_("testSoftLimit: group $i list $ls ($nb)\n", $config->{verbose}, $config->{LOG}, 2);
292                    $softnok = 1 if $nb
293                }
294            }
295        }
296    } else {
297        $softnok = 1
298    }
299    $softnok
300}
301
302sub add_one_disc {
303    my ($cdlists, $group, $cdsize, $list, $cds, $sources, $size, $g) = @_;
304    my $ncd;
305    foreach (keys %$cdlists) {
306        $ncd = $_ + 1 if $ncd <= $_
307    }
308    log_("add_one_disc: $config->{list}[$list]{cd} -- $ncd\n", $config->{verbose}, $config->{LOG}, 3);
309    if (!$config->{list}[$list]{cd} || $config->{list}[$list]{cd} >= $ncd) {
310        log_("add_one_disc: adding new disc $ncd\n", $config->{verbose}, $config->{LOG}, 4);
311        $config->{disc}[$ncd]{size} = $config->{discsize};
312        my $functions = $config->{group}{disc}{functions}{functions};
313        $cdsize->[$ncd] = $config->{discsize};
314        $config->{disc}[$ncd]{name} = $ncd;
315        $size->{optimize_space}{disc}{$ncd} = $cdsize->[$ncd];
316        $group->{disc_impacted}{$ncd} = 1;
317        my ($curdir, $srpmcurdir);
318        my $tmp = "$config->{tmp}/build/$config->{name}";
319        my $f = "$tmp/$ncd.list";
320        -f $f and unlink $f;
321        if ($config->{nolive}) {
322            log_("makeDisc: removing $tmp/$ncd\n", $config->{verbose}, $config->{LOG}, 3);
323            rmtree "$tmp/$ncd";
324            mkpath "$tmp/$ncd";
325        } else {
326            my $dir = "$config->{topdir}/build/$config->{name}";
327            rmtree "$dir/$ncd";
328            rmtree "$dir/first/$ncd";
329            mkpath "$dir/$ncd"
330        }
331        my $instcd = $group->{installDisc};
332        my ($rep, $src_rep);
333
334        my $struct_v = $config->{struct_version};
335        my $media_srpm = $config->{struct}{$struct_v}{srpm_media};
336        my $media_dir = $config->{struct}{$struct_v}{media};
337        if ($sources && $config->{list}[$list]{sources} && $config->{list}[$list]{sources}{separate}) {
338            $config->{disc}[$ncd]{serial} = "$config->{name}-$ncd-src";
339            $config->{disc}[$ncd]{longname} = "Mandriva Linux $config->{name} sources";
340            $config->{disc}[$ncd]{appname} = "Mandriva Linux $config->{name} sources disc $ncd";
341            $config->{disc}[$ncd]{label} = substr "$config->{name}-src-Disc$ncd", 0, 32;
342            $config->{disc}[$ncd]{group_list}{$g}{$list}{srpm} = 1;
343            &{$functions->{dir}[0][5]}($ncd, 3, "srpms", $media_srpm);
344            &{$functions->{generic}[0][5]}($ncd, 4, "srpms",1);
345            &{$functions->{generic}[1][5]}($ncd, 5, { source => 1 });
346            push @{$group->{installation}[1]{srpmsdir}}, [ 0, $ncd, "srpms" ];
347            $srpmcurdir = [ $ncd, "srpms" ];
348            push @{$group->{list}{$list}{srpm}}, $srpmcurdir;
349            $src_rep = $group->{maxrep}{srpm};
350            push @{$group->{replist}{srpm}}, [ $ncd, 'srpms', $group->{maxrep}{srpm}++, { $list => $srpmcurdir } ];
351        } else {
352            $config->{disc}[$ncd]{serial} = "$config->{name}-$ncd";
353            $config->{disc}[$ncd]{longname} = "Mandriva Linux $config->{name}";
354            $config->{disc}[$ncd]{appname} = "Mandriva Linux $config->{name} disc $ncd";
355            $config->{disc}[$ncd]{label} = substr "$config->{name}-Disc$ncd", 0, 32;
356            $config->{disc}[$ncd]{group_list}{$g}{$list}{rpm} = 1;
357            &{$functions->{dir}[0][5]}($ncd, 1, "rpms","$media_dir$ncd");
358            &{$functions->{generic}[0][5]}($ncd, 2, "rpms", 1);
359            $group->{orderedrep}{rpm}{"$ncd/rpms"} = $ncd;
360            #
361            # generic has no FIXED part, otherwize a call to generic with fixed=0
362            # would have been needed
363            #
364            $curdir = [$ncd, "rpms"];
365            push @{$group->{list}{$list}{rpm}}, $curdir;
366            $rep = $group->{maxrep}{rpm};
367            if ($group->{replist}{rpm}[$group->{maxrep}{rpm}-1]) {
368                die "FATAL add_one_disc: rep $group->{maxrep}{rpm} should not exist !\n"
369            } else {
370                $group->{replist}{rpm}[$group->{maxrep}{rpm}-1], [ $ncd, 'rpms', $group->{maxrep}{rpm}++, { $list => $curdir } ];
371            }
372            push @{$group->{installation}[1]{rpmsdir}}, [ 0, $ncd, "rpms" ];
373            if ($config->{list}[$list]{sources}) {
374                &{$functions->{dir}[0][5]}($ncd,3, "srpms", $media_srpm);
375                &{$functions->{generic}[0][5]}($ncd,4, "srpms",1);
376                &{$functions->{generic}[1][5]}($ncd,5, { source => 1 });
377                push @{$group->{installation}[1]{srpmsdir}}, [ 0, $ncd, "srpms" ];
378                $srpmcurdir = [ $ncd, "srpms" ];
379                $src_rep = $group->{maxrep}{srpm};
380                if ($group->{replist}{srpm}[$group->{maxrep}{srpm}-1]) {
381                    die "FATAL add_one_disc: rep $group->{maxrep}{srpm} should not exist !\n"
382                } else {
383                    $group->{replist}{srpm}[$group->{maxrep}{srpm}-1], [ $ncd, 'srpms', $group->{maxrep}{srpm}++, { $list => $srpmcurdir } ];
384                } 
385                push @{$group->{list}{$list}{srpm}}, $srpmcurdir
386            }
387        }
388        push @$cds, $ncd;
389        $cdlists->{$ncd} = 2;
390        return $curdir, $rep, $srpmcurdir, $src_rep
391    } else { return 0 }
392}
393
394sub addSRPMToDiff {
395    my ($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, $srpm, $list, $i, $cdnum) = @_;
396    for (my $s; $s < @$rpmd; $s++) {
397        if (!$rpmd->[$s][1]{nosrc} && !$done->{rep}{$srpm->[$s]}) {
398            my $srep = $srpmrep->{$srpm->[$s]};
399            my $cdnum = $srep->[0];
400            my ($srpm_size, $dev, $inode) = @{$srpmsize->[$s]};
401            my $idx = push @{$diff->{data}}, [ $srep->[2], $i, $list, $srep->[1], 2, [[$srpm->[$s],1, $rpmd->[$s], $srpm_size]], 0, $srpm_size ];
402            push @{$diff->{idx}}, $idx - 1;
403            $size->{inode}[$cdnum]{$dev}{$inode}++;
404            $size->{disc}[$cdnum] += $srpm_size;
405            $size->{rep}{$cdnum}{$srep->[2][1]}{$list} += $srpm_size;
406            log_("SIZE disc $srep->[0]: $size->{disc}[$cdnum] (+ $srpm->[$s] $srpm_size)\n", $config->{verbose}, $config->{LOG}, 2);
407        }
408        $done->{rep}{$srpm->[$s]}++;
409        $done->{list}{$srpm->[$s]} = $list
410    }
411    1
412}
413
414sub sourcesSizeCheck {
415    my ($done, $rpmd, $srpm, $group, $groups, $size, $cdsize, $list, $cdlists, $cdnum, $rpmsize, $buildlist, $cds, $i, $diff) = @_;
416    my %srpmrep;   
417    my $srpmok = 1;
418    my @srpmsize;
419    for (my $s; $s < @$srpm; $s++) {
420        $done->{rep}{$srpm->[$s]} and next;
421        $rpmd->[$s][1]{nosrc} and next;
422        my ($srpmsize, $dev, $inode) = $group->{size}{$srpm->[$s]}{$list}[0];
423        $srpmsize[$s] =  [$srpmsize, $dev, $inode];
424        for (my $k; $k < @{$group->{list}{$list}{srpm}}; $k++) {
425            my $srpmdir = $group->{list}{$list}{srpm}[$k];
426            my ($srccd, $srcrepname, $srcopt) = @$srpmdir;
427            my $src_rep_num = $group->{orderedrep}{srpm}{"$srccd/$srcrepname"};
428            log_("trying source disc $srccd\n", $config->{verbose}, $config->{LOG}, 2);
429            $cdlists->{$srccd} > 1 or next;
430            my $currentrpm;
431            $cdnum == $srccd and $currentrpm = $rpmsize;
432            my $softnok = testSoftLimit($srcopt, $srccd, $list, $groups, $buildlist);
433            $srpmsize = 0 if $size->{inode}[$srccd]{$dev}{$inode};
434            my $gain = $size->{disc}[$srccd] + $srpmsize + $currentrpm - $cdsize->[$srccd];
435            # FIXME this need to be tested
436            if ($gain <= 0 && !(defined $srcopt->{limit} && ($softnok || !defined $srcopt->{limit}{soft}) && $size->{rep}{$srccd}{$srcrepname}{$list} > $srcopt->{limit}{size})) {
437                $srpmrep{$srpm->[$s]} = [$srccd, $src_rep_num, $srpmdir];
438                last
439            } elsif ($k == $#{$group->{list}{$list}{srpm}}) {
440                if (optimize_space($config, $groups, $diff, $size, $cdsize, $srccd, $gain, $cdlists,0, $i, $list, 'srpm', $srpmsize + $currentrpm) < $gain) {
441                    $srpmok = 0
442                } else {
443                    $done = $groups->[$i]{done};
444                    $srpmrep{$srpm->[$s]} = [$srccd, $src_rep_num, $srpmdir];
445                }
446            }
447        }
448        if (!$srpmrep{$srpm->[$s]}) {
449            $srpmok = 0
450            # 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.
451        }
452    }
453    if (!$srpmok && $config->{list}[$list]{auto}) {
454        my (undef, undef, $srpmdir, $repnum) = add_one_disc($cdlists, $group, $cdsize, $list, $cds, 1, $size, $i);
455        if ($srpmdir) {
456            for (my $s; $s < @$srpm; $s++) {
457                if (!$srpmrep{$srpm->[$s]}) {
458                    $srpmrep{$srpm->[$s]} = [$srpmdir->[0], $repnum, $srpmdir];
459                }
460            }
461            $srpmok = 1
462        }
463    }
464    return \%srpmrep, \@srpmsize, $srpmok
465}
466
467sub choose_alt {
468    my ($deps, $rpmlist, $group, $cdnum, $repname, $list, $buildlist, $intopush, $tobedone, $needed, $size_matter) = @_;
469    my $r = -1;
470    my $score = [ 0, $group->{maxlist} ];
471    my $done = $group->{done};
472    my $all_rejected = 1;
473    DEPS: {
474        foreach my $testalt (1,0) {
475            foreach (@$deps) {
476                # FIXME it may have a problem here, as depslistid are not erased when the
477                # package is removed, that is to say that if the previous deps failed for
478                # any reason, alternates deps may be added, although excluded before
479                # however this _must_ not happen, and signify a bug somewhere else.
480                # Update: This may happen when the schedule deps has been rejected, and thus
481                # only the previous not selected one coud fulfil the require.
482                my $pkg = $group->{depslistid}[$_];
483
484                my (undef, $all_l) = find_list($config, $group, $pkg, $list);
485                foreach my $pkg_list (@$all_l) {
486                    log_("choose_alt: $pkg, testing list $pkg_list (rejected $group->{rejected}{$pkg}{$pkg_list})\n", $config->{verbose}, $config->{LOG}, 6);
487                    if ($group->{rejected}{$pkg}{$pkg_list}) { log_("choose_alt: $pkg is rejected, ignoring\n", $config->{verbose}, $config->{LOG}, 6); last }
488                    $all_rejected = 0;
489                    if ($testalt && ! defined $rpmlist->{$pkg_list}{$pkg} || $rpmlist->{$pkg_list}{$pkg}{noalternatives}) { log_("choose_alt: $pkg is not selected in first pass for alternatives\n", $config->{verbose}, $config->{LOG},6); next }
490                    $intopush->{$pkg} and $r = $pkg and last DEPS;
491                    log_("choose_alt: alternatives deps $pkg (noprovide $rpmlist->{$done->{list}{$pkg}}{$pkg}{noprovide} done $done->{rep}{$pkg} orderedrep " . $group->{orderedrep}{rpm}{"$cdnum/$repname"} . ")\n", $config->{verbose}, $config->{LOG}, 6);
492                    my $tcd;
493                    if ($rpmlist->{$done->{list}{$pkg}}{$pkg}{relocatable}) {
494                        $tcd = $group->{orderedrep}{rpm}{"$cdnum/$repname"}
495                    } elsif (!$rpmlist->{$done->{list}{$pkg}}{$pkg}{noprovide} || $done->{rep}{$pkg} <= $group->{orderedrep}{rpm}{"$cdnum/$repname"}) {
496                        $tcd = $done->{rep}{$pkg}
497                    }
498                    if ($tcd && $tcd <= $group->{orderedrep}{rpm}{"$cdnum/$repname"}) {
499                        log_("$pkg ($tcd) done\n", $config->{verbose}, $config->{LOG}, 6);
500                        $r = 0;
501                        last DEPS
502                    } 
503                    my $s = $size_matter ? $group->{size}{$pkg}{$pkg_list}[0] : $group->{scorelist}{$pkg};
504                    #log_("choose_alt: pkg $pkg buildlist $buildlist list $pkgList other list $list tcd $tcd list $l ($buildlist->{$pkgList})\n", $config->{verbose}, $config->{LOG}, 6);
505
506                    #log_("choose_alt: ($needed->{$pkgList}{asap})\n", $config->{verbose}, $config->{LOG}, 6);
507                    if ($pkg_list 
508                    && 
509                    $list != $pkg_list 
510                    && 
511                    (defined $needed->{$pkg_list}{asap} && (any { 
512                        if (ref $_) { 
513                            $list == $_->[0] 
514                        } else { 
515                            log_("ERROR choose_alt: [$_] should be a reference (pkg $pkg list $pkg_list asap [@{$needed->{$pkg_list}{asap}}])\n", $config->{verbose}, $config->{LOG}, 6) 
516                        } 
517                    } @{$needed->{$pkg_list}{asap}})
518                    || 
519                    ($group->{options}{sequential} 
520                    && 
521                    !$config->{list}[$pkg_list]{done} 
522                    && 
523                    @{$buildlist->{$pkg_list}}))
524                    ) { next }
525                    log_("choose_alt: $pkg list $pkg_list (tcd $tcd listmatrix $group->{listmatrix}{rpm}{$list}{$pkg_list} listsort $group->{listsort}{$pkg_list}{rpm} score->[1] $score->[1] s $s)\n", $config->{verbose}, $config->{LOG}, 6);
526                    if (!$tcd && $group->{listmatrix}{rpm}{$list}{$pkg_list}) {
527                        if ($group->{listsort}{$pkg_list}{rpm} < $score->[1] || $group->{listsort}{$pkg_list}{rpm} == $score->[1] && $s > $score->[0]) {
528                            log_("choose_alt: choosing $pkg list $pkg_list ($s, $group->{listsort}{$pkg_list}{rpm})\n", $config->{verbose}, $config->{LOG}, 6);
529                            $score = [ $s, $group->{listsort}{$pkg_list}{rpm} ];
530                            $r = $pkg;
531                            last DEPS if $tobedone->{$r}{$pkg_list};
532                            last
533                        }
534                    }
535                }
536            }
537            last if $r != -1 && $r
538        }
539    }
540    return $r, $all_rejected
541}
542
543sub check_deps {
544    my ($rpmd, $group, $done, $rpmlist, $list, $i, $tobedone, $buildlist, $rpm, $cdnum, $repname, $needed, $rep_num, $diff) = @_;
545    log_("check_deps\n", $config->{verbose}, $config->{LOG}, 5);
546    my $deps = get_pkgs_deps($rpmd, $group);
547    my $loop;
548    if (@$deps) {
549        my ($waiting, %topush, %intopush, $depsdisc);
550        $intopush{$_->[0]} = 1  foreach @$rpmd;
551        foreach (@$deps) {
552            if (!ref $_) {
553                my $a = processDeps($group->{depslistid}[$_], $group, $cdnum, $repname, $done, $rpmlist, \%topush, \%intopush, \$depsdisc, $rpmd, $list, \$loop, $i, $tobedone, $buildlist, $rpm, $needed->[$i], $diff);
554                if ($a < 0) { return 0 } elsif ($a == 0) { last } elsif ($a == 2) { next } elsif ($a == 3) { $waiting = 1; last  }
555            } else {
556                # must create a virtual package that install all of them in one loop
557                log_("check_deps: alternatives deps @$_\n", $config->{verbose}, $config->{LOG}, 5);
558                my ($r, $all_rejected) = choose_alt($_, $rpmlist->[$i], $group, $cdnum, $repname, $list, $buildlist->[$i], \%intopush, $tobedone, $needed->[$i]);
559                $intopush{$r} and next;
560                if ($r == -1) {
561                    my $reject = 0;
562                    my $msg;
563                    if ($all_rejected) {
564                        $msg = "all alternatives deps (@$_) rejected";
565                        $reject = 'deps_rejected'
566                    } elsif ($group->{orderedrep}{rpm}{"$cdnum/$repname"} >= $group->{listmaxrep}{rpm}{$list}) { 
567                        $msg = "ERROR check_deps: alternatives deps (@$_) could not be put in directory before packages @$rpm\n";
568                        $reject = 'order_pb'
569                    } else {
570                        $loop = 2
571                    }
572                    if ($reject) {
573                        my $deps_rpm = join ' ',map { $group->{depslistid}[$_] } @$_;
574                        reject_rpm($rpmd, $list, $group, $reject, $deps_rpm, \$loop, \%topush);
575                    }
576                    last
577                }
578                if ($r) { 
579                    my $a = processDeps($r, $group, $cdnum, $repname, $done, $rpmlist, \%topush, \%intopush, \$depsdisc, $rpmd, $list, \$loop, $i, $tobedone, $buildlist, $rpm, $needed->[$i]);
580                    if ($a < 0) { return 0 } elsif ($a == 0) { last } elsif ($a == 2) { next } elsif ($a == 3) { $waiting = 1; last  }
581                } else {
582                    log_("Finding better alternatives rep (@$_ - $depsdisc)\n", $config->{verbose}, $config->{LOG}, 4);
583                    my $bestdisc = keys %{$group->{orderedrep}{rpm}};
584                    if ($bestdisc >= $depsdisc) {
585                        foreach (@$_) {
586                            my $pkg = $group->{depslistid}[$_];
587                            my ($r_l, $all_l) = find_list($config, $group, $pkg, $list);
588                            if (all_rejected($all_l, $group->{rejected}{$pkg})) { log_("$pkg rejected\n", $config->{verbose}, $config->{LOG}, 2); next }
589                            my $tcd;
590                            log_("$pkg done $done->{list}{$pkg} relocatable $rpmlist->[$i]{$done->{list}{$pkg}}{$pkg}{relocatable}\n", $config->{verbose}, $config->{LOG}, 4);
591                            if ($rpmlist->[$i]{$done->{list}{$pkg}}{$pkg}{relocatable}) {
592                                $tcd = $rep_num
593                            } else {
594                                $tcd = $done->{rep}{$pkg};
595                            }
596                            $tcd or next;
597                            log_("$pkg => rep $tcd\n", $config->{verbose}, $config->{LOG}, 4);
598                            if ($tcd < $bestdisc) { $bestdisc = $tcd }
599                        }
600                        $bestdisc > $depsdisc and $depsdisc = $bestdisc
601                    }
602                    log_("Finding better alternatives rep result $depsdisc\n", $config->{verbose}, $config->{LOG}, 4);
603                }
604            }
605        }
606        $waiting and return 1;
607        if (keys %topush) {
608            $loop = 1;
609            log_("Adding dependencies, looping\n", $config->{verbose}, $config->{LOG}, 3);
610            my $test = @$rpmd > 1 ? $rpmd : $rpmd->[0];
611            push @{$buildlist->[$i]{$list}}, (@$rpmd > 1 ? $rpmd : $rpmd->[0]);
612            foreach (keys %topush) {
613                $list != $_ and push @{$needed->[$i]{$list}{asap}}, [ $_, int @{$buildlist->[$i]{$_}} ];
614                push @{$buildlist->[$i]{$_}}, @{$topush{$_}}
615            }
616        } elsif ($rep_num < $depsdisc) {
617            if ($group->{listmaxrep}{rpm}{$list} >= $depsdisc) {
618                $loop = 2;
619                # has a chance to put it after depsdisc
620                log_("Dependencies on further directories ($depsdisc < $group->{listmaxrep}{rpm}{$list} rep_num $rep_num)\n", $config->{verbose}, $config->{LOG}, 3);
621            } else {
622                $loop = 1;
623                log_("check_deps: dependencies are in further directories ($rep_num < $depsdisc), rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2);
624                foreach (@$rpm) { push @{$group->{rejected}{$_}{$list}}, ["order_pb", ""] }
625                reject_rpm($rpmd, $list, $group, 'order_pb', '', \$loop);
626            }
627        }
628    }
629    $loop
630}
631
632sub put_in_rep {
633    my ($i, $groups, $group, $size, $rpmsize, $cdsize, $needed, $rpm, $rpmd, $list, $cdlists, $buildlist, $diff, $cds, $done, $tobedone, $rpmlist, $nosrcfit) = @_; 
634    my $loop;
635    my $dn;
636    my $reject_reason;
637    log_("put_in_rep: @$rpm\n", $config->{verbose}, $config->{LOG}, 3);
638    #
639    # 20060713 warly pre-put optimization.
640    # To try to share packages between group, we can try to put a package on
641    # a CD where it is already included for an other group, to share the space.
642    # However, we should be careful not to disturb to much the other packages
643    # requiring this package.
644    # We can
645    #   - check if the package is also on the other list, an choose the first rep
646    #   which is on a CD also usable by other group
647    #   - only check if the package is already somewhere, and use the same CD if
648    #   possible
649    #   - do various thing depending of the number of packages requiring this
650    #   package (putting it in further directory can prevent to put correctly
651    #   all the needed packages in the CDs
652    #   - only optimize packages which are not marked 'no_optimization'
653    #
654    # How to check if the package can be optimized or not? How to handle
655    # group of packages, including noarch and binaries? Binaries can be shared anyway
656    # for example lilo is both in x86_64 and i586
657    #
658    # 20060718 warly
659    # This algorythm can be problematic if the two installation group are not linked
660    # (if they just chare one CD for example, and could lead to packages put in very
661    # strange directory. This should hardly happen in theory, though, as basesystem will always
662    # be put on first dirs, but can be a problem when using 'fixed' command.
663    # Then we may need to forbid hardlink optimization in some case, either by doing it manually,
664    # or just not using this kind of optimization by default, or for small packages, or for basystem packages.
665    #
666    my @all_rpmsize;
667    for (my $j; $j < @{$group->{list}{$list}{rpm}}; $j++) {
668        my $curdir = $group->{list}{$list}{rpm}[$j];
669        $config->{list}[$list]{disc}{$curdir->[0]}{$curdir->[1]}{done} and next;
670        my ($cdnum, $repname, $repopt) = @$curdir;
671        $cdlists->{$cdnum} > 1 or next;
672        foreach my $r (@$rpmd) {
673            my $rpm = $r->[0];
674            # 20060712 warly need to recalculate all_rpmsize in each rep (see below)
675            my ($rpm_size, $dev, $inode) = @{$group->{size}{$rpm}{$list}}[0,3,4];
676            log_("put_in_rep: $rpm size $rpm_size dev $dev inode $inode\n", $config->{verbose}, $config->{LOG}, 9);
677            if (! $size->{inode}[$cdnum]{$dev}{$inode}) { 
678                $all_rpmsize[$j] += $rpm_size
679            }
680        }
681    }
682    my @sorted_rep = sort { $all_rpmsize[$a] <=> $all_rpmsize[$b] || $a <=> $b } 0 .. @{$group->{list}{$list}{rpm}} - 1;
683    log_("put_in_rep: sorted rep @sorted_rep (all_rpmsize @all_rpmsize)\n", $config->{verbose}, $config->{LOG}, 9);
684    foreach my $j (@sorted_rep) {
685        last if $loop || $dn;
686        $loop = 0;
687        log_("put_in_rep: testing dir $j\n", $config->{verbose}, $config->{LOG}, 3);
688        my $curdir = $group->{list}{$list}{rpm}[$j];
689        $config->{list}[$list]{disc}{$curdir->[0]}{$curdir->[1]}{done} and next;
690        my ($cdnum, $repname, $repopt) = @$curdir;
691        my $rep_num = $group->{orderedrep}{rpm}{"$cdnum/$repname"};
692        log_("put_in_rep: testing dir $j cdnum $cdnum repname $repname\n", $config->{verbose}, $config->{LOG}, 3);
693        $cdlists->{$cdnum} > 1 or next;
694        my $not_good_rep;
695        my $all_rpmsize = $all_rpmsize[$j];
696        foreach my $r (@$rpmd) {
697            if ($r->[1]{notinrep}{$rep_num} || ref $r->[1]{inrep} && $r->[1]{inrep}{$rep_num}) {
698                    log_("put_in_rep: $r->[0] notinrep $rep_num, testing other rep\n", $config->{verbose}, $config->{LOG}, 9);
699                    $not_good_rep = 1; 
700                    last
701                }
702        }
703        if ($not_good_rep) {
704           if ($rep_num == $sorted_rep[$#sorted_rep]) {
705                log_("ERROR put_in_rep: could not handle inrep or notinrep flags for rpms @$rpm", $config->{verbose}, $config->{LOG}, 1);
706                foreach my $p (@$rpm) { push @{$group->{rejected}{$p}{$list}}, [ 'config', ""] }
707                last
708           } 
709           next
710        }
711        my ($relocatable_list, $relocatable_rep_num) = ($list, $rep_num);
712        if (defined $group->{reploc}{$rep_num} && ref $group->{reploc}{$rep_num}) {
713            ($relocatable_rep_num, $curdir, $relocatable_list) = check_last_relocatable($group, $cdnum, $repname, $rep_num, $list, $curdir);
714        }
715        # 20060712 warly all_rpmsize
716        #
717        # If we try to optimize space allocation to put shared rpm on same CDs.
718        # We should recompute all_rpmsize for each rep. However the tricky part
719        # is that this size must be dynamic, if a.noarch is in 2/i586/main/, its size
720        # should not be added in 2/x86_64/main/, but if 2/i596/main/a.noarch is then
721        # removed (reverted, moved), then its size should not be deduced from the CD size.
722        # To handle that correctly, a list of inode should be added with each CD size calculation,
723        # and count the number of inode occurence on the given CD, and only deduce the size or add
724        # the size when removing the last occurence or adding the first one.`
725       
726        my $softnok = testSoftLimit($repopt, $cdnum, $list, $groups, $buildlist);
727        my $gain = $size->{disc}[$cdnum] + $all_rpmsize - $cdsize->[$cdnum];
728        my $gain2;
729        if (defined $repopt->{limit}{size} && ($softnok || !defined $repopt->{limit}{soft})) {
730            $gain2 = ($size->{rep}{$cdnum}{$repname}{$list} + $all_rpmsize) - $repopt->{limit}{size}
731        }
732        my $phys_gain = $gain;
733        $gain = $gain2 if $gain2 > $gain;
734        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);
735        if ($gain > 0) {
736            log_("put_in_rep: reaching limit\n", $config->{verbose}, $config->{LOG}, 9);
737            if ($j == $sorted_rep[$#sorted_rep]) {
738                log_("put_in_rep: reaching limit last available rep (limit $repopt->{limit} softnok $softnok soft $repopt->{limit}{soft})\n", $config->{verbose}, $config->{LOG}, 9);
739                if ($phys_gain > 0 || !($repopt->{limit} && !$softnok && $repopt->{limit}{soft})) {
740                    log_("put_in_rep: limit reached, yo\n", $config->{verbose}, $config->{LOG}, 9);
741                    if (optimize_space($config, $groups, $diff, $size, $cdsize, $cdnum, $gain, $cdlists,0, $i, $list, 'rpm', $all_rpmsize) < $gain) {
742                    log_("put_in_rep: optimize space failed\n", $config->{verbose}, $config->{LOG}, 9);
743                        # done is rebuilt inside optimize_space and the referece needs to be locally updated
744                        if ($config->{list}[$list]{auto}) {
745                            ($curdir, $rep_num) = add_one_disc($cdlists, $group, $cdsize, $list, $cds,0, $size, $i);
746                            if ($curdir) {
747                                $cdnum = $curdir->[0]
748                            } else {
749                                log_("Could not add more disc, rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2);
750                                reject_rpm($rpmd, $list, $group, 'no_disc', '', \$loop);
751                                next
752                            }
753                        } else {
754                            reject_rpm($rpmd, $list, $group, 'no_space', '', \$loop);
755                            next
756                        }
757                    } else {
758                        $done = $groups->[$i]{done}
759                    }
760                } else { 
761                    foreach my $l (@{$config->{disc}[$cdnum]{fastgeneric}}) {
762                        my $lst = $l->[2]{list};
763                        $list == $lst and next;
764                        for (my $grp; $grp < @$groups; $grp++) {
765                            $groups->[$grp]{list}{$lst}{rpm} or next;   
766                            push(@{$needed->[$grp]{$list}{asap}}, [ $lst, 0 ]) if (!($lst->{limit} && $lst->{limit}{soft}))
767                        }
768                    }
769                }
770            } else { next }
771        }
772        if (!$config->{nodeps} && !$group->{options}{nodeps}) {
773            $loop = check_deps($rpmd, $group, $done, $rpmlist, $list, $i, $tobedone->[$i], $buildlist, $rpm, $cdnum, $repname, $needed, $rep_num, $diff)
774        }
775        if ($loop) {
776            $loop = 0 if $loop == 2;   
777            next
778        }       
779        log_("@$rpm deps ok\n", $config->{verbose}, $config->{LOG}, 4);
780        my $nosrc = 1;
781        my @srpm;
782        my $donesrpm = 1;
783        if (!$group->{options}{nosources} && @{$group->{list}{$list}{srpm}}) {
784            for (my $s; $s < @$rpmd; $s++) {
785                my $srpm = $group->{urpm}{sourcerpm}{$rpm->[$s]}; 
786                $srpm =~ s/\.rpm$//;
787                if (!$group->{size}{$srpm}{$list}) {
788                    log_("put_in_rep ERROR: $srpm not available, trying alternatives => ", $config->{verbose}, $config->{LOG}, 5);
789                    my ($srpmname) = $srpm =~ /(.*)-[^-]+-[^-]+\.src/;
790                    $srpm = $group->{srpmname}{$srpmname};
791                    if ($group->{size}{$srpm}{$list}) { 
792                        log_(" $srpm\n", $config->{verbose}, $config->{LOG}) 
793                    } else {
794                        if ($srpm) {
795                            log_("not found (but a srpm $srpm exist in another list)\n", $config->{verbose}, $config->{LOG}, 5);
796                            $srpm = 0
797                        } else {
798                            log_("not found\n", $config->{verbose}, $config->{LOG}, 5) 
799                        }
800                    }
801                }
802                if ($srpm) { 
803                    $done->{rep}{$srpm} or $donesrpm = 0;
804                    $srpm[$s] = $srpm;
805                    $rpmd->[$s][1]{nosrc} or $nosrc = 0 
806                }
807            }
808        }
809        log_("put_in_rep: group $i list $list: @$rpm (@srpm) -- $curdir->[0] -- $curdir->[1] -- disc $cdnum\n", $config->{verbose}, $config->{LOG}, 4);
810        if ($config->{nosrc} || $group->{options}{nosources} || !@{$group->{list}{$list}{srpm}} || $nosrc || $donesrpm) {
811            ($dn) = addRPMToDiff($rpm, $rpmd, $diff, $cdnum, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $i, $relocatable_list, $curdir, $size, $rpmsize, $all_rpmsize, $relocatable_rep_num, $done, $list, $rep_num, $group, $tobedone->[$i])
812        } else {
813            if ($config->{nosrcfit} || $group->{options}{nosrcfit}) {
814                $dn = addRPMToDiff($rpm, $rpmd, $diff, $cdnum, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $i, $relocatable_list, $curdir, $size, $rpmsize, $all_rpmsize, $relocatable_rep_num, $done, $list, $rep_num, $group, $tobedone->[$i]);
815                push @$nosrcfit, [$rpmd, \@srpm, $list, $i, $curdir, $cdnum]
816            } else {
817                my ($srpmrep, $srpmsize, $srpmok) = sourcesSizeCheck($done, $rpmd, \@srpm, $group, $groups, $size, $cdsize, $list, $cdlists, $cdnum, $all_rpmsize, $buildlist, $cds, $i, $diff);
818                $done = $groups->[$i]{done};
819                if ($srpmok) {
820                    addRPMToDiff($rpm, $rpmd, $diff, $cdnum, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $i, $relocatable_list, $curdir, $size, $rpmsize, $all_rpmsize, $relocatable_rep_num, $done, $list, $rep_num, $group, $tobedone->[$i]);
821                    $dn = addSRPMToDiff($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, \@srpm, $list, $i, $cdnum);
822                } else {
823                    log_("WARNING: @srpm does not fit on the discs\n",1, $config->{LOG}, 2)
824                }
825            }
826            if (!$dn) {
827                foreach my $p (@$rpm) { push @{$group->{rejected}{$p}{$list}}, [ $reject_reason || "no_space", ""] }
828                log_("WARNING: @$rpm does not fit on the disc ($size->{disc}[$cdnum] + $all_rpmsize > $cdsize->[$cdnum]) \n", $config->{verbose}, $config->{LOG}, 1)
829            }
830        }
831    }
832    return $dn
833}
834
835sub loop_on_lists {
836    my ($i, $groups, $group, $groupok, $needed, $buildlist, $tobedone, $diff, $nosrcfit, $size, $cdsize, $cds, $rpmlist, $cdlists, $ok, $mark, $groupok) = @_; 
837    #
838    # FIXME source rpms are not shared between group, it may be usefull for mutilple installation
839    # with common source dir, so that the same source rpm is shared (but this is not so common).
840    #
841    my $done = $group->{done};
842    my $rpmd_add = sub {
843        my ($rpm, $rpmd, $r, $list) = @_;
844        log_("Testing $rpm\n", $config->{verbose}, $config->{LOG},7);
845        my $d = $done->{rep}{$rpm};
846        my $reloc = $rpmlist->[$i]{$done->{list}{$rpm}}{$rpm}{relocatable};
847        if (!$d || $rpmlist->[$i]{$done->{list}{$rpm}}{$rpm}{noprovide}) { 
848            push @$rpmd, $r
849        } elsif ($d && $reloc) { 
850            my $curdir = $group->{list}{$list}{rpm}[0];
851            my ($cdnum, $repname) = @$curdir;
852            my $repnum = $group->{orderedrep}{rpm}{"$cdnum/$repname"};
853            if (!check_deps([ $r ], $group, $done, $rpmlist, $list, $i, $tobedone->[$i], $buildlist, $rpm, $cdnum, $repname, $needed, $repnum, $diff)) {
854                add_relocatable_package($group, $d, $diff, $rpm, $done->{list}{$rpm}, $i, $repnum, $repname)
855            }
856            return 0
857        }
858        1
859    };
860    my $dn;
861    log_("loop_on_lists: group $i (@{$group->{orderedlist}{rpm}})\n", $config->{verbose}, $config->{LOG}, 2);
862    while (!$dn) {
863        $groupok->[$i] = 1;
864        foreach my $list (@{$group->{orderedlist}{rpm}}) {
865            my $nb = @{$buildlist->[$i]{$list}} if ref $buildlist->[$i]{$list};
866            log_("loop_on_lists: list $list (empty $config->{list}[$list]{empty} done $config->{list}[$list]{done} nb $nb)\n", $config->{verbose}, $config->{LOG}, 3);
867            do {
868                $config->{list}[$list]{done} and goto end;
869                $config->{list}[$list]{empty} and goto end;
870                my $next;
871                $group->{options}{bypass_sequential} = 0;
872                foreach my $need (@{$needed->[$i]{$list}{asap}}) {
873                    my $nb_elt = @{$buildlist->[$i]{$need->[0]}};
874                    log_("List $list needs list $need->[0] to be <= $need->[1] ($nb_elt)\n", $config->{verbose}, $config->{LOG}, 4);
875                    $nb_elt <= $need->[1] or $next = 1
876                }
877                if ($next) {
878                    log_("List $list waiting\n",1, $config->{LOG},4);
879                    goto end
880                }
881                $needed->[$i]{$list}{asap} = [];
882                my ($trpmd, $k, $goon, @rpmd);
883                do { 
884                    $trpmd = pop @{$buildlist->[$i]{$list}} or goto end;
885                    if (ref $trpmd->[0]) {
886                        foreach (@$trpmd) {
887                            $rpmd_add->($_->[0], \@rpmd, $_, $list) or goto end;
888                        }
889                    } else { $rpmd_add->($trpmd->[0], \@rpmd, $trpmd, $list) or goto end }
890                } until @rpmd;
891                $groupok->[$i] = 0;
892                $ok = 0;
893                my @rpm;
894                my @rpmsize;
895                foreach (@rpmd) {
896                    my $r = $_->[0];
897                    !$r and log_("ERROR loop_on_lists: empty package @$_\n", $config->{verbose}, $config->{LOG}, 2);
898                    push @rpm, $r;
899                    log_("RPM $r (group $i list $list needed $_->[1]{needed})\n", $config->{verbose}, $config->{LOG},6);
900                    $tobedone->[$i]{$r}{$list} = 1;
901                    push @rpmsize, [@{$group->{size}{$r}{$list}}[0,3,4]]
902                }
903                $dn = put_in_rep($i, $groups, $group, $size, \@rpmsize, $cdsize, $needed, \@rpm, \@rpmd, $list, $cdlists, $buildlist, $diff, $cds, $done, $tobedone, $rpmlist, $nosrcfit); 
904                $done = $group->{done};
905                $groupok->[$i] = mark_and_check_lists($groups, $i, $needed, $diff, $buildlist, $rpmlist, $mark, $size, $cdsize, $groupok->[$i], $tobedone) if $group->{options}{sequential}
906            } while @$groups == 1 && $group->{options}{sequential} && !$group->{options}{bypass_sequential} && @{$buildlist->[$i]{$list}};
907            end:
908            last if $group->{options}{sequential} && !$group->{options}{bypass_sequential} && @{$buildlist->[$i]{$list}} && !$config->{list}[$list]{done}
909        }
910        $groupok->[$i] and $dn = 1
911    }
912    $ok
913}
914
915sub calc_needed_size {
916    my ($group, $i, $needed, $needed_size, $buildlist, $rpmlist, $tobedone) = @_;
917    my ($msg, %local_done);
918    $msg = "calc_needed_size\n";
919    my $done = $group->{done};
920    my $all_done = 1;
921    foreach my $rep (@{$group->{replist}{rpm}}) {
922        my ($cd, $repname, $num, $l) = @$rep;
923        $msg .= "calc_needed_size: rep $num\n";
924        my $nb;
925        foreach my $list (keys %$needed) {
926            # 2 ways here, either pre-detect that some list are done and decrease the needed
927            # flag to the greater smaller not done rep, or just let the needed as this but
928            # handle the packages included into this needed list, even if the considered rep
929            # is not including this list. But later the mark_and_check list will see it and automatically schedule
930            # the package for the previous available rep.
931            # $l->{$list} or next;
932            my $ok;
933            $ok ||= $group->{listmatrix}{rpm}{$_}{$list} foreach keys %$l;
934            $ok or next;
935            if ($config->{list}[$list]{disc}{$cd}{$repname}{done} && $all_done) {
936                log_("calc_needed_size: rep $cd/$repname for list $list is done, ignoring\n", $config->{verbose}, $config->{LOG}, 5);
937                next
938            } else {
939                $all_done = 0
940            }
941            foreach my $elt (@{$needed->{$list}{alap}[$num]}) {
942                my $rpm = $elt->[0];
943                if ($done->{rep}{$rpm} && !$rpmlist->[$i]{$group->{done}{list}{$rpm}}{$rpm}{noprovide} || $local_done{$rpm}) {
944                    next
945                }
946                # do not ignore rejected packages for space problem.
947                if ($group->{rejected}{$rpm}{$list} && ! grep { $_->[0] =~ /no_space|no_disc/ } @{$group->{rejected}{$rpm}{$list}}) {
948                    $msg .= "ERROR: $rpm is rejected, ignoring\n" if $config->{verbose} > 5;
949                    next
950                }
951                $needed_size->[$num]{fix} += $group->{size}{$rpm}{$list}[0];
952                $needed_size->[$num]{list}{$list} += $group->{size}{$rpm}{$list}[0];
953                $msg .= "calc_needed_size: list $list rpm $rpm size $group->{size}{$rpm}{$list}[0] (done $done->{rep}{$rpm} needed_size $num $needed_size->[$num]{fix})\n" if $config->{verbose} > 5;
954                $local_done{$rpm} = 1;
955                # FIXME This following code is a simplified version of check_deps. It may be overkill to use
956                # full check_deps as anyway check_deps will be used to put the package at the end.
957                foreach my $deps (@{$group->{pkgdeps}{$rpm}}) {
958                    if (ref $deps) {
959                        $local_done{"@$deps"} and next;
960                        $local_done{"@$deps"} = 1;
961                        my ($r) = choose_alt($deps, $rpmlist->[$i], $group, $cd, $num, $list, $buildlist, {}, $tobedone, $needed, 0);
962                        if ($r && $r != -1) {
963                            next if $done->{rep}{$r} && !$rpmlist->[$i]{$group->{done}{list}{$r}}{$r}{noprovide} || $local_done{$r};
964                            my ($deps_list) = find_list($config, $group, $r);
965                            $local_done{$r} = 1;
966                            $msg .= "calc_needed_size: list $deps_list alt deps $r size $group->{size}{$r}{$deps_list}[0] (done $done->{rep}{$r} needed_size $num $needed_size->[$num]{fix})\n" if $config->{verbose} > 5;
967                            $nb++;
968                            $needed_size->[$num]{var} += $group->{size}{$r}{$deps_list}[0];
969                            $needed_size->[$num]{list}{$list} += $group->{size}{$r}{$deps_list}[0] if $deps_list == $list
970                        }
971                    } else {
972                        my $pkg = $group->{depslistid}[$deps];
973                        next if $done->{rep}{$pkg} && !$rpmlist->[$i]{$group->{done}{list}{$pkg}}{$pkg}{noprovide} || $local_done{$pkg};
974                        $local_done{$pkg} = 1;
975                        my ($deps_list) = find_list($config, $group, $pkg);
976                        $msg .= "calc_needed_size: list $deps_list deps $pkg size $group->{size}{$pkg}{$deps_list}[0] (done $done->{rep}{$pkg} needed_size $num $needed_size->[$num]{fix})\n" if $config->{verbose} > 5;
977                        $nb++;
978                        $needed_size->[$num]{var} += $group->{size}{$pkg}{$deps_list}[0];
979                        $needed_size->[$num]{list}{$list} += $group->{size}{$pkg}{$deps_list}[0] if $deps_list == $list
980                    }
981                }
982            }
983        }
984        # 20060829 warly this factor is aimed at protecting against miscalculation, or to optimistic ones. the more packages the more the error can be important
985        my $val = 1 + (0.0003 * $nb);
986        $msg .= "calc_needed_size: multiplying by $val ($nb)\n" if $config->{verbose} > 5;
987        $needed_size->[$num]{var} *= $val
988    }
989    log_($msg, $config->{verbose}, $config->{LOG}, 4)
990}
991
992sub revert_to {
993    my ($groups, $i, $p2r, $diff, $size, $buildlist, $marks) = @_;
994    log_("revert_to: try to find $p2r (diff $diff $diff->{data} " . int @{$diff->{data}} . " $diff->{idx} " . int @{$diff->{idx}} . "\n", $config->{verbose}, $config->{LOG}, 3);
995    foreach (@{$diff->{data}}) {
996        $_ or next; 
997        foreach (@{$_->[5]}) {
998            goto revert_to_ok if $_->[0] eq $p2r
999        } 
1000    }
1001    log_("ERROR revert_to: $p2r is not present in movement history\n", $config->{verbose}, $config->{LOG}, 2);
1002    return 0;
1003    revert_to_ok:
1004    log_("revert_to: $p2r found\n", $config->{verbose}, $config->{LOG}, 5);
1005    my @keep;
1006    my $idx;
1007    do {
1008        $idx = pop @{$diff->{idx}};
1009        goto revert_to_endloop if any { $_->[0] eq $p2r } @{$diff->{data}[$idx][5]}; 
1010        my $step = $diff->{data}[$idx];
1011        if ($groups->[$i]{conflict}{$step->[1]}) {
1012            my ($curdir, $g, $list) = @$step;
1013            my $cdnum = $curdir->[0];
1014            foreach (@{$step->[5]}) { 
1015                my ($rpm, undef, $t, $rpmsize, $dev, $inode) = @$_; 
1016                delete $groups->[$g]{done}{rep}{$rpm}; 
1017                delete $groups->[$g]{done}{list}{$rpm}; 
1018                # we need to get back the rejected packages, because somme needed packages may have been rejected for space concern,
1019                # if the calculation of the total deps was wrong
1020                delete $groups->[$g]{rejected}{$rpm}{$list};
1021                $diff->{data}[$idx] = 0;
1022                $size->{inode}[$cdnum]{$dev}{$inode}--;
1023                $size->{disc}[$cdnum] -= $rpmsize if !$size->{inode}[$cdnum]{$dev}{$inode};
1024                $size->{rep}{$cdnum}{$curdir->[1]}{$list} -= $rpmsize;
1025                my $m = $marks->[$g]{cur}{$list};
1026                my $m_rpm = $m->[0];
1027                if ($m_rpm eq $rpm) {
1028                    log_("revert_to: current mark for list $list reverted, removing it ($m_rpm)\n", $config->{verbose}, $config->{LOG}, 3);
1029                    delete $marks->[$g]{cur}{$list}
1030                }
1031                log_("revert_to: reverting $rpm (mark $m_rpm) ID $idx for $p2r on cd $cdnum group $g list $list (cd size $size->{disc}[$cdnum])\n", $config->{verbose}, $config->{LOG}, 3);
1032            }
1033            if ($step->[4] == 1) {
1034                log_("revert_to: adding packages back in buildlist for list $list\n", $config->{verbose}, $config->{LOG}, 3);
1035                push @{$buildlist->{$list}}, $step->[6];
1036            }
1037        } else {
1038            unshift @keep, $idx;
1039        }
1040    } while @{$diff->{idx}};
1041    revert_to_endloop:
1042    die "FATAL revert_to: diff data are empty\n" if ! @{$diff->{data}};
1043    push @{$diff->{idx}}, $idx, @keep;
1044    1
1045}
1046
1047sub mark_and_check_lists {
1048    my ($groups, $i, $neededs, $diff, $buildlists, $rpmlist, $marks, $size, $cdsize, $ok, $tobedones, $force_calc) = @_;
1049    my $group = $groups->[$i];
1050    my $need_to_calc = $force_calc;
1051    my %finished;
1052    my $mark = $marks->[$i];
1053    my $buildlist = $buildlists->[$i];
1054    my $needed = $neededs->[$i];
1055    my $tobedone = $tobedones->[$i];
1056    my $groups_number = int @$groups;
1057    my $lists_number = int @{$group->{orderedlist}{rpm}};
1058    log_("mark_and_check_list: ok $ok (groups_number $groups_number, lists_number $lists_number)\n", $config->{verbose}, $config->{LOG}, 3);
1059    foreach my $list (@{$group->{orderedlist}{rpm}}) {
1060        ref $buildlist->{$list} or next;
1061        log_("mark_and_check_list: group $i list $list\n", $config->{verbose}, $config->{LOG}, 3);
1062        if ($config->{list}[$list]{done}) {
1063            log_("mark_and_check_lists: list $list is done, ignoring\n", $config->{verbose}, $config->{LOG}, 5);
1064            $finished{$list} = 1;
1065            next
1066        }
1067        my $nb = @{$buildlist->{$list}};
1068        if (defined $mark->{cur}{$list}) {
1069            log_("mark_and_check_list: mark defined ($mark->{cur}{$list}[0] nb $nb)\n", $config->{verbose}, $config->{LOG},4);
1070            $need_to_calc = 1;
1071            my $m = $mark->{cur}{$list};
1072            my $m_rpm = $m->[0];
1073            if ($group->{done}{rep}{$m_rpm}) {
1074                log_("mark_and_check_list: $m_rpm done, deleting mark for list $list\n", $config->{verbose}, $config->{LOG},4);
1075                push @{$mark->{his}},  $m;
1076                delete $mark->{cur}{$list}
1077            } elsif ($group->{rejected}{$m_rpm}{$list}) {
1078                log_("mark_and_check_list: $m_rpm rejected, deleting mark for list $list\n", $config->{verbose}, $config->{LOG},4);
1079                delete $mark->{cur}{$list}
1080            } elsif (!$nb) {
1081                log_("mark_and_check_list: list $list finished and $m_rpm not done or rejected\n",$config->{verbose}, $config->{LOG},4);
1082                $finished{$list} = 1;
1083                delete $mark->{cur}{$list}
1084            }
1085        }
1086        if (!defined $mark->{cur}{$list} && @{$buildlist->{$list}}) {
1087            $ok = 0;
1088            my $rpm;
1089            for (my $j = $#{$buildlist->{$list}}; $j >= 0; $j--) {
1090                my $t = $buildlist->{$list}[$j];
1091                $rpm = ref $t->[0] ? $t->[0] : $t;
1092                if (!$group->{done}{rep}{$rpm->[0]} || $rpm->[1]{relocatable} || $rpm->[1]{noprovide}) {
1093                    last
1094                } 
1095                # this is not necessary, but when we are at it...
1096                log_("mark_and_check_list: $rpm->[0] done (list $group->{done}{list}{$rpm->[0]} rep $group->{done}{rep}{$rpm->[0]}), removing from queue\n", $config->{verbose}, $config->{LOG},4);
1097                pop @{$buildlist->{$list}}
1098            }
1099            $mark->{cur}{$list} = $rpm;
1100            log_("mark_and_check_list: marking $rpm->[0] for $list\n", $config->{verbose}, $config->{LOG},3);
1101        }
1102        if (!$nb) {
1103            log_("mark_and_check_list: list $list is finished\n",$config->{verbose}, $config->{LOG},4);
1104            $finished{$list} = 1
1105        }
1106    }
1107    my $all_finished = 1;
1108    foreach my $list (@{$group->{orderedlist}{rpm}}) {
1109        $all_finished = 0 if !$finished{$list}
1110    }
1111    # recalc once at the end to be sure no needed packages have been rejected for space problems
1112
1113    my $calc ;
1114    if ($all_finished) {
1115        $need_to_calc = 1;
1116        $calc = 1;
1117        $mark->{needed_size} = [];
1118        calc_needed_size($group, $i, $needed, $mark->{needed_size}, $buildlist, $rpmlist, $tobedone);
1119    }
1120
1121    my $ok2 = $ok && $all_finished;
1122    log_("mark_and_check_list: ok $ok2 ($all_finished)\n", $config->{verbose}, $config->{LOG}, 3);
1123    my $recalc;
1124    while ($need_to_calc) {
1125        log_("mark_and_check_list: loop\n", $config->{verbose}, $config->{LOG}, 10);
1126        $need_to_calc = 0;
1127        $ok2 = $ok && $all_finished;
1128        my $needed_size = $mark->{needed_size};
1129        my ($need_in_rep, $av_in_rep, $total_av_in_rep, %done_disc, %list_av_in_rep, %list_need_in_rep) = (0, 0, 0, {}, {}, {});
1130        #
1131        # First impression would have been to check needed in reverse order, because we could imagine
1132        # that, in the current configuration, if needed 2 does not fit, for exemple, one package
1133        # is removed, needed 2 is put. But if needed does not fit at this moment, needed 2 is removed,
1134        # and needed 3 put, then needed 2 does not fit, and it is needed to revert more to make both of
1135        # them fit.
1136        #
1137        # If fact this could not happen, because if needed 3 does not fit when needed 2 has just been
1138        # put, this mean that the calc_needed_size is bogus.
1139        #
1140        my %count_total;
1141        my $factor;
1142        my $no_list_calc;
1143        if ($all_finished) {
1144            # force to recalculate the list needed package left (but this is supposed to be empty
1145            $av_in_rep = 0;
1146            $no_list_calc = 1;
1147            $calc = $recalc;
1148            $recalc = 1;
1149            log_("mark_and_check_list: all list finished\n", $config->{verbose}, $config->{LOG}, 4);
1150            foreach my $list (@{$group->{orderedlist}{rpm}}) {
1151                $list_av_in_rep{$list} = 0
1152            }
1153        }
1154        foreach my $rep (@{$group->{replist}{rpm}}) {
1155            my ($cd, $repname, $num, $replist_list) = @$rep;
1156            $need_in_rep += $needed_size->[$num]{var} + $needed_size->[$num]{fix};
1157            my $totalsize = $cdsize->[$cd] - $size->{fixed}[$cd];
1158            my $cdbasesize = $totalsize;
1159            if (!$no_list_calc && !$done_disc{$cd}{$repname}) {
1160                # We need to take care of the rep size limit to add needed package just after the limit is reached, and not
1161                # at the end of the CD.
1162                # As several list can be in the rep, with each specific limit, we need to
1163                # compute an overall size estimation
1164                foreach my $list (keys %{$replist_list}) {
1165                    my $max = defined $replist_list->{$list}[2]{limit}{size} ? $replist_list->{$list}[2]{limit}{size} : $totalsize;
1166                    $factor += defined $replist_list->{$list}[2]{limit}{value} ? $replist_list->{$list}[2]{limit}{value} : 1;
1167                    $max = $totalsize if $max > $totalsize;
1168                    $totalsize -= $max;
1169                    $totalsize = 0 if $totalsize < 0;
1170                    my $space = $max - $size->{rep}{$cd}{$repname}{$list};
1171                    $av_in_rep += $space;
1172                    $list_av_in_rep{$list} += $space;
1173                    $list_need_in_rep{$list} += $needed_size->[$num]{list}{$list};
1174                    if (!$count_total{$cd}) {
1175                        $total_av_in_rep += $cdsize->[$cd] - $size->{disc}[$cd];
1176                        $count_total{$cd} = 1;
1177                    } 
1178                    log_("mark_and_check_list: list $list cd $cd rep $repname totalsize $totalsize max $max av_in_rep $av_in_rep repsize $size->{rep}{$cd}{$repname}{$list} total_av_in_rep ($total_av_in_rep) factor $factor (cdsize $cd $cdsize->[$cd] size $cd $size->{disc}[$cd])\n", $config->{verbose}, $config->{LOG}, 4);
1179                }
1180               
1181                $done_disc{$cd}{$repname} = 1
1182            }
1183           
1184            # this should be better handled, I think the total_av_in_space should
1185            # only override the av_in_rep at the end of the build, when the space
1186            # available become less than 10 % the size of the CD.
1187            # More or less this are two different way to perform the same calculus
1188            # so it must have a problem if the results are different (we can have
1189            # a bogus configuration file which leads to invalid results, though, moreover
1190            # in case of dual arch CDs with shared packages, the real size of the CD
1191            # can be 30 % bigger than the physical size of the CD).
1192            #
1193            # maybe should we ponder the space available given the rep limit (if they exists)
1194            my $invfactor;
1195            if ($factor) {
1196                $invfactor = 1 / $factor
1197            } else {
1198                $invfactor = @$groups
1199            } 
1200            my $real_left_size = $total_av_in_rep/$invfactor;
1201            if ($av_in_rep > $real_left_size) {
1202                my $diff = $av_in_rep - $real_left_size;
1203                $av_in_rep = $av_in_rep - ($diff * ($cdbasesize - $real_left_size)/$cdbasesize)
1204            }
1205            $av_in_rep = 0 if $av_in_rep < 0;
1206            #if ((($total_av_in_rep/$invfactor) < ($cdbasesize/5) && ($total_av_in_rep/$invfactor) < $av_in_rep) || $av_in_rep < 0) { $av_in_rep = ($total_av_in_rep/$invfactor) }
1207            log_("mark_and_check_list: cd $cd 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]) factor $factor calc $calc recalc $recalc\n", $config->{verbose}, $config->{LOG}, 4);
1208            # Later the difference between fix and var may be used, but right now there is no code to be sure a package is on a specific disc.
1209            # if ($need_in_rep && $need_in_rep > $av_in_rep || $needed_size->[$num]{fix} > $cdsize->[$cd] - $size->{disc}[$cd])
1210            my $list_lack_space;
1211            foreach my $list (@{$group->{orderedlist}{rpm}}) {
1212                my $space = $av_in_rep < $list_av_in_rep{$list} ? $av_in_rep : $list_av_in_rep{$list};
1213                $space < 0 and $space = 0;
1214                log_("mark_and_check_list: list $list need_in_repcd $list_need_in_rep{$list} av_in_rep $space\n", $config->{verbose}, $config->{LOG}, 4);
1215                $list_lack_space = 1 if $list_need_in_rep{$list} > $space
1216            }
1217            if ($need_in_rep || $list_lack_space) {
1218                $ok2 = 0;
1219                $ok = 0
1220            }
1221            if ($need_in_rep && $need_in_rep > $av_in_rep || $list_lack_space) {
1222                if (!$calc) {
1223                    $ok = 0;
1224                    $calc = 1;
1225                    $mark->{needed_size} = [];
1226                    calc_needed_size($group, $i, $needed, $mark->{needed_size}, $buildlist, $rpmlist, $tobedone);
1227                    $need_to_calc = 1;
1228                    last
1229                }
1230                ref $mark->{his} or die "FATAL mark_and_check_list: needed packages will not fit on discs ($need_in_rep in disc $cd rep $num > $av_in_rep available)\n";
1231                # TODO check if that is necessary or not
1232                # I think it may have been when the choose_alt bug of not rejecting packages when all the alternatives are rejected.
1233                #pop @{$mark->{his}} if @{$mark->{his}};
1234                my $p2r;
1235                #$p2r = pop @{$mark->{his}};
1236                while (($p2r = pop @{$mark->{his}}) && defined $p2r->[1]{needed} && $p2r->[1]{needed} <= $num) { log_("mark_and_check_list: grepping packages history $p2r->[0] needed $p2r->[1]{needed} history " . (int @{$mark->{his}}) . " \n", $config->{verbose}, $config->{LOG}, 3); }
1237                my $countdown = $mark->{cd}{$cd}{$p2r->[0]};
1238                $p2r = pop @{$mark->{his}} while @{$mark->{his}} && $countdown-- >= 0;
1239                if (!$p2r || !$p2r->[0] || $mark->{cd}{$cd}{$p2r->[0]} > ($groups_number * $lists_number * 2)) {
1240                    die "FATAL mark_and_check_list: a previous revert to put needed packages failed, cannot order packages correctly for rep $num on disc $cd\n"
1241                } else {
1242                    log_("mark_and_check_list: not enough space for needed in rep $num on disc $cd\n", $config->{verbose}, $config->{LOG}, 3);
1243                    $mark->{cd}{$cd}{$p2r->[0]}++;
1244                    log_("mark_and_check_list: trying to revert $p2r->[0]\n", $config->{verbose}, $config->{LOG}, 4);
1245                    if (revert_to($groups, $i, $p2r->[0], $diff, $size, $buildlist, $marks)) {
1246                        $all_finished = 0;
1247                        $ok2 = 0;
1248                        $ok = 0;
1249                        log_("mark_and_check_list: $p2r->[0] reverted\n", $config->{verbose}, $config->{LOG}, 3);
1250                        foreach my $idx (0 .. @{$group->{orderedlist}{rpm}}) {
1251                            my $list = $group->{orderedlist}{rpm}[$idx];
1252                            if (!$needed->{$list}) {
1253                                if ($group->{options}{sequential}) {
1254                                    log_("mark_and_check_list: no needed in list $list, temporarly bypassing sequential mode to let other list catching up\n", $config->{verbose}, $config->{LOG}, 6);
1255                                    $group->{options}{bypass_sequential} = 1
1256                                }
1257                                next;
1258                            }
1259                            my $elt;
1260                            my $todo;
1261                            foreach my $tr (1 .. $num) {
1262                                foreach my $elt (@{$needed->{$list}{alap}[$tr]}) {
1263                                    my $rpm = $elt->[0];
1264                                    if ($group->{rejected}{$rpm}{$list} && ! grep { $_->[0] =~ /no_space|no_disc/ } @{$group->{rejected}{$rpm}{$list}}) {
1265                                        log_("ERROR: $rpm is rejected, ignoring\n", $config->{verbose}, $config->{LOG}, 6);
1266                                        next
1267                                    }
1268                                    if (!$group->{done}{rep}{$rpm}) {
1269                                        $todo = 1;
1270                                        push @{$buildlist->{$list}}, $elt;
1271                                    }
1272                                }
1273                            }
1274                            if ($group->{options}{sequential} && !$todo) {
1275                                log_("mark_and_check_list: no more needed for list $list bypassing temporarly sequential mode to let other list catching up\n", $config->{verbose}, $config->{LOG}, 6);
1276                                $group->{options}{bypass_sequential} = 1
1277                            } else {
1278                                $mark->{cur}{$list} = $elt->[0];
1279                                my $l_idx = $#{$buildlist->{$list}};
1280                                next if $l_idx < 0;
1281                                foreach my $tidx ($idx + 1 .. @{$group->{orderedlist}{rpm}}) {
1282                                    my $l = $group->{orderedlist}{rpm}[$tidx];
1283                                    # we must be carefull not to add a interlock between list
1284                                    if (!(defined $needed->{$list}{asap} && grep { $_->[0] == $l } @{$needed->{$list}{asap}})) {
1285                                        push(@{$needed->{$l}{asap}}, [ $list, $l_idx ]) if !$config->{list}[$list]{done}
1286                                    }
1287                                }
1288                            }
1289                        }
1290                        $need_to_calc = 1;
1291                        # need to recalc for all the groups caus we can have removed packages from the other groups
1292                        for (my $grp_i; $grp_i < @$groups; $grp_i++ ) {
1293                            #$grp_i == $i and next;
1294                            $marks->[$grp_i]{needed_size} = [];
1295                            calc_needed_size($groups->[$grp_i], $grp_i, $neededs->[$grp_i], $marks->[$grp_i]{needed_size}, $buildlists->[$grp_i], $rpmlist, $tobedones->[$grp_i]);
1296                        }
1297                        last
1298                    } else {
1299                        log_("ERROR mark_and_check_list: reverting to $p2r->[0] failed\n", $config->{verbose}, $config->{LOG}, 4)
1300                    }
1301                }
1302            }
1303        }
1304    }
1305    log_("mark_and_check_list: return ok $ok2\n", $config->{verbose}, $config->{LOG}, 4);
1306    $ok2
1307}
1308
1309# TODO the algo is not as beautiful as it should be
1310# ... but it is getting better
1311# ... and better
1312sub buildDiscs {
1313    my ($class, $groups, $buildlist, $rpmlist, $groupok, $size, $cdsize, $cdlists, $cds, $needed, $diff, $n) = @_;
1314    log_("buildDiscs\n", $config->{verbose}, $config->{LOG}, 3);
1315    $config = $class->{config};
1316    if ($n > 1) {
1317        foreach my $i (reverse @$cds) {
1318            $size->{optimize_space}{disc}{$i} = $size->{disc}[$i];
1319            if ($size->{disc}[$i] > $cdsize->[$i]) { 
1320                my $gain = ($size->{disc}[$i] - $cdsize->[$i])/2;
1321                next if $gain < 0;
1322                optimize_space($config, $groups, $diff, $size, $cdsize, $i, $gain, $cdlists,1)
1323            } else {
1324                log_("buildDiscs: disc $i size OK $size->{disc}[$i] ($cdsize->[$i])\n", $config->{verbose}, $config->{LOG},2)
1325            }
1326        }
1327    }
1328    my ($ok, $iti);
1329    my @groupok;
1330    my (@tobedone, @nosrcfit);
1331    my @mark;# = ({}) x @$groups;
1332    $mark[$_] = {} foreach (0 .. $#$groups);
1333    updateGenericLimit($groups, $cdsize);
1334    if (!$config->{fast}) {
1335        # need to initialise needed data
1336        for (my $i = 0; $i < @$groups; $i++) {
1337            my $group = $groups->[$i];
1338            $mark[$i]{needed_size} = [];
1339            calc_needed_size($group, $i, $needed->[$i], $mark[$i]{needed_size}, $buildlist->[$i], $rpmlist, $tobedone[$i]) 
1340        }
1341    }
1342    while (!$ok) {
1343        log_("iti: " . $iti++ . "\n", $config->{verbose}, $config->{LOG},4);
1344        $ok = 1;
1345        for (my $i = 0; $i < @$groups; $i++) {
1346            my $group = $groups->[$i];
1347            if (!$config->{fast}) {
1348                $groupok[$i] = mark_and_check_lists($groups, $i, $needed, $diff, $buildlist, $rpmlist, \@mark, $size, $cdsize, $groupok[$i], \@tobedone, !$groupok[$i]);
1349                $ok &&= $groupok[$i];
1350                $groupok[$i] and next;
1351            }
1352            my $ok2 = loop_on_lists($i, $groups, $group, \@groupok, $needed, $buildlist, \@tobedone, $diff, \@nosrcfit, $size, $cdsize, $cds, $rpmlist, $cdlists, $ok, \@mark, \@groupok); 
1353            if ($config->{fast}) {
1354                $ok &&= $ok2
1355            }
1356        }
1357    }
1358    foreach (@nosrcfit) {
1359        my ($rpmd, $srpm, $list, $i, $curdir, $cdnum) = @$_;
1360        my $group = $groups->[$i];
1361        my $done = $group->{done};
1362        my ($srpmrep, $srpmsize, $srpmok) = sourcesSizeCheck($done, $rpmd, $srpm, $group, $groups, $size, $cdsize, $list, $cdlists,0,0, $buildlist, $cds, $i, $diff);
1363        if ($srpmok) {
1364            addSRPMToDiff($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, $srpm, $list, $i, $cdnum);
1365        } else {
1366            log_("WARNING: @$srpm does not fit on the discs\n",1, $config->{LOG},2)
1367        }
1368    }
1369    log_("buildDiscs: rejected packages\n", $config->{verbose}, $config->{LOG},2);
1370    my $is_rejected;
1371    my $fatal;
1372    my @needed_list;
1373    for (my $i; $i < @$groups; $i++) {
1374        reprocess_relocatable($groups->[$i], $cdsize, $size);
1375        $groups->[$i]{rejected} or next;
1376        foreach my $rpm (%{$groups->[$i]{rejected}}) {
1377            my $local_rejected;
1378            my $gh = $groups->[$i]{rejected}{$rpm};
1379            my $msg;
1380            my $is_needed;
1381            foreach my $list (keys %$gh) {
1382                $rpmlist->[$i]{$list}{$rpm}{limit} and next;
1383                $msg .= " [ list $list ] ";
1384                $local_rejected ||= any { $msg .= "$config->{rejected_options}{$_->[0]}: $_->[1],"; $_->[0] =~ /no_disc/ || $_->[0] =~ /no_space/ } @{$gh->{$list}};
1385                $is_needed ||= $rpmlist->[$i]{$list}{$rpm}{needed} if $rpmlist->[$i]{$list}{$rpm}{needed} < $groups->[$i]{maxrep}{rpm};
1386                chop $msg
1387            }
1388            if ($is_needed) {
1389                log_("ERROR buildDisc: group $i REJECTED NEEDED $rpm ($msg)\n", $config->{verbose}, $config->{LOG}, 4) if $local_rejected;
1390                push @needed_list,  "$rpm ($msg)";
1391                $fatal = 1
1392            } else {
1393                log_("WARNING buildDisc: group $i REJECTED $rpm ($msg)\n", $config->{verbose}, $config->{LOG}, 4) if $local_rejected
1394            }
1395            $is_rejected ||= $local_rejected
1396        }
1397    }
1398    if ($fatal) {
1399        if ($config->{missing_non_fatal}) {
1400                log_("ERROR buildDisc: needed packages rejected:\n". join("\n", sort @needed_list)."\n", $config->{verbose}, $config->{LOG}, 4);
1401            } else {
1402                die("FATAL buildDisc: needed packages rejected:\n", join("\n", sort @needed_list)."\n")
1403            }
1404    }
1405    ($is_rejected)
1406}
1407
1408sub reprocess_relocatable {
1409    my ($group, $cdsize, $size) = @_;
1410    # optimize number of hdlist given available space
1411    # and
1412    # add virtual media for installation
1413    my $inst_disc = $group->{installDisc};
1414    my $inst = $group->{installation};
1415    $inst->[1]{tmp_rpmsdir} = [];
1416    my %ignore;
1417    foreach my $c (@{$inst->[1]{rpmsdir}}) {
1418        my ($ls, $cdrep, $repname, $opts) = @$c;
1419        my $min;
1420        my $ok = 1;
1421        my $repnum = $group->{orderedrep}{rpm}{"$cdrep/$repname"};
1422        log_("reprocess_relocatable: list $ls cd $cdrep repname $repname repnum $repnum\n", $config->{verbose}, $config->{LOG},3);
1423        if (defined $group->{reploc}{$repnum} && ref $group->{reploc}{$repnum}{list}) {
1424            while ($ok) {
1425                $ok = 0;
1426                log_("reprocess_relocatable: disc usage $size->{disc}[$cdrep] (disc size $cdsize->[$cdrep])\n", $config->{verbose}, $config->{LOG},6);
1427                $min = $cdsize->[$cdrep];
1428                my $idx;
1429                for (my $i; $i < @{$group->{reploc}{$repnum}{list}}; $i++) {
1430                    my $r = $group->{reploc}{$repnum}{list}[$i];
1431                    my ($new_repnum) = @$r;
1432                    log_("reprocess_relocatable: new rep_num $new_repnum size $group->{reploc}{$repnum}{size}{$new_repnum} old $group->{reploc}{$repnum}{old}{$new_repnum}\n", $config->{verbose}, $config->{LOG},6);
1433                    if (!$ignore{$new_repnum} && $group->{reploc}{$repnum}{old}{$new_repnum} && $group->{reploc}{$repnum}{size}{$new_repnum} < $min) {
1434                        log_("reprocess_relocatable: min $min idx $i\n", $config->{verbose}, $config->{LOG},6);
1435                        $min = $group->{reploc}{$repnum}{size}{$new_repnum};
1436                        $idx = $i
1437                    }
1438                }
1439                if ($size->{disc}[$cdrep] + $min < $cdsize->[$cdrep]) {
1440                    $ok = 1;
1441                    # FIXME 20060712 warly this may not be correct with the inode checking code ($min should be checked in multi installation CD not to contain packages
1442                    # which are still in other rep on the same CD)
1443                    $size->{disc}[$cdrep] += $min;
1444                    my $prev = $group->{reploc}{$repnum}{list}[$idx-1][0] if $idx > 0;
1445                    my $current = $group->{reploc}{$repnum}{list}[$idx][0];
1446                    my $next = $group->{reploc}{$repnum}{list}[$idx+1][0] if $idx < @{$group->{reploc}{$repnum}{list}};
1447                    if (!$prev) { $prev = $next; $next = 0 }
1448                    my $curdir = $group->{reploc}{$repnum}{curdir}{$prev};
1449                    my $list = $group->{reploc}{$repnum}{newlist}{$prev};
1450                    my $reallist = $group->{reploc}{$repnum}{reallist}{$prev};
1451                    log_("reprocess_relocatable: aggregating $current and $next with $prev list $list reallist $reallist\n", $config->{verbose}, $config->{LOG},6);
1452                    foreach my $rep ($current, $next) {
1453                        $rep or next;
1454                        foreach my $diff_data (@{$group->{reploc}{$repnum}{diff}{$rep}}) {
1455                            log_("reprocess_relocatable: updating diff for rep $rep rpm $diff_data->[5][0][0] (cd $curdir->[0] rep $curdir->[1])\n", $config->{verbose}, $config->{LOG},6);
1456                            $diff_data->[0] = $curdir;
1457                            $diff_data->[2] = $list;
1458                            $diff_data->[3] = $prev;
1459                            $diff_data->[8] = $reallist;
1460                            push @{$group->{reploc}{$repnum}{diff}{$prev}}, $diff_data
1461                        }
1462                    }
1463                    $group->{reploc}{$repnum}{list} = [ grep { $_->[0] != $current && $_->[0] != $next } @{$group->{reploc}{$repnum}{list}} ]
1464                }
1465            }
1466            my $fct = $config->{disc}[$cdrep]{function}{data}{generic}{$repname}[0];
1467            foreach my $r (@{$group->{reploc}{$repnum}{list}}) {
1468                my ($new_repnum, $cd, $repname, $newlist) = @$r;
1469                log_("reprocess_relocatable: new rep_num $new_repnum on cd $cd rep $repname list $newlist\n", $config->{verbose}, $config->{LOG},3);
1470                push @{$inst->[1]{tmp_rpmsdir}}, [ $newlist, $cd, $repname ];
1471                push @{$fct->[1]{lists}}, $newlist
1472            }
1473        }
1474        push @{$inst->[1]{tmp_rpmsdir}}, $c
1475    }
1476    my $i;
1477    foreach my $r (@{$inst->[1]{tmp_rpmsdir}}) {
1478        log_("reprocess_relocatable: testing $r->[1]/$r->[2] ($i)\n", $config->{verbose}, $config->{LOG},3);
1479        if (defined $inst->[1]{boot_medium} && $r->[1] == $inst_disc && $i) {
1480            log_("reprocess_relocatable: boot disc is $i\n", $config->{verbose}, $config->{LOG},3);
1481            $inst->[1]{boot_medium} = $i + 1;
1482            last
1483        }
1484        $i++
1485    }
1486}
1487
1488sub processDiff {
1489    my ($class, $groups, $diff, $discsFiles) = @_;
1490    my (@cd, @action);
1491    my %new;
1492    my $prev = $diff->{previous_idx} || {};
1493    foreach (@{$diff->{idx}}) {
1494        push @{$action[1]}, $_ if !$prev->{$_};
1495        $new{$_} = 1
1496    }
1497    foreach (keys %$prev) {
1498        push @{$action[2]}, $_ if !$new{$_}
1499    }
1500    foreach my $op (2,1) {
1501        foreach my $idx (@{$action[$op]}) {
1502            my $d = $diff->{data}[$idx];
1503            if (!$d) { log_("ERROR processDiff: THIS MUST NOT HAPPEN action is null ($d) op $op idx $idx\n", $config->{verbose}, $config->{LOG}); next }
1504            my ($curdir, $grp, $list, undef, undef, $data, undef, undef, $reallist) = @$d;
1505            my $cd = $curdir->[0];
1506            foreach my $ent (@$data) {
1507                my $rpm = $ent->[0];
1508                if (!$rpm) {
1509                    foreach (@$ent) {
1510                        if (ref $_) { log_("ERROR processDiff: @$_\n", $config->{verbose}, $config->{LOG},2) }
1511                        else { log_("ERROR processDiff: $_\n", $config->{verbose}, $config->{LOG},2) }
1512                    }
1513                }
1514                $rpm or next;
1515                my $source = $groups->[$grp]{size}{$rpm}{$reallist || $list}[1];
1516                log_("LOG disc $cd/$curdir->[1] list $list ($reallist) group $grp: ($op) $rpm ($source)\n", $config->{verbose}, $config->{LOG},3);
1517                push @{$cd[$cd]{$curdir->[1]}{$list}{$source}}, [$op, "$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}.rpm"];
1518                if ($op == 1) { $discsFiles->[$cd]{$curdir->[1]}{$list}{$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}} = $source }
1519                elsif ($op == 2) { delete $discsFiles->[$cd]{$curdir->[1]}{$list}{$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}} }
1520            }
1521        }
1522    }
1523    my %new_diff;
1524    # clear diff
1525    foreach my $idx (@{$diff->{idx}}) {
1526        my $nidx = push @{$new_diff{data}}, $diff->{data}[$idx];
1527        push @{$new_diff{idx}}, $nidx - 1;
1528        $new_diff{previous_idx}{$nidx - 1} = 1
1529    }
1530    return \@cd, \%new_diff
1531}
1532
15331
1534
1535# Changelog
1536#
1537# 2002 02 21
1538# change false $j comparaison to $depsdisc in buildDisc to new $thisorderrep value.
1539#
1540# 2002 03 03
1541# new limit option handling.
1542# add updateGenericSoft function
1543# add testSoftLimit function
1544# update size to check rep size
1545#
1546# 2002 03 08
1547# fix autoMode CD adding
1548#
1549# 2002 03 13
1550# better selection of alternatives in multi-list to take the one in the first lists.
1551#
1552# 2002 03 14
1553# add sources new sources handling method
1554# in nosrcfit mode sources are added afterwards
1555#
1556# 2002 03 19
1557# add prelist in geList for cdcom, will be useful for oem too I guess.
1558#
1559# 2002 05 02
1560# add_one_disc: add separate mode for sources mode
1561#
1562# 2002 05 09
1563# add graft structure for md5 and graft point handling
1564#
1565# 2002 05 13
1566# fix a tricky bugs in build_list about fentry shared and not recreated for each packages.
1567#
1568# 2002 06 01
1569# use perl-URPM
1570#
1571# 2002 06 15
1572# new diff mode, global, shared between disc and group, only one table.
1573#
1574# 2002 08 16
1575# new diff_idx table to sort diff data
1576#
1577# 2002 08 24
1578# optimize_space first version, still need to handle correctly needed and more advanced optimization methods.
1579#
1580# 2002 09 18
1581# optimize_space work, fixes and updates.
1582#
1583# 2002 10 25
1584# fix needed assignation pb in closeRPMslist
1585#
1586# 2004 05 27
1587# separate List.pm into List.pm and Build.pm
1588#
1589# 2004 05 28
1590# move find_list to tools as it is used in both Build and List
1591#
1592# 20060711
1593# Must have a inode check mechanism for the CD space calculation. This is more complex than that because space is used at several place to check for example current repository space used, or to check generic space limit enforcement.
1594#
Note: See TracBrowser for help on using the repository browser.