- added TransactionClass, more rbsolv porting
authorMichael Schroeder <mls@suse.de>
Wed, 16 Mar 2011 11:45:04 +0000 (12:45 +0100)
committerMichael Schroeder <mls@suse.de>
Wed, 16 Mar 2011 11:45:04 +0000 (12:45 +0100)
examples/p5solv
examples/pysolv
examples/rbsolv
examples/solv.i

index 1b5be22..a1bf8ad 100755 (executable)
@@ -527,8 +527,9 @@ sub depglob {
 }
 
 sub limitjobs {
-  my ($pool, $jobs, $flags, $evr) = @_;
+  my ($pool, $jobs, $flags, $evrstr) = @_;
   my @jobs;
+  my $evr = $pool->str2id($evrstr);
   for my $j (@$jobs) {
     my $how = $j->{'how'};
     my $sel = $how & $solv::Job::SOLVER_SELECTMASK;
@@ -536,21 +537,20 @@ sub limitjobs {
     if ($flags == $solv::REL_ARCH) {
       $how |= $solv::Job::SOLVER_SETARCH;
     } elsif ($flags == $solv::REL_EQ && $sel == $solv::Job::SOLVER_SOLVABLE_NAME) {
-      $how |= $pool->id2str($evr) =~ /-/ ? $solv::Job::SOLVER_SETEVR : $solv::Job::SOLVER_SETEV;
+      $how |= $evrstr =~ /-/ ? $solv::Job::SOLVER_SETEVR : $solv::Job::SOLVER_SETEV;
     }
     push @jobs, $pool->Job($how, $what);
   }
   return @jobs;
 }
 
-sub limitjobs_arch {
+sub limitjobs_evrarch {
   my ($pool, $jobs, $flags, $evrstr) = @_;
   if ($evrstr =~ /^(.+)\.(.+?)$/ && validarch($pool, $2)) {
-    my $evr = $pool->str2id($1);
-    my @jobs = limitjobs($pool, $jobs, $solv::REL_ARCH, $pool->str2id($2));
-    return limitjobs($pool, \@jobs, $flags, $evr);
+    $evrstr = $1;
+    $jobs = [ limitjobs($pool, $jobs, $solv::REL_ARCH, $2) ];
   }
-  return limitjobs($pool, $jobs, $flags, $pool->str2id($evrstr));
+  return limitjobs($pool, $jobs, $flags, $evrstr);
 }
 
 sub mkjobs_rel {
@@ -560,13 +560,13 @@ sub mkjobs_rel {
   $flags |= $solv::REL_EQ if $rel =~ /=/;
   $flags |= $solv::REL_GT if $rel =~ />/;
   my @jobs = depglob($pool, $name, 1, 1);
-  return limitjobs($pool, \@jobs, $flags, $pool->str2id($evr)) if @jobs;
+  return limitjobs($pool, \@jobs, $flags, $evr) if @jobs;
   if (($name =~ /^(.+)\.(.+?)$/s) && validarch($pool, $2)) {
     my $arch = $2;
     @jobs = depglob($pool, $1, 1, 1);
     if (@jobs) {
-      @jobs = limitjobs($pool, \@jobs, $solv::REL_ARCH, $pool->str2id($arch));
-      return limitjobs($pool, \@jobs, $flags, $pool->str2id($evr));
+      @jobs = limitjobs($pool, \@jobs, $solv::REL_ARCH, $arch);
+      return limitjobs($pool, \@jobs, $flags, $evr);
     }
   }
   return ();
@@ -579,17 +579,17 @@ sub mkjobs_nevra {
   if (($arg =~ /^(.+)\.(.+?)$/s) && validarch($pool, $2)) {
     my $arch = $2;
     @jobs = depglob($pool, $1, 1, 1);
-    return limitjobs($pool, \@jobs, $solv::REL_ARCH, $pool->str2id($arch)) if @jobs;
+    return limitjobs($pool, \@jobs, $solv::REL_ARCH, $arch) if @jobs;
   }
   if ($arg =~ /^(.+)-(.+?)$/s) {
     my $evr = $2;
     @jobs = depglob($pool, $1, 1, 0);
-    return limitjobs_arch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs;
+    return limitjobs_evrarch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs;
   }
   if ($arg =~ /^(.+)-(.+?-.+?)$/s) {
     my $evr = $2;
     @jobs = depglob($pool, $1, 1, 0);
-    return limitjobs_arch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs;
+    return limitjobs_evrarch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs;
   }
   return ();
 }
@@ -707,7 +707,7 @@ for my $arg (@ARGV) {
 if ($cmd eq 'list' || $cmd eq 'info') {
   die("no package matched.\n") unless @jobs;
   for my $job (@jobs) {
-    for my $s ($pool->jobsolvables($job)) {
+    for my $s ($job->solvables()) {
       if ($cmd eq 'info') {
         printf "Name:        %s\n", $s->str();
         printf "Repo:        %s\n", $s->{'repo'}->{'name'};
@@ -736,7 +736,7 @@ if ($cmd eq 'install' || $cmd eq 'erase' || $cmd eq 'up' || $cmd eq 'dup' || $cm
   }
   for my $job (@jobs) {
     if ($cmd eq 'up') {
-      if ($job->{'how'} == $solv::Job::SOLVER_SOLVABLE_ALL || grep {$_->isinstalled()} $pool->jobsolvables($job)) {
+      if ($job->{'how'} == $solv::Job::SOLVER_SOLVABLE_ALL || grep {$_->isinstalled()} $job->solvables()) {
         $job->{'how'} |= $solv::Job::SOLVER_UPDATE;
       } else {
         $job->{'how'} |= $solv::Job::SOLVER_INSTALL;
@@ -830,28 +830,27 @@ if ($cmd eq 'install' || $cmd eq 'erase' || $cmd eq 'up' || $cmd eq 'dup' || $cm
   }
   print "\nTransaction summary:\n\n";
   for my $c ($trans->classify()) {
-    my ($ctype, $pkgs, $fromid, $toid) = @$c;
-    if ($ctype == $solv::Transaction::SOLVER_TRANSACTION_ERASE) {
-      printf "%d erased packages:\n", scalar(@$pkgs);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_INSTALL) {
-      printf "%d installed packages:\n", scalar(@$pkgs);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_REINSTALLED) {
-      printf "%d reinstalled packages:\n", scalar(@$pkgs);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
-      printf "%d downgraded packages:\n", scalar(@$pkgs);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_CHANGED) {
-      printf "%d changed packages:\n", scalar(@$pkgs);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED) {
-      printf "%d upgraded packages:\n", scalar(@$pkgs);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE) {
-      printf "%d vendor changes from '%s' to '%s':\n", scalar(@$pkgs), $pool->id2str($fromid), $pool->id2str($toid);
-    } elsif ($ctype == $solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE) {
-      printf "%d arch changes from '%s' to '%s':\n", scalar(@$pkgs), $pool->id2str($fromid), $pool->id2str($toid);
+    if ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_ERASE) {
+      print "$c->{'count'} erased packages:\n";
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_INSTALL) {
+      print "$c->{'count'} installed packages:\n";
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_REINSTALLED) {
+      print "$c->{'count'} reinstalled packages:\n";
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
+      print "$c->{'count'} downgraded packages:\n";
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_CHANGED) {
+      print "$c->{'count'} changed packages:\n";
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED) {
+      print "$c->{'count'} upgraded packages:\n";
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE) {
+      printf "$c->{'count'} vendor changes from '%s' to '%s':\n", $pool->id2str($c->{'fromid'}), $pool->id2str($c->{'toid'});
+    } elsif ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE) {
+      printf "$c->{'count'} arch changes from '%s' to '%s':\n", $pool->id2str($c->{'fromid'}), $pool->id2str($c->{'toid'});
     } else {
       next;
     }
-    for my $p (@$pkgs) {
-      if ($ctype == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED || $ctype == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
+    for my $p ($c->solvables()) {
+      if ($c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_UPGRADED || $c->{'type'} == $solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED) {
        my $other = $trans->othersolvable($p);
        printf "  - %s -> %s\n", $p->str(), $other->str();
       } else {
index d3541b2..f2f78be 100755 (executable)
@@ -570,8 +570,9 @@ def validarch(pool, arch):
        return False
     return pool.isknownarch(id)
 
-def limitjobs(pool, jobs, flags, evr):
+def limitjobs(pool, jobs, flags, evrstr):
     njobs = []
+    evr = pool.str2id(evrstr)
     for j in jobs:
        how = j.how
        sel = how & Job.SOLVER_SELECTMASK
@@ -579,20 +580,19 @@ def limitjobs(pool, jobs, flags, evr):
         if flags == solv.REL_ARCH:
            how |= Job.SOLVER_SETARCH
        elif flags == solv.REL_EQ and sel == Job.SOLVER_SOLVABLE_NAME:
-           if pool.id2str(evr).find('-') >= 0:
+           if evrstr.find('-') >= 0:
                how |= Job.SOLVER_SETEVR
            else:
                how |= Job.SOLVER_SETEV
        njobs.append(pool.Job(how, what))
     return njobs
 
-def limitjobs_arch(pool, jobs, flags, evr):
-    m = re.match(r'(.+)\.(.+?)$', evr)
+def limitjobs_evrarch(pool, jobs, flags, evrstr):
+    m = re.match(r'(.+)\.(.+?)$', evrstr)
     if m and validarch(pool, m.group(2)):
-       jobs = limitjobs(pool, jobs, solv.REL_ARCH, pool.str2id(m.group(2)))
-       return limitjobs(pool, jobs, flags, pool.str2id(m.group(1)))
-    else:
-       return limitjobs(pool, jobs, flags, pool.str2id(evr))
+       jobs = limitjobs(pool, jobs, solv.REL_ARCH, m.group(2))
+        evrstr = m.group(1)
+    return limitjobs(pool, jobs, flags, evrstr)
 
 def mkjobs_filelist(pool, cmd, arg):
     if re.search(r'[[*?]', arg):
@@ -624,13 +624,13 @@ def mkjobs_rel(pool, cmd, name, rel, evr):
     if rel.find('>') >= 0: flags |= solv.REL_GT
     jobs = depglob(pool, name, True, True)
     if jobs:
-       return limitjobs(pool, jobs, flags, pool.str2id(evr))
+       return limitjobs(pool, jobs, flags, evr)
     m = re.match(r'(.+)\.(.+?)$', name)
     if m and validarch(pool, m.group(2)):
        jobs = depglob(pool, m.group(1), True, True)
        if jobs:
-           jobs = limitjobs(pool, jobs, solv.REL_ARCH, pool.str2id(m.group(2)))
-           return limitjobs(pool, jobs, flags, pool.str2id(evr))
+           jobs = limitjobs(pool, jobs, solv.REL_ARCH, m.group(2))
+           return limitjobs(pool, jobs, flags, evr)
     return []
 
 def mkjobs_nevra(pool, cmd, arg):
@@ -641,17 +641,17 @@ def mkjobs_nevra(pool, cmd, arg):
     if m and validarch(pool, m.group(2)):
        jobs = depglob(pool, m.group(1), True, True)
        if jobs:
-           return limitjobs(pool, jobs, solv.REL_ARCH, pool.str2id(m.group(2)))
+           return limitjobs(pool, jobs, solv.REL_ARCH, m.group(2))
     m = re.match(r'(.+)-(.+?)$', arg)
     if m:
        jobs = depglob(pool, m.group(1), True, False)
        if jobs:
-           return limitjobs_arch(pool, jobs, solv.REL_EQ, m.group(2))
+           return limitjobs_evrarch(pool, jobs, solv.REL_EQ, m.group(2))
     m = re.match(r'(.+)-(.+?-.+?)$', arg)
     if m:
        jobs = depglob(pool, m.group(1), True, False)
        if jobs:
-           return limitjobs_arch(pool, jobs, solv.REL_EQ, m.group(2))
+           return limitjobs_evrarch(pool, jobs, solv.REL_EQ, m.group(2))
     return []
 
 def mkjobs(pool, cmd, arg):
@@ -834,7 +834,7 @@ if cmd == 'install' or cmd == 'erase' or cmd == 'up' or cmd == 'dup' or cmd == '
     for job in jobs:
        if cmd == 'up':
            # up magic: use install instead of update if no installed package matches
-           if job.how == Job.SOLVER_SOLVABLE_ALL or filter(lambda s: s.isinstalled(), pool.jobsolvables(job)):
+           if job.how == Job.SOLVER_SOLVABLE_ALL or filter(lambda s: s.isinstalled(), job.solvables()):
                job.how |= Job.SOLVER_UPDATE
            else:
                job.how |= Job.SOLVER_INSTALL
@@ -936,26 +936,26 @@ if cmd == 'install' or cmd == 'erase' or cmd == 'up' or cmd == 'dup' or cmd == '
     print
     print "Transaction summary:"
     print
-    for ctype, pkgs, fromid, toid in trans.classify():
-       if ctype == Transaction.SOLVER_TRANSACTION_ERASE:
-           print "%d erased packages:" % len(pkgs)
-       elif ctype == Transaction.SOLVER_TRANSACTION_INSTALL:
-           print "%d installed packages:" % len(pkgs)
-       elif ctype == Transaction.SOLVER_TRANSACTION_REINSTALLED:
-           print "%d reinstalled packages:" % len(pkgs)
-       elif ctype == Transaction.SOLVER_TRANSACTION_DOWNGRADED:
-           print "%d downgraded packages:" % len(pkgs)
-       elif ctype == Transaction.SOLVER_TRANSACTION_CHANGED:
-           print "%d changed packages:" % len(pkgs)
-       elif ctype == Transaction.SOLVER_TRANSACTION_UPGRADED:
-           print "%d upgraded packages:" % len(pkgs)
-       elif ctype == Transaction.SOLVER_TRANSACTION_VENDORCHANGE:
-           print "%d vendor changes from '%s' to '%s':" % (len(pkgs), pool.id2str(fromid), pool.id2str(toid))
-       elif ctype == Transaction.SOLVER_TRANSACTION_ARCHCHANGE:
-           print "%d arch changes from '%s' to '%s':" % (len(pkgs), pool.id2str(fromid), pool.id2str(toid))
+    for cl in trans.classify():
+       if cl.type == Transaction.SOLVER_TRANSACTION_ERASE:
+           print "%d erased packages:" % cl.count
+       elif cl.type == Transaction.SOLVER_TRANSACTION_INSTALL:
+           print "%d installed packages:" % cl.count
+       elif cl.type == Transaction.SOLVER_TRANSACTION_REINSTALLED:
+           print "%d reinstalled packages:" % cl.count
+       elif cl.type == Transaction.SOLVER_TRANSACTION_DOWNGRADED:
+           print "%d downgraded packages:" % cl.count
+       elif cl.type == Transaction.SOLVER_TRANSACTION_CHANGED:
+           print "%d changed packages:" % cl.count
+       elif cl.type == Transaction.SOLVER_TRANSACTION_UPGRADED:
+           print "%d upgraded packages:" % cl.count
+       elif cl.type == Transaction.SOLVER_TRANSACTION_VENDORCHANGE:
+           print "%d vendor changes from '%s' to '%s':" % (cl.count, pool.id2str(cl.fromid), pool.id2str(cl.toid))
+       elif cl.type == Transaction.SOLVER_TRANSACTION_ARCHCHANGE:
+           print "%d arch changes from '%s' to '%s':" % (cl.count, pool.id2str(cl.fromid), pool.id2str(cl.toid))
        else:
            continue
-       for p in pkgs:
+       for p in cl.solvables():
            if ctype == Transaction.SOLVER_TRANSACTION_UPGRADED or ctype == Transaction.SOLVER_TRANSACTION_DOWNGRADED:
                op = trans.othersolvable(p)
                print "  - %s -> %s" % (p.str(), op.str())
index 3ab58f2..6c8afbe 100755 (executable)
@@ -399,6 +399,11 @@ class Repo_system < Repo_generic
 end
 
 
+def validarch?(pool, arch)
+  return false unless arch && arch != ''
+  id = pool.str2id(arch, false)
+  return id != 0 && pool.isknownarch?(id)
+end
 
 def depglob(pool, name, globname, globdep)
   id = pool.str2id(name, false)
@@ -434,6 +439,69 @@ def depglob(pool, name, globname, globdep)
   return []
 end
 
+def limitjobs(pool, jobs, flags, evrstr)
+  njobs = []
+  evr = pool.str2id(evrstr)
+  for j in jobs
+    how = j.how
+    sel = how & Solv::Job::SOLVER_SELECTMASK
+    what = pool.rel2id(j.what, evr, flags)
+    if flags == Solv::REL_ARCH
+      how |= Solv::Job::SOLVER_SETARCH
+    elsif flags == Solv::REL_EQ && sel == Solv::Job::SOLVER_SOLVABLE_NAME
+      how |= evrstr.include?(?-) ? Solv::Job::SOLVER_SETEVR : Solv::Job::SOLVER_SETEV
+    end
+    njobs << pool.Job(how, what)
+  end
+  return njobs
+end
+
+def limitjobs_evrarch(pool, jobs, flags, evrstr)
+  if evrstr =~ /^(.+)\.(.+?)$/ && validarch?(pool, $2)
+    evrstr = $1
+    jobs = limitjobs(pool, jobs, Solv::REL_ARCH, $2)
+  end
+  return limitjobs(pool, jobs, flags, evrstr)
+end
+
+def mkjobs_rel(pool, cmd, name, rel, evr)
+  flags = 0
+  flags |= Solv::REL_LT if rel.include?(?<)
+  flags |= Solv::REL_EQ if rel.include?(?=)
+  flags |= Solv::REL_GT if rel.include?(?>)
+  jobs = depglob(pool, name, true, true)
+  return limitjobs(pool, jobs, flags, evr) unless jobs.empty?
+  if (name =~ /^(.+)\.(.+?)$/) && validarch?(pool, $2)
+    arch = $2
+    jobs = depglob(pool, name, true, true)
+    return [] if jobs.empty?
+    jobs = limitjobs(pool, jobs, Solv::REL_ARCH, arch)
+    return limitjobs(pool, jobs, flags, evr)
+  end
+  return []
+end
+
+def mkjobs_nevra(pool, cmd, arg)
+  jobs = depglob(pool, arg, true, true)
+  return jobs unless jobs.empty?
+  if ((arg =~ /^(.+)\.(.+?)$/) && validarch?(pool, $2))
+    arch = $2
+    jobs = depglob(pool, $1, true, true)
+    return limitjobs(pool, jobs, Solv::REL_ARCH, arch) unless jobs.empty?
+  end
+  if (arg =~ /^(.+)-(.+?)$/)
+    evr = $2
+    jobs = depglob(pool, $1, true, false)
+    return limitjobs_evrarch(pool, jobs, Solv::REL_EQ, evr) unless jobs.empty?
+  end
+  if (arg =~ /^(.+)-(.+?-.+?)$/)
+    evr = $2
+    jobs = depglob(pool, $1, true, false)
+    return limitjobs_evrarch(pool, jobs, Solv::REL_EQ, evr) unless jobs.empty?
+  end
+  return []
+end
+
 def mkjobs_filelist(pool, cmd, arg)
   type = Solv::Dataiterator::SEARCH_STRING
   type = Solv::Dataiterator::SEARCH_GLOB if arg =~ /[\[*?]/
@@ -463,7 +531,11 @@ def mkjobs(pool, cmd, arg)
     jobs = mkjobs_filelist(pool, cmd, arg)
     return jobs unless jobs.empty?
   end
-  return depglob(pool, arg, true, true)
+  if (arg =~ /^(.+?)\s*([<=>]+)\s*(.+?)$/)
+    return mkjobs_rel(pool, cmd, $1, $2, $3)
+  else
+    return mkjobs_nevra(pool, cmd, arg)
+  end
 end
 
 args = ARGV
@@ -532,21 +604,122 @@ for arg in args
   jobs += njobs
 end
 
-for job in jobs
-  job.how |= Solv::Job::SOLVER_ERASE
+if cmd == 'list' || cmd == 'info'
+  abort("no package matched.") if jobs.empty?
+    for job in jobs
+      for s in job.solvables()
+        if cmd == 'info'
+         puts "Name:        #{s.str}"
+          puts "Repo:        #{s.repo.name}"
+          puts "Summary:     #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
+          str = s.lookup_str(Solv::SOLVABLE_URL)
+          puts "Url:         #{str}" if str
+          str = s.lookup_str(Solv::SOLVABLE_LICENSE)
+          puts "License:     #{str}" if str
+          puts "Description:\n#{s.lookup_str(Solv::SOLVABLE_DESCRIPTION)}"
+          puts
+        else
+         puts "  - #{s.str} [#{s.repo.name}]"
+         puts "    #{s.lookup_str(Solv::SOLVABLE_SUMMARY)}"
+        end
+      end
+    end
+  exit
 end
 
-solver = pool.Solver
-problems = solver.solve(jobs)
-for problem in problems
-  puts "Problem #{problem.id}:"
-  puts problem.findproblemrule.info.problemstr
-  solutions = problem.solutions
-  for solution in solutions
-    puts "  Solution #{solution.id}:"
-    elements = solution.elements
-    for element in elements
-      puts "  - type #{element.type}"
+if cmd == 'install' || cmd == 'erase' || cmd == 'up' || cmd == 'dup' || cmd == 'verify'
+  if jobs.empty?
+    if cmd == 'up' || cmd == 'verify'
+      jobs = [ pool.Job(Solv::Job::SOLVER_SOLVABLE_ALL, 0) ]
+    elsif cmd != 'dup'
+      abort("no package matched.")
+    end
+  end
+  for job in jobs
+    if cmd == 'up'
+      if job.how == Solv::Job::SOLVER_SOLVABLE_ALL || job.solvables.any? {|s| s.isinstalled?}
+        job.how |= Solv::Job::SOLVER_UPDATE
+      else
+        job.how |= Solv::Job::SOLVER_INSTALL
+      end
+    elsif cmd == 'install'
+      job.how |= Solv::Job::SOLVER_INSTALL
+    elsif cmd == 'erase'
+      job.how |= Solv::Job::SOLVER_ERASE
+    elsif cmd == 'dup'
+      job.how |= Solv::Job::SOLVER_DISTUPGRADE
+    elsif cmd == 'verify'
+      job.how |= Solv::Job::SOLVER_VERIFY
+    end
+  end
+
+  solver = nil
+  #pool.set_debuglevel(1)
+  while true
+    solver = pool.Solver
+    solver.ignorealreadyrecommended = true
+    solver.allowuninstall = true if cmd == 'erase'
+    if cmd == 'dup' && jobs.empty?
+      solver.distupgrade = true
+      solver.updatesystem = true
+      solver.allowdowngrade = true
+      solver.allowvendorchange = true
+      solver.allowarchchange = true
+      solver.dosplitprovides = true
+    elsif cmd == 'up' && jobs.length == 1 && jobs[0].how == (Solv::Job::SOLVER_UPDATE | Solv::Job::SOLVER_SOLVABLE_ALL)
+      solver.dosplitprovides = true
+    end
+    problems = solver.solve(jobs)
+    break if problems.empty?
+    for problem in problems
+      puts "Problem #{problem.id}:"
+      puts problem.findproblemrule.info.problemstr
+      solutions = problem.solutions
+      for solution in solutions
+       puts "  Solution #{solution.id}:"
+       elements = solution.elements(true)
+       for element in elements
+         puts "  - #{element.str}"
+       end
+      end
+      exit
+    end
+  end
+  trans = solver.transaction
+  solver = nil
+  if trans.isempty?
+    puts "Nothing to do."
+    exit
+  end
+  puts "\nTransaction summary:\n"
+  for cl in trans.classify()
+    if cl.type == Solv::Transaction::SOLVER_TRANSACTION_ERASE
+      puts "#{cl.count} erased packages:"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_INSTALL
+      puts "#{cl.count} installed packages:"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_REINSTALLED
+      puts "#{cl.count} reinstalled packages:"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
+      puts "#{cl.count} downgraded packages:"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_CHANGED
+      puts "#{cl.count} changed packages:"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED
+      puts "#{cl.count} upgraded packages:"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_VENDORCHANGE
+      puts "#{cl.count} vendor changes from '#{pool.id2str(cl.fromid)}' to '#{pool.id2str(cl.toid)}':"
+    elsif cl.type == Solv::Transaction::SOLVER_TRANSACTION_ARCHCHANGE
+      puts "#{cl.count} arch changes from '#{pool.id2str(cl.fromid)}' to '#{pool.id2str(cl.toid)}':"
+    else
+      next
+    end
+    for p in cl.solvables
+      if cl.type == Solv::Transaction::SOLVER_TRANSACTION_UPGRADED || cl.type == Solv::Transaction::SOLVER_TRANSACTION_DOWNGRADED
+       puts "  - #{p.str} -> #{trans.othersolvable(p).str}"
+      else
+       puts "  - #{p.str}"
+      end
     end
+    puts
   end
+  puts "install size change: #{trans.calc_installsizechange()} K\n\n"
 end
index 9ea64f2..fd38fdb 100644 (file)
@@ -352,8 +352,12 @@ typedef VALUE AppObjectPtr;
 #define true 1
 #define false 1
 
-#define SOLVER_SOLUTION_ERASE -100
-#define SOLVER_SOLUTION_REPLACE -101
+#define SOLVER_SOLUTION_ERASE                   -100
+#define SOLVER_SOLUTION_REPLACE                 -101
+#define SOLVER_SOLUTION_REPLACE_DOWNGRADE       -102
+#define SOLVER_SOLUTION_REPLACE_ARCHCHANGE      -103
+#define SOLVER_SOLUTION_REPLACE_VENDORCHANGE    -104
+
 typedef struct chksum Chksum;
 typedef int bool;
 typedef void *AppObjectPtr;
@@ -430,6 +434,15 @@ typedef struct {
   Id dep;
 } Ruleinfo;
 
+typedef struct {
+  Transaction *transaction;
+  int mode;
+  Id type;
+  int count;
+  Id fromid;
+  Id toid;
+} TransactionClass;
+
 typedef Dataiterator Datamatch;
 
 %}
@@ -591,6 +604,13 @@ typedef struct {
   Pool * const pool;
 } Transaction;
 
+typedef struct {
+  Transaction * const transaction;
+  Id const type;
+  Id const fromid;
+  Id const toid;
+  int const count;
+} TransactionClass;
 
 
 %extend Job {
@@ -976,6 +996,9 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
   Id towhatprovides(Queue q) {
     return pool_queuetowhatprovides($self, &q);
   }
+#ifdef SWIGRUBY
+  %rename("isknownarch?") isknownarch;
+#endif
   bool isknownarch(Id id) {
     Pool *pool = $self;
     if (!id || id == ID_EMPTY)
@@ -1010,7 +1033,7 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
     repo_empty($self, reuseids);
   }
 #ifdef SWIGRUBY
-  %rename("isempty?") isempty();
+  %rename("isempty?") isempty;
 #endif
   bool isempty() {
     return !$self->nsolvables;
@@ -1122,7 +1145,7 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
       repodata_create_stubs(data);
   }
 #ifdef SWIGRUBY
-  %rename("iscontiguous?") iscontiguous();
+  %rename("iscontiguous?") iscontiguous;
 #endif
   bool iscontiguous() {
     int i;
@@ -1478,11 +1501,14 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
     return solvable_get_location($self->pool->solvables + $self->id, OUTPUT);
   }
 #ifdef SWIGRUBY
-  %rename("installable?") installable();
+  %rename("installable?") installable;
 #endif
   bool installable() {
     return pool_installable($self->pool, pool_id2solvable($self->pool, $self->id));
   }
+#ifdef SWIGRUBY
+  %rename("isinstalled?") isinstalled;
+#endif
   bool isinstalled() {
     Pool *pool = $self->pool;
     return pool->installed && pool_id2solvable(pool, $self->id)->repo == pool->installed;
@@ -1612,37 +1638,105 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
   int element_count() {
     return solver_solutionelement_count($self->solv, $self->problemid, $self->id);
   }
+
   %newobject elements;
-  %typemap(out) Queue elements Queue2Array(Solutionelement *, 1, new_Solutionelement(arg1, id));
-  Queue elements() {
+  %typemap(out) Queue elements Queue2Array(Solutionelement *, 4, new_Solutionelement(arg1->solv, arg1->problemid, arg1->id, id, idp[1], idp[2], idp[3]));
+  Queue elements(bool expandreplaces=0) {
     Queue q;
     int i, cnt;
     queue_init(&q);
-    cnt = solver_solution_count($self->solv, $self->id);
+    cnt = solver_solutionelement_count($self->solv, $self->problemid, $self->id);
     for (i = 1; i <= cnt; i++)
-      queue_push(&q, i);
+      {
+        Id p, rp, type;
+        solver_next_solutionelement($self->solv, $self->problemid, $self->id, i - 1, &p, &rp);
+        if (p > 0) {
+          type = rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE;
+        } else {
+          type = p;
+          p = rp;
+          rp = 0;
+        }
+        if (type == SOLVER_SOLUTION_REPLACE && expandreplaces) {
+          int illegal = policy_is_illegal(self->solv, self->solv->pool->solvables + p, self->solv->pool->solvables + rp, 0);
+          if (illegal) {
+            if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0) {
+              queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_DOWNGRADE);
+              queue_push2(&q, p, rp);
+            }
+            if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0) {
+              queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_ARCHCHANGE);
+              queue_push2(&q, p, rp);
+            }
+            if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
+              queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_VENDORCHANGE);
+              queue_push2(&q, p, rp);
+            }
+            continue;
+          }
+        }
+        queue_push2(&q, i, type);
+        queue_push2(&q, p, rp);
+      }
     return q;
   }
 }
 
 %extend Solutionelement {
-  Solutionelement(Solution *s, Id id) {
+  Solutionelement(Solver *solv, Id problemid, Id solutionid, Id id, Id type, Id p, Id rp) {
     Solutionelement *e;
     e = sat_calloc(1, sizeof(*e));
-    e->solv = s->solv;
-    e->problemid = s->problemid;
-    e->solutionid = s->id;
+    e->solv = solv;
+    e->problemid = problemid;
+    e->solutionid = id;
     e->id = id;
-    solver_next_solutionelement(e->solv, e->problemid, e->solutionid, e->id - 1, &e->p, &e->rp);
-    if (e->p > 0) {
-      e->type = e->rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE;
-    } else {
-      e->type = e->p;
-      e->p = e->rp;
-      e->rp = 0;
-    }
+    e->type = type;
+    e->p = p;
+    e->rp = rp;
     return e;
   }
+  const char *str() {
+    Id p = $self->type;
+    Id rp = $self->p;
+    if (p == SOLVER_SOLUTION_ERASE)
+      {
+        p = rp;
+        rp = 0;
+      }
+    else if (p == SOLVER_SOLUTION_REPLACE)
+      {
+        p = rp;
+        rp = $self->rp;
+      }
+    else if (p == SOLVER_SOLUTION_REPLACE_DOWNGRADE)
+      return pool_tmpjoin($self->solv->pool, "allow ", policy_illegal2str($self->solv, POLICY_ILLEGAL_DOWNGRADE, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp), 0);
+    else if (p == SOLVER_SOLUTION_REPLACE_ARCHCHANGE)
+      return pool_tmpjoin($self->solv->pool, "allow ", policy_illegal2str($self->solv, POLICY_ILLEGAL_ARCHCHANGE, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp), 0);
+    else if (p == SOLVER_SOLUTION_REPLACE_VENDORCHANGE)
+      return pool_tmpjoin($self->solv->pool, "allow ", policy_illegal2str($self->solv, POLICY_ILLEGAL_VENDORCHANGE, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp), 0);
+    return solver_solutionelement2str($self->solv, p, rp);
+  }
+  %newobject replaceelements;
+  %typemap(out) Queue replaceelements Queue2Array(Solutionelement *, 1, new_Solutionelement(arg1->solv, arg1->problemid, arg1->solutionid, arg1->id, id, arg1->p, arg1->rp));
+  Queue replaceelements() {
+    Queue q;
+    int illegal;
+
+    queue_init(&q);
+    if ($self->type != SOLVER_SOLUTION_REPLACE || $self->p <= 0 || $self->rp <= 0)
+      illegal = 0;
+    else
+      illegal = policy_is_illegal($self->solv, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp, 0);
+    if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0)
+      queue_push(&q, SOLVER_SOLUTION_REPLACE_DOWNGRADE);
+    if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0)
+      queue_push(&q, SOLVER_SOLUTION_REPLACE_ARCHCHANGE);
+    if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0)
+      queue_push(&q, SOLVER_SOLUTION_REPLACE_VENDORCHANGE);
+    if (!q.count)
+      queue_push(&q, $self->type);
+    return q;
+  }
   int illegalreplace() {
     if ($self->type != SOLVER_SOLUTION_REPLACE || $self->p <= 0 || $self->rp <= 0)
       return 0;
@@ -1668,7 +1762,7 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
   Job *Job() {
     if ($self->type == SOLVER_SOLUTION_INFARCH || $self->type == SOLVER_SOLUTION_DISTUPGRADE)
       return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE, $self->p);
-    if ($self->type == SOLVER_SOLUTION_REPLACE)
+    if ($self->type == SOLVER_SOLUTION_REPLACE || $self->type == SOLVER_SOLUTION_REPLACE_DOWNGRADE || $self->type == SOLVER_SOLUTION_REPLACE_ARCHCHANGE || $self->type == SOLVER_SOLUTION_REPLACE_VENDORCHANGE)
       return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE, $self->rp);
     if ($self->type == SOLVER_SOLUTION_ERASE)
       return new_Job($self->solv->pool, SOLVER_ERASE|SOLVER_SOLVABLE, $self->p);
@@ -1702,6 +1796,9 @@ SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
   static const int SOLVER_SOLUTION_DISTUPGRADE = SOLVER_SOLUTION_DISTUPGRADE;
   static const int SOLVER_SOLUTION_ERASE = SOLVER_SOLUTION_ERASE;
   static const int SOLVER_SOLUTION_REPLACE = SOLVER_SOLUTION_REPLACE;
+  static const int SOLVER_SOLUTION_REPLACE_DOWNGRADE = SOLVER_SOLUTION_REPLACE_DOWNGRADE;
+  static const int SOLVER_SOLUTION_REPLACE_ARCHCHANGE = SOLVER_SOLUTION_REPLACE_ARCHCHANGE;
+  static const int SOLVER_SOLUTION_REPLACE_VENDORCHANGE = SOLVER_SOLUTION_REPLACE_VENDORCHANGE;
 
   static const int POLICY_ILLEGAL_DOWNGRADE = POLICY_ILLEGAL_DOWNGRADE;
   static const int POLICY_ILLEGAL_ARCHCHANGE = POLICY_ILLEGAL_ARCHCHANGE;
@@ -1794,55 +1891,35 @@ rb_eval_string(
     sat_free($self);
   }
 #ifdef SWIGRUBY
-  %rename("isempty?") isempty();
+  %rename("isempty?") isempty;
 #endif
   bool isempty() {
     return $self->steps.count == 0;
   }
-  Queue classify_helper(int mode) {
+
+  %newobject othersolvable;
+  XSolvable *othersolvable(XSolvable *s) {
+    Id op = transaction_obs_pkg($self, s->id);
+    return new_XSolvable($self->pool, op);
+  }
+
+  %newobject allothersolvables;
+  %typemap(out) Queue allothersolvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
+  Queue allothersolvables(XSolvable *s) {
     Queue q;
     queue_init(&q);
-    transaction_classify($self, mode, &q);
+    transaction_all_obs_pkgs($self, s->id, &q);
     return q;
   }
-  Queue classify_pkgs_helper(int mode, Id cl, Id from, Id to) {
+
+  %typemap(out) Queue classify Queue2Array(TransactionClass *, 4, new_TransactionClass(arg1, arg2, id, idp[1], idp[2], idp[3]));
+  %newobject classify;
+  Queue classify(int mode = 0) {
     Queue q;
     queue_init(&q);
-    transaction_classify_pkgs($self, mode, cl, from, to, &q);
+    transaction_classify($self, mode, &q);
     return q;
   }
-  %newobject othersolvable;
-  XSolvable *othersolvable(XSolvable *s) {
-    Id op = transaction_obs_pkg($self, s->id);
-    return new_XSolvable($self->pool, op);
-  }
-#if defined(SWIGPYTHON)
-  %pythoncode {
-    def classify(self, mode = 0):
-      r = []
-      cr = self.classify_helper(mode)
-      for type, cnt, fromid, toid in zip(*([iter(cr)] * 4)):
-        if type != self.SOLVER_TRANSACTION_IGNORE:
-          r.append([ type, [ self.pool.solvables[j] for j in self.classify_pkgs_helper(mode, type, fromid, toid) ], fromid, toid ])
-      return r
-    }
-#endif
-#if defined(SWIGPERL)
-  %perlcode {
-    sub solv::Transaction::classify {
-      my ($self, $mode) = @_;
-      $mode ||= 0;
-      my @r = $self->classify_helper($mode);
-      my @res;
-      while (@r) {
-        my ($type, $cnt, $fromid, $toid) = splice(@r, 0, 4);
-        next if $type == $solv::Transaction::SOLVER_TRANSACTION_IGNORE;
-        push @res, [$type, [ map {$self->{'pool'}->{'solvables'}->[$_]} $self->classify_pkgs_helper($mode, $type, $fromid, $toid) ], $fromid, $toid];
-      }
-      return @res;
-    }
-  }
-#endif
 
   %typemap(out) Queue newpackages Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
   %newobject newpackages;
@@ -1886,6 +1963,27 @@ rb_eval_string(
   }
 }
 
+%extend TransactionClass {
+  TransactionClass(Transaction *trans, int mode, Id type, int count, Id fromid, Id toid) {
+    TransactionClass *cl = sat_calloc(1, sizeof(*cl));
+    cl->transaction = trans;
+    cl->mode = mode;
+    cl->type = type;
+    cl->count = count;
+    cl->fromid = fromid;
+    cl->toid = toid;
+    return cl;
+  }
+  %newobject solvables;
+  %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->transaction->pool, id));
+  Queue solvables() {
+    Queue q;
+    queue_init(&q);
+    transaction_classify_pkgs($self->transaction, $self->mode, $self->type, $self->fromid, $self->toid, &q);
+    return q;
+  }
+}
+
 %extend XRule {
   XRule(Solver *solv, Id id) {
     if (!id)