package Mkcd::Package; our $VERSION = '1.0.0'; use File::NCopy qw(copy); use File::Path; use URPM; use URPM::Build; use Mkcd::Tools qw(du cleanrpmsrate printDiscsFile log_); use MDV::Packdrakeng; use strict; require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(check_rpmsrate packageOutOfRpmsrate genDeps getLeaves list_hdlist getSize rpmVersionCompare mkcd_build_hdlist %ARCH get_sorted_packages); my %ARCH; =head1 NAME Packages - mkcd module =head1 SYNOPSYS require Mkcd::Functions; =head1 DESCRIPTION C include the mkcd low level packages functions. =head1 SEE ALSO mkcd =head1 COPYRIGHT Copyright (C) 2000,2001,2002,2003,2004,2005 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 our %ARCH = ( x86_64 => 5, i586 => 3, noarch => 1, k7 => 1, ppc => 1, ia64 => 1, i686 => 4, i486 => 2, i386 => 1 ); sub genDeps { my ($top, $reps, $deps, $VERBOSE, $TMP) = @_; $top or print "ERROR genDeps: no top dir defined\n" and return 0; %$reps or return 0; -d $top or mkpath $top or die "FATAL genDeps: could not create $top\n"; # FIXME the function parse_hdlist exist and should be used if the rpms list has not changed # if ($deps || ! (-f "$top/depslist.ordered" && -f "$top/hdlist.cz")) { my @rpms; my %done; foreach my $rep (keys %$reps) { #$VERBOSE and print "genDeps: adding rep $rep\n"; foreach my $rpm (@{$reps->{$rep}}) { $done{$rpm} and next; push @rpms, "$rep/$rpm.rpm"; $done{$rpm} = 1 } } # Need to create hdlist and synsthesis on filesystem to estimate deps files # size in disc->guessHdlistSize. return mkcd_build_hdlist(1, [ 0, { rpms => \@rpms, hdlist => "$top/hdlist.cz", synthesis => "$top/synthesis.hdlist.cz", callback => sub { my ($urpm, $id) = @_; my $pkg = $urpm->{depslist}[$id]; my $fullname = $pkg->fullname; my $filename = $pkg->filename; $filename =~ s/\.rpm$//; $urpm->{sourcerpm}{$fullname} = $pkg->sourcerpm; $urpm->{rpm}{$fullname} = $pkg; $urpm->{files}{$fullname} = [ $pkg->files ]; $urpm->{rpmkey}{rpm}{$fullname} = $filename; $urpm->{rpmkey}{key}{$filename} = $fullname; $pkg->pack_header } } ], "$TMP/.mkcd_build_hdlist", "$top/depslist.ordered", "$top/provides", "$top/compss"); } sub mkcd_build_hdlist { my ($num, $hdlist, $headers_dir, $depslist, $provides, $compss) = @_; my $urpm = new URPM; -d $headers_dir or mkpath $headers_dir; my $last; print "mkcd_build_hdlist: first pass\n"; foreach (1 .. $num) { if ($hdlist->[$_]{done}) { print "mkcd_build_hdlist: reading existing hdlist $hdlist->[$_]{hdlist} (1st pass)\n"; $urpm->parse_hdlist($hdlist->[$_]{hdlist}); $hdlist->[$_]{headers} = list_hdlist([$hdlist->[$_]{hdlist}], 0, 1, $headers_dir); } else { $last = $_; $hdlist->[$_]{headers} = [ $urpm->parse_rpms_build_headers( dir => $headers_dir, rpms => $hdlist->[$_]{rpms}) ]; } } print "mkcd_build_hdlist: second pass\n"; $urpm->unresolved_provides_clean; foreach (1 .. $num) { my $e = $hdlist->[$_]; if ($e->{done} && $_ > $last) { print "mkcd_build_hdlist: reading existing hdlist $e->{hdlist} (2nd pass)\n"; $urpm->parse_hdlist($e->{hdlist}); $urpm->compute_deps; } else { print "mkcd_build_hdlist: parse header for $e->{hdlist}\n"; my ($start, $end) = $urpm->parse_headers(dir => $headers_dir, headers => $e->{headers}, callback => $hdlist->[$_]{callback}); if (!@{$e->{headers}}) { print "WARNING mkcd_build_hdlist: $e->{hdlist} and $e->{synthesis} are empty (start $start end $end)\n"; next } $urpm->compute_deps; if (length $e->{hdlist}) { print "mkcd_build_hdlist: write $e->{hdlist}\n"; $urpm->build_hdlist(start => $start, end => $end, dir => $headers_dir, hdlist => $e->{hdlist}, ratio => 9); } if (length $e->{synthesis}) { print "mkcd_build_hdlist: write $e->{synthesis}\n"; $urpm->build_synthesis(start => $start, end => $end, synthesis => $e->{synthesis}); print "done\n" } } } $urpm->build_base_files(depslist => $depslist, provides => $provides, compss => $compss); return $urpm; } sub get_sorted_packages { my ($urpm, $hdlist, $sort, $cd_rep, $dir, $nolive, $verbose, $LOG) = @_; my %done_rep; $LOG or open $LOG, "&>STDERR"; log_("get_sorted_packages\n", $verbose, $LOG, 2); my %id; for (my $i; $i < @{$urpm->{depslist}}; $i++) { $id{$urpm->{depslist}[$i]->filename} = $i } for (my $i = 1; $i < @$hdlist; $i++) { if (! ref $cd_rep->{$i}) { log_("WARNING installation: cdrep $i is emtpy, ignoring\n", $verbose, $LOG, 5); next } my ($cd, $repname) = @{$cd_rep->{$i}}; my @chunk; foreach (@{$hdlist->[$i]{rpms}}) { my ($rpm) = m,([^/]+)$,; log_("installation: sorting rpms $rpm ($id{$rpm})\n", $verbose, $LOG, 5); push @chunk, [ $id{$rpm}, $nolive ? $_ : "$dir/$repname/$rpm" ] } unshift @{$sort->{$cd}}, [ map { $_->[1] } sort { $b->[0] <=> $a->[0] } @chunk ] } } sub packageOutOfRpmsrate { my ($rpmsrate) = @_; my $rate = cleanrpmsrate($rpmsrate); print join("\n", sort(keys %$rate)), "\n"; 1 } sub check_rpmsrate { my ($rpmsrate, @rpms) = @_; my %rpm_name; my %dir; foreach (@rpms) { if (-d $_) { opendir my $dir, $_; foreach my $rpm (readdir $dir) { if ($rpm =~ /((.*)-[^-]+-[^-]+\.[^.]+)\.rpm/) { push @{$dir{$_}}, $1; push @{$rpm_name{$2}}, $rpm } } closedir $dir } } my ($rate, undef, $keyword) = @{cleanrpmsrate($rpmsrate, 0, 0, \%dir)}; foreach (keys %$rate) { if (!$rpm_name{$_} && !$keyword->{$_}) { print "$_\n" } } 1 } sub getLeaves { my ($depslist) = @_; open DEP, $depslist or die "Could not open $depslist\n"; my @name; my %pkg; my $i = 0; foreach (){ chomp; my ($name, undef, @de) = split " ", $_; ($name, my $version, my $release) = $name =~ /(.*)-([^-]*)-([^-]*)/; if ($name){ foreach my $d (@de) { if ($d !~ s/^NOTFOUND_//) { my @t = split '\|',$d ; foreach my $t (@t) { if ($t !~ s/NOTFOUND_//) { $pkg{$name[$t]}++ }} }else { $pkg{$name[$d]}++} } } $name[$i] = $name; $pkg{$name[$i]}++; $i++; } foreach (sort keys %pkg){ print $pkg{$_} - 1, " $_\n"; } 1 } sub getRpmsrate{ print "ERROR: this function is deprecated\n"; return 0; my ($rpmsrate,$reps,$tmp,$name,$VERBOSE) = @_; my $TMP = $tmp || $ENV{TMPDIR}; my $tmprpmsrate = "$TMP/$name/rpmsrate"; local *R; open R, ">$tmprpmsrate" or print "ERROR: cannot open temporary rpmsrate file $tmprpmsrate\n"; my $rate = Mkcd::Tools::cleanrpmsrate($rpmsrate,*R,@$reps); close R; unlink "$rpmsrate" and copy "$tmprpmsrate", "$rpmsrate"; local *R; open R, "$rpmsrate" or print "ERROR: cannot open rpmsrate file $rpmsrate\n"; [$rate->[0],$rate->[1]]; } sub list_hdlist { my ($hdlist, $verbose, $extract, $dir) = @_; print "list_hdlist: hdlists @$hdlist\n"; my $package_list; foreach (@$hdlist){ my $pack = MDV::Packdrakeng->open(archive => $_); my $count = scalar keys %{$pack->{files}}; $verbose and print qq($count files in archive, uncompression method is "$pack->{uncompress}"\n); my @to_extract; foreach my $file ($pack->sort_files_by_packing(keys %{$pack->{files}})){ if (! -f "$dir/$file") { push @to_extract, $file } $file =~ /(.*-[^-]+-[^-]+\.[^.]+):(.*)/ and $file = $2; push @$package_list, $file; } if ($extract) { $pack->extract($dir, @to_extract) } else { $pack->list() } } $package_list } sub getSize{ my ($group, $config, $VERBOSE) = @_; my $max; my $redeps; foreach my $listnumber (keys %{$group->{list}}) { print "getSize list $listnumber\n"; my $repnb; my $done = $config->{list}[$listnumber]{pseudo_done} || $config->{list}[$listnumber]{done}; print "getSize: list $listnumber done or pseudodone\n" if $done; #$group->{nodeps}{$listnumber} and next; ref $group->{rep}{$listnumber} or next; for (my $repnb; $repnb < @{$group->{rep}{$listnumber}}; $repnb++) { my $rep = $group->{rep}{$listnumber}[$repnb]; foreach my $dir (keys %{$rep->{rpm}}){ #$VERBOSE and print "getSize rep $dir\n"; my $size; foreach (@{$rep->{rpm}{$dir}}){ my $rpm = $group->{urpm}{rpmkey}{key}{$_} or print "getSize ERROR: $_ has no key, ignored\n" and next; #return 2; # Do we need to automatically replace package with the same name between different repositories (if the mirror is not # correct, for example)? The mirror should be correct. my @stat; my $b = Mkcd::Tools::du("$dir/$_.rpm", 0, \@stat); my ($dev, $inode) = @stat; $group->{listsize}{$listnumber}{rpm} += $b; my $c; if ($done || $config->{list}[$listnumber]{nosize}) { $c = $b; $b = 0 } else { $b or print "WARNING getSize: $rpm has a zero size\n"; } ref $group->{size}{$rpm}{$listnumber} and print "ERROR getSize: duplicate $rpm in list $listnumber, ignoring\n" and next; my $t = [$b, $dir, $repnb, $dev, $inode]; push @$t, $c if $c; $group->{size}{$rpm}{$listnumber} = $t; push @{$group->{listrpm}{$listnumber}}, $rpm; $b > $max and $max = $b; } } foreach my $dir (keys %{$rep->{srpm}}){ #$VERBOSE and print "getSize DIRECTORY $dir\n"; foreach (@{$rep->{srpm}{$dir}}){ my ($srpm,$srpmname,$key); if (($srpm,$srpmname) = /((.*)-[^-]*-[^-]*\.src)$/){ $key = $srpm; } else { ($key) = /(.*)$/; # FIXME not tested my $urpm = new URPM; my $id = $urpm->parse_rpm("$dir/$_.rpm") or print "ERROR getSize: parse_rpm $dir/$_.rpm failed\n" and next; my $pkg = $urpm->{depslist}[$id]; my $srpm = $pkg->sourcerpm or next; (undef, $srpmname) = $srpm =~ s/((.*)-[^-]+-[^-]+\.src)\.rpm/$1/ } $group->{urpm}{rpmkey}{key}{$key} = $srpm; $group->{urpm}{rpmkey}{rpm}{$srpm} = $key; my ($b, $dev, $inode); if (!$done) { my @stat; $b = Mkcd::Tools::du("$dir/$_.rpm", 0, \@stat) if !$config->{list}[$listnumber]{nosize}; my ($dev, $inode) = @stat; $b or print "WARNING getSize: $srpm has a zero size\n"; } ref $group->{size}{$srpm}{$listnumber} and print "ERROR getSize: duplicate $srpm in list $listnumber, ignoring\n" and next; $group->{size}{$srpm}{$listnumber} = [$b, $dir, $repnb, $dev, $inode]; $group->{srpmname}{$srpmname} = $srpm; } } } } $group->{maxsize} = $max; 1 } sub rpmVersionCompare_old{ my ($pkg1, $pkg2) = @_; my ($n1,$v1,$a1) = $pkg1 =~ /^(.*)-([^-]+-[^-]+)\.([^.]+)(\.rpm)?$/; my ($n2,$v2,$a2) = $pkg2 =~ /^(.*)-([^-]+-[^-]+)\.([^.]+)(\.rpm)?$/; die "ERROR rpmVersionCompare: trying to compare version of two differently named packages ($pkg1,$pkg2)\n" if (!($n1 eq $n2)) ; my $ret = URPM::ranges_overlap("== $v1","> $v2"); if ($ret){ return $ret }else{ $ret = URPM::ranges_overlap("== $v1","< $v2"); if ($ret){ return -$ret } if ($ARCH{$a1} < $ARCH{$a2}){ return -1 } elsif ($ARCH{$a1} > $ARCH{$a2}){ return 1 } else { return 0 } } } sub rpmVersionCompare{ my ($pkg1, $pkg2) = @_; my ($ae, $av, $ar) = $pkg1 =~ /^(?:([^:]*):)?([^-]*)(?:-(.*))?$/; my ($be, $bv, $br) = $pkg2 =~ /^(?:([^:]*):)?([^-]*)(?:-(.*))?$/; my $rc = 0; if(defined($ae) && ! defined($be)) { return 1; } elsif(!defined($ae) && defined($be)) { return -1; } else { $rc = RPM4::rpmvercmp($ae, $be) if (defined($ae) && defined($be)); if ($rc == 0) { $rc = RPM4::rpmvercmp($av, $bv); if ($rc == 0) { if(defined($ar) && !defined($br)) { return 1; } elsif(!defined($ar) && defined($br)) { return -1; } elsif (!defined($ar) && !defined($br)) { return 0; } else { return RPM4::rpmvercmp($ar, $br); } } else { return $rc; } } else { return $rc; } } } 1 # Changelog # # 2002 06 01 # use perl-URPM # add mkcd_build_hdlist function # # 2002 06 03 # new perl-URPM API # # 2004 07 05 # getSize check for list done or pseudo_done not to use the size (for the disc build function those rpm has a zero size)