Hello World

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);