#!/usr/bin/perl -w ################################################################ # # Copyright (c) 1995-2014 SUSE Linux Products GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or 3 as # published by the Free Software Foundation. # # 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 (see the file COPYING); if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # ################################################################ BEGIN { unshift @INC, ($::ENV{'BUILD_DIR'} || '/usr/lib/build'); } use strict; use Build; use File::Basename; my ($dist, $rpmdeps, $archs, $configdir, $useusedforbuild, $installonly, $noinstall, $usehigherdeps, $isvm); $configdir = ($::ENV{'BUILD_DIR'} || '/usr/lib/build') . '/configs'; while (@ARGV) { if ($ARGV[0] eq '--dist') { shift @ARGV; $dist = shift @ARGV; next; } if ($ARGV[0] eq '--depfile') { shift @ARGV; $rpmdeps = shift @ARGV; next; } if ($ARGV[0] eq '--archpath') { shift @ARGV; $archs = shift @ARGV; next; } if ($ARGV[0] eq '--configdir') { shift @ARGV; $configdir = shift @ARGV; next; } if ($ARGV[0] eq '--useusedforbuild') { shift @ARGV; $useusedforbuild = 1; next; } if ($ARGV[0] eq '--define') { shift @ARGV; my $def = shift @ARGV; Build::define($def); next; } if ($ARGV[0] eq '--with') { shift @ARGV; my $def = shift @ARGV; Build::define("_with_$def --with-$def"); next; } if ($ARGV[0] eq '--without') { shift @ARGV; my $def = shift @ARGV; Build::define("_without_$def --without-$def"); next; } if ($ARGV[0] eq '--usehigherdeps') { shift @ARGV; $usehigherdeps = 1; next; } if ($ARGV[0] eq '--vm') { shift @ARGV; $isvm = 1; next; } last; } $archs = '' unless defined $archs; die("you must specfiy a depfile!\n") unless defined $rpmdeps; # split args in recipe and pkgnames my $recipe; my @extradeps; for my $arg (@ARGV) { my $buildtype = Build::recipe2buildtype($arg); if ($buildtype) { die("can only work with at most one recipe file\n") if defined $recipe; $recipe = $arg; } else { push @extradeps, $arg; } } my $binarytype; my @archs = split(':', $archs); if ($recipe && $recipe =~ /(^|\/)PKGBUILD$/) { push @archs, 'any' unless grep {$_ eq 'any'} @archs; $binarytype = 'arch'; } elsif ($recipe && $recipe =~ /\.dsc$/) { push @archs, 'all' unless grep {$_ eq 'noarch'} @archs; $binarytype = 'deb'; } else { push @archs, 'noarch' unless grep {$_ eq 'noarch'} @archs; $binarytype = 'rpm'; } # read dist if we can my $cf; if (defined($dist) && $dist ne '') { $cf = Build::read_config_dist($dist, $archs[0] || 'noarch', $configdir); $binarytype = $cf->{'binarytype'} if $cf->{'binarytype'} && $cf->{'binarytype'} ne 'UNDEFINED'; } my (%fn, %prov, %req, %con, %obs, %rec, %sup); my %packs; my %repo; my %ids; my %packs_arch; my %packs_done; # XXX: move to separate tool if (!defined($dist) || $dist eq '') { my $rpmarch = (grep {$fn{"rpm.$_"}} @archs)[0]; if (!$rpmarch) { $dist = 'default'; } else { my $rpmfn = $fn{"rpm.$rpmarch"}; if ($rpmfn =~ /^[a-z]+:\/\//) { require File::Temp; my $tmpdir = File::Temp::tempdir('CLEANUP' => 1); $rpmfn =~ s/.*\//$tmpdir\// unless system("$INC[0]/download", $tmpdir, $rpmfn); } my $rpmdist = ''; if ($rpmfn =~ /^\// && -e $rpmfn) { my %res = Build::Rpm::rpmq($rpmfn, 1010); $rpmdist = $res{1010}->[0] || ''; } $dist = Build::dist_canon($rpmdist, $archs[0]); # need some extra work for sles11 :( if ($dist =~ /^sles11-/) { my %res = Build::Rpm::rpmq($rpmfn, 1049); $dist =~ s/^sles11-/sles11sp2-/ if grep {/^liblzma/} @{$res{1049} || []}; } } print STDERR "Warning: distribution not specified, assuming '$dist' (see $configdir).\n"; } $cf ||= Build::read_config_dist($dist, $archs[0] || 'noarch', $configdir); $cf->{'warnings'} = 1; my $dofileprovides = %{$cf->{'fileprovides'}}; my %exportfilters = %{$cf->{'exportfilter'}}; open(F, '<', $rpmdeps) || die("$rpmdeps: $!\n"); # WARNING: the following code assumes that the 'I' tag comes last my ($pkgF, $pkgP, $pkgR, $pkgC, $pkgO, $pkgr, $pkgs); my $verscmp = \&Build::Rpm::verscmp; if ($binarytype && $binarytype eq 'deb') { $verscmp = \&Build::Deb::verscmp; for my $arch (@archs) { $arch = Build::Deb::basearch($arch) unless $arch =~ /^i[456]86$/; } } while() { chomp; if (/^F:(.*?)-\d+\/\d+\/\d+: (.*)$/) { my $pkgname = basename($2); $pkgF = $2; next if $fn{$1}; $fn{$1} = $2; my $pack = $1; $pack =~ /^(.*)\.([^\.]+)$/ or die; push @{$packs_arch{$2}}, $1; my $basename = $1; my $arch = $2; for(keys %exportfilters) { next if ($pkgname !~ /$_/); for (@{$exportfilters{$_}}) { my $a = $_; next if ($a eq "."); next if (! grep ($_ eq $a, @archs)); $packs{$basename} = "$basename.$arch" } } } elsif (/^P:(.*?)-\d+\/\d+\/\d+: (.*)$/) { $pkgP = $2; next if $prov{$1}; $prov{$1} = $2; } elsif (/^R:(.*?)-\d+\/\d+\/\d+: (.*)$/) { $pkgR = $2; next if $req{$1}; $req{$1} = $2; } elsif (/^C:(.*?)-\d+\/\d+\/\d+: (.*)$/) { $pkgC = $2; next if $con{$1}; $con{$1} = $2; } elsif (/^O:(.*?)-\d+\/\d+\/\d+: (.*)$/) { $pkgO = $2; next if $obs{$1}; $obs{$1} = $2; } elsif (/^r:(.*?)-\d+\/\d+\/\d+: (.*)$/) { $pkgr = $2; } elsif (/^s:(.*?)-\d+\/\d+\/\d+: (.*)$/) { $pkgs = $2; } elsif (/^I:(.*?)-\d+\/\d+\/\d+: (.*)$/) { #next if $packs_done{$1}; my $r = 0; if ($usehigherdeps) { $r = 1; } else { if ($packs_done{$1}) { undef $pkgF; undef $pkgP; undef $pkgR; undef $pkgC; undef $pkgO; undef $pkgr; undef $pkgs; $r = 0; } else { $r = 1; } } next unless ($r); my ($i, $newid) = ($1, $2); undef $i unless !$ids{$i} || $verscmp->($ids{$i}, $newid) < 0; undef $i unless defined($pkgF) && defined($pkgP); if (defined $i) { $i =~ /^(.*)\.([^\.]+)$/ or die; push @{$packs_arch{$2}}, $1; $ids{$i} = $newid; $fn{$i} = $pkgF; $prov{$i} = $pkgP; delete $req{$i}; delete $rec{$i}; delete $con{$i}; delete $obs{$i}; delete $rec{$i}; delete $sup{$i}; $req{$i} = $pkgR if defined $pkgR; $con{$i} = $pkgC if defined $pkgC; $obs{$i} = $pkgO if defined $pkgO; $rec{$i} = $pkgr if defined $pkgr; $sup{$i} = $pkgs if defined $pkgs; } else { #also need to clean the below values. undef $pkgF; undef $pkgP; undef $pkgR; undef $pkgC; undef $pkgO; undef $pkgr; undef $pkgs; next if $ids{$1}; $ids{$1} = $2; } undef $pkgF; undef $pkgP; undef $pkgR; undef $pkgC; undef $pkgO; undef $pkgr; undef $pkgs; } elsif ($_ eq 'D:') { %packs_done = %ids; } } close F; for my $arch (@archs) { $packs{$_} ||= "$_.$arch" for @{$packs_arch{$arch} || []}; } for my $pack (keys %packs) { my $r = {}; my (@s, $s, @pr, @re, @co, @ob, @rc, @su); @s = split(' ', $prov{$packs{$pack}} || ''); while (@s) { $s = shift @s; next if !$dofileprovides && $s =~ /^\//; if ($s =~ /^rpmlib\(/) { splice(@s, 0, 2); next; } push @pr, $s; while (@s && $s[0] =~ /^[\(<=>|]/) { $pr[-1] .= " $s[0] $s[1]"; $pr[-1] =~ s/ \((.*)\)/ $1/; $pr[-1] =~ s/(<|>){2}/$1/; splice(@s, 0, 2); } } @s = split(' ', $req{$packs{$pack}} || ''); while (@s) { $s = shift @s; next if !$dofileprovides && $s =~ /^\//; if ($s =~ /^rpmlib\(/) { splice(@s, 0, 2); next; } push @re, $s; while (@s && $s[0] =~ /^[\(<=>|]/) { $re[-1] .= " $s[0] $s[1]"; $re[-1] =~ s/ \((.*)\)/ $1/; $re[-1] =~ s/(<|>){2}/$1/; splice(@s, 0, 2); } } @s = split(' ', $con{$packs{$pack}} || ''); while (@s) { $s = shift @s; next if !$dofileprovides && $s =~ /^\//; push @co, $s; while (@s && $s[0] =~ /^[\(<=>|]/) { $co[-1] .= " $s[0] $s[1]"; $co[-1] =~ s/ \((.*)\)/ $1/; $co[-1] =~ s/(<|>){2}/$1/; splice(@s, 0, 2); } } @s = split(' ', $obs{$packs{$pack}} || ''); while (@s) { $s = shift @s; next if !$dofileprovides && $s =~ /^\//; push @ob, $s; while (@s && $s[0] =~ /^[\(<=>|]/) { $ob[-1] .= " $s[0] $s[1]"; $ob[-1] =~ s/ \((.*)\)/ $1/; $ob[-1] =~ s/(<|>){2}/$1/; splice(@s, 0, 2); } } @s = split(' ', $rec{$packs{$pack}} || ''); while (@s) { $s = shift @s; next if !$dofileprovides && $s =~ /^\//; if ($s =~ /^rpmlib\(/) { splice(@s, 0, 2); next; } push @rc, $s; while (@s && $s[0] =~ /^[\(<=>|]/) { $rc[-1] .= " $s[0] $s[1]"; $rc[-1] =~ s/ \((.*)\)/ $1/; $rc[-1] =~ s/(<|>){2}/$1/; splice(@s, 0, 2); } } @s = split(' ', $sup{$packs{$pack}} || ''); while (@s) { $s = shift @s; next if !$dofileprovides && $s =~ /^\//; if ($s =~ /^rpmlib\(/) { splice(@s, 0, 2); next; } push @su, $s; while (@s && $s[0] =~ /^[\(<=>|]/) { $su[-1] .= " $s[0] $s[1]"; $su[-1] =~ s/ \((.*)\)/ $1/; $su[-1] =~ s/(<|>){2}/$1/; splice(@s, 0, 2); } } $r->{'provides'} = \@pr; $r->{'requires'} = \@re; $r->{'conflicts'} = \@co; $r->{'obsoletes'} = \@ob; $r->{'recommends'} = \@rc; $r->{'supplements'} = \@su; $repo{$pack} = $r; } ####################################################################### sub print_rpmlist { for (@_) { print "$_ $fn{$packs{$_}}\n"; print "rpmid: $_:$ids{$packs{$_}}\n" if exists $ids{$packs{$_}}; } print "preinstall: @{$cf->{'preinstall'} || []}\n"; print "vminstall: @{$cf->{'vminstall'} || []}\n"; print "runscripts: @{$cf->{'runscripts'} || []}\n"; print "dist: $dist\n" if defined $dist; print "installonly: $installonly\n" if defined $installonly; print "noinstall: $noinstall\n" if defined $noinstall; } if ($useusedforbuild) { die("Need a recipe file for --usedforbuild\n") unless defined $recipe; local *F; open(F, '<', $recipe) || die("$recipe: $!\n"); my @usedforbuild; my @buildrequires; while() { chomp; if (/^#\s*usedforbuild\s*(.*)$/) { push @usedforbuild, split(' ', $1); } if (/^buildrequires:\s*(.*)$/i) { push @buildrequires, split(' ', $1); } } close F; @usedforbuild = @buildrequires unless @usedforbuild; @usedforbuild = Build::unify(@usedforbuild) if @usedforbuild; my @errors; for (@usedforbuild) { push @errors, "package $_ not found" unless $packs{$_} && $fn{$packs{$_}}; } if (@errors) { print STDERR "expansion error\n"; print STDERR " $_\n" for @errors; exit(1); } print_rpmlist(@usedforbuild); exit(0); } ####################################################################### my @packdeps; my $subpacks = []; my $buildtype = ''; my $extrasysdeps; if ($recipe) { my $d = Build::parse($cf, $recipe) || {}; $buildtype = Build::recipe2buildtype($recipe) || ''; $cf->{'type'} = $buildtype if $buildtype; if ($buildtype eq 'kiwi') { # lets see if this is a product or image build $buildtype = $d->{'imagetype'} && $d->{'imagetype'}->[0] eq 'product' ? 'kiwi-product' : 'kiwi-image'; $extrasysdeps = [ grep {/^kiwi-.*:/} @{$d->{'deps'} || []} ]; } $subpacks = $d->{'subpacks'}; unshift @extradeps, @{$d->{'deps'} || []}; if ($d->{'prereqs'}) { my %deps = map {$_ => 1} (@extradeps, @{$d->{'subpacks'} || []}); push @packdeps, grep {!$deps{$_} && !/^%/} @{$d->{'prereqs'}}; } } Build::readdeps($cf, undef, \%repo); ####################################################################### my @sysdeps = Build::get_sysbuild($cf, $buildtype, $extrasysdeps); if ($buildtype eq 'kiwi-image' || $buildtype eq 'kiwi-product') { # just use the sysdeps for now, ignore real deps print_rpmlist(@sysdeps); exit(0); } push @extradeps, '--ignoreignore--' if @sysdeps; if ($isvm) { push @packdeps, @{$cf->{'vminstall'}}; } my @bdeps = Build::get_build($cf, $subpacks, @packdeps, @extradeps); if (!shift @bdeps) { print STDERR "expansion error\n"; print STDERR " $_\n" for @bdeps; exit(1); } if (@sysdeps) { if (!shift @sysdeps) { print STDERR "expansion error\n"; print STDERR " $_\n" for @sysdeps; exit(1); } my %sysdeps = map {$_ => 1} @sysdeps; my %bdeps = map {$_ => 1} @bdeps; $installonly = join(' ', grep {!$bdeps{$_}} @sysdeps); $noinstall = join(' ', grep {!$sysdeps{$_}} @bdeps); @bdeps = Build::unify(@sysdeps, @bdeps); } # make sure all preinstalls are in bdeps; @bdeps = Build::unify(@bdeps, Build::get_preinstalls($cf)); @bdeps = Build::unify(@bdeps, Build::get_vminstalls($cf)) if $isvm; print_rpmlist(@bdeps);