source: soft/build_system/build_system/mkcd/tags/V3_8_0_1mdk/pm/Mkcd/List.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: 85.5 KB
Line 
1package Mkcd::List;
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_);
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
18List - mkcd module
19
20=head1 SYNOPSYS
21
22    require Mkcd::List;
23
24=head1 DESCRIPTION
25
26C<mkcd::List> include the mkcd packages list functions.
27
28=head1 SEE ALSO
29
30mkcd
31
32=head1 COPYRIGHT
33
34Copyright (C) 2000 MandrakeSoft <warly@mandrakesoft.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 processDiff {
63    my ($class, $groups, $diff, $discsFiles) = @_;
64    my (@cd, @action);
65    my %new;
66    my $prev = $diff->{previous_idx} || {};
67    foreach (@{$diff->{idx}}) {
68        push @{$action[1]}, $_ if !$prev->{$_};
69        $new{$_} = 1
70    }
71    foreach (keys %$prev) {
72        push @{$action[2]}, $_ if !$new{$_}
73    }
74    foreach my $op (2,1) {
75        foreach my $idx (@{$action[$op]}) {
76            my $d = $diff->{data}[$idx];
77            if (!$d) { log_("ERROR processDiff: THIS MUST NOT HAPPEN action is null ($d) op $op idx $idx\n", $config->{verbose}, $config->{LOG}); next }
78            my ($curdir, $grp, $list, undef, undef, $data, undef, undef, $reallist) = @$d;
79            my $cd = $curdir->[0];
80            foreach my $ent (@$data) {
81                my $rpm = $ent->[0];
82                if (!$rpm) {
83                    foreach (@$ent) {
84                        if (ref $_) { log_("ERROR processDiff: @$_\n", $config->{verbose}, $config->{LOG},2) }
85                        else { log_("ERROR processDiff: $_\n", $config->{verbose}, $config->{LOG},2) }
86                    }
87                }
88                $rpm or next;
89                my $source = $groups->[$grp]{size}{$rpm}{$reallist || $list}[1];
90                log_("LOG disc $cd/$curdir->[1] list $list ($reallist) group $grp: ($op) $rpm ($source)\n", $config->{verbose}, $config->{LOG},3);
91                push @{$cd[$cd]{$curdir->[1]}{$list}{$source}}, [$op, "$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}.rpm"];
92                if ($op == 1) { $discsFiles->[$cd]{$curdir->[1]}{$list}{$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}} = $source }
93                elsif ($op == 2) { delete $discsFiles->[$cd]{$curdir->[1]}{$list}{$groups->[$grp]{urpm}{rpmkey}{rpm}{$rpm}} }
94            }
95        }
96    }
97    my %new_diff;
98    # clear diff
99    foreach my $idx (@{$diff->{idx}}) {
100        my $nidx = push @{$new_diff{data}}, $diff->{data}[$idx];
101        push @{$new_diff{idx}}, $nidx - 1;
102        $new_diff{previous_idx}{$nidx - 1} = 1
103    }
104    return \@cd, \%new_diff
105}
106
107sub reprocess_relocatable {
108    my ($group, $cdsize, $size) = @_;
109    # optimize number of hdlist given available space
110    # and
111    # add virtual media for installation
112    my $inst_disc = $group->{installDisc};
113    my $inst = $config->{disc}[$inst_disc]{function}{data}{installation};
114    $inst->[1]{tmp_rpmsdir} = [];
115    my %ignore;
116    foreach my $c (@{$inst->[1]{rpmsdir}}) {
117        my ($ls, $cdrep, $repname, $opts) = @$c;
118        my $min;
119        my $ok = 1;
120        my $repnum = $group->{orderedrep}{rpm}{"$cdrep/$repname"};
121        log_("reprocess_relocatable: list $ls cd $cdrep repname $repname repnum $repnum\n", $config->{verbose}, $config->{LOG},3);
122        if (defined $group->{reploc}{$repnum} && ref $group->{reploc}{$repnum}{list}) {
123            while ($ok) {
124                $ok = 0;
125                log_("reprocess_relocatable: disc usage $size->{disc}[$cdrep] (disc size $cdsize->[$cdrep])\n", $config->{verbose}, $config->{LOG},6);
126                $min = $cdsize->[$cdrep];
127                my $idx;
128                for (my $i; $i < @{$group->{reploc}{$repnum}{list}}; $i++) {
129                    my $r = $group->{reploc}{$repnum}{list}[$i];
130                    my ($new_repnum) = @$r;
131                    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);
132                    if (!$ignore{$new_repnum} && $group->{reploc}{$repnum}{old}{$new_repnum} && $group->{reploc}{$repnum}{size}{$new_repnum} < $min) {
133                        log_("reprocess_relocatable: min $min idx $i\n", $config->{verbose}, $config->{LOG},6);
134                        $min = $group->{reploc}{$repnum}{size}{$new_repnum};
135                        $idx = $i
136                    }
137                }
138                if ($size->{disc}[$cdrep] + $min < $cdsize->[$cdrep]) {
139                    $ok = 1;
140                    $size->{disc}[$cdrep] += $min;
141                    my $prev = $group->{reploc}{$repnum}{list}[$idx-1][0] if $idx > 0;
142                    my $current = $group->{reploc}{$repnum}{list}[$idx][0];
143                    my $next = $group->{reploc}{$repnum}{list}[$idx+1][0] if $idx < @{$group->{reploc}{$repnum}{list}};
144                    if (!$prev) { $prev = $next; $next = 0 }
145                    my $curdir = $group->{reploc}{$repnum}{curdir}{$prev};
146                    my $list = $group->{reploc}{$repnum}{newlist}{$prev};
147                    my $reallist = $group->{reploc}{$repnum}{reallist}{$prev};
148                    log_("reprocess_relocatable: aggregating $current and $next with $prev list $list reallist $reallist\n", $config->{verbose}, $config->{LOG},6);
149                    foreach my $rep ($current, $next) {
150                        $rep or next;
151                        foreach my $diff_data (@{$group->{reploc}{$repnum}{diff}{$rep}}) {
152                            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);
153                            $diff_data->[0] = $curdir;
154                            $diff_data->[2] = $list;
155                            $diff_data->[3] = $prev;
156                            $diff_data->[8] = $reallist;
157                            push @{$group->{reploc}{$repnum}{diff}{$prev}}, $diff_data
158                        }
159                    }
160                    $group->{reploc}{$repnum}{list} = [ grep { $_->[0] != $current && $_->[0] != $next } @{$group->{reploc}{$repnum}{list}} ]
161                }
162            }
163            my $fct = $config->{disc}[$cdrep]{function}{data}{generic}{$repname}[0];
164            foreach my $r (@{$group->{reploc}{$repnum}{list}}) {
165                my ($new_repnum, $cd, $repname, $newlist) = @$r;
166                log_("reprocess_relocatable: new rep_num $new_repnum on cd $cd rep $repname list $newlist\n", $config->{verbose}, $config->{LOG},3);
167                push @{$inst->[1]{tmp_rpmsdir}}, [ $newlist, $cd, $repname ];
168                push @{$fct->[1]{lists}}, $newlist
169            }
170        }
171        push @{$inst->[1]{tmp_rpmsdir}}, $c
172    }
173    my $i;
174    foreach my $r (@{$inst->[1]{tmp_rpmsdir}}) {
175        log_("reprocess_relocatable: testing $r->[1]/$r->[2] ($i)\n", $config->{verbose}, $config->{LOG},3);
176        if ($r->[1] == $inst_disc && $i) {
177            log_("reprocess_relocatable: boot disc is $i\n", $config->{verbose}, $config->{LOG},3);
178            $inst->[1]{boot_medium} = $i + 1;
179            last
180        }
181        $i++
182    }
183}
184
185sub getDoneList {
186    my ($config, $group, $listnumber, $discsFiles) = @_;
187
188    my $topdir = "$config->{topdir}/build/$config->{name}";
189    my $add_rpm = sub {
190            my ($d, $rpm, $cd, $rep, $type) = @_;
191            #$rpm =~ /($test)\.rpm$/ or return;
192            $rpm =~ /(.*)\.rpm$/ or return;
193            #$rpm =~ /src\.rpm$/ and return;
194            my $rpm = $group->{urpm}{rpmkey}{key}{$1};
195            $group->{done}{rep}{$rpm} = $group->{orderedrep}{$type}{"$cd/$rep"};
196            $group->{done}{list}{$rpm} = $listnumber;
197            $discsFiles->[$cd]{$rep}{$listnumber}{$1} = $d
198    };
199    my $read_dir = sub {
200        my ($dir, $cd, $rep, $type) = @_;
201        my $RPMS;
202        if (!opendir $RPMS, $dir) { 
203            log_("WARNING getGroupReps: cannot open $dir\n",1, $config->{LOG},0);
204            return
205        }
206        foreach (readdir $RPMS) {
207            if (-d "$dir/$_") {
208                opendir my $D, "$dir/$_";
209                foreach my $r (readdir $D) {
210                    $add_rpm->("$dir/$_", $r, $cd, $rep, $type)
211                }
212            } else {
213                $add_rpm->($dir, $_, $cd, $rep, $type)
214            }
215        }
216    };
217    foreach my $type ('rpm', 'srpm') {
218        foreach my $curdir (@{$group->{list}{$listnumber}{$type}}) {
219            my ($cd, $rep) = @$curdir;
220            my $path = $config->{disc}[$cd]{function}{data}{dir}{$rep}[1]{reploc};
221            my $dir = "$topdir/$cd/$path";
222            log_("getDoneList: $listnumber disc $cd rep $rep\n", $config->{verbose}, $config->{LOG},2);
223            if ($config->{nolive}) {
224                if ($config->{list}[$listnumber]{packages}) {
225                    foreach my $ent (@{$config->{list}[$listnumber]{packages}}) {
226                        foreach my $type (keys %$ent) {
227                            my $rpm_tab = $ent->{$type};
228                            log_("getDoneList: @$rpm_tab\n",1, $config->{LOG},3);
229                            foreach my $dir (@$rpm_tab) {
230                                $read_dir->($dir, $cd, $rep, $type) 
231                            }
232                        }
233                    }
234                } else {
235                    log_("ERROR getDoneList: could not find data for $listnumber disc $cd rep $rep\n", $config->{verbose}, $config->{LOG},0);
236                }
237            } elsif (-d $dir) {
238                $read_dir->($dir, $cd, $rep, $type);
239            } else {
240                log_("ERROR getDoneList: could not find data for $listnumber disc $cd rep $rep\n", $config->{verbose}, $config->{LOG},0);
241                next
242            }
243            $config->{list}[$listnumber]{disc}{$cd}{$rep}{done} = 1
244        }
245    }
246
247    foreach my $d (@{$config->{list}[$listnumber]{virtual}}) {
248        my $cd = $d->{disc};
249        my $path = $d->{path};
250        my $rep = $d->{repname};
251        my $dir = "$topdir/$cd/$path";
252        log_("getDoneList: virtual disc $cd path $path in $dir\n",1, $config->{LOG},2);
253        foreach my $list_rep (@{$group->{rep}{$listnumber}}) {
254            $list_rep->{rpm}{$dir} or next;
255            foreach (@{$list_rep->{rpm}{$dir}}) {
256                my $rpm = $group->{urpm}{rpmkey}{key}{$_};
257                $group->{done}{rep}{$rpm} = $group->{orderedrep}{rpm}{"$cd/$rep"};
258                $group->{done}{list}{$rpm} = $listnumber;
259                $discsFiles->[$cd]{$rep}{$listnumber}{$_} = $dir
260            }
261        }
262        $config->{list}[$listnumber]{disc}{$cd}{$rep}{done} = 1;
263    }
264}
265
266sub getList {
267    my ($class, $group, $discsFiles) = @_;
268    my $config = $class->{config};
269    my %filelist;
270    my @norpmsrate;
271    log_("getList: group list " . join(' ', (keys %{$group->{list}})) . "\n", $config->{verbose}, $config->{LOG},1);
272    foreach my $listnumber (keys %{$group->{list}}) {
273        my $done = $config->{list}[$listnumber]{done};
274        $done and getDoneList($config, $group, $listnumber, $discsFiles);
275        log_("getList: FILE LIST listnumber $listnumber ($config->{list}[$listnumber]{filelist}) or ($config->{list}[$listnumber]{prelist})\n", $config->{verbose}, $config->{LOG},2);
276        if ($config->{list}[$listnumber]{filelist} || $config->{list}[$listnumber]{prelist}) {
277            foreach (@{$config->{list}[$listnumber]{filelist}}) {
278                log_("getList: FILE LIST listnumber $listnumber ($_)\n", $config->{verbose}, $config->{LOG},2);
279                my $A;
280                if (! open $A, $_) { log_("ERROR: cannot open $_, ignoring\n",1, $config->{LOG}); next }
281                local $_;
282                while (<$A>) {
283                    s/#.*//;
284                    next if !$_ || /^\s*$/;
285                    my ($name, $options) = /\s*(\S+)\s*(.*)/;
286                    my @options = split ',', $options;
287                    log_("FILESLIST: $_ -> $name options @options\n", $config->{verbose}, $config->{LOG},3);
288                    my %opt;
289                    foreach (@options) {
290                        s/^\s*//;
291                        /norpmsrate/ and push @norpmsrate, $name and next;
292                        if (!/^(?:(?:nosrc|section|noalternatives|regexp|ignore|nodeps|force|limit|exclude)|(rate|notondisc|rpmsrate|needed|section) (\d+))$/) {
293                            log_("WARNING: getList: $_: unknown option\n",1, $config->{LOG}); next
294                        }
295                        $_ = $1 || $_; 
296                        $opt{$_} = $2 || 1;
297                    }
298                    log_("Adding $name -- " . join(' ', keys %opt) . "\n", $config->{verbose}, $config->{LOG},4);
299                    push @{$filelist{$listnumber}}, [ $name, \%opt ];   
300                }
301                close $A
302            }
303            foreach my $p (@{$config->{list}[$listnumber]{prelist}}) {
304                log_("Prelist Adding $p->[0] -- " . join(' ', keys %{$p->[1]}) . "\n", $config->{verbose}, $config->{LOG},3);
305                $p->[1]{norpmsrate} and push @norpmsrate, $_->[0] and next;
306                push @{$filelist{$listnumber}}, $p
307            }
308        } else {
309            if (!$done && $config->{list}[$listnumber]{auto}) {
310                push @{$filelist{$listnumber}}, [ "INSTALL", { section =>1, force => 1 } ];
311                push @{$filelist{$listnumber}}, [ "SYSTEM", { section =>1, force => 1 } ];
312                push @{$filelist{$listnumber}}, [ "kernel-(smp-|)[0-9].*", { regexp => 1, force => 1 } ];
313                push @{$filelist{$listnumber}}, [ "kernel.*linus", { regexp => 1, noalternatives => 1 } ];
314                push @{$filelist{$listnumber}}, [ ".*", { regexp => 1 } ]
315            } else {
316                log_("getList: FILE LIST listnumber $listnumber defaulting to .* regexp\n", $config->{verbose}, $config->{LOG},2);
317                push @{$filelist{$listnumber}}, [ ".*" , { regexp => 1 } ]
318            }
319        }
320        my $listdone = 1;
321        foreach my $r (@{$group->{list}{$listnumber}{rpm}}) {
322            my ($cd, $rep, $repopt, $opt) = @$r;
323            log_("getList: searching for done rep ($cd/$rep)\n", $config->{verbose}, $config->{LOG},2);
324            if ($config->{list}[$listnumber]{disc}{$cd}{$rep}{done}) {
325                if (!$opt->{dup}) {
326                    foreach my $rpmkey (keys %{$discsFiles->[$cd]{$rep}{$listnumber}}) {
327                        my $rpm = $group->{urpm}{rpmkey}{key}{$rpmkey};
328                        $group->{done}{rep}{$rpm} = $group->{orderedrep}{rpm}{"$cd/$rep"};
329                        $group->{done}{list}{$rpm} = $listnumber;
330                        log_("getList: $rpm in $cd/$rep -> $group->{done}{rep}{$rpm} (noprovide $opt->{noprovide} relocatable $opt->{relocatable} update $opt->{update})\n", $config->{verbose}, $config->{LOG},3);
331                        push @{$filelist{$listnumber}}, [ $rpm, { done => $group->{done}{rep}{$rpm}, 
332                                                                  regexp => 1, 
333                                                                  udpate => $opt->{update}, 
334                                                                  noprovide => $opt->{noprovide},
335                                                                  relocatable => $opt->{relocatable} } ];
336                    }
337                }
338            } else { $listdone = 0 }
339        }
340        if ($listdone) {
341            log_("getList: setting list $listnumber as done\n", $config->{verbose}, $config->{LOG},2);
342            $config->{list}[$listnumber]{done} = 1 }
343    }
344    (\%filelist, \@norpmsrate)
345}
346
347#
348# compute individual scoring (max_size*(rpmsrate+1)*rpmsrate_factor/(size*size_factor))
349# then add dependencies sons score ( score + deps_factor*(sons_score)
350#
351# special rpmsrate groups score could be added in the rpmsrate value
352#
353# FIXME current scoring rules make size only significant for equaly dependent packages,
354# dependencies get far more importance for packages a lot of packages depend on.
355#
356# Size scoring could be added afterwards, but this will break the autodeps created with
357# this scheme.
358#
359# TODO
360# add scoring rules to include srpm size in score.
361#
362#
363sub scoreList {
364    my ($class, $group) = @_;
365    my $scoreweight = $group->{score};
366    my $urpm = $group->{urpm};
367    my $rpmsrate = $group->{rpmsrate};
368    my $maxsize = $group->{maxsize} || 1;
369    $scoreweight ||= [1,1,1];
370    log_("scoreList: SCORE for group: @$scoreweight\n", $config->{verbose}, $config->{LOG},2);
371    log_("scoreList: Individual scoring\n", $config->{verbose}, $config->{LOG},2);
372    my ($sf, $i, $total);
373    my (@min, @max);
374    if ($scoreweight->[1]) {
375        (@min,@max) = (($maxsize*$scoreweight->[0]*6/($scoreweight->[1]*1),0), (0,0))
376    } else {
377        (@min,@max) = (($maxsize*$scoreweight->[0]*6,0), (0,0))
378    }
379    my @specialdeps;
380    foreach (keys %{$urpm->{rpm}}) {
381        # print "INFO KEYS $_\n";
382        my ($ratekey) = /(.*)-[^-]+-[^-]+\.[^.]+$/;
383        # FIXME take the bigger size when package appears in multiple lists
384        my $size;
385        foreach my $list (keys %{$group->{size}{$_}}) { $size = $group->{size}{$_}{$list}[0] if $size < $group->{size}{$_}{$list}[0] }
386        $size or log_("WARNING scoreList: $_ has zero size\n",1, $config->{LOG});
387        my $s;
388        my $rate = $group->{brokendeps}{$_} ? 0 : (defined $group->{pkgrate}{$_} ? $group->{pkgrate}{$_} : $rpmsrate->[0]{$ratekey});
389        if ($scoreweight->[1]) {
390            $sf = ($size*9)/$maxsize + 1; # from 1 to 10
391            $s = $scoreweight->[0]*($rate + 1)/($scoreweight->[1]*$sf);
392        } else {
393            $s = $scoreweight->[0]*($rate + 1);
394        }
395        $group->{scorelist}{$_} = $s;
396        $s < $min[0] and @min = ($s, $_);
397        $s > $max[0] and @max = ($s, $_);
398
399        $total+=$s;
400        $i++
401    }
402    $i and log_("scoreList: minimal $min[0] ($min[1]), maximal $max[0] ($max[1]), average " . $total/$i . "\n", $config->{verbose}, $config->{LOG},3);
403    1
404}
405
406sub autodeps {
407    my ($class, $group, $rpmlist) = @_;
408    my $scoredeps = $group->{score}[2];
409    if (!$scoredeps) { 
410        log_("autodeps: deps score is null, bypassing autodeps\n",1, $config->{LOG},1);
411        return 1
412    }
413    log_("autodeps: compute reversed depslist.ordered ($scoredeps)\n", $config->{verbose}, $config->{LOG},2);
414    my $revDeps = $group->{revdeps};
415    my %rpm;
416    foreach my $k (values %$rpmlist) { foreach (keys %$k) { $rpm{$_} = $k->{$_} } }
417    # FIXME this algo is not correct
418    ref $group->{urpm}{depslist} or return 0;
419    for (my $i = @{$group->{urpm}{depslist}} - 1; $i >= 0; $i--) {
420        my $rpm = $group->{depslistid}[$i];
421        if (!$rpm{$rpm}) { 
422            #log_("autodeps: ignoring $rpm\n",1, $config->{LOG});
423            #push @{$group->{rejected}{$rpm}}, [ "autodeps", $1 ];
424            next
425        }
426        if ($rpm{$rpm}{ignore}) { log_("autodeps: $rpm has ignore flag, do not add deps score\n", $config->{verbose}, $config->{LOG},3); next }
427        foreach (@{$revDeps->[$i]}) {
428            $group->{scorelist}{$rpm} += $scoredeps * $group->{scorelist}{$group->{depslistid}[$_]};
429        }
430    }
431    1
432}
433
434sub reverseDepslist {
435    my ($class, $group, $rpmfile) = @_;
436    my $urpm = $group->{urpm};
437    my $depslist = $urpm->{depslist};
438    if (!$depslist) {
439        log_("WARNING reverseDepslist: no depslist found\n", $config->{verbose}, $config->{LOG},3);
440        return ()
441    }
442    my $locales = $group->{lang};
443    my (@revdeps, %skip);
444    log_("reverseDepslist\n", $config->{verbose}, $config->{LOG},2);
445    # urpmi remove basesystem require from the depslist
446    my %cache;
447    my $get_name_version = sub {
448        my ($n, $s) = @_;
449        # perl need major checking
450        if ($n eq 'perl') {
451            $s and ($s =~ /:/ or $s =~ s/([>=<]+) /$1 0:/);
452        } else {
453            # Some deps are broken and as a consequence using the major rejects some packages that shouldn't
454            #$s and ($s =~ /:/ or $s =~ s/([>=<]+) /$1 0:/);
455            $s =~ /:/ and $s =~ s/ \d+:/ /;
456        }
457        $n,$s
458    };
459    my $a = time;
460    for (my $i; $i < @$depslist; $i++) {
461        my $d = $depslist->[$i];
462        foreach my $req ($d->requires) {
463            my ($n, $s) = $req =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/;
464            ($n, $s) = $get_name_version->($n, $s);
465            push @{$cache{requires}[$i]}, [$n, $s];
466            $cache{all_requires}{$n} = 1
467        }
468    }
469    for (my $i; $i < @$depslist; $i++) {
470        my $d = $depslist->[$i];
471        $cache{ver}[$i] = $d->version;
472        $cache{verrel}[$i] = sprintf "%s-%s", $d->version, $d->release;
473        $cache{name}[$i] = sprintf "%s-%s.%s", $d->name, $cache{verrel}[$i], $d->arch;
474        foreach my $prov ($d->provides, @{$urpm->{files}{$d->fullname}}) {
475            my ($deps_n, $deps_s) = $prov =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/;
476            #$deps_s and ($deps_s =~ /:/ or $deps_s =~ s/([>=<]+) /$1 0:/);
477            ($deps_n, $deps_s) = $get_name_version->($deps_n, $deps_s);
478            $cache{all_requires}{$deps_n} or next;
479            push @{$cache{provides}{$deps_n}{$i}}, $deps_s
480        }
481    }
482    my $count;
483    for (my $i; $i < @$depslist; $i++) {
484        my $d = $depslist->[$i];
485        my $rpm = $cache{name}[$i];
486        $group->{depslistid}[$i] = $rpm;
487        my $l = find_list($group, $rpm);
488        my %rev;
489       
490        my @deps = map { split '\|', $_ } split ' ', $urpm->{deps}[$i];
491        # urpm has an optimization which removes most requires/provides in depslist regarding basesystem packages.
492        @deps or next;
493        log_("reverseDepslist: $i deps for $rpm\n", $config->{verbose}, $config->{LOG}, 5);
494        if (any {m/NOTFOUND_(\S*)/} @deps) {
495            if (!$group->{options}{nodeps} && !$class->{config}{nodeps} && !(defined $rpmfile->{$l}{$rpm} && $rpmfile->{$l}{$rpm}{nodeps})) {
496                $skip{$i} = 1;
497                $group->{brokendeps}{$rpm} = 2;
498                push @{$group->{rejected}{$rpm}}, [ "deps", $1 ];
499                log_("WARNING reverseDepslist: $rpm has unresolved dependencies ($1)\n", $config->{verbose}, $config->{LOG}, 1);
500                next
501            }
502        }
503        my $first = 1;
504        my $msg;
505        foreach my $req (@{$cache{requires}[$i]}) {
506            my ($n, $s) = @$req;
507            my @pkg;
508            #log_("reverseDepslist: $rpm requires $n $s\n", $config->{verbose}, $config->{LOG}, 1);
509            foreach (keys %{$cache{provides}{$n}}) {
510                $msg = '';
511                my $deps_v = $cache{verrel}[$_];
512                my $deps_rpm = $cache{name}[$_];
513                foreach my $deps_s (@{$cache{provides}{$n}{$_}}) {
514                    if ($deps_s) {
515                        if (URPM::ranges_overlap($deps_s, $s)) {
516                            #log_("reverseDepslist: adding $deps_rpm ($n $deps_s)\n", $config->{verbose}, $config->{LOG}, 1);
517                            push @pkg, $_;
518                        } else {
519                            $msg .= " $deps_rpm provides $n $deps_s but $rpm needs $n $s"
520                        }
521                    } else { 
522                        #log_("reverseDepslist: adding $deps_rpm ($n)\n", $config->{verbose}, $config->{LOG}, 1);
523                        push @pkg, $_;
524                    } 
525                } 
526                if ($first) { 
527                    if ($locales && $group->{depslistid}[$_] =~ /locales-([^-]+)-[^-]+-[^-]+\.[^.]+/) {
528                        if (!$locales->{$1}) {
529                            log_("reverseDepslist: locale $1 ($group->{depslistid}[$_]) skipped for $rpm\n", $config->{verbose}, $config->{LOG}, 2);
530                            $skip{$i} = 1;
531                            !$group->{brokendeps}{$rpm} and $group->{brokendeps}{$rpm} = 1     
532                        }
533                    }
534                    $skip{$_} or push @{$revdeps[$_]}, $i
535                }
536            }
537            $first = 0;
538            if (@pkg == 0) {
539                $skip{$i} = 1;
540                $group->{brokendeps}{$rpm} = 2;
541                log_("WARNING reverseDepslist: rejecting $rpm on $n\n", $config->{verbose}, $config->{LOG}, 4);
542                push @{$group->{rejected}{$rpm}}, [ "deps", "$n $msg" ];
543                last
544            } elsif (@pkg == 1) {
545                push @{$group->{pkgdeps}{$rpm}}, $pkg[0]
546            } else {
547                push @{$group->{pkgdeps}{$rpm}}, \@pkg
548            }
549        }
550    }
551    my $b = time;
552    log_("reverseDepslist: reverse time " . ($b-$a)  . "\n", $config->{verbose}, $config->{LOG}, 4);
553    return \@revdeps;
554}
555
556# FIXME this function is broken
557sub check_version {
558    my ($d, $deps_d, $rpm, $special_require) = @_;
559    my ($msg, $real_ok, $ok);
560    $ok = 0;
561    my $f = 1;
562    my $deps_v = sprintf "%s-%s", $deps_d->version, $deps_d->release;
563    my $deps_rpm = sprintf "%s-%s.%s", $deps_d->name, $deps_v, $deps_d->arch;
564    $rpm ||= sprintf "%s-%s-%s.%s", $d->name, $d->version, $d->release, $d->arch;
565    foreach my $req ($d->requires) {
566        my ($n, $s) = $req =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/;
567        # perl needs major checking
568        $n eq 'perl' and $ok = 1 and next;
569        # Some deps are broken and as a consequence using the major rejects some packages that shouldn't
570        #$s and ($s =~ /:/ or $s =~ s/([>=<]+) /$1 0:/);
571        $s =~ /:/ and $s =~ s/ \d+:/ /;
572        if ($n && $s) {
573            # FIXME there is an approximation here, a package which provides a 1.3 and b 2.4 could satisfy a 1.3 but not b 2.5 for example
574            # however it will be rejected anyway. Otherwize any require should be checked when building the CDs, and this would be overkill.
575            foreach my $prov ($deps_d->provides) {
576                my ($deps_n, $deps_s) = $prov =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/;
577                #$deps_s and ($deps_s =~ /:/ or $deps_s =~ s/([>=<]+) /$1 0:/);
578                $deps_s =~ /:/ and $deps_s =~ s/ \d+:/ /;
579                $deps_s ||= $s =~ /-/ ? "= $deps_v" : "= " . $deps_d->version;
580                if ($deps_n && $deps_s) {
581                    if ($deps_n eq $n) { 
582                        if (URPM::ranges_overlap($deps_s, $s)) {
583                            $real_ok = 1 if !$special_require || $special_require eq $n;
584                            $ok = 1 && $f
585                        } else {
586                            my $t = "$deps_rpm provides $deps_n $deps_s but $rpm needs $n $s";
587                            $msg .= " $t";
588                            $ok = 0;
589                            $f = 0
590                        }
591                    }
592                }
593            }
594        } else {
595            foreach my $prov ($deps_d->provides) {
596                my ($deps_n) = $prov =~ /^([^\s\[]*)(?:\[\*\])?\[?([^\s\]]*\s*[^\s\]]*)/;
597                if ($deps_n eq $n) { 
598                    $real_ok = 1 if !$special_require || $special_require eq $n;
599                    $ok = 1 && $f
600                }
601            }
602        }
603    }
604    ($ok, $msg, $real_ok)
605}
606
607sub closeRpmsList {
608    my ($group, $rpmfile) = @_;
609    my $n = 1;
610    my %done;
611    my %doneName;
612    my %alternatives;
613    my $inst_disc = $group->{installDisc};
614    my $inst = $config->{disc}[$inst_disc]{function}{data}{installation};
615    while ($n) {
616        $n = 0;
617        foreach my $listnumber (@{$group->{orderedlist}{rpm}}) {
618            foreach my $rpm (keys %{$rpmfile->{$listnumber}}) {
619                # FIXME if the different packages have different deps, old packages may be better.
620                # this should be done in buildDisc, or better packages have to be selected here.
621                if (!$group->{options}{dup}) {
622                    my ($name, $version, $release, $arch) = $rpm =~ /^(.*)-([^-]+)-([^-]+)\.([^.]+)$/;
623                    if ($doneName{$name}) {
624                        if (!($doneName{$name}[0] eq "$version-$release.$arch")) {
625                            log_("WARNING closeRpmsList: $name-$version-$release.$arch duplicated with $doneName{$name}[0]\n", $config->{verbose}, $config->{LOG}, 1);
626                            my ($v, $r, $a) = @{$doneName{$name}[1]};   
627                            my $todel;
628                            my $vers;
629                            my $ret = rpmVersionCompare($rpm, "$name-$v-$r.$a");
630                            if ($ret < 0) {
631                                $todel = $rpm;
632                                $vers = [$v, $r, $a]
633                            } elsif ($ret > 0) {
634                                $todel = "$name-$v-$r.$a";
635                                $vers = [$version, $release, $arch]
636                            } else {
637                                log_("ERROR closeRpmsList: oops, something not possible happened in duplicate version comparaison ($rpm and $name-$v-$r.$a)\n",1, $config->{LOG});
638                            }
639                            if ($todel) {
640                                log_("closeRpmsList: deleting $todel\n", $config->{verbose}, $config->{LOG},2);
641                                $doneName{$name} = [ "$vers->[0]-$vers->[1].$vers->[2]", $vers ];
642                                if (!$inst->[1]{dup}) {
643                                    $group->{brokendeps}{$todel} = 3;
644                                    push @{$group->{rejected}{$todel}}, [ "old_version", "$name-$vers->[0]-$vers->[1].$vers->[2]"];
645                                }
646                                delete $rpmfile->{$listnumber}{$todel} if !$rpmfile->{$listnumber}{$rpm}{done};
647                                log_("closeRpmsList: resetting alternatives\n", $config->{verbose}, $config->{LOG},4);
648                                foreach (keys %alternatives) {
649                                    $alternatives{$_}[0] eq $todel and delete $alternatives{$_}
650                                }
651                                $todel eq $rpm and next 
652                            }
653                            $n = 1
654                        }
655                    } else {
656                        $doneName{$name} = [ "$version-$release.$arch", [$version, $release, $arch] ]
657                    }
658                }
659                if ($group->{brokendeps}{$rpm} == 2 || $group->{brokendeps}{$rpm} == 3) {
660                    log_("closeRpmsList: deleting $rpm (list $listnumber)\n", $config->{verbose}, $config->{LOG},2);
661                    delete $rpmfile->{$listnumber}{$rpm};
662                    $n = 1;
663                    next
664                }
665                $done{$rpm} and next;
666                $rpmfile->{$listnumber}{$rpm}{nodeps} and next;
667                my $needed;
668                $needed = 1 if $rpmfile->{$listnumber}{$rpm}{force};
669                $needed ||= $rpmfile->{$listnumber}{$rpm}{needed};
670                $needed ||= $rpmfile->{$listnumber}{$rpm}{done};
671                # $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});
672                foreach (@{$group->{pkgdeps}{$rpm}}) {
673                    if (m/NOTFOUND_(.*)/) { log_("ERROR closeRpmsList: $1 not provided\n", $config->{verbose}, $config->{LOG},1); next }
674                    my $rpmdep;
675                    my $rpmdeplist;
676                    my $specialrpmdep;
677                    if (ref $_) {
678                        if ($alternatives{"@$_"}) {
679                            ($rpmdep, $rpmdeplist) = @{$alternatives{"@$_"}};
680                            log_("closeRpmsList: $rpm taking alternatives from already selected package ($rpmdep $rpmdeplist)\n", $config->{verbose}, $config->{LOG}, 4)
681                        }
682                        if (! ref $rpmfile->{$rpmdeplist}{$rpmdep}) {
683                            foreach my $testalt (1,0) {
684                                ($rpmdep, $rpmdeplist) = (undef,undef);
685                                # FIXME this is wrong, package can come from any list
686                                my @score = ($group->{maxlist}{rpm}, int @{$group->{list}{$listnumber}{rpm}}, $group->{maxsize}, $group->{maxrep}{rpm});
687                                my @specialscore = (int @{$group->{list}{$listnumber}{rpm}}, $group->{maxsize});
688                                log_("closeRpmsList: $rpm @$_ (maxscore @score) alternative\n", $config->{verbose}, $config->{LOG}, 4);
689                                foreach (@$_) {
690                                    my $pkg = $group->{depslistid}[$_];
691                                    log_("closeRpmsList: trying $pkg (brokendeps $group->{brokendeps}{$pkg})\n", $config->{verbose}, $config->{LOG}, 4);
692                                    $group->{brokendeps}{$pkg} == 2 and next;
693                                    $group->{brokendeps}{$pkg} == 3 and next;
694                                    my $pkglist = find_list($group, $pkg, $listnumber);
695                                    if (!$pkglist) {
696                                        log_("closeRpmsList: $pkg list could not be used for $rpm dependencies\n", $config->{verbose}, $config->{LOG}, 4);
697                                        next
698                                    }
699                                    log_("closeRpmsList: list $pkglist\n", $config->{verbose}, $config->{LOG}, 4);
700
701                                    if ($rpmfile->{$pkglist}{$pkg}) {
702                                        $rpmfile->{$pkglist}{$pkg}{limit} and next;
703                                        $testalt and $rpmfile->{$pkglist}{$pkg}{noalternatives} and next;
704                                        #$testalt and $rpmfile->{$pkglist}{$pkg}{noprovide} and next;
705                                    }
706                                    my $rep = $group->{size}{$pkg}{$pkglist}[2];
707                                    my $s = $group->{size}{$pkg}{$pkglist}[0];
708                                    my $l = $group->{listsort}{$pkglist}{rpm};
709                                    my $d = $group->{done}{rep}{$pkg};
710                                    log_("\t$pkg ($l, $rep, $s, $d) (@score)\n", $config->{verbose}, $config->{LOG}, 5);
711                                    # also put an alternative from this list
712                                    if ($pkglist == $listnumber) {
713                                        if ($rep < $specialscore[1]) {
714                                            @specialscore = ($rep, $s);
715                                            $specialrpmdep = $pkg;
716                                        } elsif ($rep == $specialscore[1] && $s < $specialscore[2]) {
717                                            @specialscore = ($rep, $s);
718                                            $specialrpmdep = $pkg;
719                                        }               
720                                    }
721                                    if ($d) { 
722                                        if ($d < $score[3]) {
723                                            @score = ($l, $rep, $s, $d);
724                                            $rpmdep = $pkg;
725                                            $rpmdeplist = $pkglist;
726                                            log_("5 $rpmdep -- $rpmdeplist -- $l, $rep, $s, $d\n", $config->{verbose}, $config->{LOG}, 6);
727                                        }
728                                    } elsif ($l < $score[0]) {
729                                        @score = ($l, $rep, $s, $d);
730                                        $rpmdep = $pkg;
731                                        $rpmdeplist = $pkglist;
732                                        log_("1 $rpmdep -- $rpmdeplist -- $l, $rep, $s, $d\n", $config->{verbose}, $config->{LOG}, 6);
733                                    } elsif ($l == $score[0]) {
734                                        if ($pkglist == $listnumber) {
735                                            if ($rep < $score[1]) {
736                                                @score = ($l, $rep, $s, $d);
737                                                $rpmdep = $pkg;
738                                                $rpmdeplist = $pkglist;
739                                                log_("2 $rpmdep -- $rpmdeplist -- $l, $rep, $s, $d\n", $config->{verbose}, $config->{LOG}, 6);
740                                            } elsif ($rep == $score[1] && $s < $score[2]) {
741                                                @score = ($l, $rep, $s, $d);
742                                                $rpmdep = $pkg;
743                                                $rpmdeplist = $pkglist;
744                                                log_("3 $rpmdep -- $rpmdeplist -- $l, $rep, $s, $d\n", $config->{verbose}, $config->{LOG}, 6);
745                                            }
746                                        } elsif ($s < $score[2]) {
747                                            @score = ($l, $rep, $s, $d);
748                                            $rpmdep = $pkg;
749                                            $rpmdeplist = $pkglist;
750                                            log_("4 $rpmdep -- $rpmdeplist -- $l, $rep, $s, $d\n", $config->{verbose}, $config->{LOG}, 6);
751                                        }
752
753                                    }
754                                }
755                                last if $rpmdep && $rpmdeplist
756                            }
757                            if ($rpmdep && $rpmdeplist) {
758                                log_("\tResult:\t$rpmdep\n", $config->{verbose}, $config->{LOG}, 4);
759                                $alternatives{"@$_"} = [ $rpmdep, $rpmdeplist ]
760                            } else {
761                                log_("WARNING: $rpm has unresolved or excluded dependencies, removed\n", $config->{verbose}, $config->{LOG}, 1);
762                                log_("closeRpmsList: deleting $rpm (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 2);
763                                delete $rpmfile->{$listnumber}{$rpm};
764                                $n = 1;
765                                $group->{brokendeps}{$rpm} = 2;
766                                push @{$group->{rejected}{$rpm}}, ["deps_rejected", (join ' ', map { $group->{depslistid}[$_] } @$_)];
767                                last
768                            }
769                        }
770                    } else {   
771                        # TODO verify that there is no need to do $rpmfile->{$pkglist}{$rpmdep} or brokendeps;
772                        $rpmdep = $group->{depslistid}[$_];
773                        $rpmdeplist = find_list($group, $rpmdep, $listnumber);
774                    }
775                    # log_("rpmdep $rpmdep rpmdeplist $rpmdeplist rpm $rpm\n",1, $config->{LOG});
776                    if ($rpmdep) {
777                        if (!$rpmdeplist || $group->{brokendeps}{$rpmdep} == 2 || $group->{brokendeps}{$rpmdep} == 3) {
778                            $group->{brokendeps}{$rpm} = $group->{brokendeps}{$rpmdep};
779                            push @{$group->{rejected}{$rpm}}, [ "deps_rejected", $rpmdep ];
780                            $n = 1;
781                            log_("WARNING closeRpmsList: $rpm has unresolved or excluded dependencies ($rpmdep list $rpmdeplist), removed\n", $config->{verbose}, $config->{LOG}, 1);
782                            log_("closeRpmsList: deleting $rpm (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 2);
783                            delete $rpmfile->{$listnumber}{$rpm};
784                            next
785                        }
786                        if (! ref $rpmfile->{$rpmdeplist}{$rpmdep}) {
787                            if (!$group->{done}{rep}{$rpmdep} || $needed && $group->{done}{rep}{$rpmdep} > $needed) {
788                                $n = 1;
789                                log_("closeRpmsList: ADDED $rpmdep (list $rpmdeplist) needed $needed (done $group->{done}{rep}{$rpmdep})\n", $config->{verbose}, $config->{LOG}, 3);
790                                $rpmfile->{$rpmdeplist}{$rpmdep} = { needed => $needed }
791                            }
792                        } elsif ($needed && !$group->{done}{rep}{$rpmdep} || $group->{done}{rep}{$rpmdep} > $needed) {
793                            my $n = $rpmfile->{$rpmdeplist}{$rpmdep}{needed};
794                            $rpmfile->{$rpmdeplist}{$rpmdep}{needed} = !$n || $needed < $n ? $needed : $n;
795                            log_("closeRpmsList: setting $rpmdep needed option to $rpmfile->{$rpmdeplist}{$rpmdep}{needed}\n", $config->{verbose}, $config->{LOG}, 4);
796                        }
797                    }
798                    if ($specialrpmdep) {
799                        if (! ref $rpmfile->{$listnumber}{$specialrpmdep}) {
800                            $n = 1;
801                            log_("closeRpmsList: specialrpmdep ADDED $specialrpmdep (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 3);
802                            $rpmfile->{$listnumber}{$specialrpmdep} = { }
803                        }
804                    }
805                }
806                $done{$rpm} = 1;
807            }
808            log_("closeRpmsList: $listnumber [$n]\n", $config->{verbose}, $config->{LOG}, 2);
809        }
810    }
811}
812
813sub addRPMToList {
814    my ($group, $listnumber, $rpmfile, $done, $rpms, $fentry, $name) = @_;
815    my $exclude = sub {
816        my ($rpm, $name) = @_;
817        log_("addRPMToList: excluding $_\n",1, $config->{LOG}, 1);
818        $group->{brokendeps}{$rpm} = 3;
819        push @{$group->{rejected}{$rpm}}, ["excluded", $name];
820    };
821    $name =~ s/\+/\\+/g;
822    my @toadd;
823    if ($fentry->{regexp}) { @toadd = grep { /$name/ } @$rpms } 
824    else { @toadd = grep { /^$name-[^-]+-[^-]+\.[^.]*$/ } @$rpms }
825    log_("addRPMToList: toadd $name (regexp $fentry->{regexp}) (@toadd)\n", $config->{verbose}, $config->{LOG}, 3);
826    if ($fentry->{done}) {
827        foreach (@toadd) {
828            my ($pkg) = /^(.*)-[^-]+-[^-]+\.[^.]*$/;
829            my %ht;
830            foreach my $k (keys %$fentry) { $ht{$k} = $fentry->{$k} }   
831            $rpmfile->{$listnumber}{$_} = \%ht;
832            my $a = [ $_, $group->{size}{$_}{$listnumber}[2], \%ht, $listnumber ];
833            $done->{name}{$pkg} = $a;
834            $done->{full}{$pkg} = 1;
835            log_("addRPMToList: ADDED done $_ (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4)
836        }
837        return
838    }
839    my %pkg;
840    if ($fentry->{regexp}) { 
841        foreach (@toadd) {
842            if ($rpmfile->{$listnumber}{$_}) { log_("WARNING addRPMToList: do not replace $_ entry in rpmfile\n", $config->{verbose}, $config->{LOG}, 1); next }
843            if (!$_) { log_("ERROR addRPMToList: empty rpm\n", $config->{verbose}, $config->{LOG}, 2); next }
844            $group->{size}{$_}{$listnumber} or next;
845            $group->{brokendeps}{$_} == 2 and next;
846            $group->{brokendeps}{$_} == 3 and next;
847            my ($pkgname) = /^(.*)-[^-]+-[^-]+\.[^.]*$/;
848            $done->{full}{$_} and next;
849            my $rep = $group->{size}{$_}{$listnumber}[2];
850            if ($fentry->{exclude}) {
851                $exclude->($_, $name);
852                next
853            }
854            if ($done->{name}{$pkgname} && $done->{name}{$pkgname}[3] == $listnumber) {
855                if (!$fentry->{update} || !$done->{name}{$pkgname}[2]{done}) {
856                    if ($rep < $done->{name}{$pkgname}[1]) {
857                        $pkg{$done->{name}{$pkgname}[0]} = 0;
858                        log_("REPLACING $done->{name}{$pkgname}[0] with $_ (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4);
859                        $pkg{$_} = 1;
860                        my $a = [ $_, $rep, $fentry, $listnumber ];
861                        $done->{name}{$pkgname} = $a;
862                        $done->{full}{$_} = 1
863                    } elsif ($done->{name}{$pkgname}[1] == $rep) {
864                        if (rpmVersionCompare($done->{name}{$pkgname}[0], $_) < 0) {
865                            $pkg{$done->{name}{$pkgname}[0]} = 0;
866                            log_("REPLACING $done->{name}{$pkgname}[0] with $_ (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4);
867                            $pkg{$_} = 1;
868                            my $a = [ $_, $rep, $fentry, $listnumber ];
869                            $done->{name}{$pkgname} = $a;
870                            $done->{full}{$_} = 1
871                        }
872                    }
873                }
874            } else { 
875                $pkg{$_} = 1; 
876                my $a  = [ $_, $rep, $fentry, $listnumber ];
877                $done->{name}{$pkgname} = $a;
878                $done->{full}{$_} = 1 }
879        }
880    } else {
881        my $rep = -1;
882        my $pkg;
883        # FIXME present algorythm selects only one package per version, and choose the one in the list declared first.
884        # Maybe adding all the version and letting closeRRPMsList choose the right one is better.
885        foreach (@toadd) {
886            if (!$_) { log_("ERROR addRPMToList: empty rpm\n",1, $config->{LOG}, 2); next }
887            if ($rpmfile->{$listnumber}{$_}) { log_("WARNING addRPMToList: do not replace $_ entry in rpmfile\n", $config->{verbose}, $config->{LOG}, 1); next }
888            $group->{size}{$_}{$listnumber} or next;
889            $group->{brokendeps}{$_} == 2 and next;
890            $group->{brokendeps}{$_} == 3 and next;
891            if ($fentry->{exclude}) {
892                $exclude->($_, $name);
893                next;
894            }
895            if ($group->{size}{$_}{$listnumber}[2] < $rep || $rep == -1)  {
896                $rep = $group->{size}{$_}{$listnumber}[2];
897                log_("addRPMToList: choosing $_ (rep $rep)\n", $config->{verbose}, $config->{LOG}, 2);
898                $pkg = $_
899            } elsif ($group->{size}{$_}{$listnumber}[2] == $rep) {
900                if (rpmVersionCompare($pkg, $_) < 0) {
901                    $rep = $group->{size}{$_}{$listnumber}[2];
902                    log_("addRPMToList: choosing $_ (rep $rep)\n", $config->{verbose}, $config->{LOG}, 2);
903                    $pkg = $_
904                }
905            }
906        }
907        my ($pkgname) = $pkg =~ /^(.*)-[^-]+-[^-]+\.[^.]*$/;
908        if (!$done->{name}{$pkgname}) { 
909            $pkg{$pkg} = 1; 
910            my $a = [ $pkg, $rep, $fentry, $listnumber ]; 
911            $done->{name}{$pkgname} = $a;
912            $done->{full}{$pkg} = 1 }
913    }
914    $fentry->{exclude} and return 1;
915    foreach (keys %pkg) {
916        if ($rpmfile->{$listnumber}{$_}) {log_("WARNING addRPMToList: do not replace $_ entry in rpmfile\n", $config->{verbose}, $config->{LOG}, 1); next }
917        $pkg{$_} or next;
918        defined $fentry->{rate} and $group->{pkgrate}{$_} = $fentry->{rate} and log_("addRPMToList: setting $_ rate to $fentry->{rate}\n",1, $config->{LOG}, 2);
919        my %ht;
920        foreach my $k (keys %$fentry) { $ht{$k} = $fentry->{$k} }
921        $rpmfile->{$listnumber}{$_} = \%ht;     
922        log_("addRPMToList: ADDED $_ (list $listnumber) options " . join(" ", keys %ht) . "\n", $config->{verbose}, $config->{LOG}, 4)
923    }
924}
925
926sub build_list {
927    my ($class, $group) = @_;
928    my %rpmfile;
929    my $filelist = $group->{filelist};
930    my @fullrpm = keys %{$group->{urpm}{rpm}};
931    my @section = keys %{$group->{rpmsrate}[1]};
932    my %done;
933    foreach my $listnumber (@{$group->{orderedlist}{rpm}}) {
934        log_("build_list: list $listnumber ($group->{listrpm}{$listnumber})\n", $config->{verbose}, $config->{LOG}, 2);
935        my $rpms = $group->{listrpm}{$listnumber};
936        if (ref $rpms) {
937            log_("$listnumber -- $group->{filelist} -- " . keys(%{$group->{filelist}}) . "\n", $config->{verbose}, $config->{LOG}, 3);
938            if (!ref $filelist->{$listnumber}) { log_("WARNING: list $listnumber has an empty file list\n", $config->{verbose}, $config->{LOG}, 1); next }
939            log_("build_list: FILE LIST $listnumber (" . int @{$filelist->{$listnumber}} . ")\n", $config->{verbose}, $config->{LOG}, 4);
940            foreach my $fentry (@{$filelist->{$listnumber}}) {
941                my $name = $fentry->[0];
942                my $opt = $fentry->[1]; 
943                log_("build_list: processing $name " . join(' ', keys %$opt) . "\n", $config->{verbose}, $config->{LOG}, 5);
944                my @toadd;
945                if ($opt->{section}) {
946                    my $level = $opt->{section};
947                    log_("build_list: selecting rpmsrate package of section $name with score higher than $level\n", $config->{verbose}, $config->{LOG}, 2);
948                    $opt->{section} = 0;
949                    if ($opt->{regexp}) {
950                        $opt->{regexp} = 0;
951                        @toadd = grep { /$name/ } @section;
952                        log_("$name (@section) -> @toadd\n", $config->{verbose}, $config->{LOG}, 4);
953                        foreach (@toadd) {
954                            foreach (@{$group->{rpmsrate}[1]{$_}}) {
955                                log_("$_ -> $group->{rpmsrate}[0]{$_}\n", $config->{verbose}, $config->{LOG}, 4);
956                                if ($group->{rpmsrate}[0]{$_} >= $level) {
957                                    addRPMToList($group, $listnumber, \%rpmfile, \%done, $rpms, $opt, $_);
958                                }
959                            }
960                        }
961                    } else {
962                        my $rpmlist = $group->{rpmsrate}[1]{$name} or do { log_("ERROR build_list: $name unknown rpmsrate section\n", $config->{verbose}, $config->{LOG}, 0); next };
963                        foreach (@$rpmlist) {
964                            if ($group->{rpmsrate}[0]{$_} >= $level) {
965                                addRPMToList($group, $listnumber, \%rpmfile, \%done, $rpms, $opt, $_)
966                            }
967                        }
968                    }
969                } else {
970                    addRPMToList($group, $listnumber, \%rpmfile, \%done, $rpms, $opt, $name);
971                }
972            }
973        } else {
974            log_("WARNING: List $listnumber is empty, ignoring\n", $config->{verbose}, $config->{LOG}, 0);     
975            $class->{config}{list}[$listnumber]{empty} = 1;
976        }
977    }
978    my $revdeps;
979
980    if (!$class->{config}{nodeps} && !$group->{options}{nodeps}) {
981        my @toadd = grep { /^basesystem-[^-]+-[^-]+\.[^.]*$/ } @fullrpm; 
982        my $pkg;
983        my $listnumber;
984        foreach (@toadd) {
985            my $l = find_list($group, $_);
986            if ($group->{listmatrix}{rpm}{$listnumber}{$l} || !$listnumber) {
987                $pkg = $_;
988                $listnumber = $l
989            }
990        }
991        if ($pkg) {
992            $rpmfile{$listnumber}{$pkg} = {};
993            log_("build_list ADDED $pkg (list $listnumber)\n", $config->{verbose}, $config->{LOG}, 4);
994        } else { log_("ERROR: basesystem package is not available.\n",1, $config->{LOG}) }
995
996        foreach my $pkg (keys %{$rpmfile{13}}) {
997            log_("build_list CURRENT 1 $pkg\n", $config->{verbose}, $config->{LOG}, 4);
998        }
999        $revdeps = reverseDepslist($class, $group, \%rpmfile);
1000        foreach my $pkg (keys %{$rpmfile{13}}) {
1001            log_("build_list CURRENT 4 $pkg\n", $config->{verbose}, $config->{LOG}, 4);
1002        }
1003        # add deps
1004        closeRpmsList($group, \%rpmfile);
1005        foreach my $pkg (keys %{$rpmfile{13}}) {
1006            log_("build_list CURRENT 2 $pkg\n", $config->{verbose}, $config->{LOG}, 4);
1007        }
1008    }
1009    (\%rpmfile, $revdeps)
1010}
1011
1012sub addRPMToDiff {
1013    my ($rpm, $rpmd, $diff, $cdnum, $repnumber, $i, $list, $curdir, $size, $rpmsize, $totrpmsize, $repnum, $done, $reallist, $realrepnum, $group) = @_;
1014    my @data;
1015    for (my $s; $s < @$rpm; $s++) {
1016        push @data, [$rpm->[$s],1, $rpmd->[$s], $rpmsize->[$s]];
1017        log_("addRPMToDiff: $rpm->[$s] put in rep $repnumber\n", $config->{verbose}, $config->{LOG}, 4);
1018        $done->{rep}{$rpm->[$s]} = $repnumber;
1019        $done->{list}{$rpm->[$s]} = $list;
1020    }
1021    my $diff_data = [ $curdir, $i, $list, $repnum, 1, \@data, $rpmd, $totrpmsize, $reallist ];
1022    my $idx = push @{$diff->{data}}, $diff_data;
1023    push @{$diff->{idx}}, --$idx;
1024    push @{$group->{reploc}{$realrepnum}{diff}{$repnum}}, $diff_data if $realrepnum != $repnum;
1025    $size->{disc}[$cdnum] += $totrpmsize;
1026    $size->{rep}{$cdnum}{$curdir->[1]}{$list} += $totrpmsize;
1027    log_("addRPMToDiff: SIZE disc $cdnum: $size->{disc}[$cdnum] (+ @$rpm $totrpmsize ID $idx) added rpmd $rpmd\n", $config->{verbose}, $config->{LOG}, 3);
1028    1
1029}
1030
1031sub find_list {
1032    my ($group, $r, $list, $notdone) = @_;
1033    my $l;
1034    foreach (keys %{$group->{size}{$r}}) {
1035        #log_("find_list: for $r trying list $_ (listmatrix $l - $_ -> $group->{listmatrix}{rpm}{$l}{$_} listmatrix $list - $_ -> $group->{listmatrix}{rpm}{$list}{$_})\n",$config->{verbose}, $config->{LOG}, 7);
1036        $l = $_ if ( ($l && $group->{listmatrix}{rpm}{$l}{$_}
1037                       || 
1038                     (!$l && ($group->{listmatrix}{rpm}{$list}{$_} || !$list)))
1039                   && ($notdone && !$config->{list}[$_]{done} || !$notdone))
1040    }
1041    return $l
1042}
1043
1044sub check_last_relocatable {
1045    my ($group, $cd, $repname, $repnum, $list, $curdir_old, $old) = @_;
1046    my ($curdir, $new_repnum, $newlist);
1047    if (defined $group->{reploc}{$repnum}{last}{list} && $group->{reploc}{$repnum}{last}{list} == $list) {
1048        $new_repnum = $group->{reploc}{$repnum}{last}{rep};
1049        log_("check_last_relocatable: using previous relocatable dir $new_repnum\n",1 , $config->{LOG}, 1);
1050        $curdir = $group->{reploc}{$repnum}{last}{curdir};
1051        $newlist = $group->{reploc}{$repnum}{last}{newlist}
1052    } else {
1053        my $last = $group->{reploc}{$repnum}{last}{rep} || $repnum - 1;
1054        $new_repnum = $last+$MIN_CHUNK;
1055        log_("check_last_relocatable: using new relocatable dir $new_repnum cd $cd repname $repname repnum $repnum\n",1 , $config->{LOG}, 1);
1056        $group->{reploc}{$repnum}{last}{rep} = $new_repnum;
1057        $group->{reploc}{$repnum}{last}{list} = $list;
1058        $newlist = @{$config->{list}};
1059        $config->{list}[$newlist] = {};
1060        push @{$group->{reploc}{$repnum}{list}}, [ $new_repnum, $cd, $repname, $newlist ];
1061        $curdir = [ $cd, $repname ];
1062        push @$curdir, $curdir_old->[2], $curdir_old->[3] if ref $curdir_old;
1063        $group->{reploc}{$repnum}{last}{newlist} = $newlist;
1064        $group->{reploc}{$repnum}{last}{curdir} = $curdir;
1065        $group->{reploc}{$repnum}{curdir}{$new_repnum} = $curdir;
1066        $group->{reploc}{$repnum}{newlist}{$new_repnum} = $newlist;
1067        $group->{reploc}{$repnum}{reallist}{$new_repnum} = $list;
1068        $group->{reploc}{$repnum}{old}{$new_repnum} = $old 
1069    }
1070    return $new_repnum, $curdir, $newlist
1071}
1072
1073sub add_relocatable_package {
1074    my ($group, $tcd, $diff, $r, $list, $i, $repnum, $repname) = @_;
1075    my $done = $group->{done};
1076    log_("add_relocatable_package: going in relocatable mode for deps $r before $repnum\n",1 , $config->{LOG}, 1);
1077   
1078    my ($new_repnum, $curdir, $newlist) = check_last_relocatable($group, @{$group->{reverse_rep}{rpm}{$tcd}}{'cd','name'}, $repnum, $list, 0, 1);
1079    log_("add_relocatable_package: $r put in rep $new_repnum list $list\n", $config->{verbose}, $config->{LOG}, 4);
1080    $done->{rep}{$r} = $new_repnum;
1081    $done->{list}{$r} = $newlist;
1082
1083    my $totrpmsize = $group->{size}{$r}{$list}[0];
1084    $group->{reploc}{$repnum}{size}{$new_repnum} += $totrpmsize;
1085    my $rpmd = [[ $r, 0, 0 ]];
1086    my $data = [[ $r, 1, 0, 0 ]];
1087    my $diff_data = [ $curdir, $i, $newlist, $new_repnum, 1, $data, $rpmd, $totrpmsize, $list ];
1088    my $idx = push @{$diff->{data}}, $diff_data;
1089    push @{$diff->{idx}}, --$idx;
1090    push @{$group->{reploc}{$repnum}{diff}{$new_repnum}}, $diff_data;
1091    $new_repnum
1092}
1093
1094sub processDeps {
1095    my ($r, $group, $cdnum, $repname, $done, $rpmlist, $topush, $intopush, $depsdisc, $rpmd, $list, $loop, $i, $tobedone, $buildlist, $rpm, $needed, $diff) = @_;
1096    # FIXME I think that this is wrong, but not really sure
1097    # It may happen that package get rejected because the non-done deps could not,
1098    # for any reason, be used, and this package is not put after the done deps.
1099    # However I guess that the put_in_rep loop over directories may save the
1100    # stuff.
1101    my $repnum = $group->{orderedrep}{rpm}{"$cdnum/$repname"};
1102    my $tcd = $done->{rep}{$r} if !$rpmlist->[$i]{$done->{list}{$r}}{$r}{noprovide} || $done->{rep}{$r} <= $repnum;
1103    log_("processDeps: deps $r (tcd $tcd done $done->{rep}{$r})\n", $config->{verbose}, $config->{LOG}, 3);
1104    if ($tcd > $repnum && $rpmlist->[$i]{$done->{list}{$r}}{$r}{relocatable}) {
1105        $tcd = add_relocatable_package($group, $tcd, $diff, $r, $done->{list}{$r}, $i, $group->{orderedrep}{rpm}{"$cdnum/$repname"}, $repname)
1106    }
1107    my $l = find_list($group, $r, $list, !$tcd);
1108    if ($group->{rejected}{$r}) { 
1109        log_("ERROR processDeps: deps $r rejected, rejecting @$rpm\n",1 , $config->{LOG}, 1);
1110        log_("Rejecting @$rpm $r\n", $config->{verbose}, $config->{LOG},1);
1111        foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["deps_rejected", $r] }
1112        $$loop = 1; %$topush = (); return 0 
1113    }
1114    if ($tcd) {
1115        if ($tcd > $$depsdisc) { $$depsdisc = $tcd };
1116        log_("processDeps: deps done $r on rep $tcd ($$depsdisc) list $done->{list}{$r}\n", $config->{verbose}, $config->{LOG}, 4);
1117        return 2 
1118    }
1119    if ($tobedone->{$r}) {
1120        if ($l == $list) {
1121            log_("$r tobedone\n", $config->{verbose}, $config->{LOG},3);
1122            if ($intopush->{$r}) { log_("WARNING processDeps: $r added twice\n", $config->{verbose}, $config->{LOG}, 3); return 1 }
1123            push @$rpmd, [$r, $rpmlist->[$i]{$l}{$r}];
1124            $intopush->{$r} = 1;
1125            push @{$topush->{$l}}, $rpmd; 
1126            log_("processDeps: adding looping deps $r ($_ -- $l) with @$rpm\n", $config->{verbose}, $config->{LOG},3)
1127        } else {
1128                if ($group->{listmatrix}{rpm}{$list}{$l}) {
1129                    # FIXME tobedone may not mean dependencies loop in parallel mode for different list.
1130                    log_("processDeps: $r is already scheduled on list $l, waiting.\n", $config->{verbose}, $config->{LOG},4);
1131                    %$topush = ();
1132                    push @{$buildlist->[$i]{$list}}, @$rpmd > 1 ? $rpmd : $rpmd->[0];
1133                    return 3
1134                    #$intopush{$r} and log_("ERROR: $r added twice\n",1, $config->{LOG}) and return 0;
1135                    #$intopush{$r} = 1;
1136                    #push @{$topush{$l}}, [$r, $rpmlist->[$i]{$l}{$r}];
1137                    #log_("DEPS $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG})
1138                } else {
1139                    log_("ERROR processDeps: deps $r could not be put in directory before packages @$rpm\n", $config->{verbose}, $config->{LOG},1);
1140                    log_("Rejecting @$rpm $r\n", $config->{verbose}, $config->{LOG},1);
1141                    reject_rpm($rpm, $group, 'order_pb', $r, \$loop, $topush);
1142                    return 0
1143            }
1144        }
1145    } else {
1146        if ($l == $list) {
1147            if ($intopush->{$r}) { log_("WARNING processDeps: $r added twice\n",1, $config->{LOG},2); return 1}
1148            $intopush->{$r} = 1;
1149            push @{$topush->{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; 
1150            log_("processDeps: adding normal deps $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG},3)
1151        } else {
1152            if ($group->{options}{sequential}) {
1153                log_("WARNING processDeps: could not add interlist deps in sequential mode\n",1, $config->{LOG},2);
1154                reject_rpm($rpm, $group, 'sequential', $r, \$loop, $topush);
1155                return 0
1156            } else {
1157                if (defined $needed->{$list}{asap} && grep { $l == $_->[0] } @{$needed->{$list}{asap}}) {
1158                    log_("ERROR processDeps: list of deps $r is already wainting for @$rpm list\n",1, $config->{LOG},1);
1159                    reject_rpm($rpm, $group, 'order_pb', $r, \$loop);
1160                    return 0
1161                } else {
1162                    if ($group->{listmatrix}{rpm}{$list}{$l}) {
1163                        if ($intopush->{$r}) { log_("WARNING processDeps: $r added twice\n",1, $config->{LOG},2); return 1 }
1164                        $intopush->{$r} = 1;
1165                        push @{$topush->{$l}}, [$r, $rpmlist->[$i]{$l}{$r}]; 
1166                        log_("processDeps: adding normal deps $r ($_ -- $l)\n", $config->{verbose}, $config->{LOG},3)
1167                    } else {
1168                        log_("ERROR processDeps: deps $r could not be put in directory before packages @$rpm\n",1, $config->{LOG},1);
1169                        reject_rpm($rpm, $group, 'order_pb', $r, \$loop);
1170                        return 0
1171                    } 
1172                }
1173            }
1174        }
1175    }
1176}
1177
1178sub reject_rpm {
1179    my ($rpm, $group, $reason, $r, $loop, $topush) = @_;
1180    log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG},1);
1181    foreach (@$rpm) { push @{$group->{rejected}{$_}}, [ $reason, $r ] }
1182    %$topush = ();
1183    $$loop = 1;
1184}
1185
1186sub updateGenericLimit {
1187    my ($groups, $cdsize) = @_;
1188    log_("updateGenericLimit\n", $config->{verbose}, $config->{LOG},2);
1189    for (my $i; $i < @$groups; $i++) {
1190        foreach my $type (keys %{$groups->[$i]{orderedlist}}) {
1191            foreach my $list (@{$groups->[$i]{orderedlist}{$type}}) {
1192                foreach my $r (@{$groups->[$i]{list}{$list}{$type}}) {
1193                    my ($cd, $rep, $repopt) = @$r;
1194                    #log_("trying to update disc $cd rep $rep list $list limit repopt $repopt (",1, $config->{LOG}),keys %$repopt,") opt $opt (",keys %$opt,")\n";
1195                    $config->{list}[$list]{disc}{$cd}{$rep}{done} and next;
1196                    $repopt->{limit} or next;
1197                    $repopt->{limit}{size} = $repopt->{limit}{value} * $cdsize->[$cd];
1198                    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);
1199                }
1200            }
1201        }
1202    }
1203}
1204
1205sub testSoftLimit {
1206    my ($opt, $cd, $groups, $buildlist) = @_;
1207    log_("testSoftLimit\n", $config->{verbose}, $config->{LOG}, 2);
1208    my $softnok = 1;
1209    # FIXME this code must be tested
1210    if ($opt->{limit} && $opt->{limit}{soft}) {
1211        foreach my $l (@{$config->{disc}[$cd]{fastgeneric}}) {
1212            my $lst = $l->[2]{list};
1213            for (my $i; $i < @$groups; $i++) {
1214                $groups->[$i]{list}{$lst} or next;     
1215                $softnok = 0 if (@{$buildlist->[$i]{$lst}} && !($lst->{limit} && $lst->{limit}{soft}))   
1216            }
1217        }
1218    }
1219    return $softnok;
1220}
1221
1222sub add_one_disc {
1223    my ($cdlists, $group, $cdsize, $list, $cds, $sources, $size, $g) = @_;
1224    my $ncd;
1225    foreach (keys %$cdlists) {
1226        $ncd = $_ + 1 if $ncd <= $_
1227    }
1228    log_("add_one_disc: $config->{list}[$list]{cd} -- $ncd\n", $config->{verbose}, $config->{LOG}, 3);
1229    if (!$config->{list}[$list]{cd} || $config->{list}[$list]{cd} >= $ncd) {
1230        log_("add_one_disc: adding new disc $ncd\n", $config->{verbose}, $config->{LOG}, 4);
1231        $config->{disc}[$ncd]{size} = $config->{discsize};
1232        my $functions = $config->{group}{disc}{functions}{functions};
1233        $cdsize->[$ncd] = $config->{discsize};
1234        $config->{disc}[$ncd]{name} = $ncd;
1235        $size->{optimize_space}{disc}{$ncd} = $cdsize->[$ncd];
1236        $group->{disc_impacted}{$ncd} = 1;
1237        my ($curdir, $srpmcurdir);
1238        my $tmp = "$config->{tmp}/build/$config->{name}";
1239        my $f = "$tmp/$ncd.list";
1240        -f $f and unlink $f;
1241        if ($config->{nolive}) {
1242            log_("makeDisc: removing $tmp/$ncd\n", $config->{verbose}, $config->{LOG}, 3);
1243            rmtree "$tmp/$ncd";
1244            mkpath "$tmp/$ncd";
1245        } else {
1246            my $dir = "$config->{topdir}/build/$config->{name}";
1247            rmtree "$dir/$ncd";
1248            rmtree "$dir/first/$ncd";
1249            mkpath "$dir/$ncd"
1250        }
1251        my $instcd = $group->{installDisc};
1252        my ($rep, $src_rep);
1253
1254        if ($sources && $config->{list}[$list]{sources} && $config->{list}[$list]{sources}{separate}) {
1255            $config->{disc}[$ncd]{serial} = "$config->{name}-$ncd-src";
1256            $config->{disc}[$ncd]{longname} = "MandrakeLinux $config->{name} sources";
1257            $config->{disc}[$ncd]{appname} = "MandrakeLinux $config->{name} sources disc $ncd";
1258            $config->{disc}[$ncd]{label} = substr "MandrakeLinux-$config->{name}-$ncd.src", 0, 32;
1259            $config->{disc}[$ncd]{group_list}{$g}{$list}{srpm} = 1;
1260            &{$functions->{dir}[0][5]}($ncd, 3, "srpms", "Mandrake/SRPMS");
1261            &{$functions->{generic}[0][5]}($ncd, 4, "srpms",1);
1262            &{$functions->{generic}[1][5]}($ncd, 5, { source => 1 });
1263            push @{$config->{disc}[$instcd]{function}{data}{installation}[1]{srpmsdir}}, [ 0, $ncd, "srpms" ];
1264            $srpmcurdir = [ $ncd, "srpms" ];
1265            push @{$group->{list}{$list}{srpm}}, $srpmcurdir;
1266            $src_rep = $group->{maxrep}{srpm};
1267            push @{$group->{replist}{srpm}}, [ $ncd, 'srpms', $group->{maxrep}{srpm}++, { $list => $srpmcurdir } ];
1268        } else {
1269            $config->{disc}[$ncd]{serial} = "$config->{name}-$ncd";
1270            $config->{disc}[$ncd]{longname} = "MandrakeLinux $config->{name}";
1271            $config->{disc}[$ncd]{appname} = "MandrakeLinux $config->{name} disc $ncd";
1272            $config->{disc}[$ncd]{label} = substr "MandrakeLinux-$config->{name}-$ncd.i586", 0, 32;
1273            $config->{disc}[$ncd]{group_list}{$g}{$list}{rpm} = 1;
1274            &{$functions->{dir}[0][5]}($ncd, 1, "rpms", "Mandrake/RPMS$ncd");
1275            &{$functions->{generic}[0][5]}($ncd, 2, "rpms", 1);
1276            $group->{orderedrep}{rpm}{"$ncd/rpms"} = $ncd;
1277            #
1278            # generic has no FIXED part, otherwize a call to generic with fixed=0
1279            # would have been needed
1280            #
1281            $curdir = [$ncd, "rpms"];
1282            push @{$group->{list}{$list}{rpm}}, $curdir;
1283            $rep = $group->{maxrep}{rpm};
1284            if ($group->{replist}{rpm}[$group->{maxrep}{rpm}-1]) {
1285                die "FATAL add_one_disc: rep $group->{maxrep}{rpm} should not exist !\n"
1286            } else {
1287                $group->{replist}{rpm}[$group->{maxrep}{rpm}-1], [ $ncd, 'rpms', $group->{maxrep}{rpm}++, { $list => $curdir } ];
1288            }
1289            push @{$config->{disc}[$instcd]{function}{data}{installation}[1]{rpmsdir}}, [ 0, $ncd, "rpms" ];
1290            if ($config->{list}[$list]{sources}) {
1291                &{$functions->{dir}[0][5]}($ncd,3, "srpms", "Mandrake/SRPMS");
1292                &{$functions->{generic}[0][5]}($ncd,4, "srpms",1);
1293                &{$functions->{generic}[1][5]}($ncd,5, { source => 1 });
1294                push @{$config->{disc}[$instcd]{function}{data}{installation}[1]{srpmsdir}}, [ 0, $ncd, "srpms" ];
1295                $srpmcurdir = [ $ncd, "srpms" ];
1296                $src_rep = $group->{maxrep}{srpm};
1297                if ($group->{replist}{srpm}[$group->{maxrep}{srpm}-1]) {
1298                    die "FATAL add_one_disc: rep $group->{maxrep}{srpm} should not exist !\n"
1299                } else {
1300                    $group->{replist}{srpm}[$group->{maxrep}{srpm}-1], [ $ncd, 'srpms', $group->{maxrep}{srpm}++, { $list => $srpmcurdir } ];
1301                } 
1302                push @{$group->{list}{$list}{srpm}}, $srpmcurdir
1303            }
1304        }
1305        push @$cds, $ncd;
1306        $cdlists->{$ncd} = 2;
1307        return $curdir, $rep, $srpmcurdir, $src_rep
1308    } else { return 0 }
1309}
1310
1311sub addSRPMToDiff {
1312    my ($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, $srpm, $list, $i, $cdnum) = @_;
1313    for (my $s; $s < @$rpmd; $s++) {
1314        if (!$rpmd->[$s][1]{nosrc} && !$done->{rep}{$srpm->[$s]}) {
1315            my $srep = $srpmrep->{$srpm->[$s]};
1316            my $idx = push @{$diff->{data}}, [ $srep->[2], $i, $list, $srep->[1], 2, [[$srpm->[$s],1, $rpmd->[$s], $srpmsize->[$s]]], 0, $srpmsize->[$s] ];
1317            push @{$diff->{idx}}, $idx - 1;
1318            $size->{disc}[$srep->[0]] += $srpmsize->[$s];
1319            $size->{rep}{$srep->[0]}{$srep->[2][1]}{$list} += $srpmsize->[$s];
1320            log_("SIZE disc $srep->[0]: $size->{disc}[$srep->[0]] (+ $srpm->[$s] $srpmsize->[$s])\n", $config->{verbose}, $config->{LOG}, 2);
1321        }
1322        $done->{rep}{$srpm->[$s]}++;
1323        $done->{list}{$srpm->[$s]} = $list
1324    }
1325    1
1326}
1327
1328sub sourcesSizeCheck {
1329    my ($done, $rpmd, $srpm, $group, $groups, $size, $cdsize, $list, $cdlists, $cdnum, $rpmsize, $buildlist, $cds, $i, $diff) = @_;
1330    my %srpmrep;   
1331    my $srpmok = 1;
1332    my @srpmsize;
1333    for (my $s; $s < @$srpm; $s++) {
1334        $done->{rep}{$srpm->[$s]} and next;
1335        $rpmd->[$s][1]{nosrc} and next;
1336        my $srpmsize = $group->{size}{$srpm->[$s]}{$list}[0];
1337        $srpmsize[$s] = $srpmsize;
1338        for (my $k; $k < @{$group->{list}{$list}{srpm}}; $k++) {
1339            my $srpmdir = $group->{list}{$list}{srpm}[$k];
1340            my ($srccd, $srcrepname, $srcopt) = @$srpmdir;
1341            my $src_rep_num = $group->{orderedrep}{srpm}{"$srccd/$srcrepname"};
1342            log_("trying source disc $srccd\n", $config->{verbose}, $config->{LOG}, 2);
1343            $cdlists->{$srccd} > 1 or next;
1344            my $currentrpm;
1345            $cdnum == $srccd and $currentrpm = $rpmsize;
1346            my $softnok = testSoftLimit($srcopt, $srccd, $groups, $buildlist);
1347            my $gain = $size->{disc}[$srccd] + $srpmsize + $currentrpm - $cdsize->[$srccd];
1348            # FIXME this need to be tested
1349            if ($gain <= 0 && !($srcopt->{limit} && ($softnok || !$srcopt->{limit}{soft}) && $size->{rep}{$srccd}{$srcrepname}{$list} > $srcopt->{limit}{size})) {
1350                $srpmrep{$srpm->[$s]} = [$srccd, $src_rep_num, $srpmdir];
1351                last
1352            } elsif ($k == $#{$group->{list}{$list}{srpm}}) {
1353                if (optimize_space($config, $groups, $diff, $size, $cdsize, $srccd, $gain, $cdlists,0, $i, $list, 'srpm', $srpmsize + $currentrpm) < $gain) {
1354                    $srpmok = 0
1355                } else {
1356                    $srpmrep{$srpm->[$s]} = [$srccd, $src_rep_num, $srpmdir];
1357                }
1358            }
1359        }
1360        if (!$srpmrep{$srpm->[$s]}) {
1361            $srpmok = 0
1362            # 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.
1363        }
1364    }
1365    if (!$srpmok && $config->{list}[$list]{auto}) {
1366        my (undef, undef, $srpmdir, $repnum) = add_one_disc($cdlists, $group, $cdsize, $list, $cds, 1, $size, $i);
1367        if ($srpmdir) {
1368            for (my $s; $s < @$srpm; $s++) {
1369                if (!$srpmrep{$srpm->[$s]}) {
1370                    $srpmrep{$srpm->[$s]} = [$srpmdir->[0], $repnum, $srpmdir];
1371                }
1372            }
1373            $srpmok = 1
1374        }
1375    }
1376    return \%srpmrep, \@srpmsize, $srpmok
1377}
1378
1379sub choose_alt {
1380    my ($deps, $rpmlist, $group, $cdnum, $repname, $list, $buildlist, $intopush, $tobedone, $needed) = @_;
1381    my $r = -1;
1382    my $score = [ 0, $group->{maxlist} ];
1383    my $done = $group->{done};
1384    foreach my $testalt (1,0) {
1385        foreach (@$deps) {
1386            # FIXME it may have a problem here, as depslistid are not erased when the
1387            # package is removed, that is to say that if the previous deps failed for
1388            # any reason, alternates deps may be added, although excluded before
1389            # however this _must_ not happen, and signify a bug somewhere else.
1390            my $pkg = $group->{depslistid}[$_];
1391
1392            my $l = find_list($group, $pkg, $list);
1393            if ($testalt && $rpmlist->{$l}{$pkg}{noalternatives}) { log_("choose_alt: $pkg is not selected in first pass for alternatives\n", $config->{verbose}, $config->{LOG},6); next }
1394            $intopush->{$pkg} and $r = $pkg and last;
1395            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);
1396            if ($group->{rejected}{$pkg}) { log_("choose_alt: $pkg is rejected, ignoring\n", $config->{verbose}, $config->{LOG}, 6); next }
1397            my $tcd;
1398            if ($rpmlist->{$done->{list}{$pkg}}{$pkg}{relocatable}) {
1399                $tcd = $group->{orderedrep}{rpm}{"$cdnum/$repname"}
1400            } elsif (!$rpmlist->{$done->{list}{$pkg}}{$pkg}{noprovide} || $done->{rep}{$pkg} <= $group->{orderedrep}{rpm}{"$cdnum/$repname"}) {
1401                $tcd = $done->{rep}{$pkg}
1402            }
1403            if ($tcd && $tcd <= $group->{orderedrep}{rpm}{"$cdnum/$repname"}) {
1404                log_("$pkg ($tcd) done\n", $config->{verbose}, $config->{LOG}, 6);
1405                $r = 0;
1406                last
1407            } 
1408            my $s = $group->{scorelist}{$pkg};
1409            my $pkgList = find_list($group, $pkg, $list, !$tcd);
1410            #log_("choose_alt: pkg $pkg buildlist $buildlist list $pkgList other list $list tcd $tcd list $l ($buildlist->{$pkgList})\n", $config->{verbose}, $config->{LOG}, 6);
1411            if ($pkgList && $list != $pkgList && ((defined $needed->{$pkgList}{asap} && grep { $list == $_->[0] } @{$needed->{$pkgList}{asap}}) || ($group->{options}{sequential} && !$config->{list}[$pkgList]{done} && @{$buildlist->{$pkgList}}))) { next }
1412            log_("choose_alt: $pkg list $pkgList (tcd $tcd listmatrix $group->{listmatrix}{rpm}{$list}{$pkgList} listsort $group->{listsort}{$pkgList}{rpm} score->[1] $score->[1] s $s)\n", $config->{verbose}, $config->{LOG}, 6);
1413            if (!$tcd && $group->{listmatrix}{rpm}{$list}{$pkgList}) {
1414                if ($group->{listsort}{$pkgList}{rpm} < $score->[1] || $group->{listsort}{$pkgList}{rpm} == $score->[1] && $s > $score->[0]) {
1415                    log_("choose_alt: choosing $pkg ($s, $group->{listsort}{$pkgList}{rpm})\n", $config->{verbose}, $config->{LOG}, 6);
1416                    $score = [ $s, $group->{listsort}{$pkgList}{rpm} ];
1417                    $r = $pkg;
1418                    last if $tobedone->{$r} 
1419                }
1420            }
1421        }
1422        $r and last
1423    }
1424    return $r
1425}
1426
1427sub check_deps {
1428    my ($rpmd, $group, $done, $rpmlist, $list, $i, $tobedone, $buildlist, $rpm, $cdnum, $repname, $needed, $rep_num, $diff) = @_;
1429    log_("check_deps\n", $config->{verbose}, $config->{LOG}, 5);
1430    my $deps = get_pkgs_deps($rpmd, $group);
1431    my $loop;
1432    if (@$deps) {
1433        my ($waiting, %topush, %intopush, $depsdisc);
1434        foreach (@$deps) {
1435            if (!ref $_) {
1436                my $a = processDeps($group->{depslistid}[$_], $group, $cdnum, $repname, $done, $rpmlist, \%topush, \%intopush, \$depsdisc, $rpmd, $list, \$loop, $i, $tobedone, $buildlist, $rpm, $needed->[$i], $diff);
1437                if ($a < 0) { return 0 } elsif ($a == 0) { last } elsif ($a == 2) { next } elsif ($a == 3) { $waiting = 1; last  }
1438            } else {
1439                # must create a virtual package that install all of them in one loop
1440                log_("check_deps: alternatives deps @$_\n", $config->{verbose}, $config->{LOG}, 5);
1441                my $r = choose_alt($_, $rpmlist->[$i], $group, $cdnum, $repname, $list, $buildlist->[$i], \%intopush, $tobedone, $needed->[$i]);
1442                $intopush{$r} and next;
1443                if ($r == -1) {
1444                    log_("ERROR check_deps: alternatives deps (@$_) could not be put in directory before packages @$rpm\n", $config->{verbose}, $config->{LOG}, 2);
1445                    log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG},2);
1446                    foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, ["order_pb", "@$_"] }
1447                    %topush = ();
1448                    $loop = 1;
1449                    last
1450                }
1451                if ($r) { 
1452                    my $a = processDeps($r, $group, $cdnum, $repname, $done, $rpmlist, \%topush, \%intopush, \$depsdisc, $rpmd, $list, \$loop, $i, $tobedone, $buildlist, $rpm, $needed->[$i]);
1453                    if ($a < 0) { return 0 } elsif ($a == 0) { last } elsif ($a == 2) { next } elsif ($a == 3) { $waiting = 1; last  }
1454                } else {
1455                    log_("Finding better alternatives rep (@$_ - $depsdisc)\n", $config->{verbose}, $config->{LOG}, 4);
1456                    my $bestdisc = keys %{$group->{orderedrep}{rpm}};
1457                    if ($bestdisc >= $depsdisc) {
1458                        foreach (@$_) {
1459                            my $pkg = $group->{depslistid}[$_];
1460                            if ($group->{rejected}{$pkg}) { log_("$pkg rejected\n", $config->{verbose}, $config->{LOG}, 2); next }
1461                            my $tcd;
1462                            log_("$pkg done $done->{list}{$pkg} relocatable $rpmlist->[$i]{$done->{list}{$pkg}}{$pkg}{relocatable}\n", $config->{verbose}, $config->{LOG}, 4);
1463                            if ($rpmlist->[$i]{$done->{list}{$pkg}}{$pkg}{relocatable}) {
1464                                $tcd = $rep_num
1465                            } else {
1466                                $tcd = $done->{rep}{$pkg};
1467                            }
1468                            $tcd or next;
1469                            log_("$pkg => rep $tcd\n", $config->{verbose}, $config->{LOG}, 4);
1470                            if ($tcd < $bestdisc) { $bestdisc = $tcd }
1471                        }
1472                        $bestdisc > $depsdisc and $depsdisc = $bestdisc
1473                    }
1474                    log_("Finding better alternatives rep result $depsdisc\n", $config->{verbose}, $config->{LOG}, 4);
1475                }
1476            }
1477        }
1478        $waiting and return 1;
1479        if (keys %topush) {
1480            $loop = 1;
1481            log_("Adding dependencies, looping\n", $config->{verbose}, $config->{LOG}, 3);
1482            my $test = @$rpmd > 1 ? $rpmd : $rpmd->[0];
1483            push @{$buildlist->[$i]{$list}}, (@$rpmd > 1 ? $rpmd : $rpmd->[0]);
1484            foreach (keys %topush) {
1485                $list != $_ and push @{$needed->[$i]{$list}{asap}}, [ $_, int @{$buildlist->[$i]{$_}} ];
1486                push @{$buildlist->[$i]{$_}}, @{$topush{$_}}
1487            }
1488        } elsif ($rep_num < $depsdisc) {
1489            if ($group->{listmaxrep}{rpm}{$list} >= $depsdisc) {
1490                $loop = 2;
1491                # has a chance to put it after depsdisc
1492                log_("Dependencies on further directories ($depsdisc < $group->{listmaxrep}{rpm}{$list} rep_num $rep_num)\n", $config->{verbose}, $config->{LOG}, 3);
1493            } else {
1494                $loop = 1;
1495                log_("check_deps: dependencies are in further directories, rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2);
1496                foreach (@$rpm) { push @{$group->{rejected}{$_}}, ["order_pb", ""] }
1497            }
1498        }
1499    }
1500    foreach my $a (@{$buildlist->[$i]{13}}) {
1501        if (ref $a->[0]) {
1502            foreach (@$a) {
1503                log_("Remaining $_->[0]\n", $config->{verbose}, $config->{LOG},7);
1504            }
1505        } else { 
1506            log_("Remaining $a->[0]\n", $config->{verbose}, $config->{LOG},7);
1507        }
1508    }
1509    $loop
1510}
1511
1512sub put_in_rep {
1513    my ($i, $groups, $group, $size, $rpmsize, $all_rpmsize, $cdsize, $needed, $rpm, $rpmd, $list, $cdlists, $buildlist, $diff, $cds, $done, $tobedone, $rpmlist, $nosrcfit) = @_; 
1514    my $loop;
1515    my $dn;
1516    log_("put_in_rep: @$rpm\n", $config->{verbose}, $config->{LOG}, 3);
1517    for (my $j; !$loop && !$dn && $j < @{$group->{list}{$list}{rpm}}; $j++) {
1518        $loop = 0;
1519        log_("put_in_rep: testing dir $j\n", $config->{verbose}, $config->{LOG}, 3);
1520        my $curdir = $group->{list}{$list}{rpm}[$j];
1521        $config->{list}[$list]{disc}{$curdir->[0]}{$curdir->[1]}{done} and next;
1522        my ($cdnum, $repname, $repopt) = @$curdir;
1523        my $rep_num = $group->{orderedrep}{rpm}{"$cdnum/$repname"};
1524        log_("put_in_rep: testing dir $j cdnum $cdnum repname $repname\n", $config->{verbose}, $config->{LOG}, 3);
1525        $cdlists->{$cdnum} > 1 or next;
1526        my ($relocatable_list, $relocatable_rep_num) = ($list, $rep_num);
1527        if (defined $group->{reploc}{$rep_num} && ref $group->{reploc}{$rep_num}) {
1528            ($relocatable_rep_num, $curdir, $relocatable_list) = check_last_relocatable($group, $cdnum, $repname, $rep_num, $list, $curdir);
1529        }
1530        my $softnok = testSoftLimit($repopt, $cdnum, $groups, $buildlist);
1531        my $gain = $size->{disc}[$cdnum] + $all_rpmsize - $cdsize->[$cdnum];
1532        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);
1533        if ($gain > 0 || $repopt->{limit} && ($softnok || !$repopt->{limit}{soft}) && $size->{rep}{$cdnum}{$repname}{$list} + $all_rpmsize > $repopt->{limit}{size}) {
1534            if ($j == $#{$group->{list}{$list}{rpm}}) {
1535                if (!($repopt->{limit} && !$softnok && $repopt->{limit}{soft})) {
1536                    if (optimize_space($config, $groups, $diff, $size, $cdsize, $cdnum, $gain, $cdlists,0, $i, $list, 'rpm', $all_rpmsize) < $gain) {
1537                        if ($config->{list}[$list]{auto}) {
1538                            ($curdir, $rep_num) = add_one_disc($cdlists, $group, $cdsize, $list, $cds,0, $size, $i);
1539                            if ($curdir) {
1540                                $cdnum = $curdir->[0]
1541                            } else {
1542                                log_("Could not add more disc, rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2);
1543                                foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, [ "no_disc", "" ] }
1544                                next
1545                            }
1546                        } else {
1547                            log_("Rejecting @$rpm\n", $config->{verbose}, $config->{LOG}, 2);
1548                            foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, ["no_space", ""] }
1549                            next
1550                        }
1551                    }
1552                } else { 
1553                    foreach my $l (@{$config->{disc}[$cdnum]{fastgeneric}}) {
1554                        my $lst = $l->[2]{list};
1555                        $list == $lst and next;
1556                        for (my $i; $i < @$groups; $i++) {
1557                            $groups->[$i]{list}{$lst}{rpm} or next;     
1558                            push @{$needed->[$i]{$list}{asap}}, [ $lst, 0 ] if (!($lst->{limit} && $lst->{limit}{soft}))
1559                        }
1560                    }
1561                }
1562            } else { next }
1563        }
1564        if (!$config->{nodeps} && !$group->{options}{nodeps}) {
1565           $loop = check_deps($rpmd, $group, $done, $rpmlist, $list, $i, $tobedone->[$i], $buildlist, $rpm, $cdnum, $repname, $needed, $rep_num, $diff)
1566        }
1567        if ($loop) {
1568            $loop = 0 if $loop == 2;   
1569            next
1570        }       
1571        log_("@$rpm deps ok\n", $config->{verbose}, $config->{LOG}, 4);
1572        my $nosrc = 1;
1573        my @srpm;
1574        my $donesrpm = 1;
1575        if (!$group->{options}{nosources} && @{$group->{list}{$list}{srpm}}) {
1576            for (my $s; $s < @$rpmd; $s++) {
1577                my $srpm = $group->{urpm}{sourcerpm}{$rpm->[$s]}; 
1578                $srpm =~ s/\.rpm$//;
1579                if (!$group->{size}{$srpm}{$list}) {
1580                    log_("put_in_rep ERROR: $srpm not available, trying alternatives => ", $config->{verbose}, $config->{LOG}, 5);
1581                    my ($srpmname) = $srpm =~ /(.*)-[^-]+-[^-]+\.src/;
1582                    $srpm = $group->{srpmname}{$srpmname};
1583                    if ($group->{size}{$srpm}{$list}) { 
1584                        log_(" $srpm\n", $config->{verbose}, $config->{LOG}) 
1585                    } else {
1586                        if ($srpm) {
1587                            log_("not found (but a srpm $srpm exist in another list)\n", $config->{verbose}, $config->{LOG}, 5);
1588                            $srpm = 0
1589                        } else {
1590                            log_("not found\n", $config->{verbose}, $config->{LOG}, 5) 
1591                        }
1592                    }
1593                }
1594                if ($srpm) { 
1595                    $done->{rep}{$srpm} or $donesrpm = 0;
1596                    $srpm[$s] = $srpm;
1597                    $rpmd->[$s][1]{nosrc} or $nosrc = 0 
1598                }
1599            }
1600        }
1601        log_("put_in_rep: group $i list $list: @$rpm (@srpm) -- $curdir->[0] -- $curdir->[1] -- disc $cdnum\n", $config->{verbose}, $config->{LOG}, 4);
1602        if ($config->{nosrc} || $group->{options}{nosources} || !@{$group->{list}{$list}{srpm}} || $nosrc || $donesrpm) {
1603            ($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)
1604        } else {
1605            if ($config->{nosrcfit} || $group->{options}{nosrcfit}) {
1606                $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);
1607                push @$nosrcfit, [$rpmd, \@srpm, $list, $i, $curdir, $cdnum]
1608            } else {
1609                my ($srpmrep, $srpmsize, $srpmok) = sourcesSizeCheck($done, $rpmd, \@srpm, $group, $groups, $size, $cdsize, $list, $cdlists, $cdnum, $all_rpmsize, $buildlist, $cds, $i, $diff);
1610                if ($srpmok) {
1611                    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);
1612                    $dn = addSRPMToDiff($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, \@srpm, $list, $i, $cdnum);
1613                } else {
1614                    log_("WARNING: @srpm does not fit on the discs\n",1, $config->{LOG}, 2)
1615                }
1616            }
1617            if (!$dn) {
1618                foreach my $p (@$rpm) { push @{$group->{rejected}{$p}}, ["no_space", ""] }
1619                log_("WARNING: @$rpm does not fit on the disc ($size->{disc}[$cdnum] + $all_rpmsize > $cdsize->[$cdnum]) \n", $config->{verbose}, $config->{LOG}, 1)
1620            }
1621        }
1622    }
1623    return $dn
1624}
1625
1626sub loop_on_lists {
1627    my ($i, $groups, $group, $groupok, $needed, $buildlist, $tobedone, $diff, $nosrcfit, $size, $cdsize, $cds, $rpmlist, $cdlists, $ok, $mark, $groupok) = @_; 
1628    #
1629    # FIXME source rpms are not shared between group, it may be usefull for mutilple installation
1630    # with common source dir, so that the same source rpm is shared (but this is not so common).
1631    #
1632    my $done = $group->{done};
1633    my $rpmd_add = sub {
1634        my ($rpm, $rpmd, $r, $list) = @_;
1635        log_("Testing $rpm\n", $config->{verbose}, $config->{LOG},7);
1636        my $d = $done->{rep}{$rpm};
1637        my $reloc = $rpmlist->[$i]{$done->{list}{$rpm}}{$rpm}{relocatable};
1638        if (!$d || $rpmlist->[$i]{$done->{list}{$rpm}}{$rpm}{noprovide}) { 
1639            push @$rpmd, $r
1640        } elsif ($d && $reloc) { 
1641            my $curdir = $group->{list}{$list}{rpm}[0];
1642            my ($cdnum, $repname) = @$curdir;
1643            my $repnum = $group->{orderedrep}{rpm}{"$cdnum/$repname"};
1644            if (!check_deps([ $r ], $group, $done, $rpmlist, $list, $i, $tobedone->[$i], $buildlist, $rpm, $cdnum, $repname, $needed, $repnum, $diff)) {
1645                add_relocatable_package($group, $d, $diff, $rpm, $done->{list}{$rpm}, $i, $repnum, $repname)
1646            }
1647            return 0
1648        }
1649        1
1650    };
1651    my $dn;
1652    log_("loop_on_lists: group $i (@{$group->{orderedlist}{rpm}})\n", $config->{verbose}, $config->{LOG}, 2);
1653    while (!$dn) {
1654        $groupok->[$i] = 1;
1655        foreach my $list (@{$group->{orderedlist}{rpm}}) {
1656            my $nb = @{$buildlist->[$i]{$list}};
1657            log_("loop_on_lists: list $list (empty $config->{list}[$list]{empty} done $config->{list}[$list]{done} nb $nb)\n", $config->{verbose}, $config->{LOG}, 3);
1658                foreach my $a (@{$buildlist->[$i]{13}}) {
1659                    if (ref $a->[0]) {
1660                        foreach (@$a) {
1661                            log_("Remaining $_->[0]\n", $config->{verbose}, $config->{LOG},7);
1662                        }
1663                    } else { 
1664                        log_("Remaining $a->[0]\n", $config->{verbose}, $config->{LOG},7);
1665                    }
1666                }
1667            do {
1668                $config->{list}[$list]{done} and goto end;
1669                $config->{list}[$list]{empty} and goto end;
1670                my $next;
1671                foreach (@{$needed->[$i]{$list}{asap}}) {
1672                    my $nb_elt = @{$buildlist->[$i]{$_->[0]}};
1673                    log_("List $list need list $_->[0] to be <= $_->[1] ($nb_elt)\n", $config->{verbose}, $config->{LOG}, 4);
1674                    $nb_elt <= $_->[1] or $next = 1
1675                }
1676                if ($next){
1677                    log_("List $list waiting\n",1, $config->{LOG},4);
1678                    goto end
1679                }
1680                $needed->[$i]{$list}{asap} = [];
1681                my ($trpmd, $k, $goon, @rpmd);
1682                do { 
1683                    $trpmd = pop @{$buildlist->[$i]{$list}} or goto end;
1684                    if (ref $trpmd->[0]) {
1685                        foreach (@$trpmd) {
1686                            $rpmd_add->($_->[0], \@rpmd, $_, $list) or goto end;
1687                        }
1688                    } else { $rpmd_add->($trpmd->[0], \@rpmd, $trpmd, $list) or goto end; }
1689                } until @rpmd;
1690                $groupok->[$i] = 0;
1691                $ok = 0;
1692                my @rpm;
1693                my $all_rpmsize;
1694                my @rpmsize;
1695                foreach (@rpmd) {
1696                    my $r = $_->[0];
1697                    !$r and log_("ERROR loop_on_lists: empty package @$_\n", $config->{verbose}, $config->{LOG}, 2);
1698                    push @rpm, $r;
1699                    log_("RPM $r (group $i list $list)\n", $config->{verbose}, $config->{LOG},6);
1700                    $tobedone->[$i]{$r} = 1;
1701                    $all_rpmsize += $group->{size}{$r}{$list}[0];
1702                    push @rpmsize, $group->{size}{$r}{$list}[0]
1703                }
1704                $dn = put_in_rep($i, $groups, $group, $size, \@rpmsize, $all_rpmsize, $cdsize, $needed, \@rpm, \@rpmd, $list, $cdlists, $buildlist, $diff, $cds, $done, $tobedone, $rpmlist, $nosrcfit); 
1705                $groupok->[$i] = mark_and_check_lists($groups, $i, $needed->[$i], $diff, $buildlist->[$i], $rpmlist, $mark->[$i], $size, $cdsize, $groupok->[$i], $tobedone->[$i]) if $group->{options}{sequential}
1706            } while $group->{options}{sequential} && @{$buildlist->[$i]{$list}};
1707            end:
1708            last if $group->{options}{sequential} && @{$buildlist->[$i]{$list}} && !$config->{list}[$list]{done}
1709        } 
1710        $groupok->[$i] and $dn = 1
1711    }
1712    return $ok
1713}
1714
1715sub calc_needed_size {
1716    my ($group, $i, $needed, $needed_size, $buildlist, $rpmlist, $tobedone) = @_;
1717    my ($msg, %local_done);
1718    $msg = "calc_needed_size\n";
1719    my $done = $group->{done};
1720    foreach my $rep (@{$group->{replist}{rpm}}) {
1721        my ($cd, $repname, $num, $l) = @$rep;
1722        $msg .= "calc_needed_size: rep $num\n";
1723        foreach my $list (keys %$needed) {
1724            $l->{$list} or next;
1725            if ($config->{list}[$list]{disc}{$cd}{$repname}{done}){
1726                log_("calc_needed_size: rep $cd/$repname for list $list is done, ignoring\n", $config->{verbose}, $config->{LOG}, 5);
1727                next
1728            }
1729            foreach my $elt (@{$needed->{$list}{alap}[$num]}) {
1730                my $rpm = $elt->[0];
1731                if (($done->{rep}{$rpm} && !$rpmlist->[$i]{$group->{done}{list}{$rpm}}{$rpm}{noprovide}) || $local_done{$rpm}) {
1732                    next
1733                }
1734                if ($group->{rejected}{$rpm}) {
1735                    $msg .= "ERROR: $rpm is rejected, ignoring\n";
1736                    next
1737                }
1738                $msg .= "calc_needed_size: list $list rpm $rpm size $group->{size}{$rpm}{$list}[0] (done $done->{rep}{$rpm})\n";
1739                $needed_size->[$num]{fix} += $group->{size}{$rpm}{$list}[0];
1740                $local_done{$rpm} = 1;
1741                # FIXME This following code is a simplified version of check_deps. It may be overkill to use
1742                # full check_deps as anyway check_deps will be used to put the package at the end.
1743                foreach (@{$group->{pkgdeps}{$rpm}}) {
1744                    if (ref $_) {
1745                        $local_done{"@$_"} and next;
1746                        $local_done{"@$_"} = 1;
1747                        my $r = choose_alt($_, $rpmlist->[$i], $group, $cd, $num, $list, $buildlist, $tobedone, $needed);
1748                        if ($r != -1 && $r != 0) {
1749                            next if $local_done{$r};
1750                            $needed_size->[$num]{var} += $group->{size}{$r}{$list}[0];
1751                        }
1752                    } else {
1753                        my $pkg = $group->{depslistid}[$_];
1754                        next if ($done->{rep}{$pkg} && !$rpmlist->[$i]{$group->{done}{list}{$rpm}}{$rpm}{noprovide}) || $local_done{$pkg};
1755                        $local_done{$pkg} = 1;
1756                        $needed_size->[$num]{var} += $group->{size}{$pkg}{$list}[0];
1757                    }
1758                }
1759            }
1760        }
1761    }
1762    log_($msg, $config->{verbose}, $config->{LOG}, 3)
1763}
1764
1765sub revert_to {
1766    my ($groups, $i, $p2r, $diff, $size, $buildlist) = @_;
1767    log_("revert_to: try to find $p2r\n", $config->{verbose}, $config->{LOG}, 3);
1768    foreach (@{$diff->{data}}) {
1769        $_ or next; 
1770        foreach (@{$_->[5]}) {
1771            goto revert_to_ok if $_->[0] eq $p2r
1772        } 
1773    }
1774    log_("ERROR revert_to: $p2r is not present in movement history\n", $config->{verbose}, $config->{LOG}, 2);
1775    return 0;
1776    revert_to_ok:
1777    log_("revert_to: $p2r found\n", $config->{verbose}, $config->{LOG}, 5);
1778    my @keep;
1779    my $idx;
1780    do {
1781        $idx = pop @{$diff->{idx}};
1782        goto revert_to_endloop if any { $_->[0] eq $p2r } @{$diff->{data}[$idx][5]}; 
1783        my $step = $diff->{data}[$idx];
1784        if ($groups->[$i]{conflict}{$step->[1]}) {
1785            my ($curdir, $g, $list) = @$step;
1786            my $cdnum = $curdir->[0];
1787            foreach (@{$step->[5]}) { 
1788                my ($rpm, undef, undef, $rpmsize) = @$_; 
1789                delete $groups->[$g]{done}{rep}{$rpm}; 
1790                delete $groups->[$g]{done}{list}{$rpm}; 
1791                $diff->{data}[$idx] = 0;
1792                $size->{disc}[$cdnum] -= $rpmsize; 
1793                $size->{rep}{$cdnum}{$curdir->[1]}{$list} -= $rpmsize;
1794                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)
1795            }
1796            push @{$buildlist->{$list}}, $step->[6] if $step->[4] == 1
1797        } else {
1798            unshift @keep, $idx;
1799        }
1800    } while @{$diff->{idx}};
1801revert_to_endloop:
1802    die "FATAL revert_to: diff data are empty\n" if ! @{$diff->{data}};
1803    push @{$diff->{idx}}, $idx, @keep;
1804    1
1805}
1806
1807sub mark_and_check_lists {
1808    my ($groups, $i, $needed, $diff, $buildlist, $rpmlist, $mark, $size, $cdsize, $ok, $tobedone) = @_;
1809    my @needed_size;
1810    my $group = $groups->[$i];
1811    my $need_to_calc;
1812    foreach my $list (@{$group->{orderedlist}{rpm}}) {
1813        ref $buildlist->{$list} or next;
1814        log_("mark_and_check_list: group $i list $list\n", $config->{verbose}, $config->{LOG}, 3);
1815        if ($config->{list}[$list]{done}) {
1816            log_("mark_and_check_lists: list $list is done, ignoring\n", $config->{verbose}, $config->{LOG}, 5);
1817            next
1818        }
1819        if (defined $mark->{cur}{$list}) {
1820            $need_to_calc = 1;
1821            my $m = $mark->{cur}{$list};
1822            my $m_rpm = $m->[0];
1823            if ($group->{done}{rep}{$m_rpm}) {
1824                log_("mark_and_check_list: $m_rpm done, deleting mark for list $list\n", $config->{verbose}, $config->{LOG},4);
1825                push @{$mark->{his}}, $m;
1826                delete $mark->{cur}{$list}
1827            } elsif ($group->{rejected}{$m_rpm}) {
1828                log_("mark_and_check_list: $m_rpm rejected, deleting mark for list $list\n", $config->{verbose}, $config->{LOG},4);
1829                delete $mark->{cur}{$list}
1830            } elsif (!@{$buildlist->{$list}}) {
1831                log_("mark_and_check_list: list $list finished and $m_rpm not done or rejected\n",1, $config->{LOG},4)
1832            }
1833        }
1834        if (!defined $mark->{cur}{$list} && @{$buildlist->{$list}}) {
1835            my $rpm;
1836            for (my $j = $#{$buildlist->{$list}}; $j >= 0; $j--) {
1837                my $t = $buildlist->{$list}[$j];
1838                $rpm = ref $t->[0] ? $t->[0] : $t;
1839                if (!$group->{done}{rep}{$rpm->[0]} || $rpmlist->[$i]{$group->{done}{list}{$rpm->[0]}}{$rpm->[0]}{relocatable} || $rpmlist->[$i]{$group->{done}{list}{$rpm->[0]}}{$rpm->[0]}{noprovide} ) {
1840                    last
1841                } 
1842                # this is not necessary, but when we are at it...
1843                log_("mark_and_check_list: $rpm->[0] done (list $group->{done}{list}{$rpm->[0]} cd $group->{done}{rep}{$rpm->[0]}), removing from queue\n", $config->{verbose}, $config->{LOG},4);
1844                pop @{$buildlist->{$list}}
1845            }
1846            $mark->{cur}{$list} = $rpm;
1847            log_("mark_and_check_list: marking $rpm->[0] for $list\n", $config->{verbose}, $config->{LOG},3);
1848        }
1849    }
1850    if ($need_to_calc || $ok) {
1851        calc_needed_size($group, $i, $needed, \@needed_size, $buildlist, $rpmlist, $tobedone);
1852        my ($need_in_rep, $av_in_rep, %done_disc);
1853        #
1854        # First impression would have been to check needed in reverse order, because we could imagine
1855        # that, in the current configuration, if needed 2 does not fit, for exemple, one package
1856        # is removed, needed 2 is put. But if needed does not fit at this moment, needed 2 is removed,
1857        # and needed 3 put, then needed 2 does not fit, and it is needed to revert more to make both of
1858        # them fit.
1859        #
1860        # If fact this could not happen, because if needed 3 does not fit when needed 2 has just been
1861        # put, this mean that the calc_needed_size if bogus.
1862        #
1863        foreach my $rep (@{$group->{replist}{rpm}}) {
1864            my ($cd, undef, $num) = @$rep;
1865            $need_in_rep += $needed_size[$num]{var} + $needed_size[$num]{fix};
1866            if (! $done_disc{$num}) {
1867                $av_in_rep += $cdsize->[$cd] - $size->{disc}[$cd];
1868                $done_disc{$num} = 1
1869            }
1870            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);
1871            if ($need_in_rep && $need_in_rep > $av_in_rep || $needed_size[$num]{fix} > $cdsize->[$cd] - $size->{disc}[$cd]) {
1872                if ($mark->{cd}{$cd}) {
1873                    die "FATAL mark_and_check_list: a previous revert to put needed packages failed, cannot order packages correctly fo rep $num on disc $cd\n"
1874                } else {
1875                    log_("mark_and_check_list: not enough space for needed in rep $num on disc $cd\n", $config->{verbose}, $config->{LOG}, 3);
1876                    pop @{$mark->{his}};
1877                    $mark->{cd}{$cd} = 1;
1878                    my $p2r = pop @{$mark->{his}};
1879                    log_("mark_and_check_list: trying to revert $p2r->[0]\n", $config->{verbose}, $config->{LOG}, 4);
1880                    if (revert_to($groups, $i, $p2r->[0], $diff, $size, $buildlist)) {
1881                        log_("mark_and_check_list: $p2r->[0] reverted\n", $config->{verbose}, $config->{LOG}, 3);
1882                        foreach my $idx (0 .. @{$group->{orderedlist}{rpm}}) {
1883                            my $list = $group->{orderedlist}{rpm}[$idx];
1884                            $needed->{$list} or next;
1885                            my $elt;
1886                            foreach my $tr (1 .. $num) {
1887                                foreach my $elt (@{$needed->{$list}{alap}[$tr]}) {
1888                                    my $rpm = $elt->[0];
1889                                    if ($group->{rejected}{$rpm}) {
1890                                        log_("ERROR: $rpm is rejected, ignoring\n", $config->{verbose}, $config->{LOG}, 3);
1891                                        next
1892                                    }
1893                                    push @{$buildlist->{$list}}, $elt;
1894                                }
1895                            }
1896                            $mark->{cur}{$list} = $elt->[0];
1897                            my $l_idx = $#{$buildlist->{$list}};
1898                            next if $l_idx < 0;
1899                            foreach my $tidx ($idx + 1 .. @{$group->{orderedlist}{rpm}}) {
1900                                my $l = $group->{orderedlist}{rpm}[$tidx];
1901                                push @{$needed->{$l}{asap}}, [ $list, $l_idx ]
1902                            }
1903                        }
1904                    } else {
1905                        log_("ERROR mark_and_check_list: reverting to $p2r->[0] failed\n", $config->{verbose}, $config->{LOG}, 4)
1906                    }
1907                }
1908            }
1909        }
1910    }
1911    return $ok
1912}
1913
1914# TODO the algo is not as beautiful as it should be
1915# ... but it is getting better
1916# ... and better
1917sub buildDiscs {
1918    my ($class, $groups, $buildlist, $rpmlist, $groupok, $size, $cdsize, $cdlists, $cds, $needed, $diff, $n) = @_;
1919    log_("buildDiscs\n", $config->{verbose}, $config->{LOG}, 3);
1920    $config = $class->{config};
1921    if ($n > 1) {
1922        foreach my $i (reverse @$cds) {
1923            $size->{optimize_space}{disc}{$i} = $size->{disc}[$i];
1924            if ($size->{disc}[$i] > $cdsize->[$i]) { 
1925                my $gain = ($size->{disc}[$i] - $cdsize->[$i])/2;
1926                next if $gain < 0;
1927                optimize_space($config, $groups, $diff, $size, $cdsize, $i, $gain, $cdlists,1)
1928            } else {
1929                log_("buildDiscs: disc $i size OK $size->{disc}[$i] ($cdsize->[$i])\n", $config->{verbose}, $config->{LOG},2)
1930            }
1931        }
1932    }
1933    my ($ok, $iti);
1934    my @groupok;
1935    my (@tobedone, @nosrcfit);
1936    my @mark = ({}) x @$groups;
1937    updateGenericLimit($groups, $cdsize);
1938    while (!$ok) {
1939        log_("iti: " . $iti++ . "\n", $config->{verbose}, $config->{LOG},4);
1940        $ok = 1;
1941        for (my $i = 0; $i < @$groups; $i++) {
1942            my $group = $groups->[$i];
1943            if (!$config->{fast}) {
1944                $groupok[$i] = mark_and_check_lists($groups, $i, $needed->[$i], $diff, $buildlist->[$i], $rpmlist, $mark[$i], $size, $cdsize, $groupok[$i], $tobedone[$i]);
1945                $groupok[$i] and next;
1946            }
1947            $ok = loop_on_lists($i, $groups, $group, \@groupok, $needed, $buildlist, \@tobedone, $diff, \@nosrcfit, $size, $cdsize, $cds, $rpmlist, $cdlists, $ok, \@mark, \@groupok); 
1948        }
1949    }
1950    foreach (@nosrcfit) {
1951        my ($rpmd, $srpm, $list, $i, $curdir, $cdnum) = @$_;
1952        my $group = $groups->[$i];
1953        my $done = $group->{done};
1954        my ($srpmrep, $srpmsize, $srpmok) = sourcesSizeCheck($done, $rpmd, $srpm, $group, $groups, $size, $cdsize, $list, $cdlists,0,0, $buildlist, $cds, $i, $diff);
1955        if ($srpmok) {
1956            addSRPMToDiff($rpmd, $done, $diff, $size, $srpmrep, $srpmsize, $curdir, $srpm, $list, $i, $cdnum);
1957        } else {
1958            log_("WARNING: @$srpm does not fit on the discs\n",1, $config->{LOG},2)
1959        }
1960    }
1961    my $is_rejected;
1962    log_("buildDiscs: rejected packages\n", $config->{verbose}, $config->{LOG},2);
1963    for (my $i; $i < @$groups; $i++) {
1964        reprocess_relocatable($groups->[$i], $cdsize, $size);
1965        $groups->[$i]{rejected} or next;
1966        my $gh = $groups->[$i]{rejected};
1967        foreach (keys %$gh) {
1968            if (!$is_rejected) {
1969                $is_rejected = 1 if any { $_->[0] =~ /no_disc/ || $_->[0] =~ /no_space/ } @{$gh->{$_}};
1970            }
1971            log_("WARNING buildDisc: group $i REJECTED $_ (",$config->{verbose}, $config->{LOG},2);
1972            ref $groups->[$i]{rejected}{$_} and log_((join ',', map { "$config->{rejected_options}{$_->[0]}: $_->[1]" } @{$groups->[$i]{rejected}{$_}}), 1, $config->{LOG},2);
1973            log_(")\n", $config->{verbose}, $config->{LOG}, 2);
1974        }
1975    }
1976    ($is_rejected)
1977}
19781
1979
1980# Changelog
1981#
1982# 2002 02 21
1983# change false $j comparaison to $depsdisc in buildDisc to new $thisorderrep value.
1984#
1985# 2002 03 03
1986# new limit option handling.
1987# add updateGenericSoft function
1988# add testSoftLimit function
1989# update size to check rep size
1990#
1991# 2002 03 08
1992# fix autoMode CD adding
1993#
1994# 2002 03 13
1995# better selection of alternatives in multi-list to take the one in the first lists.
1996#
1997# 2002 03 14
1998# add sources new sources handling method
1999# in nosrcfit mode sources are added afterwards
2000#
2001# 2002 03 19
2002# add prelist in geList for cdcom, will be useful for oem too I guess.
2003#
2004# 2002 05 02
2005# add_one_disc: add separate mode for sources mode
2006#
2007# 2002 05 09
2008# add graft structure for md5 and graft point handling
2009#
2010# 2002 05 13
2011# fix a tricky bugs in build_list about fentry shared and not recreated for each packages.
2012#
2013# 2002 06 01
2014# use perl-URPM
2015#
2016# 2002 06 15
2017# new diff mode, global, shared between disc and group, only one table.
2018#
2019# 2002 08 16
2020# new diff_idx table to sort diff data
2021#
2022# 2002 08 24
2023# optimize_space first version, still need to handle correctly needed and more advanced optimization methods.
2024#
2025# 2002 09 18
2026# optimize_space work, fixes and updates.
2027#
2028# 2002 10 25
2029# fix needed assignation pb in closeRPMslist
Note: See TracBrowser for help on using the repository browser.