File expanddeps of Package build
#!/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;
my ($dist, $rpmdeps, $archs, $configdir, $useusedforbuild, $installonly, $noinstall, $isvm);
my ($obspackage, $buildflavor);
my @distmacros;
$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;
push @distmacros, $def;
next;
}
if ($ARGV[0] eq '--with') {
shift @ARGV;
my $def = shift @ARGV;
push @distmacros, "_with_$def --with-$def";
next;
}
if ($ARGV[0] eq '--without') {
shift @ARGV;
my $def = shift @ARGV;
push @distmacros, "_without_$def --without-$def";
next;
}
if ($ARGV[0] eq '--vm') {
shift @ARGV;
$isvm = 1;
next;
}
if ($ARGV[0] eq '--buildflavor') {
shift @ARGV;
$buildflavor = shift @ARGV;
next;
}
if ($ARGV[0] eq '--obspackage') {
shift @ARGV;
$obspackage = shift @ARGV;
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 $buildtype = '';
my @extradeps;
for my $arg (@ARGV) {
my $bt = Build::recipe2buildtype($arg);
if ($bt) {
die("can only work with at most one recipe file\n") if defined $recipe;
$recipe = $arg;
$buildtype = $bt;
} else {
push @extradeps, $arg;
}
}
my @archs = split(':', $archs);
my $binarytype;
$binarytype = 'arch' if $buildtype eq 'arch';
$binarytype = 'deb' if $buildtype eq 'dsc';
$binarytype ||= 'rpm';
# read dist if we can
my $cf;
$cf = Build::read_config_dist($dist, $archs[0] || 'noarch', $configdir) if defined($dist) && $dist ne '';
$binarytype = $cf->{'binarytype'} if $cf && $cf->{'binarytype'} && $cf->{'binarytype'} ne 'UNDEFINED';
my $noarch;
$noarch = 'any' if $binarytype eq 'arch';
$noarch = 'all' if $binarytype eq 'deb';
$noarch ||= 'noarch';
push @archs, $noarch unless grep {$_ eq $noarch} @archs;
my (%fn, %prov, %req, %con, %obs, %rec, %sup);
my %packs;
my %repo;
my %ids;
my %packs_arch;
my %packs_done;
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(<F>) {
chomp;
if (/^F:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgF = $2;
} elsif (/^P:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgP = $2;
} elsif (/^R:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgR = $2;
} elsif (/^C:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgC = $2;
} elsif (/^O:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgO = $2;
} elsif (/^r:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgr = $2;
} elsif (/^s:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
$pkgs = $2;
} elsif (/^I:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
if (!$packs_done{$1}) {
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;
$con{$i} = $pkgC if defined $pkgC;
$obs{$i} = $pkgO if defined $pkgO;
$rec{$i} = $pkgr if defined $pkgr;
$sup{$i} = $pkgs if defined $pkgs;
}
}
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} || []};
}
# 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 and sles15 :(
if ($dist =~ /^sles11-/) {
my %res = Build::Rpm::rpmq($rpmfn, 1049);
$dist =~ s/^sles11-/sles11sp2-/ if grep {/^liblzma/} @{$res{1049} || []};
}
if ($dist =~ /^sles15-/) {
my %res = Build::Rpm::rpmq($rpmfn, 1049);
$dist =~ s/^sles15-/sles15sp2-/ if grep {/^libgcrypt/} @{$res{1049} || []};
}
}
print STDERR "Warning: distribution not specified, assuming '$dist' (see $configdir).\n";
}
$cf ||= Build::read_config_dist($dist, $archs[0], $configdir);
$cf->{'warnings'} = 1;
$cf->{'buildflavor'} = $buildflavor if defined $buildflavor;
$cf->{'obspackage'} = $obspackage if defined $obspackage;
$cf->{'no_vminstall_expand'} = 1 unless $isvm;
Build::add_distmacro($cf, $_) for @distmacros;
my $dofileprovides = %{$cf->{'fileprovides'}};
$dofileprovides = 1 if ($binarytype || 'rpm') ne 'rpm';
sub parsedepline {
return [] unless defined $_[0];
my @s = split(' ', $_[0]);
my @r;
while (@s) {
my $s = shift @s;
next if !$dofileprovides && $s =~ /^\//;
if ($s =~ /^rpmlib\(/) {
splice(@s, 0, 2);
next;
}
if ($s =~ /^\(/) {
unshift @s, $s;
push @r, Build::Rpm::shiftrich(\@s);
next;
}
push @r, $s;
while (@s && $s[0] =~ /^\(?[<=>|]/) {
$r[-1] .= " $s[0] $s[1]";
$r[-1] =~ s/ \((.*)\)/ $1/;
$r[-1] =~ s/(<|>){2}/$1/;
splice(@s, 0, 2);
}
}
return \@r;
}
for my $pack (keys %packs) {
my $r = {};
$r->{'provides'} = parsedepline($prov{$packs{$pack}});
$r->{'requires'} = parsedepline($req{$packs{$pack}});
$r->{'conflicts'} = parsedepline($con{$packs{$pack}});
$r->{'obsoletes'} = parsedepline($obs{$packs{$pack}});
$r->{'recommends'} = parsedepline($rec{$packs{$pack}});
$r->{'supplements'} = parsedepline($sup{$packs{$pack}});
$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(<F>) {
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 $subpacks = [];
my $extrasysdeps;
sub includecallback {
my ($recipe, $file) = @_;
$file =~ s/.*\///;
$recipe =~ s/[^\/]+$//;
$file = "$recipe$file";
my $fd;
my $str;
if (open($fd, '<', $file)) {
local $/;
$str = <$fd>;
close($fd);
}
return $str;
}
if ($recipe) {
local $Build::Rpm::includecallback = sub { includecallback($recipe, @_) };
my $d = Build::parse($cf, $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 @extradeps, '--directdepsend--', 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') {
if (!shift @sysdeps) {
print STDERR "expansion error\n";
print STDERR " $_\n" for @sysdeps;
exit(1);
}
# just use the sysdeps for now, ignore real deps
print_rpmlist(@sysdeps);
exit(0);
}
push @extradeps, '--ignoreignore--' if @sysdeps;
my @bdeps = Build::get_build($cf, $subpacks, @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);
}
# get preinstalls/vminstalls and check if the packages exist
my @xdeps = Build::get_preinstalls($cf);
push @xdeps, Build::get_vminstalls($cf) if $isvm;
my @missing = grep {!$packs{$_}} @xdeps;
if (@missing) {
@missing = sort(Build::unify(@missing));
print STDERR "expansion error\n";
print STDERR " missing pre/vminstalls: ".join(', ', @missing)."\n";
exit(1);
}
# make sure all preinstalls/vminstalls are in bdeps
@bdeps = Build::unify(@bdeps, @xdeps);
print_rpmlist(@bdeps);