Recognize Recommends relations of RPMs
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Thu, 27 Oct 2016 05:26:00 +0000 (14:26 +0900)
committeryang.zhang <y0169.zhang@samsung.com>
Tue, 7 Mar 2017 05:22:08 +0000 (13:22 +0800)
When there are multiple candidate packages as a result of
Provides, the current obs-build gives an error: "have choice for".

However, according to http://www.rpm.org/wiki/PackagerDocs/Dependencies ,
Recommends tells the system to install the corresponding package
unless there is a conflict; thus, if there is a package recommended
(as described in issue #302), obs-build shall install the recommended
package, resolving "have choice for" error.

To enable, OBS project should declare:
"BuildFlags: UseRecommendsForChoices=1"
or
"BuildFlags: UseRecommendsForChoices"
in its project config.

Fixes #302

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Conflicts:
createdirdeps
expanddeps

Change-Id: I65dc6dd16a6874b7c1ae47932d37c23ec98b9cdf

Build.pm
createdirdeps
expanddeps

index 4277c46..e03c3ab 100644 (file)
--- a/Build.pm
+++ b/Build.pm
@@ -585,8 +585,8 @@ sub get_cbinstalls { return @{[]}; }
 
 sub readdeps {
   my ($config, $pkginfo, @depfiles) = @_;
-
   my %requires;
+  my %recommends;
   local *F;
   my %provides;
   my %pkgconflicts;
@@ -597,6 +597,7 @@ sub readdeps {
       for my $rr (keys %$depfile) {
        $provides{$rr} = $depfile->{$rr}->{'provides'};
        $requires{$rr} = $depfile->{$rr}->{'requires'};
+       $recommends{$rr} = $depfile->{$rr}->{'recommends'};
        $pkgconflicts{$rr} = $depfile->{$rr}->{'conflicts'};
        $pkgobsoletes{$rr} = $depfile->{$rr}->{'obsoletes'};
       }
@@ -640,7 +641,7 @@ sub readdeps {
       }
       my %ss;
       @ss = grep {!$ss{$_}++} @ss;
-      if ($s =~ /^(P|R|C|O):(.*)\.(.*)-\d+\/\d+\/\d+:$/) {
+      if ($s =~ /^(P|R|C|O|r):(.*)\.(.*)-\d+\/\d+\/\d+:$/) {
        my $pkgid = $2;
        my $arch = $3;
        if ($1 eq "P") {
@@ -656,6 +657,11 @@ sub readdeps {
          $pkginfo->{$pkgid}->{'requires'} = \@ss if $pkginfo;
          next;
        }
+       if ($1 eq "r") {
+         $recommends{$pkgid} = \@ss;
+         $pkginfo->{$pkgid}->{'recommends'} = \@ss if $pkginfo;
+         next;
+       }
        if ($1 eq "C") {
          $pkgconflicts{$pkgid} = \@ss;
          $pkginfo->{$pkgid}->{'conflicts'} = \@ss if $pkginfo;
@@ -687,6 +693,7 @@ sub readdeps {
   }
   $config->{'providesh'} = \%provides;
   $config->{'requiresh'} = \%requires;
+  $config->{'recommendsh'} = \%recommends;
   $config->{'pkgconflictsh'} = \%pkgconflicts;
   $config->{'pkgobsoletesh'} = \%pkgobsoletes;
   makewhatprovidesh($config);
@@ -714,6 +721,7 @@ sub writedeps {
   print $fh "F:$id$url$pkg->{'location'}\n";
   print $fh "P:$id".join(' ', @{$pkg->{'provides'} || []})."\n";
   print $fh "R:$id".join(' ', @{$pkg->{'requires'}})."\n" if $pkg->{'requires'};
+  print $fh "r:$id".join(' ', @{$pkg->{'recommends'}})."\n" if $pkg->{'recommends'};
   print $fh "C:$id".join(' ', @{$pkg->{'conflicts'}})."\n" if $pkg->{'conflicts'};
   print $fh "O:$id".join(' ', @{$pkg->{'obsoletes'}})."\n" if $pkg->{'obsoletes'};
   print $fh "I:$id".getbuildid($pkg)."\n";
@@ -749,6 +757,7 @@ sub forgetdeps {
   delete $config->{'providesh'};
   delete $config->{'whatprovidesh'};
   delete $config->{'requiresh'};
+  delete $config->{'recommendsh'};
   delete $config->{'pkgconflictsh'};
   delete $config->{'pkgobsoletesh'};
 }
@@ -866,6 +875,7 @@ sub expand {
 
   my $whatprovides = $config->{'whatprovidesh'};
   my $requires = $config->{'requiresh'};
+  my $recommends = $config->{'recommendsh'};
 
   my %xignore = map {substr($_, 1) => 1} grep {/^-/} @p;
   my @directdepsend;
@@ -975,6 +985,22 @@ sub expand {
                last;
            }
        }
+       if (@q > 1 && $config->{"buildflags:userecommendsforchoices"} && @{$recommends->{$p} || []} > 0) {
+         my @recommendedq;
+         my $i;
+
+         for my $iq (@q) {
+           for my $rpkg (@{$recommends->{$p}}) {
+             if ($rpkg =~ /$iq/) {
+               push @recommendedq, $iq;
+             }
+           }
+         }
+         if (@recommendedq > 0) {
+            print "recommended [@recommendedq] among [@q]\n" if $expand_dbg;
+           @q = @recommendedq;
+         }
+       }
        if (@q > 1) {
          if ($r ne $p) {
            push @error, "have choice for $r needed by $p: @q";
@@ -1020,6 +1046,7 @@ sub order {
   my ($config, @p) = @_;
 
   my $requires = $config->{'requiresh'};
+  my $recommends = $config->{'recommendsh'};
   my $whatprovides = $config->{'whatprovidesh'};
   my %deps;
   my %rdeps;
@@ -1117,6 +1144,7 @@ sub add_all_providers {
   my ($config, @p) = @_;
   my $whatprovides = $config->{'whatprovidesh'};
   my $requires = $config->{'requiresh'};
+  my $recommends = $config->{'recommendsh'};
   my %a;
   for my $p (@p) {
     for my $r (@{$requires->{$p} || [$p]}) {
index 280e4f5..b50739f 100755 (executable)
@@ -51,7 +51,7 @@ my %old;
 if (defined($oldfile) && open(F, '<', $oldfile)) {
   while (<F>) {
     chomp;
-    $old{$1} = $_ if /^([PRCOI]:[^ ]+): /;
+    $old{$1} = $_ if /^([PRrCOI]:[^ ]+): /;
   }
   close F;
 }
@@ -78,14 +78,14 @@ for my $dir (@ARGV) {
          next if $seen{$idx};
          $seen{$idx} = 1;
          print "F:$idx: $path\n";
-         for (qw{P R C O I}) {
+         for (qw{P R C O I}) {
            print $old{"$_:$idx"}."\n" if $old{"$_:$idx"};
          }
          next;
        }
       }
     }
-    my $q = Build::query($path, 'addselfprovides' => 1, 'conflicts' => 1, 'evra' => 1, 'buildtime' => 1, 'alldeps' => 1);
+    my $q = Build::query($path, 'addselfprovides' => 1, 'conflicts' => 1, 'evra' => 1, 'buildtime' => 1, 'weakdeps' => 1);
     next unless $q && defined($q->{'name'}) && defined($q->{'arch'}) && defined($q->{'version'});
     my $idx = "$q->{'name'}.$q->{'arch'}-$id";
     next if $seen{$idx};
index 2dcd3eb..19d9e7e 100755 (executable)
@@ -126,7 +126,7 @@ if (defined($dist) && $dist ne '') {
   $binarytype = $cf->{'binarytype'} if $cf->{'binarytype'} && $cf->{'binarytype'} ne 'UNDEFINED';
 }
 
-my (%fn, %prov, %req, %con, %obs);
+my (%fn, %prov, %req, %rec, %con, %obs);
 
 my %packs;
 my %repo;
@@ -170,7 +170,7 @@ 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);
+my ($pkgF, $pkgP, $pkgR, $pkgr, $pkgC, $pkgO);
 
 my $verscmp = \&Build::Rpm::verscmp;
 
@@ -210,6 +210,8 @@ while(<F>) {
     $pkgR = $2;
     next if $req{$1};
     $req{$1} = $2;
+  } elsif (/^r:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
+    $pkgr = $2;
   } elsif (/^C:(.*?)-\d+\/\d+\/\d+: (.*)$/) {
     $pkgC = $2;
     next if $con{$1};
@@ -241,9 +243,11 @@ while(<F>) {
       $fn{$i}   = $pkgF;
       $prov{$i} = $pkgP;
       delete $req{$i};
+      delete $rec{$i};
       delete $con{$i};
       delete $obs{$i};
       $req{$i}  = $pkgR;
+      $rec{$i}  = $pkgr;
       $con{$i}  = $pkgC if defined $pkgC;
       $obs{$i}  = $pkgO if defined $pkgO;
     } else {
@@ -253,6 +257,7 @@ while(<F>) {
     undef $pkgF;
     undef $pkgP;
     undef $pkgR;
+    undef $pkgr;
     undef $pkgC;
     undef $pkgO;
   } elsif ($_ eq 'D:') {
@@ -267,7 +272,7 @@ for my $arch (@archs) {
 
 for my $pack (keys %packs) {
   my $r = {};
-  my (@s, $s, @pr, @re, @co, @ob);
+  my (@s, $s, @pr, @re, @rc, @co, @ob);
   @s = split(' ', $prov{$packs{$pack}} || '');
   while (@s) {
     $s = shift @s;
@@ -300,6 +305,22 @@ for my $pack (keys %packs) {
       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(' ', $con{$packs{$pack}} || '');
   while (@s) {
     $s = shift @s;
@@ -326,6 +347,7 @@ for my $pack (keys %packs) {
   }
   $r->{'provides'} = \@pr;
   $r->{'requires'} = \@re;
+  $r->{'recommends'} = \@rc;
   $r->{'conflicts'} = \@co;
   $r->{'obsoletes'} = \@ob;
   $repo{$pack} = $r;