package Mkcd::Disc; my $VERSION = '0.1.1'; use strict; use File::Path; use Mkcd::Functions; use Mkcd::Tools qw(du compute_md5 log_ include_md5); =head1 NAME Disc - mkcd disc functions =head1 SYNOPSYS require Mkcd::Disc; =head1 DESCRIPTION C include the mkcd disc handling subroutines. =head1 SEE ALSO mkcd =head1 COPYRIGHT Copyright (C) 2000,2001 MandrakeSoft This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. =cut my $config; sub new { my ($class, $conf) = @_; $config = $conf; bless { config => $conf, functions => new Mkcd::Functions($config) }, $class; } # FIXME must add space for synthesis, however they are negligeable compared to hdlist. Only # a pb with very small CD. sub guessHdlistSize { my ($class,$group,$size,$cdsize,$lists, $discsFiles) = @_; my $FACTOR = 130; my $SynFACTOR = 90; my $msg; my $depsRep = "$config->{tmp}/$class->{config}{name}/$group->{depsrep}"; $msg = "guessHdlistSize: depsRep $depsRep\n"; # FIXME heuristic for hdlist size on installation disc, (RPMS size / $FACTOR) per discs # need genDeps to write hdlist/synthesis, overkill my $depsSize = du($depsRep); my $instdisc = $group->{installDisc}; my $sz; my (@notdone, @rem_size); push @rem_size, @$cdsize; foreach my $list (keys %{$group->{list}}) { if ($config->{list}[$list]{auto}) { if ($config->{list}[$list]{cd}) { my $tsize = ($config->{discsize}/$FACTOR) * $config->{list}[$list]{cd}; $sz += $tsize < $depsSize ? $tsize : $depsSize; } else { $sz += $depsSize } } else { my $ok; my $listsize = $group->{listsize}{$list}{rpm}; foreach my $rd (@{$group->{list}{$list}{rpm}}) { my ($cdrep,undef,undef, $opt) = @$rd; if ($opt->{nodeps}) { $ok = 1; next } if ($lists->{$cdrep}) { if ($listsize > $cdsize->[$cdrep]) { $sz += $rem_size[$cdrep] / $FACTOR; $listsize -= $rem_size[$cdrep]; $rem_size[$cdrep] = 0 } else { $sz += $listsize / $FACTOR; $rem_size[$cdrep] -= $listsize; last } } } $ok and push @notdone, $list } } $msg .= "guessHdlistSize: reserving "; if ($depsSize < $sz && $depsSize > 10000) { $msg .= $depsSize; $size->{disc}[$instdisc] += int $depsSize; if ($config->{disc}[$instdisc]{function}{data}{installation}[1]{synthesis}) { $size->{disc}[$instdisc] += int($depsSize / $SynFACTOR) } } elsif ($sz > 10000) { $msg .= $sz; $size->{disc}[$instdisc] += int $sz; if ($config->{disc}[$instdisc]{function}{data}{installation}[1]{synthesis}) { $size->{disc}[$instdisc] += int($sz / $SynFACTOR) } } else { log_("ERROR guessHdlistSize: possibly wrong estimated dependencies file size\n", $config->{verbose}, $config->{LOG},2) } $msg .= " (new size $size->{disc}[$instdisc]) on disc $instdisc ($depsSize/$sz) for dependencies files\n"; log_($msg, $config->{verbose}, $config->{LOG},2); @notdone or return 1; $sz = 0; foreach my $list (@notdone) { foreach my $rd (@{$group->{list}{$list}{rpm}}) { my ($cd,$rep,$repopt, $opt) = @$rd; if ($lists->{$cd} == 1) { $sz += du("$class-{config}{topdir}/build/$class->{config}{name}/$cd/$class->{config}{disc}[$cd]{function}{data}{dir}{$rep}") } elsif ($lists->{$cd} == 2) { foreach my $rpm (keys %{$discsFiles->[$cd]{$rep}{$list}}) { $sz += du("$discsFiles->[$cd]{$rep}{$list}{$rpm}/$rpm.rpm") } } } } $sz /= $FACTOR; $msg = "guessHdlistSize: reserving $sz"; $size->{disc}[$instdisc] += $sz; if ($config->{disc}[$instdisc]{function}{data}{installation}[1]{synthesis}) { $size->{disc}[$instdisc] += $sz / $SynFACTOR } $msg .= " (new size $size->{disc}[$instdisc]) on disc $instdisc ($sz) for extra dependencies files\n"; log_($msg, $config->{verbose}, $config->{LOG},1) } sub getBuiltDiscs { my ($class, $lists, $group, $discsFiles) = @_; foreach my $l (keys %{$group->{list}}) { log_("getBuiltDiscs: get rep from list $l\n",$config->{verbose}, $config->{LOG},2); my @rpmlist; ref $group->{list}{$l}{rpm} and push @rpmlist, @{$group->{list}{$l}{rpm}}; ref $group->{list}{$l}{srpm} and push @rpmlist, @{$group->{list}{$l}{srpm}}; for (my $i; $i < @rpmlist; $i++) { my ($cd,$rep, $repopt) = @{$rpmlist[$i]}; $lists->{$cd} == 1 or next; my $dir = "$class->{config}{topdir}/build/$class->{config}{name}/$cd/$class->{config}{disc}[$cd]{function}{data}{dir}{$rep}"; # # FIXME maybe need to unshift instead of push # $repopt->{source} ? push(@{$class->{config}{list}[$l]{packages}[0]{srpm}}, $dir) : push(@{$class->{config}{list}[$l]{packages}[0]{rpm}}, $dir); $class->{config}{list}[$l]{disc}{$cd}{$rep}{done} = 1; log_("getBuiltDiscs: get files from $dir\n", $config->{verbose}, $config->{LOG},2); local *A; opendir A,$dir; foreach (readdir A) { /(.*)\.rpm/ or next; # FIXME need to check if it is well placed in getList function # $group->{done}{$rpm} = $group->{orderedrep}{"$cd/$rep"}; $discsFiles->[$cd]{$rep}{$l}{$1} = $dir } } } 1 } sub write_graft { my ($graft, $file, $exclude) = @_; log_("write_graft: $file ($graft)\n", $config->{verbose}, $config->{LOG},2); local *A; open A, ">$file"; local *B; open B, ">$exclude"; foreach my $d (sort keys %$graft) { if (ref $graft->{$d}) { map { print A "$d=$_\n" } keys %{$graft->{$d}} } elsif ($graft->{$d} == 3) { print B "$d\n" } } } sub graft_to_md5 { my ($graft,$dir, $serial) = @_; my $mdfile = ".$serial.md5"; log_("graft_to_md5: $serial -> $dir/$mdfile ($graft)\n", $config->{verbose}, $config->{LOG},2); local *A; open A, ">$dir/$mdfile"; my %ignore; my @to_check; $graft->{$mdfile}{"$dir/$mdfile"} = 0; foreach my $f (keys %$graft) { if (ref $graft->{$f}) { foreach (keys %{$graft->{$f}}) { my ($file) = /\/([^\/]+)$/; my $dest = $f =~ /\/$/ ? "/$f/$file" : "/$f"; if ($graft->{$f}{$_}) { push @to_check, [ $dest, $_ ]; } else { $ignore{$dest} = 1; print A "$f\n" } } } else { $ignore{$f} = 1; print A "$f\n" } } my $digest = compute_md5(\@to_check, \%ignore); print A "$digest - $serial\n" } sub makeDiscs { my ($class,$fixed,$lists,$cds,$size,$mkisos,$discsFile,$graft,$inode, $cdfile) = @_; my $dir; my $name = $class->{config}{name}; my $topdir = $class->{config}{topdir}; my $tmp = "$config->{tmp}/build/$name"; if (!$class->{config}{nolive}) { $dir = "$topdir/build/$name"; -d $dir or mkpath $dir; -d $tmp or mkpath $tmp; } else { $dir = "$config->{tmp}/build/$name"; -d $dir or mkpath $dir; } if ($fixed == -1) { buildISO($class->{config},$topdir,$name,$lists,$fixed,$mkisos,$size,$cds,$cdfile,0); return 1 } log_("makeDiscs: Discs @$cds topdir $dir\n", $config->{verbose}, $config->{LOG},1); foreach my $i (@$cds) { $lists->{$i} > 1 or next; my $cd = $class->{config}{disc}[$i]; $graft->{$i} ||= {}; if ($fixed > 1 && $cdfile->[$i] == 0) { log_("makeDiscs: nothing to do for disc $i\n", $config->{verbose}, $config->{LOG},2); next } if (!$fixed) { log_("makeDisc: Fixed part of disc $i\n", $config->{verbose}, $config->{LOG},3); if ($class->{config}{nolive}) { log_("makeDisc: removing $dir/$i.list\n", $config->{verbose}, $config->{LOG},3); -f "$dir/$i.list" and unlink "$dir/$i.list"; log_("makeDisc: removing $dir/$i\n", $config->{verbose}, $config->{LOG},3); rmtree "$dir/$i"; mkdir "$dir/$i" } else { -d "$tmp/$i" or mkpath "$tmp/$i"; foreach ("$topdir/build/$name/$i", "$topdir/build/$name/first/$i") { rmtree $_; mkdir $_ } } } else { log_("Finalizing disc $i\n", $config->{verbose}, $config->{LOG},2) } my $sz; if (ref $cd->{steps}) { for (my $j; $j < @{$cd->{steps}}; $j++) { my $name = $cd->{steps}[$j][0]; log_("makeDiscs: $name ($fixed)\n", $config->{verbose}, $config->{LOG},2); if (defined $Mkcd::Functions::{$name}) { $sz += &{$Mkcd::Functions::{$name}}($class->{disc}{functions}, $cd->{steps}[$j],$dir,$fixed, $class->{config}{nolive},$i,$cd,$cdfile,$lists,$mkisos,$graft, $inode->{$i},$discsFile) } else { log_("ERROR: unrecognized function name $name\n",0, $config->{LOG}) } log_("SIZE ($name) $sz\n", $config->{verbose}, $config->{LOG},4); } } else { die "FATAL make_discs: impossible to find definition of disc $i, problem in config file ?" } if ($class->{config}{nolive}) { log_("SIZE $size->{disc}[$i] + $sz\n", $config->{verbose}, $config->{LOG},4); $size->{disc}[$i] += $sz } else { $size->{disc}[$i] = du("$dir/$i") + $sz } log_("disc $i ($dir/$i) size: $size->{disc}[$i] ($sz)\n", $config->{verbose}, $config->{LOG},3); my $mkisoopt = $class->{config}{mkisoopt}; if ($fixed) { my $isodir = $class->{config}{isodir} ? $class->{config}{isodir} : "$topdir/iso/$name"; $graft->{$i}{".rr_moved"} = 0; my $publisher = $config->{Publisher} || $config->{disc}[$i]{Publisher}; my $commkiso = "-A \"$cd->{appname}\" -P \"$publisher\" -volset \"$cd->{serial}\" -V \"$cd->{label}\" -o $isodir/$i-$name.iso"; if ($config->{nolive}) { # include_md5 replaces md5 per files #graft_to_md5($graft->{$i},"$dir/$i",$cd->{serial}); write_graft($graft->{$i}, "$dir/$i.list", "$dir/$i-exclude.list"); $mkisos->[$i] = "$mkisoopt -graft-points -path-list $dir/$i.list " . (-f "$dir/$i-exclude.list" ? "-exclude-list $dir/$i-exclude.list" : "") . " $commkiso $mkisos->[$i]" if $fixed == 1 } else { $graft->{$i}{"/"}{"$dir/$i/"} = 1; # include_md5 replaces md5 per files #graft_to_md5($graft->{$i},"$dir/$i",$cd->{serial}); if ($mkisos->[$i]) { $mkisos->[$i] = "$mkisoopt $commkiso $mkisos->[$i] $dir/$i" if $fixed == 1 } else { $mkisos->[$i] = qq($mkisoopt $commkiso "$dir/$i") if $fixed == 1 } } } } !$fixed and return 1; buildISO($class->{config},$topdir,$name,$lists,$fixed,$mkisos,$size,$cds,$cdfile,1); 1 } sub buildISO { my ($config,$topdir,$name,$lists,$fixed,$mkisos,$size,$cds,$cdfile, $checksize) = @_; my $log = $config->{LOG}; -d "$topdir/iso/$name" or mkpath "$topdir/iso/$name"; foreach my $i (@$cds) { $lists->{$i} > 1 or next; if ($fixed > 1 && $cdfile->[$i] == 0) { log_("makeDiscs: nothing to do for disc $i\n",0, $config->{LOG}); next } my $cmd = $checksize ? "mkisofs -print-size -quiet $mkisos->[$i]" : "mkisofs $mkisos->[$i]"; if ($checksize) { $size->{disc}[$i] = 1024 * 2 * `$cmd`; log_("MKISOFS disc $i size $size->{disc}[$i]\n", $config->{verbose}, $config->{LOG},1); } elsif (!$config->{noiso}) { $cmd .= " > /dev/null" if !$config->{verbose}; my $err = system $cmd; if ($err) { log_("ERROR: disc $i $cmd failed ($!)\n", 1, $config->{LOG}); print $log "WARNING: a problem may have appear, if ISOs files are not OK and you want to retry to build the ISOs, type the following command: $cmd\n " } if (!$checksize) { include_md5("$topdir/iso/$name/$i-$name.iso",1); $size->{disc}[$i] = du("$topdir/iso/$name/$i-$name.iso") } } } } sub checkSize { my ($class, $n, $size, $cdsize, $cds, $rejected) = @_; my $ok = 1; foreach my $i (@$cds) { if ($size->{save}{disc}[$i] != $size->{disc}[$i]) { $size->{save}{disc}[$i] = $size->{disc}[$i]; $ok = 0 } } if ($ok) { log_("checkSize: disc sizes has not changed, exiting\n",1, $config->{LOG}); return 1 } my $ok = 1; foreach my $i (@$cds) { $size->{disc}[$i] or next; my $origcdsize = $class->{config}{disc}[$i]{size}; log_("checkSize: disc $i size $size->{disc}[$i] ($origcdsize)\n",1, $config->{LOG}); my $d = $size->{disc}[$i] - $origcdsize; if ($size->{disc}[$i] > $origcdsize) { if ($d > $origcdsize/10) { log_("ERROR: an error must have happen, disc $i is far too big ($size->{disc}[$i] > $origcdsize), ignoring\n",1, $config->{LOG}); next } if ($d > 0 && $d > ($origcdsize*$n)/1000) { $ok = 0; $cdsize->[$i] -= $d; log_("ERROR: disc $i is too big ($size->{disc}[$i] > $origcdsize ($d)\n",1, $config->{LOG}) } else { $cdsize->[$i] = $size->{disc}[$i]+1; } } else { if ($d < 0 && $rejected) { $d = -$d; # FIXME heuristic: do not change CD size if diff is greater than 10% of the original CD size if ($d > $origcdsize/10) { log_("ERROR: an error must have happen, disc $i is far too small ($size->{disc}[$i] << $origcdsize), ignoring\n",1, $config->{LOG}); next } if ($d > ($origcdsize*$n)/300) { $ok = 0; #$cdsize->[$i] += $d/2; log_("ERROR: disc $i is too small ($size->{disc}[$i] < $origcdsize, ($d)\n",1, $config->{LOG}) } } } log_("checkSize: new disc $i size $cdsize->[$i]\n",1, $config->{LOG}); } return $ok } 1 # Changelog # # 2002 05 22 # fix a pb in graft_to_md5 that made dest incomplete when dest is a directory # # 2002 08 25 # improve checkSize to better work with optimize_space