Imported Upstream version 0.7.27 upstream upstream/0.7.27
authorTizenOpenSource <tizenopensrc@samsung.com>
Fri, 8 Dec 2023 03:23:05 +0000 (12:23 +0900)
committerTizenOpenSource <tizenopensrc@samsung.com>
Fri, 8 Dec 2023 03:23:05 +0000 (12:23 +0900)
76 files changed:
.github/workflows/ci.yml
CMakeLists.txt
NEWS
VERSION.cmake
bindings/python/CMakeLists.txt
bindings/python3/CMakeLists.txt
bindings/solv.i
bindings/tcl/CMakeLists.txt
doc/gen/libsolv-bindings.3
doc/libsolv-bindings.txt
examples/pysolv
examples/solv/CMakeLists.txt
examples/solv/repoinfo.c
examples/solv/repoinfo_config_yum.c
examples/solv/repoinfo_download.c
examples/solv/repoinfo_system_rpm.c
examples/solv/repoinfo_type_plaindir.c [new file with mode: 0644]
examples/solv/repoinfo_type_plaindir.h [new file with mode: 0644]
examples/solv/solv.c
ext/CMakeLists.txt
ext/repo_conda.c
ext/repo_conda.h
ext/repo_pubkey.c
ext/repo_rpmdb.c
ext/repo_testcase.c
ext/repo_updateinfoxml.c
ext/solv_jsonparser.c
ext/solv_jsonparser.h
ext/testcase.c
ext/testcase.h
package/libsolv.changes
package/libsolv.spec.in
src/CMakeLists.txt
src/conda.h
src/cplxdeps.c
src/dataiterator.h
src/decision.c [new file with mode: 0644]
src/knownid.h
src/libsolv.ver
src/linkedpkg.c
src/linkedpkg.h
src/policy.c
src/pool.c
src/pool.h
src/poolarch.c
src/pooltypes.h
src/problems.c
src/problems.h
src/repo.h
src/repo_write.c
src/repodata.h
src/rules.c
src/rules.h
src/solvable.c
src/solver.c
src/solver.h
src/solverdebug.c
src/transaction.h
test/testcases/alternative/job.t [new file with mode: 0644]
test/testcases/alternative/learnt.t [new file with mode: 0644]
test/testcases/alternative/recommends.t [new file with mode: 0644]
test/testcases/alternative/requires.t [new file with mode: 0644]
test/testcases/alternative/strongrecommends.t [new file with mode: 0644]
test/testcases/alternative/update.t [new file with mode: 0644]
test/testcases/alternative/yumobs.t [new file with mode: 0644]
test/testcases/choicerules/choice7.t [new file with mode: 0644]
test/testcases/cplxdeps/ifelse_rec.t [new file with mode: 0644]
test/testcases/favor/recommends2.t
test/testcases/favor/single.t
test/testcases/strongrecommends/disfavored.t
test/testcases/yumobs/split.t
test/testcases/yumobs/split2.t [new file with mode: 0644]
tools/conda2solv.c
tools/findfileconflicts.c
tools/installcheck.c
tools/testsolv.c

index 7506127..78e2a49 100644 (file)
@@ -22,7 +22,7 @@ jobs:
         run:  |
           mkdir build
           cd build
-          cmake -DDEBIAN=1 -DMULTI_SEMANTICS=1 ..
+          cmake -DDEBIAN=1 -DMULTI_SEMANTICS=1 -DCMAKE_VERBOSE_MAKEFILE=TRUE ..
           make
       - name: Test
         run:  |
index 3541f49..f899c49 100644 (file)
@@ -1,4 +1,4 @@
-PROJECT (libsolv)
+PROJECT (libsolv C)
 
 CMAKE_MINIMUM_REQUIRED (VERSION 2.8.5)
 
diff --git a/NEWS b/NEWS
index 509a991..0d3e1c6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,59 @@
 
 This file contains the major changes between libsolv versions:
 
+Version 0.7.27
+- selected bug fixes:
+  * add zstd support for the installcheck tool
+  * add putinowndirpool cache to make file list handling in
+    repo_write much faster
+
+Version 0.7.26
+- selected bug fixes:
+  * fix evr roundtrip in testcases
+  * do not use deprecated headerUnload with newer rpm versions
+
+Version 0.7.25
+- selected bug fixes:
+  * support complex deps in SOLVABLE_PREREQ_IGNOREINST
+  * fix minimization not prefering installed packages in some cases
+  * reduce memory usage in repo_updateinfoxml
+  * fix lock-step interfering with architecture selection
+  * fix choice rule handing for package downgrades
+  * fix complex dependencies with an "else" part sometimes leading
+    to unsolved dependencies
+
+Version 0.7.24
+- selected bug fixes:
+  * handle learnt rules in solver_alternativeinfo()
+- new features:
+  * support x86_64_v[234] architecture levels
+  * implement decision sorting for package decisionlists
+
+Version 0.7.23
+- selected bug fixes:
+  * fix "keep installed" jobs not disabling "best update" rules
+  * do not autouninstall suse ptf packages
+  * ensure duplinvolvedmap_all is reset when a solver is reused
+  * special case file dependencies in the testcase writer
+- new features:
+  * support stringification of multiple solvables
+    new function: pool_solvidset2str
+  * new weakdep introspection interface similar to ruleinfos
+    new functions: solver_weakdepinfo, solver_allweakdepinfos
+  * support decision reason queries
+    new functions: solver_get_decisionlist, solver_get_decisionlist_multiple,
+                   solver_get_learnt
+  * support merging of related decissions
+    new functions: solver_decisionlist_solvables, solver_decisionlist_merged,
+                   solver_calc_decisioninfo_bits, solver_merge_decisioninfo_bits
+  * support stringification of ruleinfo, decisioninfo and decision reasons
+    new functions: solver_ruleinfo2str, solver_decisioninfo2str,
+                   solver_decisionreason2str, solver_reason2str
+  * support better info about alternatives
+    new function: solver_alternativeinfo()
+  * new '-P' and '-W' options for testsolv to show proofs and
+    decision reasons
+    
 Version 0.7.22
 - selected bug fixes:
   * reworked choice rule generation to cover more usecases
index 4640fb0..614f9c2 100644 (file)
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "1")
 
 SET(LIBSOLV_MAJOR "0")
 SET(LIBSOLV_MINOR "7")
-SET(LIBSOLV_PATCH "22")
+SET(LIBSOLV_PATCH "27")
 
index 64ef528..186be41 100644 (file)
@@ -13,7 +13,7 @@ ELSE()
 ENDIF()
 FIND_PACKAGE (PythonInterp ${python_version} REQUIRED)
 
-EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_lib(True))" OUTPUT_VARIABLE PYTHON_INSTALL_DIR)
+EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import stdout; import sysconfig; stdout.write(sysconfig.get_path('platlib'))" OUTPUT_VARIABLE PYTHON_INSTALL_DIR)
 
 IF (NOT DEFINED PYTHON_VERSION_MAJOR)
     SET (PYTHON_VERSION_MAJOR 2)
index 28e8e00..021a47f 100644 (file)
@@ -10,8 +10,8 @@ IF (NOT DEFINED PYTHON3_EXECUTABLE)
 SET (PYTHON3_EXECUTABLE "/usr/bin/python3")
 ENDIF (NOT DEFINED PYTHON3_EXECUTABLE)
 
-EXECUTE_PROCESS(COMMAND ${PYTHON3_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_lib(True))" OUTPUT_VARIABLE PYTHON3_INSTALL_DIR)
-EXECUTE_PROCESS(COMMAND ${PYTHON3_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_inc())" OUTPUT_VARIABLE PYTHON3_INCLUDE_DIR)
+EXECUTE_PROCESS(COMMAND ${PYTHON3_EXECUTABLE} -c "from sys import stdout; import sysconfig; stdout.write(sysconfig.get_path('platlib'))" OUTPUT_VARIABLE PYTHON3_INSTALL_DIR)
+EXECUTE_PROCESS(COMMAND ${PYTHON3_EXECUTABLE} -c "from sys import stdout; import sysconfig; stdout.write(sysconfig.get_path('include'))" OUTPUT_VARIABLE PYTHON3_INCLUDE_DIR)
 
 SET (SWIG_PY3_FLAGS -DPYTHON3=1)
 SET (SWIG_PY3_FLAGS ${SWIG_PY3_FLAGS} -DSWIG_PYTHON_LEGACY_BOOL=1)
index 1d35bf6..a51ae2b 100644 (file)
@@ -138,7 +138,7 @@ typedef struct {
     int v;
     int e = asval_meth(o, &v);
     if (!SWIG_IsOK(e))
-      SWIG_exception_fail(SWIG_ArgError(e), "list in argument $argnum must contain only" typestr);
+      SWIG_exception_fail(SWIG_ArgError(e), "list in argument $argnum must contain only " typestr);
     queue_push(&$1, v);
   }
 }
@@ -773,13 +773,6 @@ typedef int bool;
 #define RARRAY_LEN(ary) (RARRAY(ary)->len)
 #endif
 
-#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
-#define SOLVER_SOLUTION_REPLACE_NAMECHANGE      -105
-
 typedef void *AppObjectPtr;
 typedef Id DepId;
 
@@ -839,7 +832,6 @@ typedef struct {
   Solver *solv;
   Id problemid;
   Id solutionid;
-  Id id;
 
   Id type;
   Id p;
@@ -849,7 +841,7 @@ typedef struct {
 typedef struct {
   Solver *solv;
   Id rid;
-  Id type;
+  int type;
   Id source;
   Id target;
   Id dep_id;
@@ -882,6 +874,26 @@ typedef struct {
 } Selection;
 
 typedef struct {
+  Solver *solv;
+  Id p;
+  int reason;
+  Id infoid;
+} Decision;
+
+typedef struct {
+  Solver *solv;
+  Queue decisionlistq;
+  Id p;
+  int reason;
+  Id infoid;
+  int bits;
+  int type;
+  Id source;
+  Id target;
+  Id dep_id;
+} Decisionset;
+
+typedef struct {
   FILE *fp;
 } SolvFp;
 
@@ -894,6 +906,42 @@ struct myappdata {
   int disowned;
 };
 
+/* special internal decisionset constructor from a prepared decisionlist */
+static Decisionset *decisionset_fromids(Solver *solv, Id *ids, int cnt)
+{
+  Decisionset *d = solv_calloc(1, sizeof(*d));
+  int i;
+  d->solv = solv;
+  queue_init(&d->decisionlistq);
+  d->p = ids[0];
+  d->reason = ids[1];
+  d->infoid = ids[2];
+  d->bits = ids[3];
+  d->type = ids[4];
+  d->source = ids[5];
+  d->target = ids[6];
+  d->dep_id = ids[7];
+  for (i = 0; i < cnt; i += 8)
+    queue_insertn(&d->decisionlistq, d->decisionlistq.count, 3, ids + i);
+  if (cnt > 8)
+    d->infoid = 0;
+  return d;
+}
+
+/* prepare a decisionlist so we can feed it to decisionset_fromids */
+static void prepare_decisionset_queue(Solver *solv, Queue *q) {
+  int i, cnt;
+  for (i = cnt = 0; i < q->count; cnt++)
+    {
+      i += 1 + 8 + 8 * solver_decisionlist_merged(solv, q, i);  /* +1 as we insert one element */
+      queue_insert(q, cnt, i - cnt);
+    }
+  if (cnt)
+    queue_unshift(q, 1);        /* start of first block */
+  for (i = 0; i < cnt; i++)
+    q->elements[i] += cnt - i;
+  q->count = cnt;   /* hack */
+}
 
 %}
 
@@ -1068,7 +1116,7 @@ typedef struct {
 
 typedef struct {
   Solver* const solv;
-  Id const type;
+  int const type;
   Id const dep_id;
 } Ruleinfo;
 
@@ -1120,6 +1168,26 @@ typedef struct {
   int const nsolvables;
 } Repo;
 
+%nodefaultctor Decision;
+typedef struct {
+  Solver *const solv;
+  Id const p;
+  int const reason;
+  Id const infoid;
+} Decision;
+
+%nodefaultctor Decisionset;
+%nodefaultdtor Decisionset;
+typedef struct {
+  Solver *const solv;
+  Id const p;
+  int const reason;
+  Id const infoid;
+  int const bits;
+  int const type;
+  Id const dep_id;
+} Decisionset;
+
 %nodefaultctor Solver;
 %nodefaultdtor Solver;
 typedef struct {
@@ -1197,7 +1265,6 @@ typedef struct {
   Solver *const solv;
   Id const problemid;
   Id const solutionid;
-  Id const id;
   Id const type;
 } Solutionelement;
 
@@ -1205,11 +1272,9 @@ typedef struct {
 typedef struct {
   Solver *const solv;
   Id const type;
-  Id const rid;
-  Id const from_id;
   Id const dep_id;
   Id const chosen_id;
-  int level;
+  int const level;
 } Alternative;
 
 %nodefaultctor Transaction;
@@ -1628,10 +1693,14 @@ returnself(matchsolvable)
   static const int POOL_FLAG_NOINSTALLEDOBSOLETES = POOL_FLAG_NOINSTALLEDOBSOLETES;
   static const int POOL_FLAG_HAVEDISTEPOCH = POOL_FLAG_HAVEDISTEPOCH;
   static const int POOL_FLAG_NOOBSOLETESMULTIVERSION = POOL_FLAG_NOOBSOLETESMULTIVERSION;
+  static const int POOL_FLAG_ADDFILEPROVIDESFILTERED = POOL_FLAG_ADDFILEPROVIDESFILTERED;
+  static const int POOL_FLAG_NOWHATPROVIDESAUX = POOL_FLAG_NOWHATPROVIDESAUX;
+  static const int POOL_FLAG_WHATPROVIDESWITHDISABLED = POOL_FLAG_WHATPROVIDESWITHDISABLED;
   static const int DISTTYPE_RPM = DISTTYPE_RPM;
   static const int DISTTYPE_DEB = DISTTYPE_DEB;
   static const int DISTTYPE_ARCH = DISTTYPE_ARCH;
   static const int DISTTYPE_HAIKU = DISTTYPE_HAIKU;
+  static const int DISTTYPE_CONDA = DISTTYPE_CONDA;
 
   Pool() {
     Pool *pool = pool_create();
@@ -1900,6 +1969,13 @@ returnself(matchsolvable)
   const char *solvid2str(Id solvid) {
     return pool_solvid2str($self, solvid);
   }
+  const char *solvidset2str(Queue q) {
+    return pool_solvidset2str($self, &q);
+  }
+  const char *solvableset2str(Queue solvables) {
+    return pool_solvidset2str($self, &solvables);
+  }
+
   void addfileprovides() {
     pool_addfileprovides($self);
   }
@@ -3337,6 +3413,31 @@ returnself(matchsolvable)
       queue_push(&q, i);
     return q;
   }
+  %typemap(out) Queue get_learnt Queue2Array(XRule *, 1, new_XRule(arg1->solv, id));
+  %newobject get_learnt;
+  Queue get_learnt() {
+    Queue q;
+    queue_init(&q);
+    solver_get_learnt($self->solv, $self->id, SOLVER_DECISIONLIST_PROBLEM, &q);
+    return q;
+  }
+  %typemap(out) Queue get_decisionlist Queue2Array(Decision *, 3, new_Decision(arg1->solv, id, idp[1], idp[2]));
+  %newobject get_decisionlist;
+  Queue get_decisionlist() {
+    Queue q;
+    queue_init(&q);
+    solver_get_decisionlist($self->solv, $self->id, SOLVER_DECISIONLIST_PROBLEM | SOLVER_DECISIONLIST_SORTED, &q);
+    return q;
+  }
+  %typemap(out) Queue get_decisionsetlist Queue2Array(Decisionset *, 1, decisionset_fromids(arg1->solv, idp + id, idp[1] - id + 1));
+  %newobject get_decisionsetlist;
+  Queue get_decisionsetlist() {
+    Queue q;
+    queue_init(&q);
+    solver_get_decisionlist($self->solv, $self->id, SOLVER_DECISIONLIST_PROBLEM | SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO | SOLVER_DECISIONLIST_MERGEDINFO, &q);
+    prepare_decisionset_queue($self->solv, &q);
+    return q;
+  }
 #if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
@@ -3357,94 +3458,32 @@ returnself(matchsolvable)
   int element_count() {
     return solver_solutionelement_count($self->solv, $self->problemid, $self->id);
   }
-
-  %typemap(out) Queue elements Queue2Array(Solutionelement *, 4, new_Solutionelement(arg1->solv, arg1->problemid, arg1->id, id, idp[1], idp[2], idp[3]));
+  %typemap(out) Queue elements Queue2Array(Solutionelement *, 3, new_Solutionelement(arg1->solv, arg1->problemid, arg1->id, id, idp[1], idp[2]));
   %newobject elements;
   Queue elements(bool expandreplaces=0) {
     Queue q;
-    int i, cnt;
     queue_init(&q);
-    cnt = solver_solutionelement_count($self->solv, $self->problemid, $self->id);
-    for (i = 1; i <= cnt; 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);
-            }
-            if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0) {
-              queue_push2(&q, i, SOLVER_SOLUTION_REPLACE_NAMECHANGE);
-              queue_push2(&q, p, rp);
-            }
-            continue;
-          }
-        }
-        queue_push2(&q, i, type);
-        queue_push2(&q, p, rp);
-      }
+    solver_all_solutionelements($self->solv, $self->problemid, $self->id, expandreplaces, &q);
     return q;
   }
 }
 
 %extend Solutionelement {
-  Solutionelement(Solver *solv, Id problemid, Id solutionid, Id id, Id type, Id p, Id rp) {
+  Solutionelement(Solver *solv, Id problemid, Id solutionid, Id type, Id p, Id rp) {
     Solutionelement *e;
     e = solv_calloc(1, sizeof(*e));
     e->solv = solv;
     e->problemid = problemid;
     e->solutionid = solutionid;
-    e->id = id;
     e->type = type;
     e->p = p;
     e->rp = rp;
     return e;
   }
   const char *str() {
-    Id p = $self->type;
-    Id rp = $self->p;
-    int illegal = 0;
-    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)
-      illegal = POLICY_ILLEGAL_DOWNGRADE;
-    else if (p == SOLVER_SOLUTION_REPLACE_ARCHCHANGE)
-      illegal = POLICY_ILLEGAL_ARCHCHANGE;
-    else if (p == SOLVER_SOLUTION_REPLACE_VENDORCHANGE)
-      illegal = POLICY_ILLEGAL_VENDORCHANGE;
-    else if (p == SOLVER_SOLUTION_REPLACE_NAMECHANGE)
-      illegal = POLICY_ILLEGAL_NAMECHANGE;
-    if (illegal)
-      return pool_tmpjoin($self->solv->pool, "allow ", policy_illegal2str($self->solv, illegal, $self->solv->pool->solvables + $self->p, $self->solv->pool->solvables + $self->rp), 0);
-    return solver_solutionelement2str($self->solv, p, rp);
-  }
-  %typemap(out) Queue replaceelements Queue2Array(Solutionelement *, 1, new_Solutionelement(arg1->solv, arg1->problemid, arg1->solutionid, arg1->id, id, arg1->p, arg1->rp));
+    return solver_solutionelementtype2str($self->solv, $self->type, $self->p, $self->rp);
+  }
+  %typemap(out) Queue replaceelements Queue2Array(Solutionelement *, 1, new_Solutionelement(arg1->solv, arg1->problemid, arg1->solutionid, id, arg1->p, arg1->rp));
   %newobject replaceelements;
   Queue replaceelements() {
     Queue q;
@@ -3517,6 +3556,9 @@ returnself(matchsolvable)
   static const int SOLVER_RULE_PKG_OBSOLETES = SOLVER_RULE_PKG_OBSOLETES;
   static const int SOLVER_RULE_PKG_IMPLICIT_OBSOLETES = SOLVER_RULE_PKG_IMPLICIT_OBSOLETES;
   static const int SOLVER_RULE_PKG_INSTALLED_OBSOLETES = SOLVER_RULE_PKG_INSTALLED_OBSOLETES;
+  static const int SOLVER_RULE_PKG_RECOMMENDS = SOLVER_RULE_PKG_RECOMMENDS;
+  static const int SOLVER_RULE_PKG_CONSTRAINS = SOLVER_RULE_PKG_CONSTRAINS;
+  static const int SOLVER_RULE_PKG_SUPPLEMENTS = SOLVER_RULE_PKG_SUPPLEMENTS;
   static const int SOLVER_RULE_UPDATE = SOLVER_RULE_UPDATE;
   static const int SOLVER_RULE_FEATURE = SOLVER_RULE_FEATURE;
   static const int SOLVER_RULE_JOB = SOLVER_RULE_JOB;
@@ -3528,6 +3570,11 @@ returnself(matchsolvable)
   static const int SOLVER_RULE_INFARCH = SOLVER_RULE_INFARCH;
   static const int SOLVER_RULE_CHOICE = SOLVER_RULE_CHOICE;
   static const int SOLVER_RULE_LEARNT = SOLVER_RULE_LEARNT;
+  static const int SOLVER_RULE_BEST  = SOLVER_RULE_BEST;
+  static const int SOLVER_RULE_YUMOBS = SOLVER_RULE_YUMOBS;
+  static const int SOLVER_RULE_RECOMMENDS = SOLVER_RULE_RECOMMENDS;
+  static const int SOLVER_RULE_BLACK = SOLVER_RULE_BLACK;
+  static const int SOLVER_RULE_STRICT_REPO_PRIORITY = SOLVER_RULE_STRICT_REPO_PRIORITY;
 
   static const int SOLVER_SOLUTION_JOB = SOLVER_SOLUTION_JOB;
   static const int SOLVER_SOLUTION_POOLJOB = SOLVER_SOLUTION_POOLJOB;
@@ -3584,6 +3631,8 @@ returnself(matchsolvable)
   static const int SOLVER_REASON_RESOLVE_ORPHAN = SOLVER_REASON_RESOLVE_ORPHAN;
   static const int SOLVER_REASON_RECOMMENDED = SOLVER_REASON_RECOMMENDED;
   static const int SOLVER_REASON_SUPPLEMENTED = SOLVER_REASON_SUPPLEMENTED;
+  static const int SOLVER_REASON_UNSOLVABLE = SOLVER_REASON_UNSOLVABLE;
+  static const int SOLVER_REASON_PREMISE = SOLVER_REASON_PREMISE;
 
   /* legacy */
   static const int SOLVER_RULE_RPM = SOLVER_RULE_RPM;
@@ -3617,13 +3666,14 @@ returnself(matchsolvable)
     return solver_create_transaction($self);
   }
 
+  /* legacy, use get_decision */
   int describe_decision(XSolvable *s, XRule **OUTPUT) {
-    int ruleid;
+    Id ruleid;
     int reason = solver_describe_decision($self, s->id, &ruleid);
     *OUTPUT = new_XRule($self, ruleid);
     return reason;
   }
-
+  /* legacy, use get_decision and the info/allinfos method */
   %newobject describe_weakdep_decision_raw;
   Queue describe_weakdep_decision_raw(XSolvable *s) {
     Queue q;
@@ -3666,8 +3716,8 @@ rb_eval_string(
     return solver_alternatives_count($self);
   }
 
-  %newobject alternative;
-  Alternative *alternative(Id aid) {
+  %newobject get_alternative;
+  Alternative *get_alternative(Id aid) {
     Alternative *a = solv_calloc(1, sizeof(*a));
     a->solv = $self;
     queue_init(&a->choices);
@@ -3684,9 +3734,9 @@ rb_eval_string(
     return a;
   }
 
-  %typemap(out) Queue all_alternatives Queue2Array(Alternative *, 1, Solver_alternative(arg1, id));
-  %newobject all_alternatives;
-  Queue all_alternatives() {
+  %typemap(out) Queue alternatives Queue2Array(Alternative *, 1, Solver_get_alternative(arg1, id));
+  %newobject alternatives;
+  Queue alternatives() {
     Queue q;
     int i, cnt;
     queue_init(&q);
@@ -3715,6 +3765,60 @@ rb_eval_string(
     return q;
   }
 
+  %typemap(out) Queue all_decisions Queue2Array(Decision *, 3, new_Decision(arg1, id, idp[1], idp[2]));
+  %newobject all_decisions;
+  Queue all_decisions(int filter=0) {
+    int i, j, cnt;
+    Queue q;
+    queue_init(&q);
+    solver_get_decisionqueue($self, &q);
+    if (filter) {
+      for (i = j = 0; i < q.count; i++)
+        if ((filter > 0 && q.elements[i] > 1) ||
+            (filter < 0 && q.elements[i] < 0))
+          q.elements[j++] = q.elements[i];
+      queue_truncate(&q, j);
+    }
+    cnt = q.count;
+    for (i = 0; i < cnt; i++) {
+      Id ruleid, p = q.elements[i];
+      int reason;
+      if (p == 0 || p == 1)
+        continue;       /* ignore system solvable */
+      reason = solver_describe_decision($self, p > 0 ? p : -p, &ruleid);
+      queue_push(&q, p);
+      queue_push2(&q, reason, ruleid);
+    }
+    queue_deleten(&q, 0, cnt);
+    return q;
+  }
+
+  %newobject get_decision;
+  Decision *get_decision(XSolvable *s) {
+    Id info;
+    int lvl = solver_get_decisionlevel($self, s->id);
+    Id p = lvl > 0 ? s->id : -s->id;
+    int reason = solver_describe_decision($self, p, &info);
+    return new_Decision($self, p, reason, info);
+  }
+
+  %typemap(out) Queue get_learnt Queue2Array(XRule *, 1, new_XRule(arg1, id));
+  %newobject get_learnt;
+  Queue get_learnt(XSolvable *s) {
+    Queue q;
+    queue_init(&q);
+    solver_get_learnt($self, s->id, SOLVER_DECISIONLIST_SOLVABLE, &q);
+    return q;
+  }
+  %typemap(out) Queue get_decisionlist Queue2Array(Decision *, 3, new_Decision(arg1, id, idp[1], idp[2]));
+  %newobject get_decisionlist;
+  Queue get_decisionlist(XSolvable *s) {
+    Queue q;
+    queue_init(&q);
+    solver_get_decisionlist($self, s->id, SOLVER_DECISIONLIST_SOLVABLE, &q);
+    return q;
+  }
+
   %typemap(out) Queue get_recommended Queue2Array(XSolvable *, 1, new_XSolvable(arg1->pool, id));
   %newobject get_recommended;
   Queue get_recommended(bool noselected=0) {
@@ -3913,9 +4017,9 @@ rb_eval_string(
   Ruleinfo *info() {
     Id type, source, target, dep;
     type = solver_ruleinfo($self->solv, $self->id, &source, &target, &dep);
-    return new_Ruleinfo($self, type, source, target, dep);
+    return new_Ruleinfo($self->solv, $self->id, type, source, target, dep);
   }
-  %typemap(out) Queue allinfos Queue2Array(Ruleinfo *, 4, new_Ruleinfo(arg1, id, idp[1], idp[2], idp[3]));
+  %typemap(out) Queue allinfos Queue2Array(Ruleinfo *, 4, new_Ruleinfo(arg1->solv, arg1->id, id, idp[1], idp[2], idp[3]));
   %newobject allinfos;
   Queue allinfos() {
     Queue q;
@@ -3924,6 +4028,32 @@ rb_eval_string(
     return q;
   }
 
+  %typemap(out) Queue get_learnt Queue2Array(XRule *, 1, new_XRule(arg1->solv, id));
+  %newobject get_learnt;
+  Queue get_learnt() {
+    Queue q;
+    queue_init(&q);
+    solver_get_learnt($self->solv, $self->id, SOLVER_DECISIONLIST_LEARNTRULE, &q);
+    return q;
+  }
+  %typemap(out) Queue get_decisionlist Queue2Array(Decision *, 3, new_Decision(arg1->solv, id, idp[1], idp[2]));
+  %newobject get_decisionlist;
+  Queue get_decisionlist() {
+    Queue q;
+    queue_init(&q);
+    solver_get_decisionlist($self->solv, $self->id, SOLVER_DECISIONLIST_LEARNTRULE | SOLVER_DECISIONLIST_SORTED, &q);
+    return q;
+  }
+  %typemap(out) Queue get_decisionsetlist Queue2Array(Decisionset *, 1, decisionset_fromids(arg1->solv, idp + id, idp[1] - id + 1));
+  %newobject get_decisionsetlist;
+  Queue get_decisionsetlist() {
+    Queue q;
+    queue_init(&q);
+    solver_get_decisionlist($self->solv, $self->id, SOLVER_DECISIONLIST_LEARNTRULE | SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO | SOLVER_DECISIONLIST_MERGEDINFO, &q);
+    prepare_decisionset_queue($self->solv, &q);
+    return q;
+  }
+
 #if defined(SWIGTCL)
   %rename("==") __eq__;
 #endif
@@ -3953,10 +4083,10 @@ rb_eval_string(
 }
 
 %extend Ruleinfo {
-  Ruleinfo(XRule *r, Id type, Id source, Id target, Id dep_id) {
+  Ruleinfo(Solver *solv, Id rid, Id type, Id source, Id target, Id dep_id) {
     Ruleinfo *ri = solv_calloc(1, sizeof(*ri));
-    ri->solv = r->solv;
-    ri->rid = r->id;
+    ri->solv = solv;
+    ri->rid = rid;
     ri->type = type;
     ri->source = source;
     ri->target = target;
@@ -3983,6 +4113,12 @@ rb_eval_string(
   const char *problemstr() {
     return solver_problemruleinfo2str($self->solv, $self->type, $self->source, $self->target, $self->dep_id);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("str") __str__;
+#endif
+  const char *__str__() {
+    return solver_ruleinfo2str($self->solv, $self->type, $self->source, $self->target, $self->dep_id);
+  }
 }
 
 %extend XRepodata {
@@ -4171,24 +4307,21 @@ rb_eval_string(
       return new_Dep(a->solv->pool, a->dep_id);
     }
   %}
-
   Queue choices_raw() {
-    Queue r;
-    queue_init_clone(&r, &$self->choices);
-    return r;
+    Queue q;
+    queue_init_clone(&q, &$self->choices);
+    return q;
   }
-
   %typemap(out) Queue choices Queue2Array(XSolvable *, 1, new_XSolvable(arg1->solv->pool, id));
   Queue choices() {
     int i;
-    Queue r;
-    queue_init_clone(&r, &$self->choices);
-    for (i = 0; i < r.count; i++)
-      if (r.elements[i] < 0)
-        r.elements[i] = -r.elements[i];
-    return r;
+    Queue q;
+    queue_init_clone(&q, &$self->choices);
+    for (i = 0; i < q.count; i++)
+      if (q.elements[i] < 0)
+        q.elements[i] = -q.elements[i];
+    return q;
   }
-
 #if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
@@ -4197,6 +4330,138 @@ rb_eval_string(
   }
 }
 
+%extend Decision {
+  Decision(Solver *solv, Id p, int reason, Id infoid) {
+    Decision *d = solv_calloc(1, sizeof(*d));
+    d->solv = solv;
+    d->p = p;
+    d->reason = reason;
+    d->infoid = infoid;
+    return d;
+  }
+  %newobject rule;
+  XRule * const rule;
+  %newobject solvable;
+  XSolvable * const solvable;
+  %{
+    SWIGINTERN XRule *Decision_rule_get(Decision *d) {
+      return d->reason == SOLVER_REASON_WEAKDEP || d->infoid <= 0 ? 0 : new_XRule(d->solv, d->infoid);
+    }
+    SWIGINTERN XSolvable *Decision_solvable_get(Decision *d) {
+      return new_XSolvable(d->solv->pool, d->p >= 0 ? d->p : -d->p);
+    }
+  %}
+  %newobject info;
+  Ruleinfo *info() {
+    Id type, source, target, dep;
+    if ($self->reason == SOLVER_REASON_WEAKDEP) {
+      type = solver_weakdepinfo($self->solv, $self->p, &source, &target, &dep);
+    } else if ($self->infoid) {
+      type = solver_ruleinfo($self->solv, $self->infoid, &source, &target, &dep);
+    } else {
+      return 0;
+    }
+    return new_Ruleinfo($self->solv, $self->infoid, type, source, target, dep);
+  }
+  %typemap(out) Queue allinfos Queue2Array(Ruleinfo *, 4, new_Ruleinfo(arg1->solv, arg1->infoid, id, idp[1], idp[2], idp[3]));
+  %newobject allinfos;
+  Queue allinfos() {
+    Queue q;
+    queue_init(&q);
+    if ($self->reason == SOLVER_REASON_WEAKDEP) {
+      solver_allweakdepinfos($self->solv, $self->p, &q);
+    } else if ($self->infoid) {
+      solver_allruleinfos($self->solv, $self->infoid, &q);
+    }
+    return q;
+  }
+  const char *reasonstr(bool noinfo=0) {
+    if (noinfo)
+      return solver_reason2str($self->solv, $self->reason);
+    return solver_decisionreason2str($self->solv, $self->p, $self->reason, $self->infoid);
+  }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("str") __str__;
+#endif
+  const char *__str__() {
+    Pool *pool = $self->solv->pool;
+    if ($self->p == 0 && $self->reason == SOLVER_REASON_UNSOLVABLE)
+      return "unsolvable";
+    if ($self->p >= 0)
+      return pool_tmpjoin(pool, "install ", pool_solvid2str(pool, $self->p), 0);
+    else
+      return pool_tmpjoin(pool, "conflict ", pool_solvid2str(pool, -$self->p), 0);
+  }
+}
+
+%extend Decisionset {
+  Decisionset(Solver *solv) {
+    Decisionset *d = solv_calloc(1, sizeof(*d));
+    d->solv = solv;
+    queue_init(&d->decisionlistq);
+    return d;
+  }
+  ~Decisionset() {
+    queue_free(&$self->decisionlistq);
+    solv_free($self);
+  }
+  %newobject info;
+  Ruleinfo *info() {
+    return new_Ruleinfo($self->solv, $self->infoid, $self->type, $self->source, $self->target, $self->dep_id);
+  }
+  %newobject dep;
+  Dep * const dep;
+  %{
+    SWIGINTERN Dep *Decisionset_dep_get(Decisionset *d) {
+      return new_Dep(d->solv->pool, d->dep_id);
+    }
+  %}
+  %typemap(out) Queue solvables Queue2Array(XSolvable *, 1, new_XSolvable(arg1->solv->pool, id));
+  %newobject solvables;
+  Queue solvables() {
+    Queue q;
+    int i;
+    queue_init(&q);
+    for (i = 0; i < $self->decisionlistq.count; i += 3)
+      if ($self->decisionlistq.elements[i] != 0)
+        queue_push(&q, $self->decisionlistq.elements[i] > 0 ? $self->decisionlistq.elements[i] : -$self->decisionlistq.elements[i]);
+    return q;
+  }
+  %typemap(out) Queue decisions Queue2Array(Decision *, 3, new_Decision(arg1->solv, id, idp[1], idp[2]));
+  %newobject decisions;
+  Queue decisions() {
+    Queue q;
+    queue_init_clone(&q, &$self->decisionlistq);
+    return q;
+  }
+  const char *reasonstr(bool noinfo=0) {
+    if (noinfo || !$self->type)
+      return solver_reason2str($self->solv, $self->reason);
+    return solver_decisioninfo2str($self->solv, $self->bits, $self->type, $self->source, $self->target, $self->dep_id);
+  }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("str") __str__;
+#endif
+  const char *__str__() {
+    Pool *pool = $self->solv->pool;
+    Queue q;
+    int i;
+    const char *s;
+    if (!$self->decisionlistq.elements)
+      return "";
+    if ($self->p == 0 && $self->reason == SOLVER_REASON_UNSOLVABLE)
+      return "unsolvable";
+    queue_init(&q);
+    for (i = 0; i < $self->decisionlistq.count; i += 3)
+      if ($self->decisionlistq.elements[i] != 0)
+        queue_push(&q, $self->decisionlistq.elements[i] > 0 ? $self->decisionlistq.elements[i] : -$self->decisionlistq.elements[i]);
+    s = pool_solvidset2str(pool, &q);
+    queue_free(&q);
+    return pool_tmpjoin(pool, $self->p >= 0 ? "install " : "conflict ", s, 0);
+  }
+}
+
+
 #if defined(SWIGTCL)
 %init %{
   Tcl_Eval(interp,
index f78de9f..932cf75 100644 (file)
@@ -18,7 +18,7 @@ ADD_CUSTOM_COMMAND (
     DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
 )
 
-ADD_DEFINITIONS(-Wno-unused)
+ADD_DEFINITIONS(-Wno-unused -Wno-missing-braces)
 INCLUDE_DIRECTORIES (${TCL_INCLUDE_PATH})
 
 ADD_LIBRARY (bindings_tcl SHARED solv_tcl.c)
index 6d298af..ca14b34 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv-Bindings
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
-.\"      Date: 03/02/2022
+.\"      Date: 01/31/2023
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-BINDINGS" "3" "03/02/2022" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-BINDINGS" "3" "01/31/2023" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -611,6 +611,16 @@ If a package is installed in multiversion mode, rpm used to ignore both the impl
 .RS 4
 Make the addfileprovides method only add files from the standard locations (i\&.e\&. the \(lqbin\(rq and \(lqetc\(rq directories)\&. This is useful if you have only few packages that use non\-standard file dependencies, but you still want the fast speed that addfileprovides() generates\&.
 .RE
+.PP
+\fBPOOL_FLAG_NOWHATPROVIDESAUX\fR
+.RS 4
+Disable the creation of the auxillary whatprovides index\&. This saves a bit of memory but also makes the whatprovides lookups a bit slower\&.
+.RE
+.PP
+\fBPOOL_FLAG_WHATPROVIDESWITHDISABLED\fR
+.RS 4
+Make the whatprovides index also contain disabled packages\&. This means that you do not need to recreate the index if a package is enabled/disabled, i\&.e\&. the pool→considered bitmap is changed\&.
+.RE
 .SS "METHODS"
 .sp
 .if n \{\
@@ -867,7 +877,7 @@ Filter list of solvables by repo priority, architecture and version\&.
 .\}
 .nf
 \fBSolvable *whatcontainsdep(Id\fR \fIkeyname\fR\fB, DepId\fR \fIdep\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
-my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatcontainsdep(\fR\fI$keyname\fR\fB,\fR \fI$dep\fR\fB)\fR
+my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatcontainsdep(\fR\fI$keyname\fR\fB,\fR \fI$dep\fR\fB)\fR;
 \fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatcontainsdep(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
 \fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatcontainsdep(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
 .fi
@@ -882,7 +892,7 @@ Return all solvables for which keyname contains the dependency\&.
 .\}
 .nf
 \fBSolvable *whatmatchesdep(Id\fR \fIkeyname\fR\fB, DepId\fR \fIdep\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
-my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatmatchesdep(\fR\fI$keyname\fR\fB,\fR \fI$sdep\fR\fB)\fR
+my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatmatchesdep(\fR\fI$keyname\fR\fB,\fR \fI$sdep\fR\fB)\fR;
 \fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatmatchesdep(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
 \fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatmatchesdep(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
 .fi
@@ -897,7 +907,7 @@ Return all solvables that have dependencies in keyname that match the dependency
 .\}
 .nf
 \fBSolvable *whatmatchessolvable(Id\fR \fIkeyname\fR\fB, Solvable\fR \fIsolvable\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
-my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatmatchessolvable(\fR\fI$keyname\fR\fB,\fR \fI$solvable\fR\fB)\fR
+my \fI@solvables\fR \fB=\fR \fI$pool\fR\fB\->whatmatchessolvable(\fR\fI$keyname\fR\fB,\fR \fI$solvable\fR\fB)\fR;
 \fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatmatchessolvable(\fR\fIkeyname\fR\fB,\fR \fIsolvable\fR\fB)\fR
 \fIsolvables\fR \fB=\fR \fIpool\fR\fB\&.whatmatchessolvable(\fR\fIkeyname\fR\fB,\fR \fIsolvable\fR\fB)\fR
 .fi
@@ -1215,6 +1225,21 @@ my \fI@ids\fR \fB=\fR \fI$pool\fR\fB\->get_disabled_list()\fR;
 .\}
 .sp
 Get/set the list of solvables that are not eligible for installation\&. This is basically the inverse of the \(lqconsidered\(rq methods above, i\&.e\&. calling \(lqset_disabled_list()\(rq with an empty list will make all solvables eligible for installation\&. Note you need to recreate the whatprovides hash after changing the list\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *solvableset2str(Solvable *\fR\fIsolvables\fR\fB)\fR
+my \fI$str\fR \fB=\fR \fI$pool\fR\fB\->solvableset2str(\fR\fI$solvables\fR\fB)\fR;
+\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvableset2str(\fR\fIsolvables\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvableset2str(\fR\fIsolvables\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing a list of solvables\&. The method tries to reduce the output by using version ranges if possible\&.
 .SS "DATA RETRIEVAL METHODS"
 .sp
 In the following functions, the \fIkeyname\fR argument describes what to retrieve\&. For the standard cases you can use the available Id constants\&. For example,
@@ -1405,6 +1430,21 @@ Return a string describing the Solvable with the specified id\&. The string cons
 .RS 4
 .\}
 .nf
+\fBconst char *solvidset2str(Id *\fR\fIsolvids\fR\fB)\fR
+my \fI$str\fR \fB=\fR \fI$pool\fR\fB\->solvidset2str(\e\fR\fI@solvids\fR\fB)\fR;
+\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvidset2str(\fR\fIsolvids\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIpool\fR\fB\&.solvidset2str(\fR\fIsolvids\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing a list of solvables\&. The method tries to reduce the output by using version ranges if possible\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBId str2id(const char *\fR\fIstr\fR\fB, bool\fR \fIcreate\fR \fB= 1)\fR
 my \fI$id\fR \fB=\fR \fIpool\fR\fB\->str2id(\fR\fI$string\fR\fB)\fR;
 \fIid\fR \fB=\fR \fIpool\fR\fB\&.str2id(\fR\fIstring\fR\fB)\fR
@@ -2601,7 +2641,7 @@ Generic lookup methods\&. Retrieve data stored for the specific keyname\&. The l
 .RS 4
 .\}
 .nf
-\fBconst char *lookup_location(unsigned int *\fR\fIOUTPUT\fR\fB)\fR;
+\fBconst char *lookup_location(unsigned int *\fR\fIOUTPUT\fR\fB)\fR
 my \fB(\fR\fI$location\fR\fB,\fR \fI$mediano\fR\fB) =\fR \fI$solvable\fR\fB\->lookup_location()\fR;
 \fIlocation\fR\fB,\fR \fImediano\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_location()\fR
 \fIlocation\fR\fB,\fR \fImediano\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_location()\fR
@@ -2616,7 +2656,7 @@ Return a tuple containing the on\-media location and an optional media number fo
 .RS 4
 .\}
 .nf
-\fBconst char *lookup_sourcepkg()\fR;
+\fBconst char *lookup_sourcepkg()\fR
 my \fI$sourcepkg\fR \fB=\fR \fI$solvable\fR\fB\->lookup_sourcepkg()\fR;
 \fIsourcepkg\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_sourcepkg()\fR
 \fIsourcepkg\fR \fB=\fR \fIsolvable\fR\fB\&.lookup_sourcepkg()\fR
@@ -2658,7 +2698,7 @@ Iterate over the matching data elements\&. See the Dataiterator class for more i
 .RS 4
 .\}
 .nf
-\fBvoid add_deparray(Id\fR \fIkeyname\fR\fB, DepId\fR \fIdep\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR;
+\fBvoid add_deparray(Id\fR \fIkeyname\fR\fB, DepId\fR \fIdep\fR\fB, Id\fR \fImarker\fR \fB= \-1)\fR
 \fI$solvable\fR\fB\->add_deparray(\fR\fI$keyname\fR\fB,\fR \fI$dep\fR\fB)\fR;
 \fIsolvable\fR\fB\&.add_deparray(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
 \fIsolvable\fR\fB\&.add_deparray(\fR\fIkeyname\fR\fB,\fR \fIdep\fR\fB)\fR
@@ -2673,7 +2713,7 @@ Add a new dependency to the attributes stored in keyname\&.
 .RS 4
 .\}
 .nf
-\fBvoid unset(Id\fR \fIkeyname\fR\fB)\fR;
+\fBvoid unset(Id\fR \fIkeyname\fR\fB)\fR
 \fI$solvable\fR\fB\->unset(\fR\fI$keyname\fR\fB)\fR;
 \fIsolvable\fR\fB\&.unset(\fR\fIkeyname\fR\fB)\fR
 \fIsolvable\fR\fB\&.unset(\fR\fIkeyname\fR\fB)\fR
@@ -2688,7 +2728,7 @@ Delete data stored for the specific keyname\&.
 .RS 4
 .\}
 .nf
-\fBbool installable()\fR;
+\fBbool installable()\fR
 \fI$solvable\fR\fB\->installable()\fR
 \fIsolvable\fR\fB\&.installable()\fR
 \fIsolvable\fR\fB\&.installable?\fR
@@ -2703,7 +2743,7 @@ Return true if the solvable is installable on the system\&. Solvables are not in
 .RS 4
 .\}
 .nf
-\fBbool isinstalled()\fR;
+\fBbool isinstalled()\fR
 \fI$solvable\fR\fB\->isinstalled()\fR
 \fIsolvable\fR\fB\&.isinstalled()\fR
 \fIsolvable\fR\fB\&.isinstalled?\fR
@@ -3132,7 +3172,7 @@ The value in binary form, useful for checksums and other data that cannot be rep
 .RS 4
 .\}
 .nf
-\fBDatapos pos()\fR;
+\fBDatapos pos()\fR
 my \fI$pos\fR \fB=\fR \fI$d\fR\fB\->pos()\fR;
 \fIpos\fR \fB=\fR \fId\fR\fB\&.pos()\fR
 \fIpos\fR \fB=\fR \fId\fR\fB\&.pos()\fR
@@ -3147,7 +3187,7 @@ The position object of the current match\&. It can be used to do sub\-searches s
 .RS 4
 .\}
 .nf
-\fBDatapos parentpos()\fR;
+\fBDatapos parentpos()\fR
 my \fI$pos\fR \fB=\fR \fI$d\fR\fB\->parentpos()\fR;
 \fIpos\fR \fB=\fR \fId\fR\fB\&.parentpos()\fR
 \fIpos\fR \fB=\fR \fId\fR\fB\&.parentpos()\fR
@@ -3764,7 +3804,7 @@ Return the set of solvables of the job as an array of Solvable objects\&.
 .RS 4
 .\}
 .nf
-\fBbool isemptyupdate()\fR;
+\fBbool isemptyupdate()\fR
 \fI$job\fR\fB\->isemptyupdate()\fR
 \fIjob\fR\fB\&.isemptyupdate()\fR
 \fIjob\fR\fB\&.isemptyupdate?\fR
@@ -4049,6 +4089,21 @@ To fulfill the dependencies two packages need to be installed, but one of the pa
 To fulfill the dependencies a package needs to be installed that is obsoleted by an installed package\&. See the POOL_FLAG_NOINSTALLEDOBSOLETES flag\&.
 .RE
 .PP
+\fBSOLVER_RULE_PKG_RECOMMENDS\fR
+.RS 4
+The package contains a recommended dependency\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_SUPPLEMENTS\fR
+.RS 4
+The package contains a dependency to specify it supplements another package\&.
+.RE
+.PP
+\fBSOLVER_RULE_PKG_CONSTRAINS\fR
+.RS 4
+The package contains a constraint against some other package (disttype conda)\&.
+.RE
+.PP
 \fBSOLVER_RULE_JOB_NOTHING_PROVIDES_DEP\fR
 .RS 4
 The user asked for installation of a package providing a specific dependency, but no available package provides it\&.
@@ -4204,6 +4259,16 @@ This is a special case of SOLVER_REASON_WEAKDEP\&.
 .RS 4
 This is a special case of SOLVER_REASON_WEAKDEP\&.
 .RE
+.PP
+\fBSOLVER_REASON_UNSOLVABLE\fR
+.RS 4
+This is a special case where a rule cannot be fulfilled\&.
+.RE
+.PP
+\fBSOLVER_REASON_PREMISE\fR
+.RS 4
+This is a special case for the premises of learnt rules\&.
+.RE
 .SS "ATTRIBUTES"
 .sp
 .if n \{\
@@ -4284,46 +4349,91 @@ Return the transaction to implement the calculated package changes\&. A transact
 .RS 4
 .\}
 .nf
-\fBint\fR \fIreason\fR \fB= describe_decision(Solvable *\fR\fIs\fR\fB, Rule *\fR\fIOUTPUT\fR\fB)\fR
-my \fB(\fR\fI$reason\fR\fB,\fR \fI$rule\fR\fB) =\fR \fI$solver\fR\fB\->describe_decision(\fR\fI$solvable\fR\fB)\fR;
-\fB(\fR\fIreason\fR\fB,\fR \fIrule\fR\fB) =\fR \fIsolver\fR\fB\&.describe_decision(\fR\fIsolvable\fR\fB)\fR
-\fB(\fR\fIreason\fR\fB,\fR \fIrule\fR\fB) =\fR \fIsolver\fR\fB\&.describe_decision(\fR\fIsolvable\fR\fB)\fR
+\fBSolvable *get_recommended(bool\fR \fInoselected\fR\fB=0)\fR
+my \fI@solvables\fR \fB=\fR \fI$solver\fR\fB\->get_recommended()\fR;
+\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_recommended()\fR
+\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_recommended()\fR
 .fi
 .if n \{\
 .RE
 .\}
 .sp
-Return the reason why a specific solvable was installed or erased\&. For most of the reasons the rule that triggered the decision is also returned\&.
+Return all solvables that are recommended by the solver run result\&. This includes solvables included in the result; set noselected if you want to filter those\&.
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-\fBSolvable *get_recommended(bool\fR \fInoselected\fR\fB=0)\fR;
-my \fI@solvables\fR \fB=\fR \fI$solver\fR\fB\->get_recommended()\fR;
-\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_recommended()\fR
-\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_recommended()\fR
+\fBSolvable *get_suggested(bool\fR \fInoselected\fR\fB=0)\fR
+my \fI@solvables\fR \fB=\fR \fI$solver\fR\fB\->get_suggested()\fR;
+\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_suggested()\fR
+\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_suggested()\fR
 .fi
 .if n \{\
 .RE
 .\}
 .sp
-Return all solvables that are recommended by the solver run result\&. This includes solvables included in the result, set noselected if you want to filter those\&.
+Return all solvables that are suggested by the solver run result\&. This includes solvables included in the result; set noselected if you want to filter those\&.
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-\fBSolvable *get_suggested(bool\fR \fInoselected\fR\fB=0)\fR;
-my \fI@solvables\fR \fB=\fR \fI$solver\fR\fB\->get_suggested()\fR;
-\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_suggested()\fR
-\fIsolvables\fR \fB=\fR \fIsolver\fR\fB\&.get_suggested()\fR
+\fIDecision\fR \fB= get_decision(Solvable *\fR\fIs\fR\fB)\fR
+my \fI$decision\fR \fB=\fR \fI$solver\fR\fB\->get_decision(\fR\fI$solvable\fR\fB)\fR;
+\fIdecision\fR \fB=\fR \fIsolver\fR\fB\&.get_decision(\fR\fIsolvable\fR\fB)\fR;
+\fIdecision\fR \fB=\fR \fIsolver\fR\fB\&.get_decision(\fR\fIsolvable\fR\fB)\fR;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a decision object that describes why a specific solvable was installed or erased\&. See the Decision class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDecision *get_decisionlist(Solvable *\fR\fIs\fR\fB)\fR
+my \fI@decisions\fR \fB=\fR \fI$solver\fR\fB\->get_decisionlist(\fR\fI$solvable\fR\fB)\fR;
+\fIdecisions\fR \fB=\fR \fIsolver\fR\fB\&.get_decisionlist(\fR\fIsolvable\fR\fB)\fR
+\fIdecisions\fR \fB=\fR \fIsolver\fR\fB\&.get_decisionlist(\fR\fIsolvable\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a list of decisions that caused the specific solvable to be installed or erased\&. This is usually more useful than the get_decision() method, as it returns every involved decision instead of just a single one\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBAlternative *alternatives()\fR
+my \fI@alternatives\fR \fB=\fR \fI$solver\fR\fB\->alternatives()\fR;
+\fIalternatives\fR \fB=\fR \fIsolver\fR\fB\&.alternatives()\fR
+\fIalternatives\fR \fB=\fR \fIsolver\fR\fB\&.alternatives()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all alternatives recorded in the solver run\&. See the Alternative class for more information\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint alternatives_count()\fR
+my \fI$cnt\fR \fB=\fR \fI$solver\fR\fB\->alternatives_count()\fR;
+\fIcnt\fR \fB=\fR \fIsolver\fR\fB\&.alternatives_count()\fR
+\fIcnt\fR \fB=\fR \fIsolver\fR\fB\&.alternatives_count()\fR
 .fi
 .if n \{\
 .RE
 .\}
 .sp
-Return all solvables that are suggested by the solver run result\&. This includes solvables included in the result, set noselected if you want to filter those\&.
+Return the number of alternatives without creating alternative objects\&.
 .SH "THE PROBLEM CLASS"
 .sp
 Problems are the way of the solver to interact with the user\&. You can simply list all problems and terminate your program, but a better way is to present solutions to the user and let him pick the ones he likes\&.
@@ -4394,6 +4504,51 @@ Return all rules responsible for the problem\&. The returned set of rules contai
 .RS 4
 .\}
 .nf
+\fBDecision *get_decisionlist()\fR
+my \fI@decisions\fR \fB=\fR \fI$problem\fR\fB\->get_decisionlist()\fR;
+\fIdecisions\fR \fB=\fR \fIproblem\fR\fB\&.get_decisionlist()\fR
+\fIdecisions\fR \fB=\fR \fIproblem\fR\fB\&.get_decisionlist()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a list of decisions prooving the problem\&. This is somewhat similar to the findallproblemrules(), but the output is in an order that makes it easier to understand why the solver could not find a solution\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDecisionset *get_decisionsetlist()\fR
+my \fI@decisionsets\fR \fB=\fR \fI$problem\fR\fB\->get_decisionsetlist()\fR;
+\fIdecisionsets\fR \fB=\fR \fIproblem\fR\fB\&.get_decisionsetlist()\fR
+\fIdecisionsets\fR \fB=\fR \fIproblem\fR\fB\&.get_decisionsetlist()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Like the get_decisionlist() method, but the decisions are merged into individual sets\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRule *get_learnt()\fR
+my \fI@learnt\fR \fB=\fR \fI$problem\fR\fB\->get_learnt()\fR;
+\fIlearnt\fR \fB=\fR \fIproblem\fR\fB\&.get_learnt()\fR
+\fIlearnt\fR \fB=\fR \fIproblem\fR\fB\&.get_lerant()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a list of learnt rules that are part of the problem proof\&. This is useful for presenting a complete proof to the user\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBSolution *solutions()\fR
 my \fI@solutions\fR \fB=\fR \fI$problem\fR\fB\->solutions()\fR;
 \fIsolutions\fR \fB=\fR \fIproblem\fR\fB\&.solutions()\fR
@@ -4519,6 +4674,51 @@ As the same dependency rule can get created because of multiple dependencies, on
 .RS 4
 .\}
 .nf
+\fBDecision *get_decisionlist()\fR
+my \fI@decisions\fR \fB=\fR \fI$rule\fR\fB\->get_decisionlist()\fR;
+\fIdecisions\fR \fB=\fR \fIrule\fR\fB\&.get_decisionlist()\fR
+\fIdecisions\fR \fB=\fR \fIrule\fR\fB\&.get_decisionlist()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a list of decisions prooving a learnt rule\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDecision *get_decisionsetlist()\fR
+my \fI@decisionsets\fR \fB=\fR \fI$rule\fR\fB\->get_decisionsetlist()\fR;
+\fIdecisionsets\fR \fB=\fR \fIrule\fR\fB\&.get_decisionsetlist()\fR
+\fIdecisionsets\fR \fB=\fR \fIrule\fR\fB\&.get_decisionsetlist()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Like the get_decisionlist() method, but the decisions are merged into individual sets\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRule *get_learnt()\fR
+my \fI@learnt\fR \fB=\fR \fI$rule\fR\fB\->get_learnt()\fR;
+\fIlearnt\fR \fB=\fR \fIrule\fR\fB\&.get_learnt()\fR
+\fIlearnt\fR \fB=\fR \fIrule\fR\fB\&.get_lerant()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a list of learnt rules that are part of the learnt rule proof\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fB<equality>\fR
 \fBif (\fR\fI$rule1\fR \fB==\fR \fI$rule2\fR\fB)\fR
 \fBif\fR \fIrule1\fR \fB==\fR \fIrule2\fR\fB:\fR
@@ -4584,7 +4784,7 @@ The dependency leading to the creation of the rule\&.
 .\}
 .nf
 \fBDep *dep_id;\fR                            /* read only */
-\fI$ruleinfo\fR\fB\->{\*(Aqdep_id\*(Aq}\fR
+\fI$ruleinfo\fR\fB\->{\fR\fIdep_id\fR\fB}\fR
 \fIruleinfo\fR\fB\&.dep_id\fR
 \fIruleinfo\fR\fB\&.dep_id\fR
 .fi
@@ -4622,7 +4822,7 @@ The involved Solvable, e\&.g\&. the one containing the dependency\&.
 .RE
 .\}
 .sp
-The other involved Solvable (if any), e\&.g\&. the one containing providing the dependency for conflicts\&.
+The other involved Solvable (if any), e\&.g\&. the one providing the dependency\&.
 .sp
 .if n \{\
 .RS 4
@@ -4638,6 +4838,21 @@ my \fI$str\fR \fB=\fR \fI$ruleinfo\fR\fB\->problemstr()\fR;
 .\}
 .sp
 A string describing the ruleinfo from a problem perspective\&. This probably only makes sense if the rule is part of a problem\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$ruleinfo\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIruleinfo\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIruleinfo\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+A string describing the ruleinfo, i\&.e\&. the reason why the corresponding rule has been created\&.
 .SH "THE SOLUTION CLASS"
 .sp
 A solution solves one specific problem\&. It consists of multiple solution elements that all need to be executed\&.
@@ -5076,7 +5291,7 @@ Back pointer to pool\&.
 .RS 4
 .\}
 .nf
-\fBbool isempty()\fR;
+\fBbool isempty()\fR
 \fI$trans\fR\fB\->isempty()\fR
 \fItrans\fR\fB\&.isempty()\fR
 \fItrans\fR\fB\&.isempty?\fR
@@ -5091,7 +5306,7 @@ Returns true if the transaction does not do anything, i\&.e\&. has no elements\&
 .RS 4
 .\}
 .nf
-\fBSolvable *newsolvables()\fR;
+\fBSolvable *newsolvables()\fR
 my \fI@newsolvables\fR \fB=\fR \fI$trans\fR\fB\->newsolvables()\fR;
 \fInewsolvables\fR \fB=\fR \fItrans\fR\fB\&.newsolvables()\fR
 \fInewsolvables\fR \fB=\fR \fItrans\fR\fB\&.newsolvables()\fR
@@ -5106,7 +5321,7 @@ Return all packages that are to be installed by the transaction\&. These are the
 .RS 4
 .\}
 .nf
-\fBSolvable *keptsolvables()\fR;
+\fBSolvable *keptsolvables()\fR
 my \fI@keptsolvables\fR \fB=\fR \fI$trans\fR\fB\->keptsolvables()\fR;
 \fIkeptsolvables\fR \fB=\fR \fItrans\fR\fB\&.keptsolvables()\fR
 \fIkeptsolvables\fR \fB=\fR \fItrans\fR\fB\&.keptsolvables()\fR
@@ -5121,7 +5336,7 @@ Return all installed packages that the transaction will keep installed\&.
 .RS 4
 .\}
 .nf
-\fBSolvable *steps()\fR;
+\fBSolvable *steps()\fR
 my \fI@steps\fR \fB=\fR \fI$trans\fR\fB\->steps()\fR;
 \fIsteps\fR \fB=\fR \fItrans\fR\fB\&.steps()\fR
 \fIsteps\fR \fB=\fR \fItrans\fR\fB\&.steps()\fR
@@ -5166,7 +5381,7 @@ Group the transaction elements into classes so that they can be displayed in a s
 .RS 4
 .\}
 .nf
-\fBSolvable othersolvable(Solvable *\fR\fIsolvable\fR\fB)\fR;
+\fBSolvable othersolvable(Solvable *\fR\fIsolvable\fR\fB)\fR
 my \fI$other\fR \fB=\fR \fI$trans\fR\fB\->othersolvable(\fR\fI$solvable\fR\fB)\fR;
 \fIother\fR \fB=\fR \fItrans\fR\fB\&.othersolvable(\fR\fIsolvable\fR\fB)\fR
 \fIother\fR \fB=\fR \fItrans\fR\fB\&.othersolvable(\fR\fIsolvable\fR\fB)\fR
@@ -5185,7 +5400,7 @@ Thus, the \(lqother\(rq solvable is normally the package that is also shown for
 .RS 4
 .\}
 .nf
-\fBSolvable *allothersolvables(Solvable *\fR\fIsolvable\fR\fB)\fR;
+\fBSolvable *allothersolvables(Solvable *\fR\fIsolvable\fR\fB)\fR
 my \fI@others\fR \fB=\fR \fI$trans\fR\fB\->allothersolvables(\fR\fI$solvable\fR\fB)\fR;
 \fIothers\fR \fB=\fR \fItrans\fR\fB\&.allothersolvables(\fR\fIsolvable\fR\fB)\fR
 \fIothers\fR \fB=\fR \fItrans\fR\fB\&.allothersolvables(\fR\fIsolvable\fR\fB)\fR
@@ -5200,7 +5415,7 @@ For installed packages, returns all of the packages that replace us\&. For to be
 .RS 4
 .\}
 .nf
-\fBlong long calc_installsizechange()\fR;
+\fBlong long calc_installsizechange()\fR
 my \fI$change\fR \fB=\fR \fI$trans\fR\fB\->calc_installsizechange()\fR;
 \fIchange\fR \fB=\fR \fItrans\fR\fB\&.calc_installsizechange()\fR
 \fIchange\fR \fB=\fR \fItrans\fR\fB\&.calc_installsizechange()\fR
@@ -5215,7 +5430,7 @@ Return the size change of the installed system in kilobytes (kibibytes)\&.
 .RS 4
 .\}
 .nf
-\fBvoid order(int\fR \fIflags\fR \fB= 0)\fR;
+\fBvoid order(int\fR \fIflags\fR \fB= 0)\fR
 \fI$trans\fR\fB\->order()\fR;
 \fItrans\fR\fB\&.order()\fR
 \fItrans\fR\fB\&.order()\fR
@@ -5405,7 +5620,10 @@ Create a checksum object\&. Currently the following types are supported:
 .nf
 \fBREPOKEY_TYPE_MD5\fR
 \fBREPOKEY_TYPE_SHA1\fR
+\fBREPOKEY_TYPE_SHA224\fR
 \fBREPOKEY_TYPE_SHA256\fR
+\fBREPOKEY_TYPE_SHA384\fR
+\fBREPOKEY_TYPE_SHA512\fR
 .fi
 .if n \{\
 .RE
@@ -5649,7 +5867,7 @@ Return file file descriptor of the file\&. If the file is not open, \-1 is retur
 .\}
 .nf
 \fBvoid cloexec(bool\fR \fIstate\fR\fB)\fR
-\fI$file\fR\fB\->cloexec(\fR\fI$state\fR\fB)\fR
+\fI$file\fR\fB\->cloexec(\fR\fI$state\fR\fB)\fR;
 \fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
 \fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
 .fi
@@ -5743,7 +5961,7 @@ The id of the repodata area\&. Repodata ids of different repositories overlap\&.
 .RS 4
 .\}
 .nf
-\fBinternalize()\fR;
+\fBinternalize()\fR
 \fI$data\fR\fB\->internalize()\fR;
 \fIdata\fR\fB\&.internalize()\fR
 \fIdata\fR\fB\&.internalize()\fR
@@ -5758,7 +5976,7 @@ Internalize newly added data\&. The lookup functions will only see the new data
 .RS 4
 .\}
 .nf
-\fBbool write(FILE *\fR\fIfp\fR\fB)\fR;
+\fBbool write(FILE *\fR\fIfp\fR\fB)\fR
 \fI$data\fR\fB\->write(\fR\fI$fp\fR\fB)\fR;
 \fIdata\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
 \fIdata\fR\fB\&.write(\fR\fIfp\fR\fB)\fR
@@ -5802,7 +6020,7 @@ Convert a string (directory) into an Id and back\&. If the string is currently n
 .\}
 .nf
 \fBvoid add_dirstr(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fIdir\fR\fB, const char *\fR\fIstr\fR\fB)\fR
-\fI$data\fR\fB\->add_dirstr(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$dir\fR\fB,\fR \fI$string\fR\fB)\fR
+\fI$data\fR\fB\->add_dirstr(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$dir\fR\fB,\fR \fI$string\fR\fB)\fR;
 \fIdata\fR\fB\&.add_dirstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIdir\fR\fB,\fR \fIstring\fR\fB)\fR
 \fIdata\fR\fB\&.add_dirstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIdir\fR\fB,\fR \fIstring\fR\fB)\fR
 .fi
@@ -5816,7 +6034,7 @@ Add a file path consisting of a dirname Id and a basename string\&.
 .RS 4
 .\}
 .nf
-\fBbool add_solv(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR;
+\fBbool add_solv(FILE *\fR\fIfp\fR\fB, int\fR \fIflags\fR \fB= 0)\fR
 \fI$data\fR\fB\->add_solv(\fR\fI$fp\fR\fB)\fR;
 \fIdata\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
 \fIdata\fR\fB\&.add_solv(\fR\fIfp\fR\fB)\fR
@@ -5831,8 +6049,8 @@ Replace a stub repodata object with the data from a solv file\&. This method aut
 .RS 4
 .\}
 .nf
-\fBvoid create_stubs()\fR;
-\fI$data\fR\fB\->create_stubs()\fR
+\fBvoid create_stubs()\fR
+\fI$data\fR\fB\->create_stubs()\fR;
 \fIdata\fR\fB\&.create_stubs()\fR
 \fIdata\fR\fB\&.create_stubs()\fR
 .fi
@@ -5846,7 +6064,7 @@ Create stub repodatas from the information stored in the repodata meta area\&.
 .RS 4
 .\}
 .nf
-\fBvoid extend_to_repo()\fR;
+\fBvoid extend_to_repo()\fR
 \fI$data\fR\fB\->extend_to_repo()\fR;
 \fIdata\fR\fB\&.extend_to_repo()\fR
 \fIdata\fR\fB\&.extend_to_repo()\fR
@@ -5958,7 +6176,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR;
+\fBvoid set_str(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR
 \fI$data\fR\fB\->set_str(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$str\fR\fB)\fR;
 \fIdata\fR\fB\&.set_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
 \fIdata\fR\fB\&.set_str(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
@@ -5971,7 +6189,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR;
+\fBvoid set_id(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR
 \fI$data\fR\fB\->set_id(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$id\fR\fB)\fR;
 \fIdata\fR\fB\&.set_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
 \fIdata\fR\fB\&.set_id(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
@@ -5984,7 +6202,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_num(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInum\fR\fB)\fR;
+\fBvoid set_num(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, unsigned long long\fR \fInum\fR\fB)\fR
 \fI$data\fR\fB\->set_num(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$num\fR\fB)\fR;
 \fIdata\fR\fB\&.set_num(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fInum\fR\fB)\fR
 \fIdata\fR\fB\&.set_num(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fInum\fR\fB)\fR
@@ -5997,7 +6215,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_void(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
+\fBvoid set_void(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
 \fI$data\fR\fB\->set_void(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
 \fIdata\fR\fB\&.set_void(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
 \fIdata\fR\fB\&.set_void(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
@@ -6010,7 +6228,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_poolstr(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR;
+\fBvoid set_poolstr(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, const char *\fR\fIstr\fR\fB)\fR
 \fI$data\fR\fB\->set_poolstr(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$str\fR\fB)\fR;
 \fIdata\fR\fB\&.set_poolstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
 \fIdata\fR\fB\&.set_poolstr(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIstr\fR\fB)\fR
@@ -6023,7 +6241,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Chksum *\fR\fIchksum\fR\fB)\fR;
+\fBvoid set_checksum(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Chksum *\fR\fIchksum\fR\fB)\fR
 \fI$data\fR\fB\->set_checksum(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$chksum\fR\fB)\fR;
 \fIdata\fR\fB\&.set_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIchksum\fR\fB)\fR
 \fIdata\fR\fB\&.set_checksum(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIchksum\fR\fB)\fR
@@ -6036,7 +6254,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_sourcepkg(Id\fR \fIsolvid\fR\fB, const char *\fR\fIsourcepkg\fR\fB)\fR;
+\fBvoid set_sourcepkg(Id\fR \fIsolvid\fR\fB, const char *\fR\fIsourcepkg\fR\fB)\fR
 \fI$data\fR\fB\&.set_sourcepkg(\fR\fI$solvid\fR\fB,\fR \fI$sourcepkg\fR\fB)\fR;
 \fIdata\fR\fB\&.set_sourcepkg(\fR\fIsolvid\fR\fB,\fR \fIsourcepkg\fR\fB)\fR
 \fIdata\fR\fB\&.set_sourcepkg(\fR\fIsolvid\fR\fB,\fR \fIsourcepkg\fR\fB)\fR
@@ -6049,7 +6267,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid set_location(Id\fR \fIsolvid\fR\fB, unsigned int\fR \fImediano\fR\fB, const char *\fR\fIlocation\fR\fB)\fR;
+\fBvoid set_location(Id\fR \fIsolvid\fR\fB, unsigned int\fR \fImediano\fR\fB, const char *\fR\fIlocation\fR\fB)\fR
 \fI$data\fR\fB\&.set_location(\fR\fI$solvid\fR\fB,\fR \fI$mediano\fR\fB,\fR \fI$location\fR\fB)\fR;
 \fIdata\fR\fB\&.set_location(\fR\fIsolvid\fR\fB,\fR \fImediano\fR\fB,\fR \fIlocation\fR\fB)\fR
 \fIdata\fR\fB\&.set_location(\fR\fIsolvid\fR\fB,\fR \fImediano\fR\fB,\fR \fIlocation\fR\fB)\fR
@@ -6062,7 +6280,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBvoid add_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR;
+\fBvoid add_idarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, DepId\fR \fIid\fR\fB)\fR
 \fI$data\fR\fB\->add_idarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$id\fR\fB)\fR;
 \fIdata\fR\fB\&.add_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
 \fIdata\fR\fB\&.add_idarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIid\fR\fB)\fR
@@ -6075,7 +6293,7 @@ Lookup functions\&. Return the data element stored in the specified solvable\&.
 .RS 4
 .\}
 .nf
-\fBId new_handle()\fR;
+\fBId new_handle()\fR
 my \fI$handle\fR \fB=\fR \fI$data\fR\fB\->new_handle()\fR;
 \fIhandle\fR \fB=\fR \fIdata\fR\fB\&.new_handle()\fR
 \fIhandle\fR \fB=\fR \fIdata\fR\fB\&.new_handle()\fR
@@ -6088,7 +6306,7 @@ my \fI$handle\fR \fB=\fR \fI$data\fR\fB\->new_handle()\fR;
 .RS 4
 .\}
 .nf
-\fBvoid add_flexarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fIhandle\fR\fB)\fR;
+\fBvoid add_flexarray(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB, Id\fR \fIhandle\fR\fB)\fR
 \fI$data\fR\fB\->add_flexarray(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB,\fR \fI$handle\fR\fB)\fR;
 \fIdata\fR\fB\&.add_flexarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIhandle\fR\fB)\fR
 \fIdata\fR\fB\&.add_flexarray(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB,\fR \fIhandle\fR\fB)\fR
@@ -6101,7 +6319,7 @@ my \fI$handle\fR \fB=\fR \fI$data\fR\fB\->new_handle()\fR;
 .RS 4
 .\}
 .nf
-\fBvoid unset(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR;
+\fBvoid unset(Id\fR \fIsolvid\fR\fB, Id\fR \fIkeyname\fR\fB)\fR
 \fI$data\fR\fB\->unset(\fR\fI$solvid\fR\fB,\fR \fI$keyname\fR\fB)\fR;
 \fIdata\fR\fB\&.unset(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
 \fIdata\fR\fB\&.unset(\fR\fIsolvid\fR\fB,\fR \fIkeyname\fR\fB)\fR
@@ -6151,7 +6369,7 @@ Create a Dataiterator at the position of the datapos object\&.
 .RS 4
 .\}
 .nf
-\fBconst char *lookup_deltalocation(unsigned int *\fR\fIOUTPUT\fR\fB)\fR;
+\fBconst char *lookup_deltalocation(unsigned int *\fR\fIOUTPUT\fR\fB)\fR
 my \fB(\fR\fI$location\fR\fB,\fR \fI$mediano\fR\fB) =\fR \fI$datapos\fR\fB\->lookup_deltalocation()\fR;
 \fIlocation\fR\fB,\fR \fImediano\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltalocation()\fR
 \fIlocation\fR\fB,\fR \fImediano\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltalocation()\fR
@@ -6166,7 +6384,7 @@ Return a tuple containing the on\-media location and an optional media number fo
 .RS 4
 .\}
 .nf
-\fBconst char *lookup_deltaseq()\fR;
+\fBconst char *lookup_deltaseq()\fR
 my \fI$seq\fR \fB=\fR \fI$datapos\fR\fB\->lookup_deltaseq()\fR;
 \fIseq\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltaseq()\fR;
 \fIseq\fR \fB=\fR \fIdatapos\fR\fB\&.lookup_deltaseq()\fR;
@@ -6284,6 +6502,457 @@ my \fI$di\fR \fB=\fR \fI$datapos\fR\fB\->Dataiterator(\fR\fI$keyname\fR\fB,\fR \
 .\}
 .sp
 Iterate over the matching data elements\&. See the Dataiterator class for more information\&.
+.SH "THE ALTERNATIVE CLASS"
+.sp
+An Alternative object describes a branch point in the solving process\&. The solver found more than one good way to fulfill a dependency and chose one\&. It recorded the other possibilities in the alternative object so that they can be presented to the user in the case a different solution is preferable\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR                   /* read only */
+\fI$alternative\fR\fB\->{solv}\fR
+\fIalternative\fR\fB\&.solv\fR
+\fIalternative\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId type;\fR                        /* read only */
+\fI$alternative\fR\fB\->{type}\fR
+\fIalternative\fR\fB\&.type\fR
+\fIalternative\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The type of the alternative\&. Alternatives can be created because of rule fulfillment, because of recommended packages, and because of suggested packages (currently unused)\&. See below for a list of valid types\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRule rule;\fR                      /* read only */
+\fI$alternative\fR\fB\->{rule}\fR
+\fIalternative\fR\fB\&.rule\fR
+\fIalternative\fR\fB\&.rule\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The rule that caused the creation of the alternative (SOLVER_ALTERNATIVE_TYPE_RULE)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *dep;\fR                       /* read only */
+\fI$ruleinfo\fR\fB\->{dep}\fR
+\fIruleinfo\fR\fB\&.dep\fR
+\fIruleinfo\fR\fB\&.dep\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The dependency that caused the creation of the alternative (SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *depsolvable;\fR               /* read only */
+\fI$ruleinfo\fR\fB\->{depsolvable}\fR
+\fIruleinfo\fR\fB\&.depsolvable\fR
+\fIruleinfo\fR\fB\&.depsolvable\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The package containing the dependency (SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable chosen;\fR                /* read only */
+\fI$alternative\fR\fB\->{chosen}\fR
+\fIalternative\fR\fB\&.chosen\fR
+\fIalternative\fR\fB\&.chosen\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The solvable that the solver chose from the alternative\(cqs package set\&.
+.SS "CONSTANTS"
+.PP
+\fBSOLVER_ALTERNATIVE_TYPE_RULE\fR
+.RS 4
+The alternative was created when fulfilling a rule\&.
+.RE
+.PP
+\fBSOLVER_ALTERNATIVE_TYPE_RECOMMENDS\fR
+.RS 4
+The alternative was created when fulfilling a recommends dependency\&.
+.RE
+.PP
+\fBSOLVER_ALTERNATIVE_TYPE_SUGGESTS\fR
+.RS 4
+The alternative was created when fulfilling a suggests dependency\&.
+.RE
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *choices()\fR
+my \fI@choices\fR \fB=\fR \fI$alternative\fR\fB\->choices()\fR;
+\fIchoices\fR \fB=\fR \fIalternative\fR\fB\&.choices\fR
+\fIchoices\fR \fB=\fR \fIalternative\fR\fB\&.choices\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return the set of solvables that the solver could choose from when creating the alternative\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$alternative\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIalternative\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIalternative\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the alternative\&.
+.SH "THE DECISION CLASS"
+.sp
+A decision is created when the solver fulfills dependencies\&. It can be either to install a package to satisfy a dependency or to conflict a dependency because it conflicts with another package or its dependencies cannot be met\&. Most decisions are caused by rule processing, but there are some other types like orphaned package handling or weak dependency handling\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR                   /* read only */
+\fI$decision\fR\fB\->{solv}\fR
+\fIdecision\fR\fB\&.solv\fR
+\fIdecision\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId p;\fR                           /* read only */
+\fI$decision\fR\fB\->{p}\fR
+\fIdecision\fR\fB\&.p\fR
+\fIdecision\fR\fB\&.p\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The decision package id, positive for installs and negative for conflicts\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint reason;\fR                     /* read only */
+\fI$decision\fR\fB\->{reason}\fR
+\fIdecision\fR\fB\&.reason\fR
+\fIdecision\fR\fB\&.reason\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The reason for the decision\&. See the SOLVER_REASON_ constants\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint infoid;\fR                     /* read only */
+\fI$decision\fR\fB\->{infoid}\fR
+\fIdecision\fR\fB\&.infoid\fR
+\fIdecision\fR\fB\&.infoid\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Extra info for the decision\&. This is the rule id for decisions caused by rule fulfillment\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable solvable;\fR              /* read only */
+\fI$decision\fR\fB\->{solvable}\fR
+\fIdecision\fR\fB\&.solvable\fR
+\fIdecision\fR\fB\&.solvable\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The decision package object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRule rule()\fR                     /* read only */
+\fI$decision\fR\fB\->{rule}\fR
+\fIdecision\fR\fB\&.rule\fR
+\fIdecision\fR\fB\&.rule\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The rule object for decisions that where caused by rule fulfilment\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRuleinfo info()\fR
+my \fI$info\fR \fB=\fR \fI$decision\fR\fB\->info()\fR;
+\fIinfo\fR \fB=\fR \fIdecision\fR\fB\&.info()\fR
+\fIinfo\fR \fB=\fR \fIdecision\fR\fB\&.info()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a Ruleinfo object describing the decision\&. Some reasons like SOLVER_REASON_WEAKDEP are not caused by rules, but can be expressed by a Ruleinfo object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBRuleinfo *allinfos()\fR
+my \fI@infos\fR \fB=\fR \fI$decision\fR\fB\->allinfos()\fR;
+\fIinfos\fR \fB=\fR \fIdecision\fR\fB\&.allinfos()\fR
+\fIinfos\fR \fB=\fR \fIdecision\fR\fB\&.allinfos()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Same as info(), but all Ruleinfo objects describing the decision are returned\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *reasonstr()\fR
+my \fIstr\fR \fB=\fR \fI$decision\fR\fB\->reasonstr()\fR
+\fIstr\fR \fB=\fR \fIdecision\fR\fB\&.reasonstr()\fR
+\fIstr\fR \fB=\fR \fIdecision\fR\fB\&.reasonstr()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing why a decision was done (but without the decision itself)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$decison\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIdecision\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIdecision\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the decision (but without the reason)\&.
+.SH "THE DECISIONSET CLASS"
+.sp
+A decisionset consists of multiple decisions of the same reason and type that can be presented to the user as a single action\&.
+.SS "ATTRIBUTES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolver *solv;\fR                   /* read only */
+\fI$decision\fR\fB\->{solv}\fR
+\fIdecision\fR\fB\&.solv\fR
+\fIdecision\fR\fB\&.solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Back pointer to solver object\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBId p;\fR                           /* read only */
+\fI$decision\fR\fB\->{p}\fR
+\fIdecision\fR\fB\&.p\fR
+\fIdecision\fR\fB\&.p\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The package id of the first decision, positive for installs and negative for conflicts\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint reason;\fR                     /* read only */
+\fI$decision\fR\fB\->{reason}\fR
+\fIdecision\fR\fB\&.reason\fR
+\fIdecision\fR\fB\&.reason\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The reason for the decisions in the set\&. See the SOLVER_REASON_ constants\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBint type;\fR                       /* read only */
+\fI$ruleinfo\fR\fB\->{type}\fR
+\fIruleinfo\fR\fB\&.type\fR
+\fIruleinfo\fR\fB\&.type\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The type of the decision info\&. See the constant section of the solver class for the rule type list and the special type list\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *dep;\fR                       /* read only */
+\fI$ruleinfo\fR\fB\->{dep}\fR
+\fIruleinfo\fR\fB\&.dep\fR
+\fIruleinfo\fR\fB\&.dep\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The dependency that caused the decision
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDep *dep_id;\fR                    /* read only */
+\fI$ruleinfo\fR\fB\->{\fR\fIdep_id\fR\fB}\fR
+\fIruleinfo\fR\fB\&.dep_id\fR
+\fIruleinfo\fR\fB\&.dep_id\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The Id of the dependency that caused the decision\&.
+.SS "METHODS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBDecision *decisions()\fR
+my \fI@decisions\fR \fB=\fR \fI$decisionset\fR\fB\->decisions()\fR;
+\fIdecisions\fR \fB=\fR \fIdecisionset\fR\fB\&.decisions()\fR
+\fIdecisions\fR \fB=\fR \fIdecisionset\fR\fB\&.decisions()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all the decisions of the set\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBSolvable *solvables()\fR
+my \fI@pkgs\fR \fB=\fR \fI$decisionset\fR\fB\->solvables()\fR;
+\fIpkgs\fR \fB=\fR \fIdecisionset\fR\fB\&.solvables()\fR
+\fIpkgs\fR \fB=\fR \fIdecisionset\fR\fB\&.solvables()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return all the packages that were decided in the set\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBconst char *reasonstr()\fR
+my \fIstr\fR \fB=\fR \fI$decision\fR\fB\->reasonstr()\fR;
+\fIstr\fR \fB=\fR \fIdecision\fR\fB\&.reasonstr()\fR
+\fIstr\fR \fB=\fR \fIdecision\fR\fB\&.reasonstr()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing why the decisions were done (but without the decisions themself)\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fB<stringification>\fR
+my \fI$str\fR \fB=\fR \fI$decison\fR\fB\->str\fR;
+\fIstr\fR \fB= str(\fR\fIdecision\fR\fB)\fR
+\fIstr\fR \fB=\fR \fIdecision\fR\fB\&.to_s\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Return a string describing the decisions (but without the reason)\&.
 .SH "AUTHOR"
 .sp
 Michael Schroeder <mls@suse\&.de>
index 5d46c08..b1069a9 100644 (file)
@@ -352,6 +352,15 @@ useful if you have only few packages that use non-standard file
 dependencies, but you still want the fast speed that addfileprovides()
 generates.
 
+*POOL_FLAG_NOWHATPROVIDESAUX*::
+Disable the creation of the auxillary whatprovides index. This saves
+a bit of memory but also makes the whatprovides lookups a bit slower.
+
+*POOL_FLAG_WHATPROVIDESWITHDISABLED*::
+Make the whatprovides index also contain disabled packages. This
+means that you do not need to recreate the index if a package is
+enabled/disabled, i.e. the pool->considered bitmap is changed.
+
 === METHODS ===
 
        void free()
@@ -496,21 +505,21 @@ a Dep object or a simple Id as argument.
 Filter list of solvables by repo priority, architecture and version.
 
        Solvable *whatcontainsdep(Id keyname, DepId dep, Id marker = -1)
-       my @solvables = $pool->whatcontainsdep($keyname, $dep)
+       my @solvables = $pool->whatcontainsdep($keyname, $dep);
        solvables = pool.whatcontainsdep(keyname, dep)
        solvables = pool.whatcontainsdep(keyname, dep)
 
 Return all solvables for which keyname contains the dependency.
 
        Solvable *whatmatchesdep(Id keyname, DepId dep, Id marker = -1)
-       my @solvables = $pool->whatmatchesdep($keyname, $sdep)
+       my @solvables = $pool->whatmatchesdep($keyname, $sdep);
        solvables = pool.whatmatchesdep(keyname, dep)
        solvables = pool.whatmatchesdep(keyname, dep)
 
 Return all solvables that have dependencies in keyname that match the dependency.
 
        Solvable *whatmatchessolvable(Id keyname, Solvable solvable, Id marker = -1)
-       my @solvables = $pool->whatmatchessolvable($keyname, $solvable)
+       my @solvables = $pool->whatmatchessolvable($keyname, $solvable);
        solvables = pool.whatmatchessolvable(keyname, solvable)
        solvables = pool.whatmatchessolvable(keyname, solvable)
 
@@ -688,6 +697,14 @@ basically the inverse of the ``considered'' methods above, i.e. calling
 installation. Note you need to recreate the whatprovides hash after changing the
 list.
 
+       const char *solvableset2str(Solvable *solvables)
+       my $str = $pool->solvableset2str($solvables);
+       str = pool.solvableset2str(solvables)
+       str = pool.solvableset2str(solvables)
+
+Return a string describing a list of solvables. The method tries to reduce
+the output by using version ranges if possible.
+
 === DATA RETRIEVAL METHODS ===
 
 In the following functions, the _keyname_ argument describes what to retrieve.
@@ -781,6 +798,14 @@ Lookup an existing Repository by id. You can also do this by using the
 Return a string describing the Solvable with the specified id. The string
 consists of the name, version, and architecture of the Solvable.
 
+       const char *solvidset2str(Id *solvids)
+       my $str = $pool->solvidset2str(\@solvids);
+       str = pool.solvidset2str(solvids)
+       str = pool.solvidset2str(solvids)
+
+Return a string describing a list of solvables. The method tries to reduce
+the output by using version ranges if possible.
+
        Id str2id(const char *str, bool create = 1)
        my $id = pool->str2id($string);
        id = pool.str2id(string)
@@ -1449,7 +1474,7 @@ correct marker for the keyname and returns the first part of the
 array, use 1 to select the second part or 0 to retrieve all ids
 including the marker.
 
-       const char *lookup_location(unsigned int *OUTPUT);
+       const char *lookup_location(unsigned int *OUTPUT)
        my ($location, $mediano) = $solvable->lookup_location();
        location, mediano = solvable.lookup_location()
        location, mediano = solvable.lookup_location()
@@ -1458,7 +1483,7 @@ Return a tuple containing the on-media location and an optional
 media number for multi-part repositories (e.g. repositories
 spawning multiple DVDs).
 
-       const char *lookup_sourcepkg();
+       const char *lookup_sourcepkg()
        my $sourcepkg = $solvable->lookup_sourcepkg();
        sourcepkg = solvable.lookup_sourcepkg()
        sourcepkg = solvable.lookup_sourcepkg()
@@ -1477,21 +1502,21 @@ Return a sourcepkg name associated with solvable.
 Iterate over the matching data elements. See the Dataiterator class for more
 information.
 
-       void add_deparray(Id keyname, DepId dep, Id marker = -1);
+       void add_deparray(Id keyname, DepId dep, Id marker = -1)
        $solvable->add_deparray($keyname, $dep);
        solvable.add_deparray(keyname, dep)
        solvable.add_deparray(keyname, dep)
 
 Add a new dependency to the attributes stored in keyname.
 
-       void unset(Id keyname);
+       void unset(Id keyname)
        $solvable->unset($keyname);
        solvable.unset(keyname)
        solvable.unset(keyname)
 
 Delete data stored for the specific keyname.
 
-       bool installable();
+       bool installable()
        $solvable->installable()
        solvable.installable()
        solvable.installable?
@@ -1499,7 +1524,7 @@ Delete data stored for the specific keyname.
 Return true if the solvable is installable on the system. Solvables
 are not installable if the system does not support their architecture.
 
-       bool isinstalled();
+       bool isinstalled()
        $solvable->isinstalled()
        solvable.isinstalled()
        solvable.isinstalled?
@@ -1626,6 +1651,7 @@ one.
 Iterate through the matches. If there is a match, the object
 in d will be of type Datamatch.
 
+
 The Datamatch Class
 -------------------
 Objects of this type will be created for every value matched
@@ -1730,7 +1756,7 @@ that cannot be represented as a string.
 
 === METHODS ===
 
-       Datapos pos();
+       Datapos pos()
        my $pos = $d->pos();
        pos = d.pos()
        pos = d.pos()
@@ -1739,7 +1765,7 @@ The position object of the current match. It can be used to do
 sub-searches starting at the match (if it is of an array type).
 See the Datapos class for more information.
 
-       Datapos parentpos();
+       Datapos parentpos()
        my $pos = $d->parentpos();
        pos = d.parentpos()
        pos = d.parentpos()
@@ -1966,6 +1992,7 @@ Do a matchsolvable operation and combine the result with the current selection.
 
 Return a string describing the selection.
 
+
 The Job Class
 -------------
 Jobs are the way to specify to the dependency solver what to do.
@@ -2172,7 +2199,7 @@ selection part of the ``how'' attribute.
 Return the set of solvables of the job as an array of Solvable
 objects.
 
-       bool isemptyupdate();
+       bool isemptyupdate()
        $job->isemptyupdate()
        job.isemptyupdate()
        job.isemptyupdate?
@@ -2252,6 +2279,7 @@ different architecture. When using the Selection class to create jobs
 the set bits are automatically added, e.g. selecting ``screen.i586'' will
 automatically add SOLVER_SETARCH, and thus no problem will be reported.
 
+
 The Solver Class
 ----------------
 Dependency solving is what this library is about. A solver object is needed
@@ -2468,6 +2496,15 @@ To fulfill the dependencies a package needs to be installed that is
 obsoleted by an installed package. See the POOL_FLAG_NOINSTALLEDOBSOLETES
 flag.
 
+*SOLVER_RULE_PKG_RECOMMENDS*::
+The package contains a recommended dependency.
+
+*SOLVER_RULE_PKG_SUPPLEMENTS*::
+The package contains a dependency to specify it supplements another package.
+
+*SOLVER_RULE_PKG_CONSTRAINS*::
+The package contains a constraint against some other package (disttype conda).
+
 *SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP*::
 The user asked for installation of a package providing a specific
 dependency, but no available package provides it.
@@ -2587,6 +2624,11 @@ This is a special case of SOLVER_REASON_WEAKDEP.
 *SOLVER_REASON_SUPPLEMENTED*::
 This is a special case of SOLVER_REASON_WEAKDEP.
 
+*SOLVER_REASON_UNSOLVABLE*::
+This is a special case where a rule cannot be fulfilled.
+
+*SOLVER_REASON_PREMISE*::
+This is a special case for the premises of learnt rules.
 
 === ATTRIBUTES ===
 
@@ -2631,29 +2673,53 @@ Return the transaction to implement the calculated package changes. A transactio
 is available even if problems were found, this is useful for interactive user
 interfaces that show both the job result and the problems.
 
-       int reason = describe_decision(Solvable *s, Rule *OUTPUT)
-       my ($reason, $rule) = $solver->describe_decision($solvable);
-       (reason, rule) = solver.describe_decision(solvable)
-       (reason, rule) = solver.describe_decision(solvable)
-
-Return the reason why a specific solvable was installed or erased. For most of
-the reasons the rule that triggered the decision is also returned.
-
-       Solvable *get_recommended(bool noselected=0);
+       Solvable *get_recommended(bool noselected=0)
        my @solvables = $solver->get_recommended();
        solvables = solver.get_recommended()
        solvables = solver.get_recommended()
 
 Return all solvables that are recommended by the solver run result. This includes
-solvables included in the result, set noselected if you want to filter those.
+solvables included in the result; set noselected if you want to filter those.
 
-       Solvable *get_suggested(bool noselected=0);
+       Solvable *get_suggested(bool noselected=0)
        my @solvables = $solver->get_suggested();
        solvables = solver.get_suggested()
        solvables = solver.get_suggested()
 
 Return all solvables that are suggested by the solver run result. This includes
-solvables included in the result, set noselected if you want to filter those.
+solvables included in the result; set noselected if you want to filter those.
+
+       Decision = get_decision(Solvable *s)
+       my $decision = $solver->get_decision($solvable);
+       decision = solver.get_decision(solvable);
+       decision = solver.get_decision(solvable);
+
+Return a decision object that describes why a specific solvable was installed or erased.
+See the Decision class for more information.
+
+       Decision *get_decisionlist(Solvable *s)
+       my @decisions = $solver->get_decisionlist($solvable);
+       decisions = solver.get_decisionlist(solvable)
+       decisions = solver.get_decisionlist(solvable)
+
+Return a list of decisions that caused the specific solvable to be installed or
+erased. This is usually more useful than the get_decision() method, as it
+returns every involved decision instead of just a single one.
+
+       Alternative *alternatives()
+       my @alternatives = $solver->alternatives();
+       alternatives = solver.alternatives()
+       alternatives = solver.alternatives()
+
+Return all alternatives recorded in the solver run. See the Alternative class
+for more information.
+
+       int alternatives_count()
+       my $cnt = $solver->alternatives_count();
+       cnt = solver.alternatives_count()
+       cnt = solver.alternatives_count()
+
+Return the number of alternatives without creating alternative objects.
 
 
 The Problem Class
@@ -2700,6 +2766,31 @@ all the needed information why there was a problem, but it's hard to present
 them to the user in a sensible way. The default is to filter out all update and
 job rules (unless the returned rules only consist of those types).
 
+       Decision *get_decisionlist()
+       my @decisions = $problem->get_decisionlist();
+       decisions = problem.get_decisionlist()
+       decisions = problem.get_decisionlist()
+
+Return a list of decisions prooving the problem. This is somewhat similar to
+the findallproblemrules(), but the output is in an order that makes it easier
+to understand why the solver could not find a solution.
+
+       Decisionset *get_decisionsetlist()
+       my @decisionsets = $problem->get_decisionsetlist();
+       decisionsets = problem.get_decisionsetlist()
+       decisionsets = problem.get_decisionsetlist()
+
+Like the get_decisionlist() method, but the decisions are merged into
+individual sets.
+
+       Rule *get_learnt()
+       my @learnt = $problem->get_learnt();
+       learnt = problem.get_learnt()
+       learnt = problem.get_lerant()
+
+Return a list of learnt rules that are part of the problem proof. This
+is useful for presenting a complete proof to the user.
+
        Solution *solutions()
        my @solutions = $problem->solutions();
        solutions = problem.solutions()
@@ -2724,6 +2815,7 @@ Return a string describing the problem. This is a convenience function, it is
 a shorthand for calling findproblemrule(), then ruleinfo() on the problem
 rule and problemstr() on the ruleinfo object.
 
+
 The Rule Class
 --------------
 Rules are the basic block of sat solving. Each package dependency gets translated
@@ -2771,6 +2863,28 @@ As the same dependency rule can get created because of multiple dependencies, on
 Ruleinfo is not enough to describe the reason. Thus the allinfos() method returns
 an array of all infos about a rule.
 
+       Decision *get_decisionlist()
+       my @decisions = $rule->get_decisionlist();
+       decisions = rule.get_decisionlist()
+       decisions = rule.get_decisionlist()
+
+Return a list of decisions prooving a learnt rule.
+
+       Decision *get_decisionsetlist()
+       my @decisionsets = $rule->get_decisionsetlist();
+       decisionsets = rule.get_decisionsetlist()
+       decisionsets = rule.get_decisionsetlist()
+
+Like the get_decisionlist() method, but the decisions are merged into
+individual sets.
+
+       Rule *get_learnt()
+       my @learnt = $rule->get_learnt();
+       learnt = rule.get_learnt()
+       learnt = rule.get_lerant()
+
+Return a list of learnt rules that are part of the learnt rule proof.
+
        <equality>
        if ($rule1 == $rule2)
        if rule1 == rule2:
@@ -2778,6 +2892,7 @@ an array of all infos about a rule.
 
 Two rules are equal if they belong to the same solver and have the same id.
 
+
 The Ruleinfo Class
 ------------------
 A Ruleinfo describes one reason why a rule was created.
@@ -2807,7 +2922,7 @@ rule type list and the special type list.
 The dependency leading to the creation of the rule.
 
        Dep *dep_id;                            /* read only */
-       $ruleinfo->{'dep_id'}
+       $ruleinfo->{dep_id}
        ruleinfo.dep_id
        ruleinfo.dep_id
 
@@ -2825,8 +2940,8 @@ The involved Solvable, e.g. the one containing the dependency.
        ruleinfo.othersolvable
        ruleinfo.othersolvable
 
-The other involved Solvable (if any), e.g. the one containing providing
-the dependency for conflicts.
+The other involved Solvable (if any), e.g. the one providing
+the dependency.
 
        const char *problemstr();
        my $str = $ruleinfo->problemstr();
@@ -2836,6 +2951,15 @@ the dependency for conflicts.
 A string describing the ruleinfo from a problem perspective. This probably
 only makes sense if the rule is part of a problem.
 
+       <stringification>
+       my $str = $ruleinfo->str;
+       str = str(ruleinfo)
+       str = ruleinfo.to_s
+
+A string describing the ruleinfo, i.e. the reason why the corresponding rule
+has been created.
+
+
 The Solution Class
 ------------------
 A solution solves one specific problem. It consists of multiple solution elements
@@ -2992,6 +3116,7 @@ you should replace the old job with the new one.
 
 A string describing the change the solution element consists of.
 
+
 The Transaction Class
 ---------------------
 Transactions describe the output of a solver run. A transaction contains
@@ -3126,14 +3251,14 @@ Back pointer to pool.
 
 === METHODS ===
 
-       bool isempty();
+       bool isempty()
        $trans->isempty()
        trans.isempty()
        trans.isempty?
 
 Returns true if the transaction does not do anything, i.e. has no elements.
 
-       Solvable *newsolvables();
+       Solvable *newsolvables()
        my @newsolvables = $trans->newsolvables();
        newsolvables = trans.newsolvables()
        newsolvables = trans.newsolvables()
@@ -3141,14 +3266,14 @@ Returns true if the transaction does not do anything, i.e. has no elements.
 Return all packages that are to be installed by the transaction. These are
 the packages that need to be downloaded from the repositories.
 
-       Solvable *keptsolvables();
+       Solvable *keptsolvables()
        my @keptsolvables = $trans->keptsolvables();
        keptsolvables = trans.keptsolvables()
        keptsolvables = trans.keptsolvables()
 
 Return all installed packages that the transaction will keep installed.
 
-       Solvable *steps();
+       Solvable *steps()
        my @steps = $trans->steps();
        steps = trans.steps()
        steps = trans.steps()
@@ -3176,7 +3301,7 @@ the result to match your preferences, see the mode argument flag in
 the CONSTANTS section. See the TransactionClass class for how to deal
 with the returned objects.
 
-       Solvable othersolvable(Solvable *solvable);
+       Solvable othersolvable(Solvable *solvable)
        my $other = $trans->othersolvable($solvable);
        other = trans.othersolvable(solvable)
        other = trans.othersolvable(solvable)
@@ -3194,7 +3319,7 @@ a package with the same name.
 Thus, the ``other'' solvable is normally the package that is also shown
 for a given package.
 
-       Solvable *allothersolvables(Solvable *solvable);
+       Solvable *allothersolvables(Solvable *solvable)
        my @others = $trans->allothersolvables($solvable);
        others = trans.allothersolvables(solvable)
        others = trans.allothersolvables(solvable)
@@ -3204,14 +3329,14 @@ be installed packages, returns all of the packages that the new package
 replaces. The special ``other'' solvable is always the first entry of the
 returned array.
 
-       long long calc_installsizechange();
+       long long calc_installsizechange()
        my $change = $trans->calc_installsizechange();
        change = trans.calc_installsizechange()
        change = trans.calc_installsizechange()
 
 Return the size change of the installed system in kilobytes (kibibytes).
 
-       void order(int flags = 0);
+       void order(int flags = 0)
        $trans->order();
        trans.order()
        trans.order()
@@ -3242,6 +3367,7 @@ in active mode. If the mode contains SOLVER_TRANSACTION_SHOW_ALL, the
 passive mode list will be unchanged but the active mode list will just
 contain A-2-1.
 
+
 The Transactionclass Class
 --------------------------
 Objects of this type are returned by the classify() Transaction method.
@@ -3306,6 +3432,7 @@ The id of the new vendor or architecture.
 
 Return the solvables for all transaction elements in the class.
 
+
 Checksums
 ---------
 Checksums (also called hashes) are used to make sure that downloaded data is
@@ -3322,7 +3449,10 @@ Create a checksum object. Currently the following types are supported:
 
        REPOKEY_TYPE_MD5
        REPOKEY_TYPE_SHA1
+       REPOKEY_TYPE_SHA224
        REPOKEY_TYPE_SHA256
+       REPOKEY_TYPE_SHA384
+       REPOKEY_TYPE_SHA512
 
 These keys are constants in the *solv* class.
 
@@ -3455,7 +3585,7 @@ Return file file descriptor of the file. If the file is not open, `-1` is
 returned.
 
        void cloexec(bool state)
-       $file->cloexec($state)
+       $file->cloexec($state);
        file.cloexec(state)
        file.cloexec(state)
 
@@ -3518,7 +3648,7 @@ The id of the repodata area. Repodata ids of different repositories overlap.
 
 === METHODS ===
 
-       internalize();
+       internalize()
        $data->internalize();
        data.internalize()
        data.internalize()
@@ -3526,7 +3656,7 @@ The id of the repodata area. Repodata ids of different repositories overlap.
 Internalize newly added data. The lookup functions will only see the new data
 after it has been internalized.
 
-       bool write(FILE *fp);
+       bool write(FILE *fp)
        $data->write($fp);
        data.write(fp)
        data.write(fp)
@@ -3547,13 +3677,13 @@ Convert a string (directory) into an Id and back. If the string is currently not
 pool and _create_ is false, zero is returned.
 
        void add_dirstr(Id solvid, Id keyname, Id dir, const char *str)
-       $data->add_dirstr($solvid, $keyname, $dir, $string)
+       $data->add_dirstr($solvid, $keyname, $dir, $string);
        data.add_dirstr(solvid, keyname, dir, string)
        data.add_dirstr(solvid, keyname, dir, string)
 
 Add a file path consisting of a dirname Id and a basename string.
 
-       bool add_solv(FILE *fp, int flags = 0);
+       bool add_solv(FILE *fp, int flags = 0)
        $data->add_solv($fp);
        data.add_solv(fp)
        data.add_solv(fp)
@@ -3562,15 +3692,15 @@ Replace a stub repodata object with the data from a solv file. This method
 automatically adds the REPO_USE_LOADING flag. It should only be used from
 a load callback.
 
-       void create_stubs();
-       $data->create_stubs()
+       void create_stubs()
+       $data->create_stubs();
        data.create_stubs()
        data.create_stubs()
 
 Create stub repodatas from the information stored in the repodata meta
 area.
 
-       void extend_to_repo();
+       void extend_to_repo()
        $data->extend_to_repo();
        data.extend_to_repo()
        data.extend_to_repo()
@@ -3627,62 +3757,62 @@ SOLVID_META solvid that stores repodata meta information.
 
 === DATA STORAGE METHODS ===
 
-       void set_str(Id solvid, Id keyname, const char *str);
+       void set_str(Id solvid, Id keyname, const char *str)
        $data->set_str($solvid, $keyname, $str);
        data.set_str(solvid, keyname, str)
        data.set_str(solvid, keyname, str)
 
-       void set_id(Id solvid, Id keyname, DepId id);
+       void set_id(Id solvid, Id keyname, DepId id)
        $data->set_id($solvid, $keyname, $id);
        data.set_id(solvid, keyname, id)
        data.set_id(solvid, keyname, id)
 
-       void set_num(Id solvid, Id keyname, unsigned long long num);
+       void set_num(Id solvid, Id keyname, unsigned long long num)
        $data->set_num($solvid, $keyname, $num);
        data.set_num(solvid, keyname, num)
        data.set_num(solvid, keyname, num)
 
-       void set_void(Id solvid, Id keyname);
+       void set_void(Id solvid, Id keyname)
        $data->set_void($solvid, $keyname);
        data.set_void(solvid, keyname)
        data.set_void(solvid, keyname)
 
-       void set_poolstr(Id solvid, Id keyname, const char *str);
+       void set_poolstr(Id solvid, Id keyname, const char *str)
        $data->set_poolstr($solvid, $keyname, $str);
        data.set_poolstr(solvid, keyname, str)
        data.set_poolstr(solvid, keyname, str)
 
-       void set_checksum(Id solvid, Id keyname, Chksum *chksum);
+       void set_checksum(Id solvid, Id keyname, Chksum *chksum)
        $data->set_checksum($solvid, $keyname, $chksum);
        data.set_checksum(solvid, keyname, chksum)
        data.set_checksum(solvid, keyname, chksum)
 
-       void set_sourcepkg(Id solvid, const char *sourcepkg);
+       void set_sourcepkg(Id solvid, const char *sourcepkg)
        $data.set_sourcepkg($solvid, $sourcepkg);
        data.set_sourcepkg(solvid, sourcepkg)
        data.set_sourcepkg(solvid, sourcepkg)
 
-       void set_location(Id solvid, unsigned int mediano, const char *location);
+       void set_location(Id solvid, unsigned int mediano, const char *location)
        $data.set_location($solvid, $mediano, $location);
        data.set_location(solvid, mediano, location)
        data.set_location(solvid, mediano, location)
 
-       void add_idarray(Id solvid, Id keyname, DepId id);
+       void add_idarray(Id solvid, Id keyname, DepId id)
        $data->add_idarray($solvid, $keyname, $id);
        data.add_idarray(solvid, keyname, id)
        data.add_idarray(solvid, keyname, id)
 
-       Id new_handle();
+       Id new_handle()
        my $handle = $data->new_handle();
        handle = data.new_handle()
        handle = data.new_handle()
 
-       void add_flexarray(Id solvid, Id keyname, Id handle);
+       void add_flexarray(Id solvid, Id keyname, Id handle)
        $data->add_flexarray($solvid, $keyname, $handle);
        data.add_flexarray(solvid, keyname, handle)
        data.add_flexarray(solvid, keyname, handle)
 
-       void unset(Id solvid, Id keyname);
+       void unset(Id solvid, Id keyname)
        $data->unset($solvid, $keyname);
        data.unset(solvid, keyname)
        data.unset(solvid, keyname)
@@ -3722,7 +3852,7 @@ Back pointer to repository object.
 
 Create a Dataiterator at the position of the datapos object.
 
-       const char *lookup_deltalocation(unsigned int *OUTPUT);
+       const char *lookup_deltalocation(unsigned int *OUTPUT)
        my ($location, $mediano) = $datapos->lookup_deltalocation();
        location, mediano = datapos.lookup_deltalocation()
        location, mediano = datapos.lookup_deltalocation()
@@ -3731,7 +3861,7 @@ Return a tuple containing the on-media location and an optional media number
 for a delta rpm. This obviously only works if the data position points to
 structure describing a delta rpm.
 
-       const char *lookup_deltaseq();
+       const char *lookup_deltaseq()
        my $seq = $datapos->lookup_deltaseq();
        seq = datapos.lookup_deltaseq();
        seq = datapos.lookup_deltaseq();
@@ -3785,6 +3915,263 @@ the Ids of the global pool even if the repodata area contains its own pool.
 Iterate over the matching data elements. See the Dataiterator class for more
 information.
 
+
+The Alternative Class
+---------------------
+An Alternative object describes a branch point in the solving process. The
+solver found more than one good way to fulfill a dependency and chose one.
+It recorded the other possibilities in the alternative object so that they
+can be presented to the user in the case a different solution is preferable.
+
+=== ATTRIBUTES ===
+
+       Solver *solv;                   /* read only */
+       $alternative->{solv}
+       alternative.solv
+       alternative.solv
+
+Back pointer to solver object.
+
+       Id type;                        /* read only */
+       $alternative->{type}
+       alternative.type
+       alternative.type
+
+The type of the alternative. Alternatives can be created because of rule
+fulfillment, because of recommended packages, and because of suggested
+packages (currently unused). See below for a list of valid types.
+
+       Rule rule;                      /* read only */
+       $alternative->{rule}
+       alternative.rule
+       alternative.rule
+
+The rule that caused the creation of the alternative (SOLVER_ALTERNATIVE_TYPE_RULE).
+
+       Dep *dep;                       /* read only */
+       $ruleinfo->{dep}
+       ruleinfo.dep
+       ruleinfo.dep
+
+The dependency that caused the creation of the alternative (SOLVER_ALTERNATIVE_TYPE_RECOMMENDS).
+
+       Dep *depsolvable;               /* read only */
+       $ruleinfo->{depsolvable}
+       ruleinfo.depsolvable
+       ruleinfo.depsolvable
+
+The package containing the dependency (SOLVER_ALTERNATIVE_TYPE_RECOMMENDS).
+
+       Solvable chosen;                /* read only */
+       $alternative->{chosen}
+       alternative.chosen
+       alternative.chosen
+
+The solvable that the solver chose from the alternative's package set.
+
+=== CONSTANTS ===
+
+*SOLVER_ALTERNATIVE_TYPE_RULE*::
+
+The alternative was created when fulfilling a rule.
+
+*SOLVER_ALTERNATIVE_TYPE_RECOMMENDS*::
+
+The alternative was created when fulfilling a recommends dependency.
+
+*SOLVER_ALTERNATIVE_TYPE_SUGGESTS*::
+
+The alternative was created when fulfilling a suggests dependency.
+
+=== METHODS ===
+
+       Solvable *choices()
+       my @choices = $alternative->choices();
+       choices = alternative.choices
+       choices = alternative.choices
+
+Return the set of solvables that the solver could choose from when
+creating the alternative.
+
+       <stringification>
+       my $str = $alternative->str;
+       str = str(alternative)
+       str = alternative.to_s
+
+Return a string describing the alternative.
+
+
+The Decision Class
+------------------
+A decision is created when the solver fulfills dependencies. It can be
+either to install a package to satisfy a dependency or to conflict a
+dependency because it conflicts with another package or its dependencies
+cannot be met. Most decisions are caused by rule processing, but there
+are some other types like orphaned package handling or weak dependency
+handling.
+
+=== ATTRIBUTES ===
+
+       Solver *solv;                   /* read only */
+       $decision->{solv}
+       decision.solv
+       decision.solv
+
+Back pointer to solver object.
+
+       Id p;                           /* read only */
+       $decision->{p}
+       decision.p
+       decision.p
+
+The decision package id, positive for installs and negative for conflicts.
+
+       int reason;                     /* read only */
+       $decision->{reason}
+       decision.reason
+       decision.reason
+
+The reason for the decision. See the SOLVER_REASON_ constants.
+
+       int infoid;                     /* read only */
+       $decision->{infoid}
+       decision.infoid
+       decision.infoid
+
+Extra info for the decision. This is the rule id for decisions caused
+by rule fulfillment.
+
+       Solvable solvable;              /* read only */
+       $decision->{solvable}
+       decision.solvable
+       decision.solvable
+
+The decision package object.
+
+       Rule rule()                     /* read only */
+       $decision->{rule}
+       decision.rule
+       decision.rule
+
+The rule object for decisions that where caused by rule fulfilment.
+
+=== METHODS ===
+
+       Ruleinfo info()
+       my $info = $decision->info();
+       info = decision.info()
+       info = decision.info()
+
+Return a Ruleinfo object describing the decision. Some reasons like
+SOLVER_REASON_WEAKDEP are not caused by rules, but can be expressed
+by a Ruleinfo object.
+
+       Ruleinfo *allinfos()
+       my @infos = $decision->allinfos();
+       infos = decision.allinfos()
+       infos = decision.allinfos()
+
+Same as info(), but all Ruleinfo objects describing the decision are
+returned.
+
+       const char *reasonstr()
+       my str = $decision->reasonstr()
+       str = decision.reasonstr()
+       str = decision.reasonstr()
+
+Return a string describing why a decision was done (but without
+the decision itself).
+
+       <stringification>
+       my $str = $decison->str;
+       str = str(decision)
+       str = decision.to_s
+
+Return a string describing the decision (but without the reason).
+
+
+The Decisionset Class
+---------------------
+A decisionset consists of multiple decisions of the same reason and type
+that can be presented to the user as a single action.
+
+=== ATTRIBUTES ===
+
+       Solver *solv;                   /* read only */
+       $decision->{solv}
+       decision.solv
+       decision.solv
+
+Back pointer to solver object.
+
+       Id p;                           /* read only */
+       $decision->{p}
+       decision.p
+       decision.p
+
+The package id of the first decision, positive for installs and negative for conflicts.
+
+       int reason;                     /* read only */
+       $decision->{reason}
+       decision.reason
+       decision.reason
+
+The reason for the decisions in the set. See the SOLVER_REASON_ constants.
+
+       int type;                       /* read only */
+       $ruleinfo->{type}
+       ruleinfo.type
+       ruleinfo.type
+
+The type of the decision info. See the constant section of the solver class for the
+rule type list and the special type list.
+
+       Dep *dep;                       /* read only */
+       $ruleinfo->{dep}
+       ruleinfo.dep
+       ruleinfo.dep
+
+The dependency that caused the decision
+
+       Dep *dep_id;                    /* read only */
+       $ruleinfo->{dep_id}
+       ruleinfo.dep_id
+       ruleinfo.dep_id
+
+The Id of the dependency that caused the decision.
+
+=== METHODS ===
+
+       Decision *decisions()
+       my @decisions = $decisionset->decisions();
+       decisions = decisionset.decisions()
+       decisions = decisionset.decisions()
+
+Return all the decisions of the set.
+
+       Solvable *solvables()
+       my @pkgs = $decisionset->solvables();
+       pkgs = decisionset.solvables()
+       pkgs = decisionset.solvables()
+
+Return all the packages that were decided in the set.
+
+       const char *reasonstr()
+       my str = $decision->reasonstr();
+       str = decision.reasonstr()
+       str = decision.reasonstr()
+
+Return a string describing why the decisions were done (but without
+the decisions themself).
+
+       <stringification>
+       my $str = $decison->str;
+       str = str(decision)
+       str = decision.to_s
+
+Return a string describing the decisions (but without the reason).
+
+
 Author
 ------
 Michael Schroeder <mls@suse.de>
index 75067f2..b69234e 100755 (executable)
@@ -43,6 +43,7 @@ from optparse import OptionParser
 
 #import gc
 #gc.set_debug(gc.DEBUG_LEAK)
+#rpm.setVerbosity(rpm.RPMLOG_DEBUG)
 
 class repo_generic(dict):
     def __init__(self, name, type, attribs = {}):
@@ -787,6 +788,11 @@ while True:
             sys.stdout.write("Please choose a solution: ")
             sys.stdout.flush()
             sol = sys.stdin.readline().strip()
+            if sol == 'p':
+                print('')
+                for decisionset in problem.get_decisionsetlist():
+                    print("%s: %s" % (decisionset, decisionset.reasonstr()))
+                print('')
         if sol == 's':
             continue        # skip problem
         if sol == 'q':
@@ -802,7 +808,6 @@ while True:
                     
 # no problems, show transaction
 trans = solver.transaction()
-del solver
 if trans.isempty():
     print("Nothing to do.")
     sys.exit(0)
@@ -838,6 +843,20 @@ for cl in trans.classify(solv.Transaction.SOLVER_TRANSACTION_SHOW_OBSOLETES | so
 print("install size change: %d K" % trans.calc_installsizechange())
 print('')
 
+alternatives = solver.alternatives()
+if alternatives:
+    print('Alternatives:')
+    for a in alternatives:
+        print('')
+        print(a)
+        aidx = 1
+        for ac in a.choices():
+            print("%6d: %s" % (aidx, ac))
+            aidx = aidx + 1
+    print('')
+
+del solver
+
 while True:
     sys.stdout.write("OK to continue (y/n)? ")
     sys.stdout.flush()
@@ -944,7 +963,9 @@ if checkproblems:
 ts.order()
 def runCallback(reason, amount, total, p, d):
     if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:
-        return newpkgsfp[p.id].fileno()
+        f = newpkgsfp[p.id]
+        os.lseek(f.fileno(), 0, os.SEEK_SET)
+        return f.fileno()
     if reason == rpm.RPMCALLBACK_INST_START:
         print("install %s" % p)
     if reason == rpm.RPMCALLBACK_UNINST_START:
index 0f3bd47..72deb4a 100644 (file)
@@ -18,6 +18,7 @@ repoinfo_system_rpm.c
 repoinfo_type_debian.c
 repoinfo_type_mdk.c
 repoinfo_type_rpmmd.c
+repoinfo_type_plaindir.c
 repoinfo_type_susetags.c
 )
 
index 6273972..f7c3346 100644 (file)
@@ -47,6 +47,7 @@
 #ifdef ENABLE_MDKREPO
 #include "repoinfo_type_mdk.h"
 #endif
+#include "repoinfo_type_plaindir.h"
 
 static int
 repoinfos_sort_cmp(const void *ap, const void *bp)
@@ -252,6 +253,11 @@ read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
 
       switch (cinfo->type)
        {
+#if defined(ENABLE_RPMDB) || defined(ENABLE_RPMPKG)
+        case TYPE_PLAINDIR:
+         plaindir_load(cinfo, &sigpool);
+         break;
+#endif
 #ifdef ENABLE_RPMMD
         case TYPE_RPMMD:
          repomd_load(cinfo, &sigpool);
index efccf1e..9a36f9b 100644 (file)
 # define REPOINFO_PATH "/etc/zypp/repos.d"
 #endif
 
+static char *
+find_releaseevr(Pool *pool)
+{
+#ifdef SUSE
+  extern char *suse_find_baseproduct_evr(Pool *);
+#endif
+  void *rpmstate;
+  char *releaseevr = 0;
+  Queue q;
+
+#ifdef SUSE
+  if ((releaseevr = suse_find_baseproduct_evr(pool)) != 0)
+    return releaseevr;
+#endif
+  queue_init(&q);
+  rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+  rpm_installedrpmdbids(rpmstate, "Providename", "system-release", &q);
+  if (q.count)
+    {
+      void *handle;
+      char *p;
+      handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
+      releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
+      if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
+       *p = 0;
+    }
+  rpm_state_free(rpmstate);
+  queue_free(&q);
+  return releaseevr;
+}
+
 char *
 yum_substitute(Pool *pool, char *line)
 {
@@ -43,28 +74,15 @@ yum_substitute(Pool *pool, char *line)
        {
          if (!releaseevr)
            {
-             void *rpmstate;
-             Queue q;
-       
-             queue_init(&q);
-             rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
-             rpm_installedrpmdbids(rpmstate, "Providename", "system-release", &q);
-             if (q.count)
-               {
-                 void *handle;
-                 char *p;
-                 handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
-                 releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
-                 if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
-                   *p = 0;
-               }
-             rpm_state_free(rpmstate);
-             queue_free(&q);
+             releaseevr = find_releaseevr(pool);
              if (!releaseevr)
                {
-                 fprintf(stderr, "no installed package provides 'system-release', cannot determine $releasever\n");
+                 fprintf(stderr, "Cannot determine $releasever\n");
                  exit(1);
                }
+             /* we only need the version */
+             if ((p = strchr(releaseevr, '-')) != 0)
+               *p = 0;
            }
          *p2 = 0;
          p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
index f5ba8b9..2a26629 100644 (file)
@@ -194,6 +194,10 @@ downloadpackage(Solvable *s, const char *loc)
       const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
       loc = pool_tmpjoin(s->repo->pool, datadir ? datadir : "suse", "/", loc);
     }
+  else if (cinfo->type == TYPE_PLAINDIR)
+    {
+     return fopen(loc, "r");
+    }
 #endif
   chksumtype = 0;
   chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
index b556afc..b5cfd8a 100644 (file)
@@ -161,4 +161,29 @@ commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp)
     }
 }
 
+#ifdef SUSE
+char *
+suse_find_baseproduct_evr(Pool *pool)
+{
+  char *evrstr = 0;
+  Pool *productspool = pool_create();
+  Repo *productsrepo = repo_create(productspool, "products");
+  Id p;
+  Solvable *s;
+
+  pool_set_rootdir(productspool, pool_get_rootdir(pool));
+  repo_add_products(productsrepo, PRODUCTS_PATH, REPO_USE_ROOTDIR);
+  FOR_REPO_SOLVABLES(productsrepo, p, s)
+    {
+      const char *type = solvable_lookup_str(s, PRODUCT_TYPE);
+      if (!type || strcmp(type, "base") != 0)
+       continue;
+      evrstr = solv_strdup(pool_id2str(productspool, s->evr));
+      break;
+    }
+  pool_free(productspool);
+  return evrstr;
+}
+#endif
+
 #endif
diff --git a/examples/solv/repoinfo_type_plaindir.c b/examples/solv/repoinfo_type_plaindir.c
new file mode 100644 (file)
index 0000000..9c53e27
--- /dev/null
@@ -0,0 +1,79 @@
+#if defined(ENABLE_RPMDB) || defined(ENABLE_RPMPKG)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_rpmmd.h"
+#include "ext/repo_rpmdb.h"
+
+static inline int endswith(const char* str, const char* suf)
+{
+  if (strlen(str) < strlen(suf))
+    return 0;
+  return strcmp(str + strlen(str) - strlen(suf), suf) == 0;
+}
+
+int
+plaindir_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+  Repo *repo = cinfo->repo;
+  Repodata *data;
+  DIR *dp;
+  struct dirent *de;
+  struct stat stb;
+
+  printf("plaindir repo '%s':", cinfo->alias);
+  fflush(stdout);
+  if (stat(cinfo->path, &stb))
+    {
+      perror(cinfo->path);
+      return -1;
+    }
+  calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, NULL, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 1))
+    {
+      printf(" cached\n");
+      return 1;
+    }
+  printf(" reading\n");
+  if ((dp = opendir(cinfo->path)) == 0)
+    {
+      perror(cinfo->path);
+      return -1;
+    }
+  while ((de = readdir(dp)) != 0)
+    {
+      if (de->d_name[0] == 0 || de->d_name[0] == '.')
+        continue;
+      if (!endswith(de->d_name, ".rpm") || endswith(de->d_name, ".delta.rpm") || endswith(de->d_name, ".patch.rpm"))
+        continue;
+      char* fn = solv_dupjoin(cinfo->path, "/", de->d_name);
+      repo_add_rpm(repo, fn, 0);
+      solv_free(fn);
+    }
+  closedir(dp);
+
+#ifdef SUSE
+  repo_add_autopattern(repo, 0);
+#endif
+  data = repo_add_repodata(repo, 0);
+  repodata_internalize(data);
+  writecachedrepo(cinfo, 0, 0);
+  repodata_create_stubs(repo_last_repodata(repo));
+  return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_plaindir.h b/examples/solv/repoinfo_type_plaindir.h
new file mode 100644 (file)
index 0000000..15fa7c7
--- /dev/null
@@ -0,0 +1 @@
+extern int plaindir_load(struct repoinfo *cinfo, Pool **sigpoolp);
index 3deb1a0..2239530 100644 (file)
@@ -130,10 +130,77 @@ showdiskusagechanges(Transaction *trans)
   duc[3].path = "/etc";
   transaction_calc_duchanges(trans, duc, 4);
   for (i = 0; i < 4; i++)
-    printf("duchanges %s: %lld K  %lld inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
+    if (duc[i].kbytes || duc[i].files)
+      printf("duchanges %s: %lld K  %lld inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
 }
 #endif
 
+static void
+doshowproof(Solver *solv, Id id, int flags, Queue *lq)
+{
+  Pool *pool = solv->pool;
+  Queue q, qp;
+  int i, j;
+
+  queue_init(&q);
+  queue_init(&qp);
+  solver_get_decisionlist(solv, id, flags | SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO | SOLVER_DECISIONLIST_MERGEDINFO, &q);
+  for (i = 0; i < q.count; i += 8)
+    {
+      Id v = q.elements[i];
+      int reason = q.elements[i + 1], bits = q.elements[i + 3], type = q.elements[i + 4];
+      Id from = q.elements[i + 5], to = q.elements[i + 6], dep = q.elements[i + 7];
+      if (reason != SOLVER_REASON_UNSOLVABLE && type == SOLVER_RULE_PKG_SAME_NAME)
+        continue;       /* do not show "obvious" decisions */
+      solver_decisionlist_solvables(solv, &q, i, &qp);
+      if (qp.count)
+        i += qp.count * 8 - 8;
+      if (reason == SOLVER_REASON_UNSOLVABLE)
+        printf("unsolvable: ");
+      else
+        printf("%s %s: ", v < 0 ? "conflicted" : "installed", pool_solvidset2str(pool, &qp));
+      if (type == 0)
+        {
+          printf("%s\n", solver_reason2str(solv, reason));
+          continue;
+        }
+      if (type == SOLVER_RULE_LEARNT && lq)
+        {
+          for (j = 0; j < lq->count; j++)
+            if (lq->elements[j] == q.elements[i + 2])
+              break;
+          if (j < lq->count)
+            {
+              printf("learnt rule #%d\n", j + 1);
+              continue;
+            }
+        }
+      printf("%s\n", solver_decisioninfo2str(solv, bits, type, from, to, dep));
+    }
+  queue_free(&qp);
+  queue_free(&q);
+  printf("\n");
+}
+
+static void
+showproof(Solver *solv, int problem)
+{
+  Queue lq;
+  int i;
+
+  printf("\n");
+  queue_init(&lq);
+  solver_get_learnt(solv, problem, SOLVER_DECISIONLIST_PROBLEM, &lq);
+  for (i = 0; i < lq.count; i++)
+    {
+      printf("Learnt rule #%d:\n", i + 1);
+      doshowproof(solv, lq.elements[i], SOLVER_DECISIONLIST_LEARNTRULE, &lq);
+    }
+  printf("Proof:\n");
+  doshowproof(solv, problem, SOLVER_DECISIONLIST_PROBLEM, &lq);
+  queue_free(&lq);
+}
+
 static Id
 find_repo(const char *name, Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
 {
@@ -226,7 +293,7 @@ main(int argc, char **argv)
   char *rootdir = 0;
   char *keyname = 0;
   int keyname_depstr = 0;
-  int keyname_alldeps = 0;             /* dnf repoquesy --alldeps */
+  int keyname_alldeps = 0;             /* dnf repoquery --alldeps */
   int debuglevel = 0;
   int answer, acnt = 0;
   char *testcase = 0;
@@ -318,7 +385,7 @@ main(int argc, char **argv)
        }
       else if (argc > 1 && !strcmp(argv[1], "--alldeps"))
        {
-         keyname_alldeps = 1;          /* dnf repoquesy --alldeps */
+         keyname_alldeps = 1;          /* dnf repoquery --alldeps */
          argc--;
          argv++;
        }
@@ -721,14 +788,17 @@ rerunsolver:
       for (problem = 1; problem <= pcnt; problem++)
        {
          int take = 0;
-         printf("Problem %d/%d:\n", problem, pcnt);
-         solver_printprobleminfo(solv, problem);
-         printf("\n");
+         printf("Problem %d/%d:\n%s\n\n", problem, pcnt, solver_problem2str(solv, problem));
          scnt = solver_solution_count(solv, problem);
          for (solution = 1; solution <= scnt; solution++)
            {
+             Queue sq;
              printf("Solution %d:\n", solution);
-             solver_printsolution(solv, problem, solution);
+             queue_init(&sq);
+             solver_all_solutionelements(solv, problem, solution, 1, &sq);
+             for (i = 0; i < sq.count; i += 3)
+               printf("  - %s\n", solver_solutionelementtype2str(solv, sq.elements[i], sq.elements[i + 1], sq.elements[i + 2]));
+             queue_free(&sq);
              printf("\n");
            }
          for (;;)
@@ -755,6 +825,8 @@ rerunsolver:
                  take = 0;
                  break;
                }
+             if (*ip == 'p')
+               showproof(solv, problem);
              if (*ip == 'q')
                {
                  printf("Abort.\n");
index f01c023..589fa94 100644 (file)
@@ -103,6 +103,7 @@ IF (ENABLE_CUDFREPO)
 ENDIF (ENABLE_CUDFREPO)
 
 IF (ENABLE_HAIKU)
+    enable_language(CXX)
     SET (libsolvext_SRCS ${libsolvext_SRCS}
        repo_haiku.cpp)
     SET (libsolvext_HEADERS ${libsolvext_HEADERS}
index c35dc62..356d3b1 100644 (file)
 #include "solv_jsonparser.h"
 #include "conda.h"
 #include "repo_conda.h"
+#include "solv_xfopen.h"
+
+struct sigdata {
+  char *sigs;
+};
+
+struct xdata {
+  char *fn;
+  char *pkgjson;
+  int delayedlocation;
+};
 
 struct parsedata {
   Pool *pool;
   Repo *repo;
   Repodata *data;
+  int flags;
+
+  char *subdir;
+  char *error;
 
   Stringpool fnpool;
   Queue fndata;
+
+  Stringpool sigpool;
+  struct sigdata *sigdata;
+  int nsigdata;
+
+  struct xdata *xdata;
+  int nxdata;
 };
 
 static int
@@ -135,8 +157,103 @@ fn2data(struct parsedata *pd, const char *fn, Id *fntypep, int create)
   return pd->fndata.elements + 2 * fnid;
 }
 
+struct sigdata *
+fn2sigdata(struct parsedata *pd, const char *fn, int create)
+{
+  Id id = stringpool_str2id(&pd->sigpool, fn, create);
+  if (!id && !create)
+    return 0;
+  if (id >= pd->nsigdata)
+    {
+      int n = id - pd->nsigdata + 1;
+      pd->sigdata = solv_realloc2(pd->sigdata, pd->nsigdata + n, sizeof(struct sigdata));
+      memset(pd->sigdata + pd->nsigdata, 0, n * sizeof(struct sigdata));
+      pd->nsigdata += n;
+    }
+  return pd->sigdata + id;
+}
+
+void
+freesigdata(struct parsedata *pd)
+{
+  int i;
+  for (i = 0; i < pd->nsigdata; i++)
+    solv_free(pd->sigdata[i].sigs);
+  pd->sigdata = solv_free(pd->sigdata);
+  pd->nsigdata = 0;
+}
+
+static void
+set_xdata(struct parsedata *pd, int handle, char *fn, char *pkgjson, int delayedlocation)
+{
+  struct xdata *xd;
+  handle -= pd->repo->start;
+  if (handle >= pd->nxdata)
+    {
+      int n;
+      if (!fn && !pkgjson && !delayedlocation)
+       return;
+      n = handle - pd->nxdata + 16;
+      pd->xdata = solv_realloc2(pd->xdata, pd->nxdata + n, sizeof(struct xdata));
+      memset(pd->xdata + pd->nxdata, 0, n * sizeof(struct xdata));
+      pd->nxdata += n;
+    }
+  xd = pd->xdata + handle;
+  if (xd->fn)
+    solv_free(xd->fn);
+  if (xd->pkgjson)
+    solv_free(xd->pkgjson);
+  xd->fn = fn;
+  xd->pkgjson = pkgjson;
+  xd->delayedlocation = delayedlocation;
+}
+
+static void
+move_xdata(struct parsedata *pd, int fromhandle, int tohandle)
+{
+  char *fn = 0, *pkgjson = 0;
+  int delayedlocation = 0;
+  fromhandle -= pd->repo->start;
+  if (fromhandle < pd->nxdata)
+    {
+      struct xdata *xd = pd->xdata + fromhandle;
+      fn = xd->fn;
+      pkgjson = xd->pkgjson;
+      delayedlocation = xd->delayedlocation;
+      xd->fn = 0;
+      xd->pkgjson = 0;
+      xd->delayedlocation = 0;
+    }
+  set_xdata(pd, tohandle, fn, pkgjson, delayedlocation);
+}
+
+static int parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson);
+
+static int
+parse_package_with_pkgjson(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
+{
+  FILE *fp;
+  int type;
+  char *pkgjson = NULL;
+  int line = jp->line;
+
+  type = jsonparser_collect(jp, JP_OBJECT, &pkgjson);
+  if (type == JP_OBJECT_END && (fp = solv_fmemopen(pkgjson, strlen(pkgjson), "r")) != 0)
+    {
+      struct solv_jsonparser jp2;
+      jsonparser_init(&jp2, fp);
+      jp2.line = line;
+      type = jsonparser_parse(&jp2);
+      type = type == JP_OBJECT ? parse_package(pd, &jp2, kfn, pkgjson) : JP_ERROR;
+      jsonparser_free(&jp2);
+      fclose(fp);
+    }
+  solv_free(pkgjson);
+  return type;
+}
+
 static int
-parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
+parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn, char *pkgjson)
 {
   int type = JP_OBJECT;
   Pool *pool= pd->pool;
@@ -147,6 +264,9 @@ parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
   char *subdir = 0;
   Id *fndata = 0, fntype = 0;
 
+  if (!pkgjson && (pd->flags & CONDA_ADD_WITH_SIGNATUREDATA) != 0)
+    return parse_package_with_pkgjson(pd, jp, kfn);
+
   handle = repo_add_solvable(pd->repo);
   s = pool_id2solvable(pool, handle);
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
@@ -191,9 +311,28 @@ parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
       else
        type = jsonparser_skip(jp, type);
     }
+  /* if we have a global subdir make sure that it matches */
+  if (subdir && pd->subdir && strcmp(subdir, pd->subdir) != 0)
+    {
+      /* we used to return an error here, but classic conda
+       * just overwrites the package subdir with the global
+       * subdir */
+#if 0
+      pd->error = "subdir mismatch";
+      return JP_ERROR;
+#else
+      solv_free(subdir);
+      subdir = solv_strdup(pd->subdir);
+#endif
+    }
+
   if (fn || kfn)
     {
-      repodata_set_location(data, handle, 0, subdir, fn ? fn : kfn);
+      int delayedlocation = (subdir || pd->subdir) ? 0 : 1;
+      if (pkgjson || delayedlocation)
+       set_xdata(pd, handle, solv_strdup(fn ? fn : kfn), pkgjson ? solv_strdup(pkgjson) : 0, delayedlocation);
+      if (!delayedlocation)
+        repodata_set_location(data, handle, 0, subdir ? subdir : pd->subdir, fn ? fn : kfn);
       fndata = fn2data(pd, fn ? fn : kfn, &fntype, 1);
     }
   solv_free(fn);
@@ -210,12 +349,14 @@ parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
        {
          /* ignore this package */
          repo_free_solvable(pd->repo, handle, 1);
+         set_xdata(pd, handle, 0, 0, 0);
          return type;
        }
       if (fndata[0] && fndata[0] < fntype)
        {
          /* replace old package */
          swap_solvables(pool, data, handle, fndata[1]);
+         move_xdata(pd, handle, fndata[1]);
          repo_free_solvable(pd->repo, handle, 1);
          handle = fndata[1];
        }
@@ -234,7 +375,7 @@ parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
       if (type == JP_OBJECT)
        {
          char *fn = solv_strdup(jp->key);
-         type = parse_package(pd, jp, fn);
+         type = parse_package(pd, jp, fn, 0);
          solv_free(fn);
        }
       else
@@ -250,7 +391,7 @@ parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
     {
       if (type == JP_OBJECT)
-       type = parse_package(pd, jp, 0);
+       type = parse_package(pd, jp, 0, 0);
       else
        type = jsonparser_skip(jp, type);
     }
@@ -258,19 +399,62 @@ parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
 }
 
 static int
-parse_main(struct parsedata *pd, struct solv_jsonparser *jp, int flags)
+parse_info(struct parsedata *pd, struct solv_jsonparser *jp)
+{
+  int type = JP_OBJECT;
+  while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
+    {
+      if (type == JP_STRING && !strcmp(jp->key, "subdir"))
+       {
+         if (!pd->subdir)
+           pd->subdir = strdup(jp->value);
+         else if (strcmp(pd->subdir, jp->value))
+           {
+             pd->error = "subdir mismatch";
+             return JP_ERROR;
+           }
+       }
+    }
+  return type;
+}
+
+static int
+parse_signatures(struct parsedata *pd, struct solv_jsonparser *jp)
+{
+  int type = JP_OBJECT;
+  while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
+    {
+      struct sigdata *sd;
+      if (type != JP_OBJECT)
+       {
+         type = jsonparser_skip(jp, type);
+         continue;
+       }
+      sd = fn2sigdata(pd, jp->key, 1);
+      sd->sigs = solv_free(sd->sigs);
+      type = jsonparser_collect(jp, type, &sd->sigs);
+    }
+  return type;
+}
+
+static int
+parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
 {
   int type = JP_OBJECT;
   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
     {
+      if (type == JP_OBJECT && !strcmp("info", jp->key))
+       type = parse_info(pd, jp);
       if (type == JP_OBJECT && !strcmp("packages", jp->key))
        type = parse_packages(pd, jp);
       else if (type == JP_ARRAY && !strcmp("packages", jp->key))
        type = parse_packages2(pd, jp);
-      else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key) && !(flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
+      else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key) && !(pd->flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
        type = parse_packages(pd, jp);
-      else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key) && !(flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
+      else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key) && !(pd->flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
        type = parse_packages2(pd, jp);
+      if (type == JP_OBJECT && !strcmp("signatures", jp->key))
+       type = parse_signatures(pd, jp);
       else
        type = jsonparser_skip(jp, type);
     }
@@ -292,18 +476,57 @@ repo_add_conda(Repo *repo, FILE *fp, int flags)
   pd.pool = pool;
   pd.repo = repo;
   pd.data = data;
+  pd.flags = flags;
   stringpool_init_empty(&pd.fnpool);
+  stringpool_init_empty(&pd.sigpool);
   queue_init(&pd.fndata);
 
   jsonparser_init(&jp, fp);
   if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
     ret = pool_error(pool, -1, "repository does not start with an object");
-  else if ((type = parse_main(&pd, &jp, flags)) != JP_OBJECT_END)
-    ret = pool_error(pool, -1, "parse error line %d", jp.line);
+  else if ((type = parse_main(&pd, &jp)) != JP_OBJECT_END)
+    {
+      if (pd.error)
+        ret = pool_error(pool, -1, "parse error line %d: %s", jp.line, pd.error);
+      else
+        ret = pool_error(pool, -1, "parse error line %d", jp.line);
+    }
   jsonparser_free(&jp);
 
+  /* finalize parsed packages */
+  if (pd.xdata)
+    {
+      int i;
+      struct xdata *xd = pd.xdata;
+      for (i = 0; i < pd.nxdata; i++, xd++)
+       {
+         if (!xd->fn)
+           continue;
+         if (xd->delayedlocation)
+           repodata_set_location(data, repo->start + i, 0, pd.subdir, xd->fn);
+         if (xd->pkgjson && pd.nsigdata)
+           {
+             struct sigdata *sd = fn2sigdata(&pd, xd->fn, 0);
+             if (sd && sd->sigs)
+               {
+                 char *s = pool_tmpjoin(pool, "{\"info\":", xd->pkgjson, ",\"signatures\":");
+                 s = pool_tmpappend(pool, s, sd->sigs, "}");
+                 repodata_set_str(data, repo->start + i, SOLVABLE_SIGNATUREDATA, s);
+               }
+           }
+         solv_free(xd->fn);
+         solv_free(xd->pkgjson);
+       }
+      solv_free(pd.xdata);
+    }
+
+  if (pd.sigdata)
+    freesigdata(&pd);
+  stringpool_free(&pd.sigpool);
+
   queue_free(&pd.fndata);
   stringpool_free(&pd.fnpool);
+  solv_free(pd.subdir);
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
 
index fa626f1..39911eb 100644 (file)
@@ -5,6 +5,24 @@
  * for further information
  */
 
-#define CONDA_ADD_USE_ONLY_TAR_BZ2  (1 << 8)
+#ifndef LIBSOLV_REPO_CONDA_H
+#define LIBSOLV_REPO_CONDA_H
+
+#include <stdio.h>
+
+#include "repo.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONDA_ADD_USE_ONLY_TAR_BZ2     (1 << 8)
+#define CONDA_ADD_WITH_SIGNATUREDATA   (1 << 9)
 
 extern int repo_add_conda(Repo *repo, FILE *fp, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSOLV_REPO_CONDA_H */
index a965a65..fdcb9ea 100644 (file)
@@ -458,11 +458,11 @@ pgpsig_init(struct pgpsig *sig, unsigned char *p, int l)
                  sig->haveissuer = 1;
                  memcpy(sig->issuer, q + 1, 8);
                }
-             if (x == 2 && j == 0)
+             if (x == 2 && sl == 5 && j == 0)
                sig->created = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
-             if (x == 3 && j == 0)
+             if (x == 3 && sl == 5 && j == 0)
                sig->expires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
-             if (x == 9 && j == 0)
+             if (x == 9 && sl == 5 && j == 0)
                sig->keyexpires = q[1] << 24 | q[2] << 16 | q[3] << 8 | q[4];
              q += sl;
              ql -= sl;
index d78893e..17334e6 100644 (file)
@@ -678,29 +678,32 @@ makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf,
       if ((f[i] & (DEP_LESS|DEP_EQUAL|DEP_GREATER)) == 0 && n[i][0] == '(')
        {
          id = pool_parserpmrichdep(pool, n[i]);
-         if (id)
-           *ida++ = id;
-         else
-           cc--;
-         continue;
+         if (!id)
+           {
+             cc--;
+             continue;
+           }
        }
+      else
 #endif
-      id = pool_str2id(pool, n[i], 1);
-      if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
        {
-         Id evr;
-         int fl = 0;
-         if ((f[i] & DEP_LESS) != 0)
-           fl |= REL_LT;
-         if ((f[i] & DEP_EQUAL) != 0)
-           fl |= REL_EQ;
-         if ((f[i] & DEP_GREATER) != 0)
-           fl |= REL_GT;
-         if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
-           evr = pool_str2id(pool, v[i] + 2, 1);
-         else
-           evr = pool_str2id(pool, v[i], 1);
-         id = pool_rel2id(pool, id, evr, fl, 1);
+         id = pool_str2id(pool, n[i], 1);
+         if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
+           {
+             Id evr;
+             int fl = 0;
+             if ((f[i] & DEP_LESS) != 0)
+               fl |= REL_LT;
+             if ((f[i] & DEP_EQUAL) != 0)
+               fl |= REL_EQ;
+             if ((f[i] & DEP_GREATER) != 0)
+               fl |= REL_GT;
+             if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
+               evr = pool_str2id(pool, v[i] + 2, 1);
+             else
+               evr = pool_str2id(pool, v[i], 1);
+             id = pool_rel2id(pool, id, evr, fl, 1);
+           }
        }
       *ida++ = id;
       if (haspre == 2 && ignq)
@@ -2523,7 +2526,11 @@ rpm_byrpmh(void *rpmstate, Header h)
   if (!h)
     return 0;
 #ifndef RPM5
+# ifdef RPM_MASK_TYPE
+  uh = headerExport(h, NULL);
+# else
   uh = headerUnload(h);
+# endif
 #else
   uh = headerUnload(h, NULL);
 #endif
index 38676ef..cbdd42a 100644 (file)
@@ -388,7 +388,13 @@ writedeps(Repo *repo, FILE *fp, const char *tag, Id key, Solvable *s, Offset off
          fprintf(fp, "+%s\n", tag);
          tagwritten = 1;
        }
-      fprintf(fp, "%s\n", idstr);
+      if (*idstr == '/' && !ISRELDEP(id)) {
+        fprintf(fp, "%s\n", pool_id2str(pool, id));
+      } else if (*idstr == '/') {
+        fprintf(fp, "\\2f%s\n", idstr + 1);
+      } else {
+        fprintf(fp, "%s\n", idstr);
+      }
     }
   if (tagwritten)
     fprintf(fp, "-%s\n", tag);
@@ -616,7 +622,7 @@ testcase_add_testtags(Repo *repo, FILE *fp, int flags)
          /* join back version and release */
          if (sp[2] && !(sp[2][0] == '-' && !sp[2][1]))
            sp[2][-1] = '-';
-         s->evr = makeevr(pool, sp[1]);
+         s->evr = pool_str2id(pool, sp[1], 1);
          s->arch = strcmp(sp[3], "-") ? pool_str2id(pool, sp[3], 1) : 0;
          continue;
        default:
index 22f7093..0725f03 100644 (file)
@@ -113,7 +113,7 @@ struct parsedata {
   Id pkghandle;
   struct solv_xmlparser xmlp;
   struct joindata jd;
-  Id collhandle;
+  Queue collectionq;
 };
 
 /*
@@ -289,9 +289,7 @@ startElement(struct solv_xmlparser *xmlp, int state, const char *name, const cha
       break;
 
     case STATE_COLLECTION:
-      {
-        pd->collhandle = repodata_new_handle(pd->data);
-      }
+      queue_empty(&pd->collectionq);
       break;
 
       /*   <package arch="ppc64" name="imlib-debuginfo" release="6.fc8"
@@ -371,7 +369,7 @@ startElement(struct solv_xmlparser *xmlp, int state, const char *name, const cha
         if (arch)
           repodata_set_poolstr(pd->data, module_handle, UPDATE_MODULE_ARCH, arch);
         repodata_add_flexarray(pd->data, pd->handle, UPDATE_MODULE, module_handle);
-        repodata_add_flexarray(pd->data, pd->collhandle, UPDATE_MODULE, module_handle);
+       queue_push2(&pd->collectionq, UPDATE_MODULE, module_handle);
         break;
       }
 
@@ -436,13 +434,19 @@ endElement(struct solv_xmlparser *xmlp, int state, char *content)
       break;
 
     case STATE_COLLECTION:
-      repodata_add_flexarray(pd->data, pd->handle, UPDATE_COLLECTIONLIST, pd->collhandle);
-      pd->collhandle = 0;
+      {
+       Id collhandle = repodata_new_handle(pd->data);
+       int i;
+       for (i = 0; i < pd->collectionq.count; i += 2)
+         repodata_add_flexarray(pd->data, collhandle, pd->collectionq.elements[i], pd->collectionq.elements[i + 1]);
+       repodata_add_flexarray(pd->data, pd->handle, UPDATE_COLLECTIONLIST, collhandle);
+       queue_empty(&pd->collectionq);
+      }
       break;
 
     case STATE_PACKAGE:
       repodata_add_flexarray(pd->data, pd->handle, UPDATE_COLLECTION, pd->pkghandle);
-      repodata_add_flexarray(pd->data, pd->collhandle, UPDATE_COLLECTION, pd->pkghandle);
+      queue_push2(&pd->collectionq, UPDATE_COLLECTION, pd->pkghandle);
       pd->pkghandle = 0;
       break;
 
@@ -499,11 +503,13 @@ repo_add_updateinfoxml(Repo *repo, FILE *fp, int flags)
   pd.pool = pool;
   pd.repo = repo;
   pd.data = data;
+  queue_init(&pd.collectionq);
   solv_xmlparser_init(&pd.xmlp, stateswitches, &pd, startElement, endElement);
   if (solv_xmlparser_parse(&pd.xmlp, fp) != SOLV_XMLPARSER_OK)
     pd.ret = pool_error(pool, -1, "repo_updateinfoxml: %s at line %u:%u", pd.xmlp.errstr, pd.xmlp.line, pd.xmlp.column);
   solv_xmlparser_free(&pd.xmlp);
   join_freemem(&pd.jd);
+  queue_free(&pd.collectionq);
 
   if (!(flags & REPO_NO_INTERNALIZE))
     repodata_internalize(data);
index ea31923..b3f25d8 100644 (file)
@@ -136,6 +136,8 @@ parseutf8(struct solv_jsonparser *jp, int surrogate)
        return -1;
       r = (r << 4) | c;
     }
+  if (r == 0)
+    return -1;         /* no embedded NULs for now */
   if (!surrogate && r >= 0xd800 && r < 0xdc00)
     {
       /* utf16 surrogate pair encodes 0x10000 - 0x10ffff */
@@ -197,11 +199,47 @@ parsestring(struct solv_jsonparser *jp)
 }
 
 static int
+parsestring_raw(struct solv_jsonparser *jp)
+{
+  int c;
+  savec(jp, '\"');
+  for (;;)
+    {
+      if ((c = nextc(jp)) < 32)
+       return JP_ERROR;
+      if (c == '"')
+       break;
+      if (c == '\\')
+       {
+         c = nextc(jp);
+         if (!c || !strchr("\"\\/\nbfnrtu", c))
+           return JP_ERROR;
+         savec(jp, '\\');
+         if (c == 'u')
+           {
+             int i;
+             for (i = 0; i < 4; i++)
+               {
+                 savec(jp, c);
+                 c = nextc(jp);
+                 if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
+                   return JP_ERROR;
+               }
+           }
+       }
+      savec(jp, c);
+    }
+  savec(jp, '\"');
+  savec(jp, 0);
+  return JP_STRING;
+}
+
+static int
 parsevalue(struct solv_jsonparser *jp)
 {
   int c = skipspace(jp);
   if (c == '"')
-    return parsestring(jp);
+    return jp->flags & JP_FLAG_RAWSTRINGS ? parsestring_raw(jp) : parsestring(jp);
   if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
     return parsenumber(jp, c);
   if ((c >= 'a' && c <= 'z'))
@@ -287,3 +325,54 @@ jsonparser_skip(struct solv_jsonparser *jp, int type)
   return type;
 }
 
+int
+jsonparser_collect(struct solv_jsonparser *jp, int type, char **jsonp)
+{
+  char *buf = 0;
+  size_t nbuf = 0;
+  int depth = jp->depth + 1, endtype = type + 1;
+  int oldflags = jp->flags;
+
+  if (type == JP_NUMBER || type == JP_BOOL || type == JP_NULL)
+    {
+      *jsonp = solv_strdup(jp->value);
+      return type;
+    }
+  if (type != JP_ARRAY && type != JP_OBJECT)
+    {
+      *jsonp = 0;
+      return JP_ERROR;
+    }
+  buf = solv_extend(buf, nbuf, 1, 1, 255);
+  buf[nbuf++] = type == JP_OBJECT ? '{' : '[';
+  jp->flags |= JP_FLAG_RAWSTRINGS;
+  while (type > 0 && (type != endtype || jp->depth != depth))
+    {
+      type = jsonparser_parse(jp);
+      if (type <= 0)
+        break;
+      buf = solv_extend(buf, nbuf, jp->keylen + jp->valuelen + 2, 1, 255);
+      if (type == JP_OBJECT_END || type == JP_ARRAY_END)
+        {
+          if (buf[nbuf - 1] == ',')
+            nbuf--;
+          buf[nbuf++] = type == JP_OBJECT_END ? '}' : ']';
+        }
+      else if (jp->key)
+        {
+          memcpy(buf + nbuf, jp->key, jp->keylen);
+          nbuf += jp->keylen;
+          buf[nbuf++] = ':';
+        }
+      if (jp->valuelen)
+        memcpy(buf + nbuf, jp->value, jp->valuelen);
+      nbuf += jp->valuelen;
+      buf[nbuf++] = type == JP_OBJECT ? '{' : type == JP_ARRAY ? '[' : ',';
+    }
+  jp->flags = oldflags;
+  buf[nbuf - 1] = 0;   /* overwrites trailing ',' */
+  if (type != endtype)
+    buf = solv_free(buf);
+  *jsonp = buf;
+  return type;
+}
index f728fb6..0fd8823 100644 (file)
@@ -12,6 +12,7 @@
 
 struct solv_jsonparser {
   FILE *fp;
+  int flags;
   int line;
   int depth;
 
@@ -29,6 +30,8 @@ struct solv_jsonparser {
   size_t aspace;
 };
 
+#define JP_FLAG_RAWSTRINGS     1
+
 #define JP_ERROR       -1
 #define JP_END         0
 #define JP_START       1
@@ -45,5 +48,6 @@ void jsonparser_init(struct solv_jsonparser *jp, FILE *fp);
 void jsonparser_free(struct solv_jsonparser *jp);
 int jsonparser_parse(struct solv_jsonparser *jp);
 int jsonparser_skip(struct solv_jsonparser *jp, int type);
+int jsonparser_collect(struct solv_jsonparser *jp, int type, char **jsonp);
 
 #endif /* SOLV_JSONPARSER_H */
index 035cfdb..f46f738 100644 (file)
@@ -102,6 +102,7 @@ static struct resultflags2str {
   { TESTCASE_RESULT_USERINSTALLED,     "userinstalled" },
   { TESTCASE_RESULT_ORDER,             "order" },
   { TESTCASE_RESULT_ORDEREDGES,                "orderedges" },
+  { TESTCASE_RESULT_PROOF,             "proof" },
   { 0, 0 }
 };
 
@@ -1359,6 +1360,82 @@ testcase_solverresult(Solver *solv, int resultflags)
        }
     }
 
+  if ((resultflags & TESTCASE_RESULT_PROOF) != 0)
+    {
+      char *probprefix;
+      int pcnt, problem;
+      Queue q, lq;
+
+      queue_init(&q);
+      queue_init(&lq);
+      pcnt = solver_problem_count(solv);
+      for (problem = 1; problem <= pcnt + lq.count; problem++)
+       {
+         if (problem <= pcnt)
+           {
+             s = testcase_problemid(solv, problem);
+             solver_get_decisionlist(solv, problem, SOLVER_DECISIONLIST_PROBLEM, &q);
+           }
+         else
+           {
+             s = testcase_ruleid(solv, lq.elements[problem - pcnt - 1]);
+             solver_get_decisionlist(solv, lq.elements[problem - pcnt - 1], SOLVER_DECISIONLIST_LEARNTRULE, &q);
+           }
+         probprefix = solv_dupjoin("proof ", s, 0);
+         for (i = 0; i < q.count; i += 3)
+           {
+             SolverRuleinfo rclass;
+             Queue rq;
+             Id truelit = q.elements[i];
+             Id rid = q.elements[i + 2];
+             char *rprefix;
+             char nbuf[16];
+
+             rclass = solver_ruleclass(solv, rid);
+             if (rclass == SOLVER_RULE_LEARNT)
+               queue_pushunique(&lq, rid);
+             queue_init(&rq);
+             solver_ruleliterals(solv, rid, &rq);
+             sprintf(nbuf, "%3d", i / 3);
+             rprefix = solv_dupjoin(probprefix, " ", nbuf);
+             if (q.elements[i + 1] == SOLVER_REASON_PREMISE)
+               {
+                 rprefix = solv_dupappend(rprefix, " premise", 0);
+                 queue_empty(&rq);
+                 queue_push(&rq, truelit);
+               }
+             else
+               {
+                 rprefix = solv_dupappend(rprefix, " ", testcase_rclass2str(rclass));
+                 rprefix = solv_dupappend(rprefix, " ", testcase_ruleid(solv, rid));
+               }
+             strqueue_push(&sq, rprefix);
+             solv_free(rprefix);
+             rprefix = solv_dupjoin(probprefix, " ", nbuf);
+             rprefix = solv_dupappend(rprefix, ": ", 0);
+             for (j = 0; j < rq.count; j++)
+               {
+                 const char *s;
+                 Id p = rq.elements[j];
+                 if (p == truelit)
+                   s = pool_tmpjoin(pool, rprefix, "-->", 0);
+                 else
+                   s = pool_tmpjoin(pool, rprefix, "   ", 0);
+                 if (p < 0)
+                   s = pool_tmpappend(pool, s, " -", testcase_solvid2str(pool, -p));
+                 else
+                   s = pool_tmpappend(pool, s, "  ", testcase_solvid2str(pool, p));
+                 strqueue_push(&sq, s);
+               }
+             solv_free(rprefix);
+             queue_free(&rq);
+           }
+         solv_free(probprefix);
+       }
+      queue_free(&q);
+      queue_free(&lq);
+    }
+
   if ((resultflags & TESTCASE_RESULT_ORPHANED) != 0)
     {
       Queue q;
@@ -1481,59 +1558,20 @@ testcase_solverresult(Solver *solv, int resultflags)
     }
   if ((resultflags & TESTCASE_RESULT_ALTERNATIVES) != 0)
     {
-      char *altprefix;
-      Queue q, rq;
+      Queue q;
       int cnt;
       Id alternative;
       queue_init(&q);
-      queue_init(&rq);
       cnt = solver_alternatives_count(solv);
       for (alternative = 1; alternative <= cnt; alternative++)
        {
          Id id, from, chosen;
-         char num[20];
+         char num[20], *s;
          int type = solver_get_alternative(solv, alternative, &id, &from, &chosen, &q, 0);
-         altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
+         char *altprefix = solv_dupjoin("alternative ", testcase_alternativeid(solv, type, id, from), " ");
          strcpy(num, " 0 ");
-         if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
-           {
-             char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, from));
-             s = pool_tmpappend(pool, s, " recommends ", testcase_dep2str(pool, id));
-             strqueue_push(&sq, s);
-           }
-         else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
-           {
-             /* map choice rules back to pkg rules */
-             if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
-               id = solver_rule2pkgrule(solv, id);
-             if (solver_ruleclass(solv, id) == SOLVER_RULE_RECOMMENDS)
-               id = solver_rule2pkgrule(solv, id);
-             solver_allruleinfos(solv, id, &rq);
-             for (i = 0; i < rq.count; i += 4)
-               {
-                 int rtype = rq.elements[i];
-                 if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
-                   {
-                     const char *js = testcase_job2str(pool, rq.elements[i + 2], rq.elements[i + 3]);
-                     char *s = pool_tmpjoin(pool, altprefix, num, "job ");
-                     s = pool_tmpappend(pool, s, js, 0);
-                     strqueue_push(&sq, s);
-                   }
-                 else if (rtype == SOLVER_RULE_PKG_REQUIRES)
-                   {
-                     char *s = pool_tmpjoin(pool, altprefix, num, testcase_solvid2str(pool, rq.elements[i + 1]));
-                     s = pool_tmpappend(pool, s, " requires ", testcase_dep2str(pool, rq.elements[i + 3]));
-                     strqueue_push(&sq, s);
-                   }
-                 else if (rtype == SOLVER_RULE_UPDATE || rtype == SOLVER_RULE_FEATURE)
-                   {
-                     const char *js = testcase_solvid2str(pool, rq.elements[i + 1]);
-                     char *s = pool_tmpjoin(pool, altprefix, num, "update ");
-                     s = pool_tmpappend(pool, s, js, 0);
-                     strqueue_push(&sq, s);
-                   }
-               }
-           }
+          s = pool_tmpjoin(pool, altprefix, num, solver_alternative2str(solv, type, id, from));
+         strqueue_push(&sq, s);
          for (i = 0; i < q.count; i++)
            {
              Id p = q.elements[i];
@@ -1552,7 +1590,6 @@ testcase_solverresult(Solver *solv, int resultflags)
          solv_free(altprefix);
        }
       queue_free(&q);
-      queue_free(&rq);
     }
   if ((resultflags & TESTCASE_RESULT_RULES) != 0)
     {
@@ -2448,7 +2485,10 @@ testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **res
                }
            }
          if (resultp)
+         {
+           solv_free(*resultp);
            *resultp = result;
+         }
          else
            solv_free(result);
          if (resultflagsp)
index d57f116..fae6379 100644 (file)
@@ -23,6 +23,7 @@
 #define TESTCASE_RESULT_USERINSTALLED  (1 << 11)
 #define TESTCASE_RESULT_ORDER          (1 << 12)
 #define TESTCASE_RESULT_ORDEREDGES     (1 << 13)
+#define TESTCASE_RESULT_PROOF          (1 << 14)
 
 /* reuse solver hack, testsolv use only */
 #define TESTCASE_RESULT_REUSE_SOLVER   (1 << 31)
index 75dadab..591f01d 100644 (file)
@@ -1,4 +1,57 @@
 -------------------------------------------------------------------
+Thu Nov 23 15:12:55 CET 2023 - mls@suse.de
+
+- add zstd support for the installcheck tool
+- add putinowndirpool cache to make file list handling in
+  repo_write much faster
+- bump version to 0.7.27
+
+-------------------------------------------------------------------
+Tue Nov  7 12:45:44 CET 2023 - mls@suse.de
+
+- fix evr roundtrip in testcases
+- do not use deprecated headerUnload with newer rpm versions
+- bump version to 0.7.26
+
+-------------------------------------------------------------------
+Thu Sep 14 14:17:08 CEST 2023 - mls@suse.de
+
+- support complex deps in SOLVABLE_PREREQ_IGNOREINST
+- fix minimization not prefering installed packages in some cases
+- reduce memory usage in repo_updateinfoxml
+- fix lock-step interfering with architecture selection
+- fix choice rule handing for package downgrades
+- fix complex dependencies with an "else" part sometimes leading
+  to unsolved dependencies
+- bump version to 0.7.25
+
+-------------------------------------------------------------------
+Thu Apr 13 16:03:04 CEST 2023 - mls@suse.de
+
+- handle learnt rules in solver_alternativeinfo()
+- support x86_64_v[234] architecture levels
+- implement decision sorting for package decisionlists
+- add back findutils requires for the libsolv-tools packagse
+  [bsc#1195633]
+- bump version to 0.7.24
+
+-------------------------------------------------------------------
+Tue Dec 13 11:47:50 CET 2022 - mls@suse.de
+
+- fix "keep installed" jobs not disabling "best update" rules
+- do not autouninstall suse ptf packages
+- ensure duplinvolvedmap_all is reset when a solver is reused
+- special case file dependencies in the testcase writer
+- support stringification of multiple solvables
+- new weakdep introspection interface similar to ruleinfos
+- support decision reason queries
+- support merging of related decissions
+- support stringification of ruleinfo, decisioninfo and decision reasons
+- support better info about alternatives
+- new '-P' and '-W' options for testsolv
+- bump version to 0.7.23
+
+-------------------------------------------------------------------
 Mon Mar 21 10:05:32 CET 2022 - mls@suse.de
 
 - reworked choice rule generation to cover more usecases
index 5e4e30d..6bcb448 100644 (file)
@@ -90,13 +90,13 @@ BuildRequires:  swig
 %endif
 
 %if %{with python}
-%global python_sitearch %(python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(True))")
+%global python_sitearch %(python -c "from sysconfig import get_path; print(get_path('platlib'))")
 BuildRequires:  python-devel
 BuildRequires:  swig
 %endif
 
 %if %{with python3}
-%global python3_sitearch %(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(True))")
+%global python3_sitearch %(python3 -c "from sysconfig import get_path; print(get_path('platlib'))")
 BuildRequires:  python3-devel
 BuildRequires:  swig
 %endif
@@ -153,6 +153,7 @@ Group:          System/Management
 Conflicts:      satsolver-tools-obsolete
 Obsoletes:      satsolver-tools < 0.18
 Provides:       satsolver-tools = 0.18
+Requires:       findutils
 
 %description tools
 libsolv is a library for solving packages and reading repositories.
index bbf30ba..ca04b39 100644 (file)
@@ -20,7 +20,7 @@ SET (libsolv_SRCS
     transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
     chksum.c md5.c sha1.c sha2.c solvversion.c selection.c
     fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c
-    userinstalled.c filelistfilter.c)
+    userinstalled.c filelistfilter.c decision.c)
 
 SET (libsolv_HEADERS
     bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h
index 3bcfa2d..07703be 100644 (file)
 #ifndef LIBSOLV_CONDA_H
 #define LIBSOLV_CONDA_H
 
+#include "pooltypes.h"
+#include "solvable.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 int pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode);
 int solvable_conda_matchversion(Solvable *s, const char *version);
 Id pool_addrelproviders_conda(Pool *pool, Id name, Id evr, Queue *plist);
 Id pool_conda_matchspec(Pool *pool, const char *name);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LIBSOLV_CONDA_H */
 
index 6c40752..26e754d 100644 (file)
@@ -405,6 +405,7 @@ pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg)
              Reldep *rd2 = GETRELDEP(pool, rd->evr);
              if (rd2->flags == REL_ELSE)
                {
+                 pool_add_pos_literals_complex_dep(pool, rd2->name, q, m, !neg);
                  pool_add_pos_literals_complex_dep(pool, rd2->evr, q, m, !neg);
                  dep = rd2->name;
                }
index 0649258..357ae59 100644 (file)
@@ -20,8 +20,6 @@
 extern "C" {
 #endif
 
-struct s_Repo;
-
 typedef struct s_KeyValue {
   Id id;
   const char *str;
@@ -106,8 +104,8 @@ typedef struct s_Dataiterator
   int flags;
 
   Pool *pool;
-  struct s_Repo *repo;
-  struct s_Repodata *data;
+  Repo *repo;
+  Repodata *data;
 
   /* data pointers */
   unsigned char *dp;
@@ -165,9 +163,9 @@ typedef struct s_Dataiterator
  * keyname: if non-null, limit search to this keyname
  * match:   if non-null, limit search to this match
  */
-int  dataiterator_init(Dataiterator *di, Pool *pool, struct s_Repo *repo, Id p, Id keyname, const char *match, int flags);
+int  dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags);
 void dataiterator_init_clone(Dataiterator *di, Dataiterator *from);
-void dataiterator_set_search(Dataiterator *di, struct s_Repo *repo, Id p);
+void dataiterator_set_search(Dataiterator *di, Repo *repo, Id p);
 void dataiterator_set_keyname(Dataiterator *di, Id keyname);
 int  dataiterator_set_match(Dataiterator *di, const char *match, int flags);
 
@@ -181,7 +179,7 @@ void dataiterator_skip_attribute(Dataiterator *di);
 void dataiterator_skip_solvable(Dataiterator *di);
 void dataiterator_skip_repo(Dataiterator *di);
 void dataiterator_jump_to_solvid(Dataiterator *di, Id solvid);
-void dataiterator_jump_to_repo(Dataiterator *di, struct s_Repo *repo);
+void dataiterator_jump_to_repo(Dataiterator *di, Repo *repo);
 void dataiterator_entersub(Dataiterator *di);
 void dataiterator_clonepos(Dataiterator *di, Dataiterator *from);
 void dataiterator_seek(Dataiterator *di, int whence);
diff --git a/src/decision.c b/src/decision.c
new file mode 100644 (file)
index 0000000..c0cf4ad
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (c) 2022, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * decision.c
+ *
+ * solver decision and alternative introspection code
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "solver.h"
+#include "solver_private.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "util.h"
+#include "evr.h"
+
+int
+solver_get_decisionlevel(Solver *solv, Id p)
+{
+  return solv->decisionmap[p];
+}
+
+void
+solver_get_decisionqueue(Solver *solv, Queue *decisionq)
+{
+  queue_free(decisionq);
+  queue_init_clone(decisionq, &solv->decisionq);
+}
+
+int
+solver_get_lastdecisionblocklevel(Solver *solv)
+{
+  Id p;
+  if (solv->decisionq.count == 0)
+    return 0;
+  p = solv->decisionq.elements[solv->decisionq.count - 1];
+  if (p < 0)
+    p = -p;
+  return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p];
+}
+
+void
+solver_get_decisionblock(Solver *solv, int level, Queue *decisionq)
+{
+  Id p;
+  int i;
+
+  queue_empty(decisionq);
+  for (i = 0; i < solv->decisionq.count; i++)
+    {
+      p = solv->decisionq.elements[i];
+      if (p < 0)
+       p = -p;
+      if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
+        break;
+    }
+  if (i == solv->decisionq.count)
+    return;
+  for (i = 0; i < solv->decisionq.count; i++)
+    {
+      p = solv->decisionq.elements[i];
+      if (p < 0)
+       p = -p;
+      if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
+        queue_push(decisionq, p);
+      else
+        break;
+    }
+}
+
+/* return the reason and some extra info (i.e. a rule id) why
+ * a package was installed/conflicted */
+int
+solver_describe_decision(Solver *solv, Id p, Id *infop)
+{
+  int i;
+  Id pp, why;
+
+  if (infop)
+    *infop = 0;
+  if (!solv->decisionmap[p])
+    return SOLVER_REASON_UNRELATED;
+  pp = solv->decisionmap[p] < 0 ? -p : p;
+  for (i = 0; i < solv->decisionq.count; i++)
+    if (solv->decisionq.elements[i] == pp)
+      break;
+  if (i == solv->decisionq.count)      /* just in case... */
+    return SOLVER_REASON_UNRELATED;
+  why = solv->decisionq_why.elements[i];
+  if (infop)
+    *infop = why >= 0 ? why : -why;
+  if (why > 0)
+    return SOLVER_REASON_UNIT_RULE;
+  i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p];
+  return solv->decisionq_reason.elements[i];
+}
+
+/* create pseudo ruleinfo elements why a package was installed if
+ * the reason was SOLVER_REASON_WEAKDEP */
+int
+solver_allweakdepinfos(Solver *solv, Id p, Queue *whyq)
+{
+  Pool *pool = solv->pool;
+  int i;
+  int level = solv->decisionmap[p];
+  int decisionno;
+  Solvable *s;
+
+  queue_empty(whyq);
+  if (level < 0)
+    return 0;  /* huh? */
+  for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++)
+    if (solv->decisionq.elements[decisionno] == p)
+      break;
+  if (decisionno == solv->decisionq.count)
+    return 0;  /* huh? */
+  i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p];
+  if (solv->decisionq_reason.elements[i] != SOLVER_REASON_WEAKDEP)
+    return 0;  /* huh? */
+
+  /* 1) list all packages that recommend us */
+  for (i = 1; i < pool->nsolvables; i++)
+    {
+      Id *recp, rec, pp2, p2;
+      if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level)
+       continue;
+      s = pool->solvables + i;
+      if (!s->recommends)
+       continue;
+      if (!solv->addalreadyrecommended && s->repo == solv->installed)
+       continue;
+      recp = s->repo->idarraydata + s->recommends;
+      while ((rec = *recp++) != 0)
+       {
+         int found = 0;
+         FOR_PROVIDES(p2, pp2, rec)
+           {
+             if (p2 == p)
+               found = 1;
+             else
+               {
+                 /* if p2 is already installed, this recommends is ignored */
+                 if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
+                   break;
+               }
+           }
+         if (!p2 && found)
+           {
+             queue_push2(whyq, SOLVER_RULE_PKG_RECOMMENDS, i);
+             queue_push2(whyq, 0, rec);
+           }
+       }
+    }
+  /* 2) list all supplements */
+  s = pool->solvables + p;
+  if (s->supplements && level > 0)
+    {
+      Id *supp, sup, pp2, p2;
+      /* this is a hack. to use solver_dep_fulfilled we temporarily clear
+       * everything above our level in the decisionmap */
+      for (i = decisionno; i < solv->decisionq.count; i++ )
+       {
+         p2 = solv->decisionq.elements[i];
+         if (p2 > 0)
+           solv->decisionmap[p2] = -solv->decisionmap[p2];
+       }
+      supp = s->repo->idarraydata + s->supplements;
+      while ((sup = *supp++) != 0)
+       if (solver_dep_fulfilled(solv, sup))
+         {
+           int found = 0;
+           /* let's see if this is an easy supp */
+           FOR_PROVIDES(p2, pp2, sup)
+             {
+               if (!solv->addalreadyrecommended && solv->installed)
+                 {
+                   if (pool->solvables[p2].repo == solv->installed)
+                     continue;
+                 }
+               if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
+                 {
+                   queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p);
+                   queue_push2(whyq, p2, sup);
+                   found = 1;
+                 }
+             }
+           if (!found)
+             {
+               /* hard case, just note dependency with no package */
+               queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p);
+               queue_push2(whyq, 0, sup);
+             }
+         }
+      for (i = decisionno; i < solv->decisionq.count; i++)
+       {
+         p2 = solv->decisionq.elements[i];
+         if (p2 > 0)
+           solv->decisionmap[p2] = -solv->decisionmap[p2];
+       }
+    }
+  return whyq->count / 4;
+}
+
+SolverRuleinfo
+solver_weakdepinfo(Solver *solv, Id p, Id *fromp, Id *top, Id *depp)
+{
+  Queue iq;
+  queue_init(&iq);
+  solver_allweakdepinfos(solv, p, &iq);
+  if (fromp)
+    *fromp = iq.count ? iq.elements[1] : 0;
+  if (top)
+    *top = iq.count ? iq.elements[2] : 0;
+  if (depp)
+    *depp = iq.count ? iq.elements[3] : 0;
+  return iq.count ? iq.elements[0] : SOLVER_RULE_UNKNOWN;
+}
+
+/* deprecated, use solver_allweakdepinfos instead */
+void
+solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
+{
+  int i, j;
+  solver_allweakdepinfos(solv, p, whyq);
+  for (i = j = 0; i < whyq->count; i += 4)
+    {
+      if (whyq->elements[i] == SOLVER_RULE_PKG_RECOMMENDS)
+        {
+         whyq->elements[j++] = SOLVER_REASON_RECOMMENDED;
+         whyq->elements[j++] = whyq->elements[i + 1];
+         whyq->elements[j++] = whyq->elements[i + 3];
+        }
+      else if (whyq->elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS)
+       {
+         whyq->elements[j++] = SOLVER_REASON_SUPPLEMENTED;
+         whyq->elements[j++] = whyq->elements[i + 2];
+         whyq->elements[j++] = whyq->elements[i + 3];
+       }
+    }
+  queue_truncate(whyq, j);
+}
+
+static int
+decisionsort_cmp(const void *va, const void *vb, void *vd)
+{
+  Solver *solv = vd;
+  Pool *pool = solv->pool;
+  const Id *a = va, *b = vb;   /* (decision, reason, rid, bits, type, from, to, dep) */
+  Solvable *as, *bs;
+  if (a[4] != b[4])    /* type */
+    return a[4] - b[4];
+  if (a[7] != b[7])    /* dep id */
+    return a[7] - b[7];
+  as = pool->solvables + a[5];
+  bs = pool->solvables + b[5];
+  if (as->name != bs->name)
+    return strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name));
+  if (as->evr != bs->evr)
+    {
+      int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE);
+      if (r)
+       return r;
+    }
+  as = pool->solvables + a[6];
+  bs = pool->solvables + b[6];
+  if (as->name != bs->name)
+    return strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name));
+  if (as->evr != bs->evr)
+    {
+      int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE);
+      if (r)
+       return r;
+    }
+  return 0;
+}
+
+static void
+decisionmerge(Solver *solv, Queue *q)
+{
+  Pool *pool = solv->pool;
+  int i, j;
+
+  for (i = 0; i < q->count; i += 8)
+    {
+      Id p = q->elements[i] >= 0 ? q->elements[i] : -q->elements[i];
+      int reason = q->elements[i + 1];
+      int bits = q->elements[i + 3];
+      Id name =  pool->solvables[p].name;
+      for (j = i + 8; j < q->count; j += 8)
+       {
+         int merged;
+         p = q->elements[j] >= 0 ? q->elements[j] : -q->elements[j];
+         if (reason != q->elements[j + 1] || name != pool->solvables[p].name)
+           break;
+         merged = solver_merge_decisioninfo_bits(solv, bits, q->elements[i + 4], q->elements[i + 5], q->elements[i + 6], q->elements[i + 7], q->elements[j + 3], q->elements[j + 4], q->elements[j + 5], q->elements[j + 6], q->elements[j + 7]);
+         if (!merged)
+           break;
+         bits = merged;
+       }
+      j -= 8;
+      for (; i < j; i += 8)
+       q->elements[i + 3] = bits;
+    }
+}
+
+/* move a decison from position "from" to a smaller position "to" */
+static inline void
+move_decision(Queue *q, int to, int from)
+{
+  queue_insertn(q, to, 8, 0);
+  memmove(q->elements + to, q->elements + from + 8, 8 * sizeof(Id));
+  queue_deleten(q, from + 8, 8); 
+}
+
+/* sort a block of SOLVER_REASON_UNIT_RULE decisions */
+static void
+sort_unit_decisions(Solver *solv, Queue *q, int start, int end, Map *m)
+{
+  Pool *pool = solv->pool;
+  int i, j, k, doing = 1;
+  if (start + 8 == end)
+    {
+      Id truelit = q->elements[start];
+      MAPSET(m, truelit >= 0 ? truelit : -truelit);
+      return;
+    }
+  /* alternate between conflicts and installs, starting with conflicts */
+  for (i = start; i < end; doing ^= 1)
+    {
+      for (j = k = i; j < end; j += 8)
+       {
+         Rule *or;
+         Id p, pp;
+         Id truelit = q->elements[j];
+         if ((doing == 0 && truelit < 0) || (doing != 0 && truelit >= 0))
+           continue;
+         or = solv->rules + q->elements[j + 2];
+         FOR_RULELITERALS(p, pp, or)
+           if (p != truelit && !MAPTST(m, p >= 0 ? p : -p))
+             break;
+         if (p)
+           continue;   /* not unit yet */
+         if (j > k)
+           move_decision(q, k, j);
+         k += 8;
+       }
+      if (k == i)
+       continue;
+      if (i + 8 < k)
+       solv_sort(q->elements + i, (k - i) / 8, 8 * sizeof(Id), decisionsort_cmp, solv);
+      for (; i < k; i += 8)
+       {
+         Id truelit = q->elements[i];
+         MAPSET(m, truelit >= 0 ? truelit : -truelit);
+       }
+    }
+}
+
+static void
+solver_get_proof(Solver *solv, Id id, int flags, Queue *q)
+{
+  Pool *pool = solv->pool;
+  Map seen, seent;     /* seent: was the literal true or false */
+  Id rid, truelit;
+  int first = 1;
+  int why, i, cnt;
+
+  queue_empty(q);
+  if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_PROBLEM)
+    why = solv->problems.elements[2 * id - 2];
+  else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE && id >= solv->learntrules && id < solv->nrules)
+    why = solv->learnt_why.elements[id - solv->learntrules];
+  else
+    return;
+  if (!solv->learnt_pool.elements[why])
+    return;
+
+  map_init(&seen, pool->nsolvables);
+  map_init(&seent, pool->nsolvables);
+  while ((rid = solv->learnt_pool.elements[why++]) != 0)
+    {
+      Rule *r = solv->rules + rid;
+      Id p, pp;
+      truelit = 0;
+      FOR_RULELITERALS(p, pp, r)
+       {
+         Id vv = p > 0 ? p : -p;
+         if (MAPTST(&seen, vv))
+           {
+             if ((p > 0 ? 1 : 0) == (MAPTST(&seent, vv) ? 1 : 0))
+               {
+                 if (truelit)
+                   abort();
+                 truelit = p;          /* the one true literal! */
+               }
+           }
+         else
+           {
+             /* a new literal. it must be false as the rule is unit */
+             MAPSET(&seen, vv);
+             if (p < 0)
+               MAPSET(&seent, vv);
+           }
+       }
+      if (truelit)
+        queue_push(q, truelit);
+      else if (!first)
+       abort();
+      queue_push(q, rid);
+      first = 0;
+    }
+
+  /* add ruleinfo data to all rules (and also reverse the queue) */
+  cnt = q->count;
+  for (i = q->count - 1; i >= 0; i -= 2)
+    {
+      SolverRuleinfo type;
+      Id from = 0, to = 0, dep = 0;
+      rid = q->elements[i];
+      type = solver_ruleinfo(solv, rid, &from, &to, &dep);
+      if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
+       {
+         /* use pkg ruleinfo for choice/recommends rules */
+         Id rid2 = solver_rule2pkgrule(solv, rid);
+         if (rid2)
+           type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
+       }
+      queue_insertn(q, q->count, 8, 0);
+      q->elements[q->count - 8] = i > 0 ? q->elements[i - 1] : 0;
+      q->elements[q->count - 8 + 1] = i > 0 ? SOLVER_REASON_UNIT_RULE : SOLVER_REASON_UNSOLVABLE;
+      q->elements[q->count - 8 + 2] = rid;
+      q->elements[q->count - 8 + 4] = type;
+      q->elements[q->count - 8 + 5] = from;
+      q->elements[q->count - 8 + 6] = to;
+      q->elements[q->count - 8 + 7] = dep;
+    }
+  queue_deleten(q, 0, cnt);
+
+  /* switch last two decisions if the unsolvable rule is of type SOLVER_RULE_RPM_SAME_NAME */
+  if (q->count >= 16 && q->elements[q->count - 8 + 3] == SOLVER_RULE_RPM_SAME_NAME && q->elements[q->count - 16] > 0)
+    {
+      Rule *r = solv->rules + q->elements[q->count - 8 + 1];
+      /* make sure that the rule is a binary conflict and it matches the installed element */
+      if (r->p < 0 && (r->d == 0 || r->d == -1) && r->w2 < 0
+        && (q->elements[q->count - 16] == -r->p || q->elements[q->count - 16] -r->w2))
+       {
+         /* looks good! swap decisions and fixup truelit entries */
+         move_decision(q, q->count - 16, q->count - 8);
+         q->elements[q->count - 16] = -q->elements[q->count - 8];
+         q->elements[q->count - 8] = 0;
+       }
+    }
+
+  /* put learnt rule premises in front */
+  MAPZERO(&seen);
+  MAPSET(&seen, 1);
+  i = 0;
+  if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE)
+    {
+      /* insert learnt prereqs at front */
+      Rule *r = solv->rules + id;
+      Id p, pp;
+      i = 0;
+      FOR_RULELITERALS(p, pp, r)
+       {
+         queue_insertn(q, i, 8, 0);
+         q->elements[i] = -p;
+         q->elements[i + 1] = SOLVER_REASON_PREMISE;
+         q->elements[i + 5] = p >= 0 ? p : -p;
+         MAPSET(&seen, p >= 0 ? p : -p);
+         i += 8;
+       }
+    }
+
+  if (flags & SOLVER_DECISIONLIST_SORTED)
+    {
+      /* sort premise block */
+      if (i > 8)
+       solv_sort(q->elements, i / 8, 8 * sizeof(Id), decisionsort_cmp, solv);
+      sort_unit_decisions(solv, q, i, q->count - 8, &seen);
+    }
+
+  map_free(&seen);
+  map_free(&seent);
+
+  if (!(flags & SOLVER_DECISIONLIST_WITHINFO))
+    {
+      int j;
+      for (i = j = 0; i < q->count; i += 8)
+       {
+         q->elements[j++] = q->elements[i];
+         q->elements[j++] = q->elements[i + 1];
+         q->elements[j++] = q->elements[i + 2];
+       }
+      queue_truncate(q, j);
+    }
+  else
+    {
+      /* set decisioninfo bits */
+      for (i = 0; i < q->count; i += 8)
+       q->elements[i + 3] = solver_calc_decisioninfo_bits(solv, q->elements[i], q->elements[i + 4], q->elements[i + 5], q->elements[i + 6], q->elements[i + 7]);
+      if (flags & SOLVER_DECISIONLIST_MERGEDINFO)
+       decisionmerge(solv, q);
+    }
+}
+
+void
+solver_get_learnt(Solver *solv, Id id, int flags, Queue *q)
+{
+  int why = -1;
+  Queue todo;
+
+  queue_empty(q);
+  queue_init(&todo);
+  if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_PROBLEM)
+    why = solv->problems.elements[2 * id - 2];
+  else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE && id >= solv->learntrules && id < solv->nrules)
+    why = solv->learnt_why.elements[id - solv->learntrules];
+  else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_SOLVABLE)
+    {
+      int i, j, cnt;
+      solver_get_decisionlist(solv, id, 0, &todo);
+      cnt = todo.count;
+      for (i = 0; i < cnt; i += 3)
+       {
+         int rid = todo.elements[i + 2];
+         if (rid < solv->learntrules || rid >= solv->nrules)
+           continue;
+         /* insert sorted and unified */
+         for (j = 0; j < q->count; j++)
+           {
+             if (q->elements[j] < rid)
+               continue;
+             if (q->elements[j] == rid)
+               rid = 0;
+             break;
+           }
+         if (!rid)
+           continue;   /* already in list */
+         queue_insert(q, j, rid);
+         queue_push(&todo, solv->learnt_why.elements[rid - solv->learntrules]);
+       }
+      queue_deleten(&todo, 0, cnt);
+    }
+  else
+    {
+      queue_free(&todo);
+      return;
+    }
+  if (why >= 0)
+    queue_push(&todo, why);
+  while (todo.count)
+    {
+      int i, rid;
+      why = queue_pop(&todo);
+      while ((rid = solv->learnt_pool.elements[why++]) != 0)
+       {
+         if (rid < solv->learntrules || rid >= solv->nrules)
+           continue;
+         /* insert sorted and unified */
+         for (i = 0; i < q->count; i++)
+           {
+             if (q->elements[i] < rid)
+               continue;
+             if (q->elements[i] == rid)
+               rid = 0;
+             break;
+           }
+         if (!rid)
+           continue;   /* already in list */
+         queue_insert(q, i, rid);
+         queue_push(&todo, solv->learnt_why.elements[rid - solv->learntrules]);
+       }
+    }
+  queue_free(&todo);
+}
+
+static void
+getdecisionlist(Solver *solv, Map *dm, int flags, Queue *decisionlistq)
+{
+  Pool *pool = solv->pool;
+  int i, ii, reason, info;
+  Queue iq;
+
+  queue_empty(decisionlistq);
+  if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE)
+    return;
+  queue_init(&iq);
+  for (ii = solv->decisionq.count - 1; ii > 0; ii--)
+    {
+      Id v = solv->decisionq.elements[ii];
+      Id vv = (v > 0 ? v : -v);
+      if (!MAPTST(dm, vv))
+       continue;
+      info = solv->decisionq_why.elements[ii];
+      if (info > 0)
+       reason = SOLVER_REASON_UNIT_RULE;
+      else if (info <= 0)
+       {
+         info = -info;
+         reason = solv->decisionmap[vv];
+         reason = solv->decisionq_reason.elements[reason >= 0 ? reason : -reason];
+       }
+      if (flags & (SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO))
+       {
+         queue_insertn(decisionlistq, 0, 5, 0);
+         if (reason == SOLVER_REASON_WEAKDEP)
+           {
+             solver_allweakdepinfos(solv, v, &iq);
+             if (iq.count)
+               {
+                 decisionlistq->elements[1] = iq.elements[0];
+                 decisionlistq->elements[2] = iq.elements[1];
+                 decisionlistq->elements[3] = iq.elements[2];
+                 decisionlistq->elements[4] = iq.elements[3];
+               }
+           }
+         else if (info > 0)
+           {
+             Id from, to, dep;
+              int type = solver_ruleinfo(solv, info, &from, &to, &dep);
+             if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
+               {
+                 /* use pkg ruleinfo for choice/recommends rules */
+                 Id rid2 = solver_rule2pkgrule(solv, info);
+                 if (rid2)
+                   type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
+               }
+             decisionlistq->elements[1] = type;
+             decisionlistq->elements[2] = from;
+             decisionlistq->elements[3] = to;
+             decisionlistq->elements[4] = dep;
+           }
+       }
+      queue_unshift(decisionlistq, info);
+      queue_unshift(decisionlistq, reason);
+      queue_unshift(decisionlistq, v);
+      switch (reason)
+       {
+       case SOLVER_REASON_WEAKDEP:
+         if (v <= 0)
+           break;
+         solver_allweakdepinfos(solv, v, &iq);
+         for (i = 0; i < iq.count; i += 4)
+           {
+             if (iq.elements[i + 1])
+               MAPSET(dm, iq.elements[i + 1]);
+             if (iq.elements[i + 2])
+               MAPSET(dm, iq.elements[i + 2]);
+             else if (iq.elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS)
+               {
+                 Id p2, pp2, id = iq.elements[i + 3];
+                 FOR_PROVIDES(p2, pp2, id)
+                   if (solv->decisionmap[p2] > 0)
+                     MAPSET(dm, p2);
+               }
+           }
+         break;
+       case SOLVER_REASON_RESOLVE_JOB:
+       case SOLVER_REASON_UNIT_RULE:
+       case SOLVER_REASON_RESOLVE:
+         solver_ruleliterals(solv, info, &iq);
+         for (i = 0; i < iq.count; i++)
+           {
+             Id p2 = iq.elements[i];
+             if (p2 < 0)
+               MAPSET(dm, -p2);
+             else if (solv->decisionmap[p2] > 0)
+               MAPSET(dm, p2);
+           }
+         break;
+       default:
+         break;
+       }
+    }
+  queue_free(&iq);
+  if ((flags & SOLVER_DECISIONLIST_SORTED) != 0)
+    {
+      /* reuse dm as "seen" map */
+      MAPZERO(dm);
+      MAPSET(dm, 1);
+      for (i = 0; i < decisionlistq->count; i += 8)
+       {
+         if (decisionlistq->elements[i + 1] != SOLVER_REASON_UNIT_RULE)
+           {
+             Id p = decisionlistq->elements[i] < 0 ? -decisionlistq->elements[i] : decisionlistq->elements[i];
+             MAPSET(dm, p);
+           }
+         else
+           {
+             int j;
+             for (j = i + 8; j < decisionlistq->count; j += 8)
+               if (decisionlistq->elements[j + 1] != SOLVER_REASON_UNIT_RULE)
+                 break;
+             sort_unit_decisions(solv, decisionlistq, i, j, dm);
+           }
+       }
+    }
+  if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0)
+    {
+      /* set decisioninfo bits */
+      for (i = 0; i < decisionlistq->count; i += 8)
+       decisionlistq->elements[i + 3] = solver_calc_decisioninfo_bits(solv, decisionlistq->elements[i], decisionlistq->elements[i + 4], decisionlistq->elements[i + 5], decisionlistq->elements[i + 6], decisionlistq->elements[i + 7]);
+      if (flags & SOLVER_DECISIONLIST_MERGEDINFO)
+       decisionmerge(solv, decisionlistq);
+    }
+  else if ((flags & SOLVER_DECISIONLIST_SORTED) != 0)
+    {
+      /* strip the info elements we added for sorting */
+      int j;
+      for (i = j = 0; i < decisionlistq->count; i += 8)
+       {
+         decisionlistq->elements[j++] = decisionlistq->elements[i];
+         decisionlistq->elements[j++] = decisionlistq->elements[i + 1];
+         decisionlistq->elements[j++] = decisionlistq->elements[i + 2];
+       }
+      queue_truncate(decisionlistq, j);
+    }
+}
+
+void
+solver_get_decisionlist(Solver *solv, Id id, int flags, Queue *decisionlistq)
+{
+  Pool *pool = solv->pool;
+  Map dm;
+  if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE)
+    return solver_get_proof(solv, id, flags, decisionlistq);
+  map_init(&dm, pool->nsolvables);
+  MAPSET(&dm, id);
+  getdecisionlist(solv, &dm, flags, decisionlistq);
+  map_free(&dm);
+  if (!decisionlistq->count)
+    {
+      queue_push(decisionlistq, -id);
+      queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0);
+      if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0)
+       {
+         queue_push(decisionlistq, solver_calc_decisioninfo_bits(solv, -id, 0, 0, 0, 0));
+         queue_push2(decisionlistq, 0, 0);
+         queue_push2(decisionlistq, 0, 0);
+       }
+    }
+}
+
+void
+solver_get_decisionlist_multiple(Solver *solv, Queue *idq, int flags, Queue *decisionlistq)
+{
+  Pool *pool = solv->pool;
+  int i;
+  Map dm;
+  queue_empty(decisionlistq);
+  if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE)
+    return;
+  map_init(&dm, pool->nsolvables);
+  for (i = 0; i < idq->count; i++)
+    {
+      Id p = idq->elements[i];
+      if (solv->decisionmap[p] != 0)
+       MAPSET(&dm, p);
+    }
+  getdecisionlist(solv, &dm, flags, decisionlistq);
+  map_free(&dm);
+  for (i = 0; i < idq->count; i++)
+    {
+      Id p = idq->elements[i];
+      if (solv->decisionmap[p] != 0)
+       continue;
+      queue_push(decisionlistq, -p);
+      queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0);
+      if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0)
+       {
+         queue_push(decisionlistq, solver_calc_decisioninfo_bits(solv, -p, 0, 0, 0, 0));
+         queue_push2(decisionlistq, 0, 0);
+         queue_push2(decisionlistq, 0, 0);
+       }
+    }
+}
+
+
+const char *
+solver_reason2str(Solver *solv, int reason)
+{
+  switch(reason)
+    {
+    case SOLVER_REASON_WEAKDEP:
+      return "a weak dependency";
+    case SOLVER_REASON_RESOLVE_JOB:
+      return "a job rule";
+    case SOLVER_REASON_RESOLVE:
+      return "a rule";
+    case SOLVER_REASON_UNIT_RULE:
+      return "an unit rule";
+    case SOLVER_REASON_KEEP_INSTALLED:
+      return "update/keep installed";
+    case SOLVER_REASON_UPDATE_INSTALLED:
+      return "update installed";
+    case SOLVER_REASON_CLEANDEPS_ERASE:
+      return "cleandeps erase";
+    case SOLVER_REASON_RESOLVE_ORPHAN:
+      return "orphaned package";
+    case SOLVER_REASON_UNSOLVABLE:
+      return "unsolvable";
+    case SOLVER_REASON_PREMISE:
+      return "learnt rule premise";
+    case SOLVER_REASON_UNRELATED:
+      return "it is unrelated";
+    default:
+      break;
+    }
+  return "an unknown reason";
+}
+
+static const char *
+decisionruleinfo2str(Solver *solv, Id decision, int type, Id from, Id to, Id dep)
+{
+  int bits = solver_calc_decisioninfo_bits(solv, decision, type, from, to, dep);
+  return solver_decisioninfo2str(solv, bits, type, from, to, dep);
+}
+
+const char *
+solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info)
+{
+  if (reason == SOLVER_REASON_WEAKDEP && decision > 0)
+    {
+      Id from, to, dep;
+      int type = solver_weakdepinfo(solv, decision, &from, &to, &dep);
+      if (type)
+       return decisionruleinfo2str(solv, decision, type, from, to, dep);
+    }
+  if ((reason == SOLVER_REASON_RESOLVE_JOB || reason == SOLVER_REASON_UNIT_RULE || reason == SOLVER_REASON_RESOLVE || reason == SOLVER_REASON_UNSOLVABLE) && info > 0)
+    {
+      Id from, to, dep;
+      int type = solver_ruleinfo(solv, info, &from, &to, &dep);
+      if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
+       {
+         Id rid2 = solver_rule2pkgrule(solv, info);
+         if (rid2)
+           {
+              type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
+             if (type)
+               return decisionruleinfo2str(solv, decision, type, from, to, dep);
+            }
+       }
+      if (type)
+       return decisionruleinfo2str(solv, decision, type, from, to, dep);
+    }
+  return solver_reason2str(solv, reason);
+}
+
+/* decision merge state bits */
+#define DMS_INITED             (1 << 0)
+#define DMS_IDENTICAL_FROM     (1 << 1)
+#define DMS_IDENTICAL_TO       (1 << 2)
+#define DMS_MERGED             (1 << 3)
+#define DMS_NEGATIVE           (1 << 4)
+#define DMS_NOMERGE            (1 << 5)
+
+/* add some bits about the decision and the ruleinfo so we can join decisions */
+int
+solver_calc_decisioninfo_bits(Solver *solv, Id decision, int type, Id from, Id to, Id dep)
+{
+  Id decisionpkg = decision >= 0 ? decision : -decision;
+  int bits = DMS_INITED | (decision < 0 ? DMS_NEGATIVE : 0);
+  if (!decision)
+    return bits | DMS_NOMERGE;
+  switch (type)
+    {
+    case SOLVER_RULE_DISTUPGRADE:
+    case SOLVER_RULE_INFARCH:
+    case SOLVER_RULE_UPDATE:
+    case SOLVER_RULE_FEATURE:
+    case SOLVER_RULE_BLACK:
+    case SOLVER_RULE_STRICT_REPO_PRIORITY:
+    case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+    case SOLVER_RULE_PKG_REQUIRES:
+    case SOLVER_RULE_PKG_RECOMMENDS:
+    case SOLVER_RULE_PKG_SUPPLEMENTS:
+      if (decisionpkg == from)
+       bits |= DMS_IDENTICAL_FROM;
+      break;
+    case SOLVER_RULE_PKG_SAME_NAME:
+    case SOLVER_RULE_PKG_CONFLICTS:
+    case SOLVER_RULE_PKG_OBSOLETES:
+    case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+    case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+    case SOLVER_RULE_PKG_CONSTRAINS:
+      if (decisionpkg == from)
+       bits |= DMS_IDENTICAL_FROM;
+      else if (decisionpkg == to)
+       bits |= DMS_IDENTICAL_TO;
+      break;
+    default:
+      break;
+    }
+  return bits;
+}
+
+/* try to merge the ruleinfos of two decisions */
+int
+solver_merge_decisioninfo_bits(Solver *solv, int bits1, int type1, Id from1, Id to1, Id dep1, int bits2, int type2, Id from2, Id to2, Id dep2)
+{
+  int merged = 0;
+  if (type1 != type2 || dep1 != dep2)
+    return 0;
+  if (!bits1 || !bits2 || ((bits1 | bits2) & DMS_NOMERGE) != 0 || ((bits1 ^ bits2) & DMS_NEGATIVE) != 0)
+    return 0;
+  merged = (((bits1 ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | (bits2 ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO))) ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | DMS_MERGED;
+  if (((bits1 & DMS_MERGED) != 0 && bits1 != merged) || ((bits2 & DMS_MERGED) != 0 && bits2 != merged))
+    return 0;
+  if (((merged & DMS_IDENTICAL_FROM) == 0 && from1 != from2) || ((merged & DMS_IDENTICAL_TO) == 0 && to1 != to2))
+    return 0;
+  return merged;
+}
+
+void
+solver_decisionlist_solvables(Solver *solv, Queue *decisionlistq, int pos, Queue *q)
+{
+  queue_empty(q);
+  for (; pos < decisionlistq->count; pos += 8)
+    {
+      Id p = decisionlistq->elements[pos];
+      queue_push(q, p > 0 ? p : -p);
+      if ((decisionlistq->elements[pos + 3] & DMS_MERGED) == 0)
+       break;
+    }
+}
+
+int
+solver_decisionlist_merged(Solver *solv, Queue *decisionlistq, int pos)
+{
+  int cnt = 0;
+  for (; pos < decisionlistq->count; pos += 8, cnt++)
+    if ((decisionlistq->elements[pos + 3] & DMS_MERGED) == 0)
+      break;
+  return cnt;
+}
+
+/* special version of solver_ruleinfo2str which supports merged decisions */
+const char *
+solver_decisioninfo2str(Solver *solv, int bits, int type, Id from, Id to, Id dep)
+{
+  Pool *pool = solv->pool;
+  const char *s;
+  int multiple = bits & DMS_MERGED;
+
+  /* use it/they variants if DMS_IDENTICAL_FROM is set */
+  if ((bits & DMS_IDENTICAL_FROM) != 0)
+    {
+      switch (type)
+       {
+       case SOLVER_RULE_DISTUPGRADE:
+         return multiple ? "they do not belong to a distupgrade repository" : "it does not belong to a distupgrade repository";
+       case SOLVER_RULE_INFARCH:
+         return multiple ? "they have inferior architecture": "it has inferior architecture";
+       case SOLVER_RULE_UPDATE:
+         return multiple ? "they need to stay installed or be updated" : "it needs to stay installed or be updated";
+       case SOLVER_RULE_FEATURE:
+         return multiple ? "they need to stay installed or be updated/downgraded" : "it needs to stay installed or be updated/downgraded";
+       case SOLVER_RULE_BLACK:
+         return multiple ? "they can only be installed by a direct request" : "it can only be installed by a direct request";
+       case SOLVER_RULE_STRICT_REPO_PRIORITY:
+         return multiple ? "they are excluded by strict repo priority" : "it is excluded by strict repo priority";
+
+       case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+         return pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
+       case SOLVER_RULE_PKG_REQUIRES:
+         return pool_tmpjoin(pool, multiple ? "they require " : "it requires ", pool_dep2str(pool, dep), 0);
+       case SOLVER_RULE_PKG_RECOMMENDS:
+         return pool_tmpjoin(pool,  multiple ? "they recommend " : "it recommends ", pool_dep2str(pool, dep), 0);
+       case SOLVER_RULE_PKG_SUPPLEMENTS:
+         s = pool_tmpjoin(pool, multiple ? "they  supplement " : "it supplements ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_SAME_NAME:
+         return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, to), 0);
+       case SOLVER_RULE_PKG_CONFLICTS:
+         s = pool_tmpappend(pool, multiple ? "they conflict with " : "it conflicts with ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_OBSOLETES:
+         s = pool_tmpappend(pool, multiple ? "they obsolete " : "it obsoletes ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+         s = pool_tmpjoin(pool, multiple ? "they are installed and obsolete " : "it is installed and obsoletes ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+         s = pool_tmpjoin(pool, multiple ? "they implicitly obsolete " : "it implicitly obsoletes ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_CONSTRAINS:
+         s = pool_tmpappend(pool, multiple ? "they have constraint " : "it has constraint ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, to));
+         return s;
+       default:
+         break;
+       }
+    }
+
+  /* in some cases we can drop the "to" part if DMS_IDENTICAL_TO is set */
+  if ((bits & DMS_IDENTICAL_TO) != 0)
+    {
+      switch (type)
+       {
+       case SOLVER_RULE_PKG_SAME_NAME:
+         return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, from), 0);
+       case SOLVER_RULE_PKG_CONFLICTS:
+       case SOLVER_RULE_PKG_OBSOLETES:
+       case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+       case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+       case SOLVER_RULE_PKG_CONSTRAINS:
+         bits &= ~DMS_IDENTICAL_TO;
+         to = 0;
+         break;
+       default:
+         break;
+       }
+    }
+
+  /* fallback to solver_ruleinfo2str if we can */
+  if (multiple && (bits & (DMS_IDENTICAL_FROM|DMS_IDENTICAL_TO)) != 0)
+      return "unsupported decision merge?";
+  return solver_ruleinfo2str(solv, type, from, to, dep);
+}
+
+int
+solver_alternatives_count(Solver *solv)
+{
+  Id *elements = solv->branches.elements;
+  int res, count;
+  for (res = 0, count = solv->branches.count; count; res++)
+    count -= elements[count - 2];
+  return res;
+}
+
+int
+solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp)
+{
+  int cnt = solver_alternatives_count(solv);
+  int count = solv->branches.count;
+  Id *elements = solv->branches.elements;
+  if (choices)
+    queue_empty(choices);
+  if (alternative <= 0 || alternative > cnt)
+    return 0;
+  elements += count;
+  for (; cnt > alternative; cnt--)
+    elements -= elements[-2];
+  if (levelp)
+    *levelp = elements[-1];
+  if (fromp)
+    *fromp = elements[-4];
+  if (idp)
+    *idp = elements[-3];
+  if (chosenp)
+    {
+      int i;
+      *chosenp = 0;
+      for (i = elements[-2]; i > 4; i--)
+       {
+         Id p = -elements[-i];
+         if (p > 0 && solv->decisionmap[p] == elements[-1] + 1)
+           {
+             *chosenp = p;
+             break;
+           }
+       }
+    }
+  if (choices)
+    queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]);
+  return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE;
+}
+
+static int
+find_alternative_rule_from_learnt_rec(Solver *solv, int rid, Map *m, int cnt)
+{
+  Pool *pool = solv->pool;
+  int why = solv->learnt_why.elements[rid - solv->learntrules];
+  while ((rid = solv->learnt_pool.elements[why++]) != 0)
+    {
+      Rule *r = solv->rules + rid;
+      Id p, pp;
+      int c;
+      if (rid >= solv->learntrules)
+       {
+         if ((rid = find_alternative_rule_from_learnt_rec(solv, rid, m, cnt)))
+           return rid;
+         continue;
+       }
+      c = 0;
+      FOR_RULELITERALS(p, pp, r)
+       if (p > 0 && MAPTST(m, p))
+         c++;
+      if (c == cnt)    /* all bits hit */
+       return rid;
+    }
+  return 0;
+}
+
+static int
+find_alternative_rule_from_learnt(Solver *solv, int rid)
+{
+  Pool *pool = solv->pool;
+  Map m;
+  int i, count, cnt;
+  Id *elements = solv->branches.elements;
+
+  /* find alternative by rule id */
+  for (count = solv->branches.count; count; count -= elements[count - 2])
+    if (elements[count - 4] == 0 && elements[count - 3] == rid)
+      break;
+  if (!count)
+    return 0;
+  map_init(&m, pool->nsolvables);
+  cnt = 0;
+  for (i = count - elements[count - 2]; i < count - 4; i++)
+    if (elements[i] > 0)
+      {
+        MAPSET(&m, elements[i]);
+        cnt++;
+      }
+  rid = find_alternative_rule_from_learnt_rec(solv, rid, &m, cnt);
+  map_free(&m);
+  return rid;
+}
+
+int
+solver_alternativeinfo(Solver *solv, int type, Id id, Id from, Id *fromp, Id *top, Id *depp)
+{
+  if (fromp)
+    *fromp = 0;
+  if (top)
+    *top = 0;
+  if (depp)
+    *depp = 0;
+  if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
+    {
+      if (fromp)
+        *fromp = from;
+      if (depp)
+        *depp = id;
+      return SOLVER_RULE_PKG_RECOMMENDS;
+    }
+  else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
+    {
+      int rclass = solver_ruleclass(solv, id);
+      if (rclass == SOLVER_RULE_LEARNT)
+       {
+         id = find_alternative_rule_from_learnt(solv, id);
+         if (!id)
+           return SOLVER_RULE_LEARNT;
+          rclass = solver_ruleclass(solv, id);
+       }
+      if (rclass == SOLVER_RULE_CHOICE || rclass == SOLVER_RULE_RECOMMENDS)
+       id = solver_rule2pkgrule(solv, id);
+      else if (rclass == SOLVER_RULE_BEST)
+       {
+         Id info = solv->bestrules_info[id - solv->bestrules];
+         if (info > 0)
+           {
+             /* best update */
+             if (fromp)
+               *fromp = info;
+             return SOLVER_RULE_UPDATE;
+           }
+         id = -info;           /* best job, delegate to job rule */
+       }
+      return solver_ruleinfo(solv, id, fromp, top, depp);
+    }
+  return 0;
+}
+
+const char *
+solver_alternative2str(Solver *solv, int type, Id id, Id from)
+{
+  const char *s;
+  Pool *pool = solv->pool;
+  Id to, dep;
+  type = solver_alternativeinfo(solv, type, id, from, &from, &to, &dep);
+  switch (type)
+    {
+    case SOLVER_RULE_PKG_RECOMMENDS:
+      s = pool_dep2str(pool, dep);
+      if (from)
+        s =  pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from));
+      return s;
+    case SOLVER_RULE_PKG_REQUIRES:
+      s = pool_dep2str(pool, dep);
+      if (from)
+        s =  pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, from));
+      return s;
+    case SOLVER_RULE_PKG_CONFLICTS:
+      s = pool_dep2str(pool, dep);
+      if (from)
+        s =  pool_tmpappend(pool, s, ", conflicted by  ", pool_solvid2str(pool, from));
+      return s;
+    case SOLVER_RULE_YUMOBS:
+      return pool_tmpjoin(pool, pool_id2str(pool, pool->solvables[to].name), ", obsoleting ", pool_dep2str(pool, dep));;
+    case SOLVER_RULE_JOB:
+      if ((to & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES || (to & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_NAME)
+       return pool_dep2str(pool, dep);
+      return solver_select2str(pool, to & SOLVER_SELECTMASK, dep);
+    case SOLVER_RULE_UPDATE:
+    case SOLVER_RULE_FEATURE:
+      return pool_solvid2str(pool, from);
+    default:
+      break;
+    }
+  return solver_ruleinfo2str(solv, type, from, to, dep);
+}
+
index 2bb27b2..19f67ae 100644 (file)
@@ -272,6 +272,7 @@ KNOWNID(SOLVABLE_LANGONLY,          "solvable:langonly"),
 
 KNOWNID(UPDATE_COLLECTIONLIST,         "update:collectionlist"),       /* list of UPDATE_COLLECTION (actually packages) and UPDATE_MODULE */
 KNOWNID(SOLVABLE_MULTIARCH,            "solvable:multiarch"),          /* debian multi-arch field */
+KNOWNID(SOLVABLE_SIGNATUREDATA,                "solvable:signaturedata"),      /* conda */
 
 KNOWNID(ID_NUM_INTERNAL,               0)
 
index c77f8d3..b9f9f96 100644 (file)
@@ -113,6 +113,7 @@ SOLV_1.0 {
                pool_shrink_rels;
                pool_shrink_strings;
                pool_solvable2str;
+               pool_solvidset2str;
                pool_str2id;
                pool_strn2id;
                pool_tmpappend;
@@ -341,9 +342,13 @@ SOLV_1.0 {
                solvable_trivial_installable_queue;
                solvable_trivial_installable_repo;
                solvable_unset;
+               solver_all_solutionelements;
                solver_allruleinfos;
+               solver_allweakdepinfos;
                solver_alternative2str;
+               solver_alternativeinfo;
                solver_alternatives_count;
+               solver_calc_decisioninfo_bits;
                solver_calc_duchanges;
                solver_calc_installsizechange;
                solver_calculate_multiversionmap;
@@ -351,6 +356,10 @@ SOLV_1.0 {
                solver_create;
                solver_create_state_maps;
                solver_create_transaction;
+               solver_decisioninfo2str;
+               solver_decisionlist_merged;
+               solver_decisionlist_solvables;
+               solver_decisionreason2str;
                solver_describe_decision;
                solver_describe_weakdep_decision;
                solver_findallproblemrules;
@@ -360,13 +369,17 @@ SOLV_1.0 {
                solver_get_alternative;
                solver_get_decisionblock;
                solver_get_decisionlevel;
+               solver_get_decisionlist;
+               solver_get_decisionlist_multiple;
                solver_get_decisionqueue;
                solver_get_flag;
                solver_get_lastdecisionblocklevel;
+               solver_get_learnt;
                solver_get_orphaned;
                solver_get_recommendations;
                solver_get_unneeded;
                solver_get_userinstalled;
+               solver_merge_decisioninfo_bits;
                solver_next_problem;
                solver_next_solution;
                solver_next_solutionelement;
@@ -387,6 +400,7 @@ SOLV_1.0 {
                solver_problem2str;
                solver_problem_count;
                solver_problemruleinfo2str;
+               solver_reason2str;
                solver_rule2job;
                solver_rule2jobidx;
                solver_rule2pkgrule;
@@ -394,12 +408,14 @@ SOLV_1.0 {
                solver_rule2solvable;
                solver_ruleclass;
                solver_ruleinfo;
+               solver_ruleinfo2str;
                solver_ruleliterals;
                solver_rulecmp;
                solver_select2str;
                solver_set_flag;
                solver_solution_count;
                solver_solutionelement2str;
+               solver_solutionelementtype2str;
                solver_solutionelement_count;
                solver_solutionelement_internalid;
                solver_solutionelement_extrajobflags;
@@ -408,6 +424,7 @@ SOLV_1.0 {
                solver_take_solutionelement;
                solver_trivial_installable;
                solver_unifyrules;
+               solver_weakdepinfo;
                stringpool_clone;
                stringpool_free;
                stringpool_freehash;
index 5912f98..26fb706 100644 (file)
@@ -44,7 +44,7 @@
 
 #ifdef ENABLE_LINKED_PKGS
 
-void
+static void
 find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
 {
   Id req = 0;
@@ -121,7 +121,7 @@ find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp
     *prvidp = prv;
 }
 
-void
+static void
 find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
 {
   Id p, pp, namerelid;
@@ -232,7 +232,7 @@ find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Qu
     *prvidp = solvable_selfprovidedep(s);
 }
 
-void
+static void
 find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
 {
   Id p, pp, *pr, apevr = 0, aprel = 0;
@@ -274,37 +274,31 @@ find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Qu
     *prvidp = aprel;
 }
 
-/* the following two functions are used in solvable_lookup_str_base to do
+/* the following function is used in solvable_lookup_str_base to do
  * translated lookups on the product/pattern packages
  */
 Id
-find_autopattern_name(Pool *pool, Solvable *s)
+find_autopackage_name(Pool *pool, Solvable *s)
 {
+  const char *autoprv = 0;
+  const char *n = pool_id2str(pool, s->name);
   Id prv, *prvp;
-  if (!s->provides)
-    return 0;
-  for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
-    if (ISRELDEP(prv))
-      {
-        Reldep *rd = GETRELDEP(pool, prv);
-        if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
-          return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
-      }
-  return 0;
-}
 
-Id
-find_autoproduct_name(Pool *pool, Solvable *s)
-{
-  Id prv, *prvp;
-  if (!s->provides)
+  if (*n == 'p')
+    {
+      if (!strncmp("pattern:", n, 8))
+       autoprv = "autopattern()";
+      if (!strncmp("product:", n, 8))
+       autoprv = "autoproduct()";
+    }
+  if (!autoprv)
     return 0;
   for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
     if (ISRELDEP(prv))
       {
         Reldep *rd = GETRELDEP(pool, prv);
-        if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()"))
-          return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0;
+        if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), autoprv))
+          return rd->evr;
       }
   return 0;
 }
index 51b82a5..4cf5561 100644 (file)
@@ -25,12 +25,7 @@ has_package_link(Pool *pool, Solvable *s)
   return 0;
 }
 
-extern void find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-extern void find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-extern void find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
-
-extern Id find_autopattern_name(Pool *pool, Solvable *s);
-extern Id find_autoproduct_name(Pool *pool, Solvable *s);
+extern Id find_autopackage_name(Pool *pool, Solvable *s);
 
 /* generic */
 extern void find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp);
index c02d237..02548ea 100644 (file)
@@ -232,13 +232,13 @@ check_complex_dep(Solver *solv, Id dep, Map *m, Queue **cqp)
 {
   Pool *pool = solv->pool;
   Queue q;
-  queue_init(&q);
   Id p;
   int i, qcnt;
 
 #if 0
   printf("check_complex_dep %s\n", pool_dep2str(pool, dep));
 #endif
+  queue_init(&q);
   i = pool_normalize_complex_dep(pool, dep, &q, CPLXDEPS_EXPAND);
   if (i == 0 || i == 1)
     {
@@ -1511,7 +1511,7 @@ policy_create_obsolete_index(Solver *solv)
        {
          FOR_PROVIDES(p, pp, obs)
            {
-             Solvable *ps = pool->solvables + p;;
+             Solvable *ps = pool->solvables + p;
              if (ps->repo != installed)
                continue;
              if (ps->name == s->name)
@@ -1545,7 +1545,7 @@ policy_create_obsolete_index(Solver *solv)
        {
          FOR_PROVIDES(p, pp, obs)
            {
-             Solvable *ps = pool->solvables + p;;
+             Solvable *ps = pool->solvables + p;
              if (ps->repo != installed)
                continue;
              if (ps->name == s->name)
@@ -1562,6 +1562,29 @@ policy_create_obsolete_index(Solver *solv)
 }
 
 
+/* return true if solvable s obsoletes solvable with id pi */
+static inline int
+is_obsoleting(Pool *pool, Solvable *s, Id pi)
+{
+  Id p, pp, obs, *obsp;
+  Solvable *si = pool->solvables + pi;
+  if (pool->obsoleteusescolors && !pool_colormatch(pool, si, s))
+    return 0;
+  obsp = s->repo->idarraydata + s->obsoletes;
+  while ((obs = *obsp++) != 0) /* for all obsoletes */
+    {
+      FOR_PROVIDES(p, pp, obs)   /* and all matching providers of the obsoletes */
+       {
+         if (p != pi)
+           continue;
+         if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs))
+           continue;
+         return 1;
+       }
+    }
+  return 0;
+}
+
 /*
  * find update candidates
  *
@@ -1576,8 +1599,7 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
 {
   /* installed packages get a special upgrade allowed rule */
   Pool *pool = solv->pool;
-  Id p, pp, n, p2, pp2;
-  Id obs, *obsp;
+  Id p, pp, n;
   Solvable *ps;
   int haveprovobs = 0;
   int allowdowngrade = allow_all ? 1 : solv->allowdowngrade;
@@ -1618,31 +1640,14 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
        continue;
       else if ((!solv->noupdateprovide || solv->needupdateprovide) && ps->obsoletes)   /* provides/obsoletes combination ? */
        {
-         /* check if package ps obsoletes installed package s */
+         /* check if package ps that provides s->name obsoletes installed package s */
          /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
           * use it to limit our update candidates */
-         if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, ps))
+         if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
            continue;
-         obsp = ps->repo->idarraydata + ps->obsoletes;
-         while ((obs = *obsp++) != 0)  /* for all obsoletes */
-           {
-             FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
-               {
-                 Solvable *ps2 = pool->solvables + p2;
-                 if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
-                   continue;
-                 if (p2 == n)          /* match ! */
-                   break;
-               }
-             if (p2)                   /* match! */
-               break;
-           }
-         if (!obs)                     /* continue if no match */
+         if (!is_obsoleting(pool, ps, n))
            continue;
-         /* here we have 'p' with a matching provides/obsoletes combination
-          * thus flagging p as a valid update candidate for s
-          */
-         haveprovobs = 1;
+         haveprovobs = 1;              /* have matching provides/obsoletes combination */
        }
       else
         continue;
@@ -1664,14 +1669,14 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
        {
          ps = pool->solvables + p;
-         if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
-           continue;
-         if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
-           continue;
          /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
           * use it to limit our update candidates */
          if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
            continue;
+         if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
+           continue;
+         if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
+           continue;
          queue_push(qs, p);
        }
     }
index 54226e9..45e85ea 100644 (file)
@@ -1461,7 +1461,7 @@ pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker)
     {
       Id pp;
       FOR_PROVIDES(p, pp, dep)
-        if (pool_match_dep(pool, p, dep))
+        if (pool_match_nevr(pool, pool->solvables + p, dep))
          queue_push(q, p);
       return;
     }
index 935a326..08cf1d0 100644 (file)
@@ -36,13 +36,11 @@ extern "C" {
 
 /*----------------------------------------------- */
 
-struct s_Repo;
-struct s_Repodata;
 struct s_Repokey;
 struct s_KeyValue;
 
 typedef struct s_Datapos {
-  struct s_Repo *repo;
+  Repo *repo;
   Id solvid;
   Id repodataid;
   Id schema;
@@ -66,16 +64,16 @@ struct s_Pool_tmpspace {
 struct s_Pool {
   void *appdata;               /* application private pointer */
 
-  struct s_Stringpool ss;
+  Stringpool ss;
 
   Reldep *rels;                        /* table of rels: Id -> Reldep */
   int nrels;                   /* number of unique rels */
 
-  struct s_Repo **repos;
+  Repo **repos;
   int nrepos;                  /* repos allocated */
   int urepos;                  /* repos in use */
 
-  struct s_Repo *installed;    /* packages considered installed */
+  Repo *installed;     /* packages considered installed */
 
   Solvable *solvables;
   int nsolvables;              /* solvables allocated */
@@ -109,16 +107,16 @@ struct s_Pool {
   Map *considered;
 
   /* callback for REL_NAMESPACE dependencies handled by the application  */
-  Id (*nscallback)(struct s_Pool *, void *data, Id name, Id evr);
+  Id (*nscallback)(Pool *, void *data, Id name, Id evr);
   void *nscallbackdata;
 
   /* debug mask and callback */
   int  debugmask;
-  void (*debugcallback)(struct s_Pool *, void *data, int type, const char *str);
+  void (*debugcallback)(Pool *, void *data, int type, const char *str);
   void *debugcallbackdata;
 
   /* load callback */
-  int (*loadcallback)(struct s_Pool *, struct s_Repodata *, void *);
+  int (*loadcallback)(Pool *, Repodata *, void *);
   void *loadcallbackdata;
 
   /* search position */
@@ -155,7 +153,7 @@ struct s_Pool {
 
   char *rootdir;
 
-  int (*custom_vendorcheck)(struct s_Pool *, Solvable *, Solvable *);
+  int (*custom_vendorcheck)(Pool *, Solvable *, Solvable *);
 
   int addfileprovidesfiltered; /* 1: only use filtered file list for addfileprovides */
   int addedfileprovides;       /* true: application called addfileprovides */
@@ -251,14 +249,14 @@ extern int  pool_set_flag(Pool *pool, int flag, int value);
 extern int  pool_get_flag(Pool *pool, int flag);
 
 extern void pool_debug(Pool *pool, int type, const char *format, ...) __attribute__((format(printf, 3, 4)));
-extern void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct s_Pool *, void *data, int type, const char *str), void *debugcallbackdata);
+extern void pool_setdebugcallback(Pool *pool, void (*debugcallback)(Pool *pool, void *data, int type, const char *str), void *debugcallbackdata);
 extern void pool_setdebugmask(Pool *pool, int mask);
-extern void pool_setloadcallback(Pool *pool, int (*cb)(struct s_Pool *, struct s_Repodata *, void *), void *loadcbdata);
-extern void pool_setnamespacecallback(Pool *pool, Id (*cb)(struct s_Pool *, void *, Id, Id), void *nscbdata);
+extern void pool_setloadcallback(Pool *pool, int (*cb)(Pool *, Repodata *, void *), void *loadcbdata);
+extern void pool_setnamespacecallback(Pool *pool, Id (*cb)(Pool *, void *, Id, Id), void *nscbdata);
 extern void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr);
 
-extern void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(struct s_Pool *, Solvable *, Solvable *));
-extern int (*pool_get_custom_vendorcheck(Pool *pool))(struct s_Pool *, Solvable *, Solvable *);
+extern void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *));
+extern int (*pool_get_custom_vendorcheck(Pool *pool))(Pool *, Solvable *, Solvable *);
 
 extern char *pool_alloctmpspace(Pool *pool, int len);
 extern void  pool_freetmpspace(Pool *pool, const char *space);
@@ -266,7 +264,7 @@ extern char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const
 extern char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3);
 extern const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len);
 
-extern void pool_set_installed(Pool *pool, struct s_Repo *repo);
+extern void pool_set_installed(Pool *pool, Repo *repo);
 
 extern int  pool_error(Pool *pool, int ret, const char *format, ...) __attribute__((format(printf, 3, 4)));
 extern char *pool_errstr(Pool *pool);
@@ -297,6 +295,7 @@ static inline const char *pool_solvid2str(Pool *pool, Id p)
 {
   return pool_solvable2str(pool, pool->solvables + p);
 }
+extern const char *pool_solvidset2str(Pool *pool, Queue *q);
 
 void pool_set_languages(Pool *pool, const char **languages, int nlanguages);
 Id pool_id2langid(Pool *pool, Id id, const char *lang, int create);
@@ -362,7 +361,7 @@ void pool_set_whatprovides(Pool *pool, Id id, Id providers);
  *   key   - search only this key
  *   match - key must match this string
  */
-void pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct s_Repodata *data, struct s_Repokey *key, struct s_KeyValue *kv), void *cbdata);
+void pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, struct s_Repokey *key, struct s_KeyValue *kv), void *cbdata);
 
 void pool_clear_pos(Pool *pool);
 
index 6727760..e5fb5bf 100644 (file)
 
 static const char *archpolicies[] = {
 #if defined(FEDORA) || defined(MAGEIA)
+  "x86_64_v4", "x86_64_v4:x86_64_v3:x86_64_v2:x86_64:athlon:i686:i586:i486:i386",
+  "x86_64_v3", "x86_64_v3:x86_64_v2:x86_64:athlon:i686:i586:i486:i386",
+  "x86_64_v2", "x86_64_v2:x86_64:athlon:i686:i586:i486:i386",
   "x86_64",    "x86_64:athlon:i686:i586:i486:i386",
 #else
+  "x86_64_v4", "x86_64_v4:x86_64_v3:x86_64_v2:x86_64:i686:i586:i486:i386",
+  "x86_64_v3", "x86_64_v3:x86_64_v2:x86_64:i686:i586:i486:i386",
+  "x86_64_v2", "x86_64_v2:x86_64:i686:i586:i486:i386",
   "x86_64",    "x86_64:i686:i586:i486:i386",
 #endif
   "i686",      "i686:i586:i486:i386",
index c69f375..bccdfc1 100644 (file)
 #ifndef LIBSOLV_POOLTYPES_H
 #define LIBSOLV_POOLTYPES_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* format version number for .solv files */
 #define SOLV_VERSION_0 0
 #define SOLV_VERSION_1 1
@@ -36,10 +40,23 @@ typedef struct s_Stringpool Stringpool;
 struct s_Pool;
 typedef struct s_Pool Pool;
 
+struct s_Repo;
+typedef struct s_Repo Repo;
+
+struct s_Repodata;
+typedef struct s_Repodata Repodata;
+
+struct s_Solvable;
+typedef struct s_Solvable Solvable;
+
 /* identifier for string values */
 typedef int Id;                /* must be signed!, since negative Id is used in solver rules to denote negation */
 
 /* offset value, e.g. used to 'point' into the stringspace */
 typedef unsigned int Offset;
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LIBSOLV_POOLTYPES_H */
index 0bd48d4..b0a421c 100644 (file)
@@ -22,6 +22,7 @@
 #include "pool.h"
 #include "util.h"
 #include "evr.h"
+#include "policy.h"
 #include "solverdebug.h"
 
 /**********************************************************************************/
@@ -220,6 +221,16 @@ solver_disableproblemset(Solver *solv, int start)
     solver_disableproblem(solv, solv->problems.elements[i]);
 }
 
+#ifdef SUSE
+static inline int
+suse_isptf(Pool *pool, Solvable *s)
+{
+  if (!strncmp("ptf-", pool_id2str(pool, s->name), 4))
+    return 1;
+  return 0;
+}
+#endif
+
 /*-------------------------------------------------------------------
  * try to fix a problem by auto-uninstalling packages
  */
@@ -250,6 +261,10 @@ solver_autouninstall(Solver *solv, int start)
          Id p = solv->installed->start + (v - solv->updaterules);
          if (m && !MAPTST(m, v - solv->updaterules))
            continue;
+#ifdef SUSE
+         if (suse_isptf(pool, pool->solvables + p))
+           continue;   /* do not autouninstall ptf packages */
+#endif
          if (pool->considered && !MAPTST(pool->considered, p))
            continue;   /* do not uninstalled disabled packages */
          if (solv->bestrules_info && solv->bestrules_end > solv->bestrules)
@@ -972,6 +987,7 @@ solver_solutionelement_internalid(Solver *solv, Id problem, Id solution)
   return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 3];
 }
 
+/* currently just SOLVER_CLEANDEPS */
 Id
 solver_solutionelement_extrajobflags(Solver *solv, Id problem, Id solution)
 {
@@ -1026,6 +1042,56 @@ solver_next_solutionelement(Solver *solv, Id problem, Id solution, Id element, I
   return element + 1;
 }
 
+static inline void
+queue_push3(Queue *q, Id id1, Id id2, Id id3)
+{
+  queue_push(q, id1);
+  queue_push2(q, id2, id3);
+}
+
+static void
+add_expanded_replace(Solver *solv, Id p, Id rp, Queue *q)
+{
+  int illegal = policy_is_illegal(solv, solv->pool->solvables + p, solv->pool->solvables + rp, 0);
+  if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0)
+    queue_push3(q, SOLVER_SOLUTION_REPLACE_DOWNGRADE, p, rp);
+  if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0)
+    queue_push3(q, SOLVER_SOLUTION_REPLACE_ARCHCHANGE, p, rp);
+  if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0)
+    queue_push3(q, SOLVER_SOLUTION_REPLACE_VENDORCHANGE, p, rp);
+  if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0)
+    queue_push3(q, SOLVER_SOLUTION_REPLACE_NAMECHANGE, p, rp);
+  if (!illegal || (illegal & ~(POLICY_ILLEGAL_DOWNGRADE | POLICY_ILLEGAL_ARCHCHANGE | POLICY_ILLEGAL_VENDORCHANGE | POLICY_ILLEGAL_NAMECHANGE)))
+    queue_push3(q, SOLVER_SOLUTION_REPLACE, p, rp);
+}
+
+/* solutionelements are (type, p, rp) triplets */
+void
+solver_all_solutionelements(Solver *solv, Id problem, Id solution, int expandreplaces, Queue *q)
+{
+  int i, cnt;
+  Id solidx = solv->problems.elements[problem * 2 - 1];
+  solidx = solv->solutions.elements[solidx + solution];
+  queue_empty(q);
+  if (!solidx)
+    return;
+  cnt = solv->solutions.elements[solidx++];
+  for (i = 0; i < cnt; i++)
+    {
+      Id p = solv->solutions.elements[solidx++];
+      Id rp = solv->solutions.elements[solidx++];
+      if (p > 0)
+       {
+         if (rp && expandreplaces)
+           add_expanded_replace(solv, p, rp, q);
+         else
+           queue_push3(q, rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE, p, rp);
+       }
+      else
+        queue_push3(q, p, rp, 0);
+    }
+}
+
 void
 solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job)
 {
@@ -1043,6 +1109,11 @@ solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue *
       job->elements[rp] = 0;
       return;
     }
+  if (p == SOLVER_SOLUTION_ERASE)
+    {
+      p = rp;
+      rp = 0;
+    }
   if (rp <= 0 && p <= 0)
     return;    /* just in case */
   if (rp > 0)
@@ -1388,62 +1459,66 @@ solver_problem2str(Solver *solv, Id problem)
 }
 
 const char *
-solver_solutionelement2str(Solver *solv, Id p, Id rp)
+solver_solutionelementtype2str(Solver *solv, int type, Id p, Id rp)
 {
   Pool *pool = solv->pool;
-  if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB)
-    {
-      Id how, what;
-      if (p == SOLVER_SOLUTION_JOB)
-       rp += solv->pooljobcnt;
-      how = solv->job.elements[rp - 1];
-      what = solv->job.elements[rp];
-      return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, how, what, 0), 0);
-    }
-  else if (p == SOLVER_SOLUTION_INFARCH)
+  Solvable *s;
+  const char *str;
+
+  switch (type)
     {
-      Solvable *s = pool->solvables + rp;
+    case SOLVER_SOLUTION_JOB:
+    case SOLVER_SOLUTION_POOLJOB:
+      if (type == SOLVER_SOLUTION_JOB)
+       p += solv->pooljobcnt;
+      return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, solv->job.elements[p - 1], solv->job.elements[p], 0), 0);
+    case SOLVER_SOLUTION_INFARCH:
+      s = pool->solvables + p;
       if (solv->installed && s->repo == solv->installed)
         return pool_tmpjoin(pool, "keep ", pool_solvable2str(pool, s), " despite the inferior architecture");
       else
         return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the inferior architecture");
-    }
-  else if (p == SOLVER_SOLUTION_DISTUPGRADE)
-    {
-      Solvable *s = pool->solvables + rp;
+    case SOLVER_SOLUTION_DISTUPGRADE:
+      s = pool->solvables + p;
       if (solv->installed && s->repo == solv->installed)
         return pool_tmpjoin(pool, "keep obsolete ", pool_solvable2str(pool, s), 0);
       else
         return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " from excluded repository");
-    }
-  else if (p == SOLVER_SOLUTION_BEST)
-    {
-      Solvable *s = pool->solvables + rp;
+    case SOLVER_SOLUTION_BEST:
+      s = pool->solvables + p;
       if (solv->installed && s->repo == solv->installed)
         return pool_tmpjoin(pool, "keep old ", pool_solvable2str(pool, s), 0);
       else
         return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the old version");
+    case SOLVER_SOLUTION_BLACK:
+      return pool_tmpjoin(pool, "install ", pool_solvid2str(pool, p), 0);
+    case SOLVER_SOLUTION_STRICTREPOPRIORITY:
+      return pool_tmpjoin(pool, "install ", pool_solvid2str(pool, p), " despite the repo priority");
+
+    /* replace types: p -> rp */
+    case SOLVER_SOLUTION_ERASE:
+      return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0);
+    case SOLVER_SOLUTION_REPLACE:
+      str = pool_tmpjoin(pool, "allow replacement of ", pool_solvid2str(pool, p), 0);
+      return pool_tmpappend(pool, str, " with ", pool_solvid2str(pool, rp));
+    case SOLVER_SOLUTION_REPLACE_DOWNGRADE:
+      return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_DOWNGRADE, pool->solvables + p, pool->solvables + rp), 0);
+    case SOLVER_SOLUTION_REPLACE_ARCHCHANGE:
+      return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_ARCHCHANGE, pool->solvables + p, pool->solvables + rp), 0);
+    case SOLVER_SOLUTION_REPLACE_VENDORCHANGE:
+      return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_VENDORCHANGE, pool->solvables + p, pool->solvables + rp), 0);
+    case SOLVER_SOLUTION_REPLACE_NAMECHANGE:
+      return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_NAMECHANGE, pool->solvables + p, pool->solvables + rp), 0);
+    default:
+      break;
     }
-  else if (p == SOLVER_SOLUTION_BLACK)
-    {
-      Solvable *s = pool->solvables + rp;
-      return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), 0);
-    }
-  else if (p > 0 && rp == 0)
-    return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0);
-  else if (p == SOLVER_SOLUTION_STRICTREPOPRIORITY)
-    {
-      Solvable *s = pool->solvables + rp;
-      return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the repo priority");
-    }
-  else if (p > 0 && rp > 0)
-    {
-      const char *sp = pool_solvid2str(pool, p);
-      const char *srp = pool_solvid2str(pool, rp);
-      const char *str = pool_tmpjoin(pool, "allow replacement of ", sp, 0);
-      return pool_tmpappend(pool, str, " with ", srp);
-    }
-  else
-    return "bad solution element";
+  return "bad solution element";
 }
 
+const char *
+solver_solutionelement2str(Solver *solv, Id p, Id rp)
+{
+  if (p > 0)
+    return solver_solutionelementtype2str(solv, rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE, p, rp);
+  return solver_solutionelementtype2str(solv, p, rp, 0);
+}
index f9b1efc..9e4ad1f 100644 (file)
@@ -29,6 +29,13 @@ struct s_Solver;
 #define SOLVER_SOLUTION_POOLJOB                (-4)
 #define SOLVER_SOLUTION_BLACK          (-5)
 #define SOLVER_SOLUTION_STRICTREPOPRIORITY     (-6)
+/* replace solution types */
+#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)
+#define SOLVER_SOLUTION_REPLACE_NAMECHANGE      (-105)
 
 void solver_recordproblem(struct s_Solver *solv, Id rid);
 void solver_fixproblem(struct s_Solver *solv, Id rid);
@@ -45,6 +52,7 @@ unsigned int solver_solutionelement_count(struct s_Solver *solv, Id problem, Id
 Id solver_solutionelement_internalid(struct s_Solver *solv, Id problem, Id solution);
 Id solver_solutionelement_extrajobflags(struct s_Solver *solv, Id problem, Id solution);
 Id solver_next_solutionelement(struct s_Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp);
+void solver_all_solutionelements(struct s_Solver *solv, Id problem, Id solution, int expandreplaces, Queue *q);
 
 void solver_take_solutionelement(struct s_Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job);
 void solver_take_solution(struct s_Solver *solv, Id problem, Id solution, Queue *job);
@@ -55,6 +63,7 @@ void solver_findallproblemrules(struct s_Solver *solv, Id problem, Queue *rules)
 extern const char *solver_problemruleinfo2str(struct s_Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep);
 extern const char *solver_problem2str(struct s_Solver *solv, Id problem);
 extern const char *solver_solutionelement2str(struct s_Solver *solv, Id p, Id rp);
+extern const char *solver_solutionelementtype2str(struct s_Solver *solv, int type, Id p, Id rp);
 
 #ifdef __cplusplus
 }
index b503431..1f270d7 100644 (file)
@@ -24,7 +24,7 @@
 extern "C" {
 #endif
 
-typedef struct s_Repo {
+struct s_Repo {
   const char *name;            /* name pointer */
   Id repoid;                   /* our id */
   void *appdata;               /* application private pointer */
@@ -56,7 +56,7 @@ typedef struct s_Repo {
   int lastmarker;
   Offset lastmarkerpos;
 #endif /* LIBSOLV_INTERNAL */
-} Repo;
+};
 
 extern Repo *repo_create(Pool *pool, const char *name);
 extern void repo_free(Repo *repo, int reuseids);
index a73eebf..c965a76 100644 (file)
@@ -266,6 +266,8 @@ struct extdata {
   int len;
 };
 
+#define DIRIDCACHE_SIZE 1024
+
 struct cbdata {
   Pool *pool;
   Repo *repo;
@@ -300,6 +302,8 @@ struct cbdata {
 
   Id lastdirid;                /* last dir id seen in this repodata */
   Id lastdirid_own;    /* last dir id put in own pool */
+
+  Id diridcache[3 * DIRIDCACHE_SIZE];
 };
 
 #define NEEDID_BLOCK 1023
@@ -564,22 +568,40 @@ putinownpool(struct cbdata *cbdata, Repodata *data, Id id)
 static Id
 putinowndirpool_slow(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
 {
-  Id compid, parent;
+  Id compid, parent, id;
+  Id *cacheent;
 
   parent = dirpool_parent(dp, dir);
   if (parent)
-    parent = putinowndirpool_slow(cbdata, data, dp, parent);
+    {
+      /* put parent in own pool first */
+      cacheent = cbdata->diridcache + (parent & (DIRIDCACHE_SIZE - 1));
+      if (cacheent[0] == parent && cacheent[DIRIDCACHE_SIZE] == data->repodataid)
+        parent = cacheent[2 * DIRIDCACHE_SIZE];
+      else
+        parent = putinowndirpool_slow(cbdata, data, dp, parent);
+    }
   compid = dirpool_compid(dp, dir);
   if (cbdata->ownspool && compid > 1 && (!cbdata->clonepool || data->localpool))
     compid = putinownpool(cbdata, data, compid);
-  return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
+  id = dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
+  /* cache result */
+  cacheent = cbdata->diridcache + (dir & (DIRIDCACHE_SIZE - 1));
+  cacheent[0] = dir;
+  cacheent[DIRIDCACHE_SIZE] = data->repodataid;
+  cacheent[2 * DIRIDCACHE_SIZE] = id;
+  return id;
 }
 
 static inline Id
 putinowndirpool(struct cbdata *cbdata, Repodata *data, Id dir)
 {
+  Id *cacheent;
   if (dir && dir == cbdata->lastdirid)
     return cbdata->lastdirid_own;
+  cacheent = cbdata->diridcache + (dir & (DIRIDCACHE_SIZE - 1));
+  if (dir && cacheent[0] == dir && cacheent[DIRIDCACHE_SIZE] == data->repodataid)
+    return cacheent[2 * DIRIDCACHE_SIZE];
   cbdata->lastdirid = dir;
   cbdata->lastdirid_own = putinowndirpool_slow(cbdata, data, &data->dirpool, dir);
   return cbdata->lastdirid_own;
@@ -1019,6 +1041,7 @@ static Id verticals[] = {
   SOLVABLE_LEADSIGID,
   SOLVABLE_CHANGELOG_AUTHOR,
   SOLVABLE_CHANGELOG_TEXT,
+  SOLVABLE_SIGNATUREDATA,
   0
 };
 
index 2504f2a..fda463e 100644 (file)
@@ -34,7 +34,6 @@ extern "C" {
 #define SIZEOF_SHA384  48
 #define SIZEOF_SHA512  64
 
-struct s_Repo;
 struct s_KeyValue;
 
 typedef struct s_Repokey {
@@ -67,13 +66,13 @@ struct dircache;
 #define REPODATA_FILELIST_FILTERED     1
 #define REPODATA_FILELIST_EXTENSION    2
 
-typedef struct s_Repodata {
+struct s_Repodata {
   Id repodataid;               /* our id */
-  struct s_Repo *repo;         /* back pointer to repo */
+  Repo *repo;                  /* back pointer to repo */
 
   int state;                   /* available, stub or error */
 
-  void (*loadcallback)(struct s_Repodata *);
+  void (*loadcallback)(Repodata *);
 
   int start;                   /* start of solvables this repodata is valid for */
   int end;                     /* last solvable + 1 of this repodata */
@@ -140,7 +139,7 @@ typedef struct s_Repodata {
   struct dircache *dircache;
 #endif
 
-} Repodata;
+};
 
 #define SOLVID_META            -1
 #define SOLVID_POS             -2
@@ -149,7 +148,7 @@ typedef struct s_Repodata {
 /*-----
  * management functions
  */
-void repodata_initdata(Repodata *data, struct s_Repo *repo, int localpool);
+void repodata_initdata(Repodata *data, Repo *repo, int localpool);
 void repodata_freedata(Repodata *data);
 
 void repodata_free(Repodata *data);
index 2c56959..7d57cfd 100644 (file)
@@ -183,7 +183,7 @@ solver_unifyrules(Solver *solv)
            binr++;
          else
            {
-             dp = solv->pool->whatprovidesdata + r->d;
+             dp = pool->whatprovidesdata + r->d;
              while (*dp++)
                lits++;
            }
@@ -659,8 +659,12 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
              int oldcount = solv->ruleinfoq->count;
              addpkgrule(solv, qele[0], 0, d, type, dep);
              /* fixup from element of ruleinfo */
-             if (solv->ruleinfoq->count > oldcount)
-               solv->ruleinfoq->elements[oldcount + 1] = p;
+             if (solv->ruleinfoq->count > oldcount && solv->ruleinfoq->elements[oldcount + 1] != p)
+               {
+                 if (solv->ruleinfoq->elements[oldcount + 2])
+                   solv->ruleinfoq->elements[oldcount + 2] = solv->ruleinfoq->elements[oldcount + 1];
+                 solv->ruleinfoq->elements[oldcount + 1] = p;
+               }
            }
          else
            addpkgrule(solv, qele[0], 0, d, type, dep);
@@ -1668,6 +1672,16 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
       if (first)
        continue;               /* not the first in the group */
 
+      if (!bestscore && allowedarchs.count > 1 && pool->implicitobsoleteusescolors)
+       {
+         for (j = 0; j < allowedarchs.count; j++)
+           {
+             a = pool_arch2score(pool, allowedarchs.elements[j]);
+             if (a && a != 1 && (!bestscore || a < bestscore))
+               bestscore = a;
+           }
+       }
+
       if (!bestscore)
        continue;               /* did not find a score for this group */
 
@@ -1931,8 +1945,8 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
 void
 solver_createdupmaps(Solver *solv)
 {
-  Queue *job = &solv->job;
   Pool *pool = solv->pool;
+  Queue *job = &solv->job;
   Repo *installed = solv->installed;
   Id select, how, what, p, pp;
   Solvable *s;
@@ -2199,12 +2213,12 @@ reenableblackrule(Solver *solv, Id p)
 void
 solver_addblackrules(Solver *solv)
 {
-  int i;
-  Id how, select, what, p, pp;
-  Queue *job = &solv->job;
   Pool *pool = solv->pool;
   Repo *installed = solv->installed;
+  Id how, select, what, p, pp;
+  Queue *job = &solv->job;
   Map updatemap;
+  int i;
 
   map_init(&updatemap, 0);
   solv->blackrules = solv->nrules;
@@ -2353,6 +2367,31 @@ reenablerepopriorule(Solver *solv, Id name)
 #define DISABLE_BLACK   4
 #define DISABLE_REPOPRIO 5
 
+/* check if installed package p is in lock-step with another installed package */
+static int
+installed_is_in_lockstep(Solver *solv, Id p)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  int rid;
+  Id pp, l;
+  Rule *r;
+
+  if (!installed)
+    return 0;
+  for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
+    {
+      if (r->p >= 0)
+       continue;
+      if (pool->solvables[-r->p].repo != installed)
+       continue;
+      FOR_RULELITERALS(l, pp, r)
+       if (l == p)
+         return 1;
+    }
+  return 0;
+}
+
 static void
 jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
 {
@@ -2485,6 +2524,20 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
        return;
       /* now the hard part: disable some update rules */
 
+      /* if the job asks for a single solvable to stay, disable the update rule */
+      if (select == SOLVER_SOLVABLE && pool->solvables[what].repo == installed && solv->bestrules_info)
+        if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
+         {
+           int ni = solv->bestrules_end - solv->bestrules;
+           for (i = solv->bestrules_up - solv->bestrules; i < ni; i++)
+             if (solv->bestrules_info[i] == what)
+               {
+                 queue_push2(q, DISABLE_UPDATE, what);         /* will also disable the best rule */
+                 break;
+               }
+           return;
+         }
+
       /* first check if we have installed or multiversion packages in the job */
       FOR_JOB_SELECT(p, pp, select, what)
        {
@@ -2560,6 +2613,20 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
     case SOLVER_ERASE:
       if (!installed)
        break;
+      set = how & SOLVER_SETMASK;
+      if (!(set & (SOLVER_NOAUTOSET | SOLVER_SETARCH)) && pool->implicitobsoleteusescolors && solv->infarchrules != solv->infarchrules_end)
+       {
+         if (select == SOLVER_SOLVABLE)
+           set |= SOLVER_SETARCH;
+         else if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(what))
+           {
+             Reldep *rd = GETRELDEP(pool, what);
+             if (rd->flags <= 7 && ISRELDEP(rd->name))
+               rd = GETRELDEP(pool, rd->name);
+             if (rd->flags == REL_ARCH)
+               set |= SOLVER_SETARCH;
+           }
+       }
       if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
        {
          FOR_REPO_SOLVABLES(installed, p, s)
@@ -2569,6 +2636,9 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
        if (pool->solvables[p].repo == installed)
          {
            queue_push2(q, DISABLE_UPDATE, p);
+           if ((set & SOLVER_SETARCH) != 0 && pool->implicitobsoleteusescolors && solv->infarchrules != solv->infarchrules_end)
+             if (installed_is_in_lockstep(solv, p))
+               queue_push2(q, DISABLE_INFARCH, pool->solvables[p].name);               /* allow to break the lock-step */
 #ifdef ENABLE_LINKED_PKGS
            if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
              queue_push2(q, DISABLE_UPDATE, solv->instbuddy[p - installed->start]);
@@ -3008,7 +3078,7 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
          qp = rq.elements[i + 1];
          qo = rq.elements[i + 2];
          qd = rq.elements[i + 3];
-         if (type == SOLVER_RULE_PKG || type > qt)
+         if (type == SOLVER_RULE_PKG || qt == SOLVER_RULE_PKG_SAME_NAME || type > qt)
            {
              type = qt;
              if (fromp)
@@ -3017,6 +3087,8 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
                *top = qo;
              if (depp)
                *depp = qd;
+             if (qt == SOLVER_RULE_PKG_SAME_NAME)
+               break;                  /* prefer SOLVER_RULE_PKG_SAME_NAME */
            }
        }
       queue_free(&rq);
@@ -3076,8 +3148,12 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
     }
   if (rid >= solv->bestrules && rid < solv->bestrules_end)
     {
+      /* > 0: the package we are updating */
       if (fromp && solv->bestrules_info[rid - solv->bestrules] > 0)
        *fromp = solv->bestrules_info[rid - solv->bestrules];
+      /* < 0: the job rule */
+      if (top && solv->bestrules_info[rid - solv->bestrules] < 0)
+       *top = -solv->bestrules_info[rid - solv->bestrules];
       return SOLVER_RULE_BEST;
     }
   if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
@@ -3109,9 +3185,17 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
       return SOLVER_RULE_STRICT_REPO_PRIORITY;
     }
   if (rid >= solv->choicerules && rid < solv->choicerules_end)
-    return SOLVER_RULE_CHOICE;
+    {
+      if (solv->choicerules_info && fromp)
+       *fromp = solv->choicerules_info[rid - solv->choicerules];
+      return SOLVER_RULE_CHOICE;
+    }
   if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end)
-    return SOLVER_RULE_RECOMMENDS;
+    {
+      if (solv->recommendsrules_info && fromp)
+       *fromp = solv->recommendsrules_info[rid - solv->recommendsrules];
+      return SOLVER_RULE_RECOMMENDS;
+    }
   if (rid >= solv->learntrules)
     return SOLVER_RULE_LEARNT;
   return SOLVER_RULE_UNKNOWN;
@@ -3196,10 +3280,10 @@ solver_rule2job(Solver *solv, Id rid, Id *whatp)
 Id
 solver_rule2solvable(Solver *solv, Id rid)
 {
-  if (rid >= solv->updaterules && rid < solv->updaterules_end)
-    return rid - solv->updaterules;
-  if (rid >= solv->featurerules && rid < solv->featurerules_end)
-    return rid - solv->featurerules;
+  if (rid >= solv->updaterules && rid < solv->updaterules_end && solv->installed)
+    return solv->installed->start + (rid - solv->updaterules);
+  if (rid >= solv->featurerules && rid < solv->featurerules_end && solv->installed)
+    return solv->installed->start + (rid - solv->featurerules);
   return 0;
 }
 
@@ -3365,6 +3449,7 @@ solver_addchoicerules(Solver *solv)
   int lastaddedcnt;
   unsigned int now;
   int isinstalled;
+  int dodowngradecheck = solv->allowdowngrade;
 
   solv->choicerules = solv->nrules;
   if (!pool->installed)
@@ -3373,6 +3458,8 @@ solver_addchoicerules(Solver *solv)
       return;
     }
   now = solv_timems(0);
+  if ((solv->dupinvolvedmap_all || solv->dupinvolvedmap.size) && solv->dup_allowdowngrade)
+    dodowngradecheck = 1;
   queue_init(&q);
   queue_init(&qi);
   queue_init(&qcheck);
@@ -3448,8 +3535,16 @@ solver_addchoicerules(Solver *solv)
          /* do extra checking for packages related to installed packages */
          for (i = j = 0; i < qi.count; i += 2)
            {
+             int isdowngrade = 0;
              p2 = qi.elements[i];
-             if (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p2 - solv->installed->start)))
+             if (dodowngradecheck)
+               {
+                 p = qi.elements[i + 1];
+                 if (pool->solvables[p2].name == pool->solvables[p].name)
+                   if (pool_evrcmp(pool, pool->solvables[p2].evr, pool->solvables[p].evr, EVRCMP_COMPARE) > 0)
+                     isdowngrade = 1;
+               }
+             if (isdowngrade || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p2 - solv->installed->start)))
                {
                  if (solver_choicerulecheck(solv, p2, r, &m, &qcheck))
                    continue;
@@ -3593,6 +3688,7 @@ solver_disablechoicerules(Solver *solv, Rule *r)
       if (p)
        solver_disablerule(solv, r);
     }
+  map_free(&m);
 }
 
 static void
@@ -3957,7 +4053,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q)
     }
   /* find names so that we can build groups */
   queue_init_clone(&qn, q);
-  prune_to_best_version(solv->pool, &qn);
+  prune_to_best_version(pool, &qn);
 #if 0
 {
   for (i = 0; i < qn.count; i++)
@@ -4078,7 +4174,7 @@ for (j = 0; j < qq.count; j++)
 
       if (!qq.count)
        continue;
-      /* at least two goups, build rules */
+      /* at least two groups, build rules */
       group = 0;
       for (j = 0; j < qq.count; j++)
        {
@@ -4260,3 +4356,122 @@ solver_check_brokenorphanrules(Solver *solv, Queue *dq)
     }
 }
 
+const char *
+solver_ruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep)
+{
+  Pool *pool = solv->pool;
+  char *s;
+  Solvable *ss;
+  switch (type)
+    {
+    case SOLVER_RULE_DISTUPGRADE:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not belong to a distupgrade repository", 0);
+    case SOLVER_RULE_INFARCH:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " has inferior architecture", 0);
+    case SOLVER_RULE_UPDATE:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " needs to stay installed or be updated", 0);
+    case SOLVER_RULE_FEATURE:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " needs to stay installed or be updated/downgraded", 0);
+    case SOLVER_RULE_JOB:
+      return pool_tmpjoin(pool, "job ", pool_job2str(pool, target, dep, 0), 0);
+    case SOLVER_RULE_JOB_UNSUPPORTED:
+      return pool_tmpjoin(pool, "unsupported job ", pool_job2str(pool, target, dep, 0), 0);
+    case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
+      return pool_tmpjoin(pool, "nothing provides requested ", pool_dep2str(pool, dep), 0);
+    case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
+      return pool_tmpjoin(pool, "requested package ", pool_dep2str(pool, dep), " does not exist");
+    case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
+      return pool_tmpjoin(pool, "requested ", pool_dep2str(pool, dep), " is provided by the system");
+    case SOLVER_RULE_BEST:
+      if (source > 0)
+        return pool_tmpjoin(pool, "install best update candidate for ", pool_solvid2str(pool, source), 0);
+      if (target > 0)
+       {
+         target = solver_rule2job(solv, target, &dep);
+         return pool_tmpjoin(pool, "best package for job ", pool_job2str(pool, target, dep, 0), 0);
+       }
+     return "best rule";
+    case SOLVER_RULE_PKG:
+      return "bad pkg rule type";
+    case SOLVER_RULE_PKG_NOT_INSTALLABLE:
+      ss = pool->solvables + source;
+      if (pool_disabled_solvable(pool, ss))
+        return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is disabled", 0);
+      if (ss->arch && ss->arch != ARCH_SRC && ss->arch != ARCH_NOSRC &&
+          pool->id2arch && pool_arch2score(pool, ss->arch) == 0)
+        return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not have a compatible architecture", 0);
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is not installable", 0);
+    case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+      s = pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
+      return pool_tmpappend(pool, s, " needed by ", pool_solvid2str(pool, source));
+    case SOLVER_RULE_PKG_SAME_NAME:
+      s = pool_tmpjoin(pool, "cannot install both ", pool_solvid2str(pool, source), 0);
+      return pool_tmpappend(pool, s, " and ", pool_solvid2str(pool, target));
+    case SOLVER_RULE_PKG_CONFLICTS:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " conflicts with ", pool_dep2str(pool, dep));
+      if (target)
+        s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_SELF_CONFLICT:
+      s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " conflicts with ", 0);
+      return pool_tmpappend(pool, s, pool_dep2str(pool, dep), " provided by itself");
+    case SOLVER_RULE_PKG_OBSOLETES:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " obsoletes ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+      s = pool_tmpjoin(pool, "installed ", pool_solvid2str(pool, source), 0);
+      s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " implicitly obsoletes ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_PKG_REQUIRES:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " requires ", pool_dep2str(pool, dep));
+    case SOLVER_RULE_PKG_RECOMMENDS:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " recommends ", pool_dep2str(pool, dep));
+    case SOLVER_RULE_PKG_CONSTRAINS:
+      s = pool_tmpappend(pool, pool_solvid2str(pool, source), " has constraint ", pool_dep2str(pool, dep));
+      return pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, target));
+    case SOLVER_RULE_PKG_SUPPLEMENTS:
+      s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " supplements ", pool_dep2str(pool, dep));
+      if (target)
+       s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
+      return s;
+    case SOLVER_RULE_YUMOBS:
+      s = pool_tmpjoin(pool, "both ", pool_solvid2str(pool, source), " and ");
+      s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete ");
+      return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0);
+    case SOLVER_RULE_BLACK:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " can only be installed by a direct request", 0);
+    case SOLVER_RULE_STRICT_REPO_PRIORITY:
+      return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is excluded by strict repo priority", 0);
+    case SOLVER_RULE_LEARNT:
+      return "learnt rule";
+    case SOLVER_RULE_CHOICE:
+      if (source > 0)
+       {
+         const char *s2;
+          type = solver_ruleinfo(solv, source, &source, &target, &dep);
+         s2 = solver_ruleinfo2str(solv, type, source, target, dep);
+         return pool_tmpjoin(pool, s2, " (limited version)", 0);
+       }
+      return "choice rule";
+    case SOLVER_RULE_RECOMMENDS:
+      if (source > 0)
+       {
+         const char *s2;
+          type = solver_ruleinfo(solv, source, &source, &target, &dep);
+         s2 = solver_ruleinfo2str(solv, type, source, target, dep);
+         return pool_tmpjoin(pool, s2, " (limited version)", 0);
+       }
+      return "recommends rule";
+    default:
+      return "bad rule type";
+    }
+}
index 043d0a0..17af2df 100644 (file)
 #ifndef LIBSOLV_RULES_H
 #define LIBSOLV_RULES_H
 
+#include "pooltypes.h"
+#include "bitmap.h"
+#include "queue.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+
 /* ----------------------------------------------
  * Rule
  *
@@ -60,6 +65,7 @@ typedef enum {
   SOLVER_RULE_PKG_INSTALLED_OBSOLETES,
   SOLVER_RULE_PKG_RECOMMENDS,
   SOLVER_RULE_PKG_CONSTRAINS,
+  SOLVER_RULE_PKG_SUPPLEMENTS,
   SOLVER_RULE_UPDATE = 0x200,
   SOLVER_RULE_FEATURE = 0x300,
   SOLVER_RULE_JOB = 0x400,
@@ -161,6 +167,11 @@ extern Id   solver_rule2job(struct s_Solver *solv, Id rid, Id *whatp);
 extern Id   solver_rule2solvable(struct s_Solver *solv, Id rid);
 extern void solver_rule2rules(struct s_Solver *solv, Id rid, Queue *q, int recursive);
 extern Id   solver_rule2pkgrule(struct s_Solver *solv, Id rid);
+extern const char *solver_ruleinfo2str(struct s_Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep);
+
+/* rule infos for weakdep decisions */
+extern int  solver_allweakdepinfos(struct s_Solver *solv, Id p, Queue *rq);
+extern SolverRuleinfo solver_weakdepinfo(struct s_Solver *solv, Id p, Id *fromp, Id *top, Id *depp);
 
 /* orphan handling */
 extern void solver_breakorphans(struct s_Solver *solv);
index 181d9bc..657a8b8 100644 (file)
@@ -24,6 +24,7 @@
 #include "poolvendor.h"
 #include "chksum.h"
 #include "linkedpkg.h"
+#include "evr.h"
 
 const char *
 pool_solvable2str(Pool *pool, Solvable *s)
@@ -188,16 +189,11 @@ solvable_lookup_str_base(Solvable *s, Id keyname, Id basekeyname, int usebase)
        }
 #ifdef ENABLE_LINKED_PKGS
       /* autopattern/product translation magic */
-      if (pass)
+      if (pass == 1 && name == s->name)
        {
-         const char *n = pool_id2str(pool, name);
-         if (*n == 'p')
-           {
-             if (!strncmp("pattern:", n, 8) && (name = find_autopattern_name(pool, s)) != 0)
-               pass = -1;
-             if (!strncmp("product:", n, 8) && (name = find_autoproduct_name(pool, s)) != 0)
-               pass = -1;
-           }
+         name = find_autopackage_name(pool, s);
+         if (name && name != s->name)
+           pass = -1;  /* start over with new name */
        }
 #endif
     }
@@ -746,3 +742,124 @@ solvable_matchessolvable(Solvable *s, Id keyname, Id solvid, Queue *depq, int ma
   queue_free(&qq);
   return res;
 }
+
+static int
+solvidset2str_evrcmp(Pool *pool, Id a, Id b)
+{
+  Solvable *as = pool->solvables + a, *bs = pool->solvables + b;
+  return as->evr != bs->evr ? pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE) : 0;
+}
+
+static int
+solvidset2str_sortcmp(const void *va, const void *vb, void *vd)
+{
+  Pool *pool = vd;
+  Solvable *as = pool->solvables + *(Id *)va, *bs = pool->solvables + *(Id *)vb;
+  if (as->name != bs->name)
+    {
+      int r = strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name));
+      if (r)
+       return r;
+      return as->name - bs->name;
+    }
+  if (as->evr != bs->evr)
+    {
+      int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE);
+      if (r)
+        return r;
+    }
+  return *(Id *)va - *(Id *)vb;
+}
+
+static const char *
+solvidset2str_striprelease(Pool *pool, Id evr, Id otherevr)
+{
+  const char *evrstr = pool_id2str(pool, evr);
+  const char *r = strchr(evrstr, '-');
+  char *evrstr2;
+  int cmp;
+  if (!r)
+    return evrstr;
+  evrstr2 = pool_tmpjoin(pool, evrstr, 0, 0);
+  evrstr2[r - evrstr] = 0;
+  cmp = pool_evrcmp_str(pool, evrstr2, pool_id2str(pool, otherevr), pool->disttype != DISTTYPE_DEB ? EVRCMP_MATCH_RELEASE : EVRCMP_COMPARE);
+  return cmp == 1 ? evrstr2 : evrstr;
+}
+
+const char *
+pool_solvidset2str(Pool *pool, Queue *q)
+{
+  Queue pq;
+  Queue pr;
+  char *s = 0;
+  int i, j, k, kstart;
+  Id name = 0;
+
+  if (!q->count)
+    return "";
+  if (q->count == 1)
+    return pool_solvid2str(pool, q->elements[0]);
+  queue_init_clone(&pq, q);
+  queue_init(&pr);
+  solv_sort(pq.elements, pq.count, sizeof(Id), solvidset2str_sortcmp, pool);
+
+  for (i = 0; i < pq.count; i++)
+    {
+      Id p = pq.elements[i];
+      if (s)
+       s = pool_tmpappend(pool, s, ", ", 0);
+
+      if (i == 0 || pool->solvables[p].name != name)
+       {
+         Id p2, pp2;
+         name = pool->solvables[p].name;
+         queue_empty(&pr);
+         FOR_PROVIDES(p2, pp2, name)
+           if (pool->solvables[p].name == name)
+             queue_push(&pr, p2);
+         if (pr.count > 1)
+           solv_sort(pr.elements, pr.count, sizeof(Id), solvidset2str_sortcmp, pool);
+       }
+
+      for (k = 0; k < pr.count; k++)
+       if (pr.elements[k] == p)
+         break;
+      if (k == pr.count)
+       {
+         /* not in provides, list as singularity */
+         s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0);
+         continue;
+       }
+      if (k && solvidset2str_evrcmp(pool, pr.elements[k], pr.elements[k - 1]) == 0)
+       {
+         /* unclear start, list as single package */
+         s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0);
+         continue;
+       }
+      kstart = k;
+      for (j = i + 1, k = k + 1; j < pq.count; j++, k++)
+        if (k == pr.count || pq.elements[j] != pr.elements[k])
+         break;
+      while (j > i + 1 && k && k < pr.count && solvidset2str_evrcmp(pool, pr.elements[k], pr.elements[k - 1]) == 0)
+       {
+         j--;
+         k--;
+       }
+      if (k == 0 || j == i + 1)
+       {
+         s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0);
+         continue;
+       }
+      /* create an interval */
+      s = pool_tmpappend(pool, s, pool_id2str(pool, name), 0);
+      if (kstart > 0)
+        s = pool_tmpappend(pool, s, " >= ", solvidset2str_striprelease(pool, pool->solvables[pr.elements[kstart]].evr, pool->solvables[pr.elements[kstart - 1]].evr));
+      if (k < pr.count)
+        s = pool_tmpappend(pool, s, " < ", solvidset2str_striprelease(pool, pool->solvables[pr.elements[k]].evr, pool->solvables[pr.elements[k - 1]].evr));
+      i = j - 1;
+    }
+  queue_free(&pq);
+  queue_free(&pr);
+  return s;
+}
+
index 28341d6..bdce9a9 100644 (file)
@@ -151,11 +151,11 @@ makeruledecisions(Solver *solv, int disablerules)
              solv->decisionmap[vv] = v > 0 ? 1 : -1;
              IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
                {
-                 Solvable *s = solv->pool->solvables + vv;
+                 Solvable *s = pool->solvables + vv;
                  if (v < 0)
-                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (assertion)\n", pool_solvable2str(solv->pool, s));
+                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (assertion)\n", pool_solvable2str(pool, s));
                  else
-                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing  %s (assertion)\n", pool_solvable2str(solv->pool, s));
+                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing  %s (assertion)\n", pool_solvable2str(pool, s));
                }
              continue;
            }
@@ -311,11 +311,11 @@ makeruledecisions(Solver *solv, int disablerules)
              solv->decisionmap[vv] = v > 0 ? 1 : -1;
              IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE)
                {
-                 Solvable *s = solv->pool->solvables + vv;
+                 Solvable *s = pool->solvables + vv;
                  if (v < 0)
-                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (weak assertion)\n", pool_solvable2str(solv->pool, s));
+                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (weak assertion)\n", pool_solvable2str(pool, s));
                  else
-                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing  %s (weak assertion)\n", pool_solvable2str(solv->pool, s));
+                   POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing  %s (weak assertion)\n", pool_solvable2str(pool, s));
                }
              continue;
            }
@@ -986,7 +986,7 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
   FOR_RULELITERALS(v, pp, r)
     {
       if (DECISIONMAP_TRUE(v)) /* the one true literal */
-         continue;
+         abort();
       vv = v > 0 ? v : -v;
       MAPSET(&involved, vv);
     }
@@ -1005,8 +1005,12 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
       analyze_unsolvable_rule(solv, r, &weakq, &rseen);
       FOR_RULELITERALS(v, pp, r)
        {
-         if (DECISIONMAP_TRUE(v))      /* the one true literal */
+         if (DECISIONMAP_TRUE(v))      /* the one true literal, i.e. our decision */
+           {
+             if (v != solv->decisionq.elements[idx])
+               abort();
              continue;
+           }
          vv = v > 0 ? v : -v;
          MAPSET(&involved, vv);
        }
@@ -1117,10 +1121,77 @@ setpropagatelearn(Solver *solv, int level, Id decision, int disablerules, Id rul
 }
 
 static void
+queue_prunezeros(Queue *q)
+{
+  int i, j;
+  for (i = 0; i < q->count; i++)
+    if (q->elements[i] == 0)
+      break;
+  if (i == q->count)
+    return;
+  for (j = i++; i < q->count; i++)
+    if (q->elements[i])
+      q->elements[j++] = q->elements[i];
+  queue_truncate(q, j);
+}
+
+static int
+replaces_installed_package(Pool *pool, Id p, Map *noupdate)
+{
+  Repo *installed = pool->installed;
+  Solvable *s = pool->solvables + p, *s2;
+  Id p2, pp2;
+  Id obs, *obsp;
+
+  if (s->repo == installed && !(noupdate && MAPTST(noupdate, p - installed->start)))
+    return 1;
+  FOR_PROVIDES(p2, pp2, s->name)
+    {
+      s2 = pool->solvables + p2;
+      if (s2->repo == installed && s2->name == s->name && !(noupdate && MAPTST(noupdate, p - installed->start)))
+       return 1;
+    }
+  if (!s->obsoletes)
+    return 0;
+  obsp = s->repo->idarraydata + s->obsoletes;
+  while ((obs = *obsp++) != 0)
+    {
+      FOR_PROVIDES(p2, pp2, obs)
+       {
+         s2 = pool->solvables + p2;
+         if (s2->repo != pool->installed || (noupdate && MAPTST(noupdate, p - installed->start)))
+           continue;
+         if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs))
+           continue;
+         if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+           continue;
+         return 1;
+       }
+    }
+  return 0;
+}
+
+static void
+prune_dq_for_future_installed(Solver *solv, Queue *dq)
+{
+  Pool *pool = solv->pool;
+  int i, j;
+  for (i = j = 0; i < dq->count; i++)
+    {
+      Id p = dq->elements[i];
+      if (replaces_installed_package(pool, p, &solv->noupdate))
+        dq->elements[j++] = p;
+    }
+  if (j)
+    queue_truncate(dq, j);
+}
+
+
+static void
 reorder_dq_for_future_installed(Solver *solv, int level, Queue *dq)
 {
   Pool *pool = solv->pool;
-  int i, j, haveone = 0, dqcount = dq->count;
+  int i, haveone = 0, dqcount = dq->count;
   int decisionqcount = solv->decisionq.count;
   Id p;
   Solvable *s;
@@ -1161,10 +1232,7 @@ reorder_dq_for_future_installed(Solver *solv, int level, Queue *dq)
          dq->elements[i] = 0;
         }
     }
-  for (i = j = 0; i < dq->count; i++)
-    if (dq->elements[i])
-      dq->elements[j++] = dq->elements[i];
-  queue_truncate(dq, j);
+  queue_prunezeros(dq);
   FOR_REPO_SOLVABLES(solv->installed, p, s)
     if (solv->decisionmap[p] == level + 1)
       solv->decisionmap[p] = 0;
@@ -1238,6 +1306,46 @@ takebranch(Solver *solv, int pos, int end, const char *msg, int disablerules)
   return setpropagatelearn(solv, level, p, disablerules, why, reason);
 }
 
+static void
+prune_yumobs(Solver *solv, Queue *dq, Id ruleid)
+{
+  Pool *pool = solv->pool;
+  Rule *r;
+  Map m;
+  int i, j, rid;
+
+  map_init(&m, 0);
+  for (i = 0; i < dq->count - 1; i++)
+    {
+      Id p2, pp, p = dq->elements[i];
+      if (!p || !pool->solvables[p].obsoletes)
+        continue;
+      for (rid = solv->yumobsrules, r = solv->rules + rid; rid < solv->yumobsrules_end; rid++, r++)
+       if (r->p == -p)
+         break;
+      if (rid == solv->yumobsrules_end)
+       continue;
+      if (!m.size)
+       map_grow(&m, pool->nsolvables);
+      else
+       MAPZERO(&m);
+      for (; rid < solv->yumobsrules_end; rid++, r++)
+       {
+         if (r->p != -p)
+           continue;
+         FOR_RULELITERALS(p2, pp, r)
+           if (p2 > 0)
+             MAPSET(&m, p2);
+       }
+      for (j = i + 1; j < dq->count; j++)
+       if (MAPTST(&m, dq->elements[j]))
+         dq->elements[j] = 0;
+    }
+  map_free(&m);
+  queue_prunezeros(dq);
+}
+
+
 /*-------------------------------------------------------------------
  *
  * select and install
@@ -1258,9 +1366,16 @@ selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid
   if (dq->count > 1)
     policy_filter_unwanted(solv, dq, POLICY_MODE_CHOOSE);
   /* if we're resolving rules and didn't resolve the installed packages yet,
-   * do some special supplements ordering */
+   * do some special pruning and supplements ordering */
   if (dq->count > 1 && solv->do_extra_reordering)
-    reorder_dq_for_future_installed(solv, level, dq);
+    {
+      prune_dq_for_future_installed(solv, dq);
+      if (dq->count > 1)
+       reorder_dq_for_future_installed(solv, level, dq);
+    }
+  /* check if the candidates are all connected via yumobs rules */
+  if (dq->count > 1 && solv->yumobsrules_end > solv->yumobsrules)
+    prune_yumobs(solv, dq, ruleid);
   /* if we have multiple candidates we open a branch */
   if (dq->count > 1)
     createbranch(solv, level, dq, 0, ruleid);
@@ -2921,6 +3036,8 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                        continue;
                      if (solv->favormap && solv->favormap[p] > solv->favormap[solv->branches.elements[lastsi]])
                        continue;       /* current selection is more favored */
+                     if (replaces_installed_package(pool, p, &solv->noupdate))
+                       continue;       /* current selection replaces an installed package */
                      if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
                        {
                          lasti = lastsi;
@@ -3533,6 +3650,7 @@ solver_solve(Solver *solv, Queue *job)
   map_zerosize(&solv->bestupdatemap);
   solv->fixmap_all = 0;
   map_zerosize(&solv->fixmap);
+  solv->dupinvolvedmap_all = 0;
   map_zerosize(&solv->dupmap);
   map_zerosize(&solv->dupinvolvedmap);
   solv->process_orphans = 0;
@@ -4580,194 +4698,6 @@ solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap)
   pool_create_state_maps(solv->pool, &solv->decisionq, installedmap, conflictsmap);
 }
 
-/*-------------------------------------------------------------------
- *
- * decision introspection
- */
-
-int
-solver_get_decisionlevel(Solver *solv, Id p)
-{
-  return solv->decisionmap[p];
-}
-
-void
-solver_get_decisionqueue(Solver *solv, Queue *decisionq)
-{
-  queue_free(decisionq);
-  queue_init_clone(decisionq, &solv->decisionq);
-}
-
-int
-solver_get_lastdecisionblocklevel(Solver *solv)
-{
-  Id p;
-  if (solv->decisionq.count == 0)
-    return 0;
-  p = solv->decisionq.elements[solv->decisionq.count - 1];
-  if (p < 0)
-    p = -p;
-  return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p];
-}
-
-void
-solver_get_decisionblock(Solver *solv, int level, Queue *decisionq)
-{
-  Id p;
-  int i;
-
-  queue_empty(decisionq);
-  for (i = 0; i < solv->decisionq.count; i++)
-    {
-      p = solv->decisionq.elements[i];
-      if (p < 0)
-       p = -p;
-      if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
-        break;
-    }
-  if (i == solv->decisionq.count)
-    return;
-  for (i = 0; i < solv->decisionq.count; i++)
-    {
-      p = solv->decisionq.elements[i];
-      if (p < 0)
-       p = -p;
-      if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
-        queue_push(decisionq, p);
-      else
-        break;
-    }
-}
-
-int
-solver_describe_decision(Solver *solv, Id p, Id *infop)
-{
-  int i;
-  Id pp, why;
-
-  if (infop)
-    *infop = 0;
-  if (!solv->decisionmap[p])
-    return SOLVER_REASON_UNRELATED;
-  pp = solv->decisionmap[p] < 0 ? -p : p;
-  for (i = 0; i < solv->decisionq.count; i++)
-    if (solv->decisionq.elements[i] == pp)
-      break;
-  if (i == solv->decisionq.count)      /* just in case... */
-    return SOLVER_REASON_UNRELATED;
-  why = solv->decisionq_why.elements[i];
-  if (infop)
-    *infop = why > 0 ? why : -why;
-  if (why > 0)
-    return SOLVER_REASON_UNIT_RULE;
-  i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p];
-  return solv->decisionq_reason.elements[i];
-}
-
-
-void
-solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
-{
-  Pool *pool = solv->pool;
-  int i;
-  int level = solv->decisionmap[p];
-  int decisionno;
-  Solvable *s;
-
-  queue_empty(whyq);
-  if (level < 0)
-    return;    /* huh? */
-  for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++)
-    if (solv->decisionq.elements[decisionno] == p)
-      break;
-  if (decisionno == solv->decisionq.count)
-    return;    /* huh? */
-  i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p];
-  if (solv->decisionq_reason.elements[i] != SOLVER_REASON_WEAKDEP)
-    return;    /* huh? */
-
-  /* 1) list all packages that recommend us */
-  for (i = 1; i < pool->nsolvables; i++)
-    {
-      Id *recp, rec, pp2, p2;
-      if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level)
-       continue;
-      s = pool->solvables + i;
-      if (!s->recommends)
-       continue;
-      if (!solv->addalreadyrecommended && s->repo == solv->installed)
-       continue;
-      recp = s->repo->idarraydata + s->recommends;
-      while ((rec = *recp++) != 0)
-       {
-         int found = 0;
-         FOR_PROVIDES(p2, pp2, rec)
-           {
-             if (p2 == p)
-               found = 1;
-             else
-               {
-                 /* if p2 is already installed, this recommends is ignored */
-                 if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
-                   break;
-               }
-           }
-         if (!p2 && found)
-           {
-             queue_push(whyq, SOLVER_REASON_RECOMMENDED);
-             queue_push2(whyq, i, rec);
-           }
-       }
-    }
-  /* 2) list all supplements */
-  s = pool->solvables + p;
-  if (s->supplements && level > 0)
-    {
-      Id *supp, sup, pp2, p2;
-      /* this is a hack. to use solver_dep_fulfilled we temporarily clear
-       * everything above our level in the decisionmap */
-      for (i = decisionno; i < solv->decisionq.count; i++ )
-       {
-         p2 = solv->decisionq.elements[i];
-         if (p2 > 0)
-           solv->decisionmap[p2] = -solv->decisionmap[p2];
-       }
-      supp = s->repo->idarraydata + s->supplements;
-      while ((sup = *supp++) != 0)
-       if (solver_dep_fulfilled(solv, sup))
-         {
-           int found = 0;
-           /* let's see if this is an easy supp */
-           FOR_PROVIDES(p2, pp2, sup)
-             {
-               if (!solv->addalreadyrecommended && solv->installed)
-                 {
-                   if (pool->solvables[p2].repo == solv->installed)
-                     continue;
-                 }
-               if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
-                 {
-                   queue_push(whyq, SOLVER_REASON_SUPPLEMENTED);
-                   queue_push2(whyq, p2, sup);
-                   found = 1;
-                 }
-             }
-           if (!found)
-             {
-               /* hard case, just note dependency with no package */
-               queue_push(whyq, SOLVER_REASON_SUPPLEMENTED);
-               queue_push2(whyq, 0, sup);
-             }
-         }
-      for (i = decisionno; i < solv->decisionq.count; i++)
-       {
-         p2 = solv->decisionq.elements[i];
-         if (p2 > 0)
-           solv->decisionmap[p2] = -solv->decisionmap[p2];
-       }
-    }
-}
-
 void
 pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what)
 {
@@ -4799,7 +4729,7 @@ pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what)
 int
 pool_isemptyupdatejob(Pool *pool, Id how, Id what)
 {
-  Id p, pp, pi, pip;
+  Id p, pp;
   Id select = how & SOLVER_SELECTMASK;
   if ((how & SOLVER_JOBMASK) != SOLVER_UPDATE)
     return 0;
@@ -4812,85 +4742,11 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what)
       return 0;
   /* hard work */
   FOR_JOB_SELECT(p, pp, select, what)
-    {
-      Solvable *s = pool->solvables + p;
-      FOR_PROVIDES(pi, pip, s->name)
-       {
-         Solvable *si = pool->solvables + pi;
-         if (si->repo != pool->installed || si->name != s->name)
-           continue;
-         return 0;
-       }
-      if (s->obsoletes)
-       {
-         Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
-         while ((obs = *obsp++) != 0)
-           {
-             FOR_PROVIDES(pi, pip, obs)
-               {
-                 Solvable *si = pool->solvables + pi;
-                 if (si->repo != pool->installed)
-                   continue;
-                 if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs))
-                   continue;
-                 if (pool->obsoleteusescolors && !pool_colormatch(pool, s, si))
-                   continue;
-                 return 0;
-               }
-           }
-       }
-    }
+    if (replaces_installed_package(pool, p, 0))
+      return 0;
   return 1;
 }
 
-int
-solver_alternatives_count(Solver *solv)
-{
-  Id *elements = solv->branches.elements;
-  int res, count;
-  for (res = 0, count = solv->branches.count; count; res++)
-    count -= elements[count - 2];
-  return res;
-}
-
-int
-solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp)
-{
-  int cnt = solver_alternatives_count(solv);
-  int count = solv->branches.count;
-  Id *elements = solv->branches.elements;
-  if (choices)
-    queue_empty(choices);
-  if (alternative <= 0 || alternative > cnt)
-    return 0;
-  elements += count;
-  for (; cnt > alternative; cnt--)
-    elements -= elements[-2];
-  if (levelp)
-    *levelp = elements[-1];
-  if (fromp)
-    *fromp = elements[-4];
-  if (idp)
-    *idp = elements[-3];
-  if (chosenp)
-    {
-      int i;
-      *chosenp = 0;
-      for (i = elements[-2]; i > 4; i--)
-       {
-         Id p = -elements[-i];
-         if (p > 0 && solv->decisionmap[p] == elements[-1] + 1)
-           {
-             *chosenp = p;
-             break;
-           }
-       }
-    }
-  if (choices)
-    queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]);
-  return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE;
-}
-
 const char *
 solver_select2str(Pool *pool, Id select, Id what)
 {
@@ -5042,39 +4898,3 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
   return pool_tmpappend(pool, s, "]", 0);
 }
 
-const char *
-solver_alternative2str(Solver *solv, int type, Id id, Id from)
-{
-  Pool *pool = solv->pool;
-  if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
-    {
-      const char *s = pool_dep2str(pool, id);
-      return pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from));
-    }
-  if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
-    {
-      int rtype;
-      Id depfrom, depto, dep;
-      char buf[64];
-      if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
-       id = solver_rule2pkgrule(solv, id);
-      if (solver_ruleclass(solv, id) == SOLVER_RULE_RECOMMENDS)
-       id = solver_rule2pkgrule(solv, id);
-      rtype = solver_ruleinfo(solv, id, &depfrom, &depto, &dep);
-      if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
-       {
-         if ((depto & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES)
-           return pool_dep2str(pool, dep);
-         return solver_select2str(pool, depto & SOLVER_SELECTMASK, dep);
-       }
-      if (rtype == SOLVER_RULE_PKG_REQUIRES)
-       {
-         const char *s = pool_dep2str(pool, dep);
-         return pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, depfrom));
-       }
-      sprintf(buf, "Rule #%d", id);
-      return pool_tmpjoin(pool, buf, 0, 0);
-    }
-  return "unknown alternative type";
-}
-
index 287003c..34c045e 100644 (file)
@@ -302,8 +302,11 @@ typedef struct s_Solver Solver;
 #define SOLVER_REASON_WEAKDEP          7
 #define SOLVER_REASON_RESOLVE_ORPHAN   8
 
-#define SOLVER_REASON_RECOMMENDED      16
-#define SOLVER_REASON_SUPPLEMENTED     17
+#define SOLVER_REASON_RECOMMENDED      16              /* deprecated */
+#define SOLVER_REASON_SUPPLEMENTED     17              /* deprecated */
+
+#define SOLVER_REASON_UNSOLVABLE       18
+#define SOLVER_REASON_PREMISE          19
 
 
 #define SOLVER_FLAG_ALLOW_DOWNGRADE            1
@@ -343,6 +346,16 @@ typedef struct s_Solver Solver;
 #define SOLVER_ALTERNATIVE_TYPE_RECOMMENDS     2
 #define SOLVER_ALTERNATIVE_TYPE_SUGGESTS       3
 
+/* solver_get_decisionlist / solver_get_learnt flags */
+#define SOLVER_DECISIONLIST_SOLVABLE           (1 << 1)
+#define SOLVER_DECISIONLIST_PROBLEM            (1 << 2)
+#define SOLVER_DECISIONLIST_LEARNTRULE         (1 << 3)
+#define SOLVER_DECISIONLIST_WITHINFO           (1 << 8)
+#define SOLVER_DECISIONLIST_SORTED             (1 << 9)
+#define SOLVER_DECISIONLIST_MERGEDINFO         (1 << 10)
+
+#define SOLVER_DECISIONLIST_TYPEMASK           (0xff)
+
 extern Solver *solver_create(Pool *pool);
 extern void solver_free(Solver *solv);
 extern int  solver_solve(Solver *solv, Queue *job);
@@ -363,10 +376,16 @@ extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int fl
 extern void solver_get_cleandeps(Solver *solv, Queue *cleandepsq);
 
 extern int  solver_describe_decision(Solver *solv, Id p, Id *infop);
-extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);
+
+extern void solver_get_decisionlist(Solver *solv, Id p, int flags, Queue *decisionlistq);
+extern void solver_get_decisionlist_multiple(Solver *solv, Queue *pq, int flags, Queue *decisionlistq);
+extern void solver_get_learnt(Solver *solv, Id id, int flags, Queue *q);
+extern void solver_decisionlist_solvables(Solver *solv, Queue *decisionlistq, int pos, Queue *q);
+extern int solver_decisionlist_merged(Solver *solv, Queue *decisionlistq, int pos);
 
 extern int solver_alternatives_count(Solver *solv);
 extern int solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp);
+extern int solver_alternativeinfo(Solver *solv, int type, Id id, Id from, Id *fromp, Id *top, Id *depp);
 
 extern void solver_calculate_multiversionmap(Pool *pool, Queue *job, Map *multiversionmap);
 extern void solver_calculate_noobsmap(Pool *pool, Queue *job, Map *multiversionmap);   /* obsolete */
@@ -378,11 +397,21 @@ extern int solver_calc_installsizechange(Solver *solv);
 extern void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);
 extern int  pool_isemptyupdatejob(Pool *pool, Id how, Id what);
 
+/* decisioninfo merging */
+extern int solver_calc_decisioninfo_bits(Solver *solv, Id decision, int type, Id from, Id to, Id dep);
+extern int solver_merge_decisioninfo_bits(Solver *solv, int state1, int type1, Id from1, Id to1, Id dep1, int state2, int type2, Id from2, Id to2, Id dep2);
+
 extern const char *solver_select2str(Pool *pool, Id select, Id what);
 extern const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);
 extern const char *solver_alternative2str(Solver *solv, int type, Id id, Id from);
+extern const char *solver_reason2str(Solver *solv, int reason);
+extern const char *solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info);
+extern const char *solver_decisioninfo2str(Solver *solv, int bits, int type, Id from, Id to, Id dep);
 
 
+/* deprecated, use solver_allweakdepinfos/solver_weakdepinfo instead */
+extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);
+
 /* iterate over all literals of a rule */
 #define FOR_RULELITERALS(l, pp, r)                             \
     for (pp = r->d < 0 ? -r->d - 1 : r->d,                     \
index 5902cdb..1c53d4e 100644 (file)
@@ -505,43 +505,17 @@ solver_printcompleteprobleminfo(Solver *solv, Id problem)
   queue_free(&q);
 }
 
-static int illegals[] = {
-  POLICY_ILLEGAL_DOWNGRADE,
-  POLICY_ILLEGAL_NAMECHANGE,
-  POLICY_ILLEGAL_ARCHCHANGE,
-  POLICY_ILLEGAL_VENDORCHANGE,
-  0
-};
-
 void
 solver_printsolution(Solver *solv, Id problem, Id solution)
 {
   Pool *pool = solv->pool;
-  Id p, rp, element;
-
-  element = 0;
-  while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
-    {
-      if (p > 0 && rp > 0)
-       {
-         /* for replacements we want to know why it was illegal */
-         Solvable *s = pool->solvables + p, *rs = pool->solvables + rp;
-         int illegal = policy_is_illegal(solv, s, rs, 0);
-         if (illegal)
-           {
-             int i;
-             for (i = 0; illegals[i]; i++)
-               if ((illegal & illegals[i]) != 0)
-                 {
-                   POOL_DEBUG(SOLV_DEBUG_RESULT, "  - allow %s\n", policy_illegal2str(solv, illegals[i], s, rs));
-                   illegal ^= illegals[i];
-                 }
-             if (!illegal)
-               continue;
-           }
-       }
-      POOL_DEBUG(SOLV_DEBUG_RESULT, "  - %s\n", solver_solutionelement2str(solv, p, rp));
-    }
+  Queue q;
+  int i;
+  queue_init(&q);
+  solver_all_solutionelements(solv, problem, solution, 1, &q);
+  for (i = 0; i < q.count; i += 3)
+    POOL_DEBUG(SOLV_DEBUG_RESULT, "  - %s\n", solver_solutionelementtype2str(solv, q.elements[i], q.elements[i + 1], q.elements[i + 2]));
+  queue_free(&q);
 }
 
 void
index 001412d..8b8fe6d 100644 (file)
 extern "C" {
 #endif
 
-struct s_Pool;
 struct s_DUChanges;
 struct s_TransactionOrderdata;
 
 typedef struct s_Transaction {
-  struct s_Pool *pool;         /* back pointer to pool */
+  Pool *pool;                  /* back pointer to pool */
 
   Queue steps;                 /* the transaction steps */
 
@@ -93,8 +92,8 @@ typedef struct s_Transaction {
 #define SOLVER_ORDERCYCLE_NORMAL               1
 #define SOLVER_ORDERCYCLE_CRITICAL             2
 
-extern Transaction *transaction_create(struct s_Pool *pool);
-extern Transaction *transaction_create_decisionq(struct s_Pool *pool, Queue *decisionq, Map *multiversionmap);
+extern Transaction *transaction_create(Pool *pool);
+extern Transaction *transaction_create_decisionq(Pool *pool, Queue *decisionq, Map *multiversionmap);
 extern Transaction *transaction_create_clone(Transaction *srctrans);
 extern void transaction_free(Transaction *trans);
 
diff --git a/test/testcases/alternative/job.t b/test/testcases/alternative/job.t
new file mode 100644 (file)
index 0000000..18e3b78
--- /dev/null
@@ -0,0 +1,22 @@
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Prv: X
+#>=Pkg: B 1 1 noarch
+#>=Prv: X
+system unset * system
+job install provides X
+result transaction,problems,alternatives <inline>
+#>alternative 63535fbb  0 X
+#>alternative 63535fbb  1 + A-1-1.noarch@test
+#>alternative 63535fbb  2   B-1-1.noarch@test
+#>install A-1-1.noarch@test
+
+nextjob
+job install oneof A-1-1.noarch@test B-1-1.noarch@test
+result transaction,problems,alternatives <inline>
+#>alternative 63535fbb  0 A-1-1.noarch, B-1-1.noarch
+#>alternative 63535fbb  1 + A-1-1.noarch@test
+#>alternative 63535fbb  2   B-1-1.noarch@test
+#>install A-1-1.noarch@test
+
diff --git a/test/testcases/alternative/learnt.t b/test/testcases/alternative/learnt.t
new file mode 100644 (file)
index 0000000..35626dc
--- /dev/null
@@ -0,0 +1,44 @@
+### or in CC1
+####>=Con: CC2 <UNLESS> (A1 | A2 | A3)
+###
+feature complex_deps
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: Q 1 1 noarch
+#>=Req: A
+#>=Pkg: A1 1 1 noarch
+#>=Pkg: A2 1 2 noarch
+#>=Pkg: A3 1 3 noarch
+#>=Req: C = 2-1
+#>=Pkg: A 2 1 noarch
+#>=Con: A1
+#>=Con: A2
+#>=Con: A3
+#>=Req: B
+#>=Pkg: B 1 1 noarch
+#>=Req: X
+#>=Pkg: C 2 1 noarch
+#>=Prv: X
+#>=Req: CC1
+#>=Req: CC2
+#>=Pkg: CC1 1 1 noarch
+#>=Pkg: CC2 1 1 noarch
+#>=Req: (A1 | A2 | A3) <IF> CC1
+#>=Pkg: C 2 2 noarch
+#>=Prv: X
+#>=Req: CC1
+#>=Req: CC2
+#>=Pkg: A 1 1 noarch
+#>=Req: C = 2-2
+system unset rpm system
+job install name Q
+result transaction,problems,alternatives <inline>
+#>alternative d80be72d  0 ((A1 or A2 or A3) if CC1), required by CC2-1-1.noarch
+#>alternative d80be72d  1 + A1-1-1.noarch@test
+#>alternative d80be72d  2   A2-1-2.noarch@test
+#>install A-1-1.noarch@test
+#>install A1-1-1.noarch@test
+#>install C-2-2.noarch@test
+#>install CC1-1-1.noarch@test
+#>install CC2-1-1.noarch@test
+#>install Q-1-1.noarch@test
diff --git a/test/testcases/alternative/recommends.t b/test/testcases/alternative/recommends.t
new file mode 100644 (file)
index 0000000..923e6c6
--- /dev/null
@@ -0,0 +1,16 @@
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Rec: X
+#>=Pkg: B 1 1 noarch
+#>=Prv: X
+#>=Pkg: C 1 1 noarch
+#>=Prv: X
+system unset * system
+job install name A
+result transaction,problems,alternatives <inline>
+#>alternative f1989d4c  0 X, recommended by A-1-1.noarch
+#>alternative f1989d4c  1 + B-1-1.noarch@test
+#>alternative f1989d4c  2   C-1-1.noarch@test
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
diff --git a/test/testcases/alternative/requires.t b/test/testcases/alternative/requires.t
new file mode 100644 (file)
index 0000000..953708b
--- /dev/null
@@ -0,0 +1,16 @@
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Req: X
+#>=Pkg: B 1 1 noarch
+#>=Prv: X
+#>=Pkg: C 1 1 noarch
+#>=Prv: X
+system unset * system
+job install name A
+result transaction,problems,alternatives <inline>
+#>alternative 8f2fa5fa  0 X, required by A-1-1.noarch
+#>alternative 8f2fa5fa  1 + B-1-1.noarch@test
+#>alternative 8f2fa5fa  2   C-1-1.noarch@test
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
diff --git a/test/testcases/alternative/strongrecommends.t b/test/testcases/alternative/strongrecommends.t
new file mode 100644 (file)
index 0000000..db7c51d
--- /dev/null
@@ -0,0 +1,31 @@
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Rec: X
+#>=Pkg: B 1 1 noarch
+#>=Prv: X
+#>=Pkg: C 1 1 noarch
+#>=Prv: X
+#>=Pkg: D 1 1 noarch
+#>=Prv: X
+system unset * system
+solverflags strongrecommends
+job install name A
+result transaction,problems,alternatives <inline>
+#>alternative 432b0214  0 X, recommended by A-1-1.noarch
+#>alternative 432b0214  1 + B-1-1.noarch@test
+#>alternative 432b0214  2   C-1-1.noarch@test
+#>alternative 432b0214  3   D-1-1.noarch@test
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+nextjob
+solverflags strongrecommends
+job install name A
+job disfavor name B
+result transaction,problems,alternatives <inline>
+#>alternative eb7c0cd8  0 X, recommended by A-1-1.noarch
+#>alternative eb7c0cd8  1 + C-1-1.noarch@test
+#>alternative eb7c0cd8  2   D-1-1.noarch@test
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
diff --git a/test/testcases/alternative/update.t b/test/testcases/alternative/update.t
new file mode 100644 (file)
index 0000000..b5f92f4
--- /dev/null
@@ -0,0 +1,21 @@
+repo system 0 testtags <inline>
+#>=Pkg: X 1 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: X 2 1 noarch
+#>=Pkg: B 1 1 noarch
+#>=Obs: X = 1-1
+system unset * system
+job update all packages
+result transaction,problems,alternatives <inline>
+#>alternative 6014509b  0 X-1-1.noarch
+#>alternative 6014509b  1 + X-2-1.noarch@test
+#>alternative 6014509b  2   B-1-1.noarch@test
+#>upgrade X-1-1.noarch@system X-2-1.noarch@test
+
+nextjob
+job distupgrade all packages [forcebest]
+result transaction,problems,alternatives <inline>
+#>alternative 6014509b  0 X-1-1.noarch
+#>alternative 6014509b  1 + X-2-1.noarch@test
+#>alternative 6014509b  2   B-1-1.noarch@test
+#>upgrade X-1-1.noarch@system X-2-1.noarch@test
diff --git a/test/testcases/alternative/yumobs.t b/test/testcases/alternative/yumobs.t
new file mode 100644 (file)
index 0000000..6e6378e
--- /dev/null
@@ -0,0 +1,19 @@
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+repo test 0 testtags <inline>
+#>=Pkg: A 2 1 noarch
+#>=Obs: A < 2
+#>=Pkg: B 2 1 noarch
+#>=Obs: A < 2
+#>=Pkg: B 2 2 noarch
+#>=Obs: A < 2
+system unset rpm system
+solverflags yumobsoletes
+job update all packages
+job disfavor name B = 2-2
+result transaction,problems,alternatives <inline>
+#>alternative 80ce092e  0 B, obsoleting A < 2
+#>alternative 80ce092e  1 + B-2-1.noarch@test
+#>alternative 80ce092e  2   B-2-2.noarch@test
+#>install B-2-1.noarch@test
+#>upgrade A-1-1.noarch@system A-2-1.noarch@test
diff --git a/test/testcases/choicerules/choice7.t b/test/testcases/choicerules/choice7.t
new file mode 100644 (file)
index 0000000..db821b6
--- /dev/null
@@ -0,0 +1,16 @@
+# make sure that orphaned packages do not get
+# deinstalled by choice rules bnc#1190465
+repo system 0 testtags <inline>
+#>=Pkg: A 1 1 noarch
+#>=Req: P = 1
+#>=Pkg: P 1 1 noarch
+repo available 0 testtags <inline>
+#>=Pkg: P 2 1 noarch
+#>=Pkg: Q 2 1 noarch
+#>=Prv: P = 1
+system i686 rpm system
+
+job distupgrade all packages
+result transaction,problems <inline>
+#>install Q-2-1.noarch@available
+#>upgrade P-1-1.noarch@system P-2-1.noarch@available
diff --git a/test/testcases/cplxdeps/ifelse_rec.t b/test/testcases/cplxdeps/ifelse_rec.t
new file mode 100644 (file)
index 0000000..3e052eb
--- /dev/null
@@ -0,0 +1,11 @@
+feature complex_deps
+repo appstream 0 testtags <inline>
+#>=Pkg: xorg-x11-server-Xorg 1.20.11 18.el9 noarch
+#>=Req: missing-req
+#>=Pkg: pass 1.7.4 6.el9 noarch
+#>=Rec: xclip <IF> (xorg-x11-server-Xorg <ELSE> wl-clipboard)
+repo @System 0 empty
+system unset * @System
+job install pkg pass-1.7.4-6.el9.noarch@appstream
+result transaction,problems <inline>
+#>install pass-1.7.4-6.el9.noarch@appstream
index 9950661..b81c725 100644 (file)
@@ -13,7 +13,7 @@ solverflags ignorerecommended
 job install pkg a-1-1.noarch@available
 job favor name b2
 result transaction,problems,alternatives <inline>
-#>alternative 64eb4d87  0 a-1-1.noarch@available requires b
+#>alternative 64eb4d87  0 b, required by a-1-1.noarch
 #>alternative 64eb4d87  1 + b2-1-1.noarch@available
 #>alternative 64eb4d87  2   b1-1-1.noarch@available
 #>install a-1-1.noarch@available
index 3a1d93b..e0d0e61 100644 (file)
@@ -11,7 +11,7 @@ system unset * system
 job install name build-tools
 job favor pkg gcc-5-1.noarch@available
 result transaction,problems,alternatives <inline>
-#>alternative 6e50ec1a  0 build-tools-1-1.noarch@available requires gcc
+#>alternative 6e50ec1a  0 gcc, required by build-tools-1-1.noarch
 #>alternative 6e50ec1a  1 + gcc-5-1.noarch@available
 #>alternative 6e50ec1a  2   gcc-6-1.noarch@available
 #>install build-tools-1-1.noarch@available
@@ -22,7 +22,7 @@ nextjob
 job install name build-tools
 job disfavor pkg gcc-6-1.noarch@available
 result transaction,problems,alternatives <inline>
-#>alternative 6e50ec1a  0 build-tools-1-1.noarch@available requires gcc
+#>alternative 6e50ec1a  0 gcc, required by build-tools-1-1.noarch
 #>alternative 6e50ec1a  1 + gcc-5-1.noarch@available
 #>alternative 6e50ec1a  2   gcc-6-1.noarch@available
 #>install build-tools-1-1.noarch@available
index 14792fa..d4f6642 100644 (file)
@@ -26,6 +26,7 @@ job install name B
 job install name B2
 job disfavor name C
 result transaction,problems,alternatives <inline>
+#>alternative 6b91d100  0 C, recommended by B-1-1.noarch
 #>alternative 6b91d100  1 + D-1-1.noarch@available
 #>alternative 6b91d100  2   E-1-1.noarch@available
 #>install B-1-1.noarch@available
index a3921ed..48d4eb3 100644 (file)
@@ -18,7 +18,7 @@ result transaction,problems <inline>
 nextjob
 solverflags yumobsoletes
 job update all packages
-result transaction,problems <inline>
+result transaction,problems,alternatives <inline>
 #>erase c-27-1.x86_64@system d-28-1.x86_64@available
 #>install d-28-1.x86_64@available
 #>install e-28-1.x86_64@available
diff --git a/test/testcases/yumobs/split2.t b/test/testcases/yumobs/split2.t
new file mode 100644 (file)
index 0000000..bb6b4ae
--- /dev/null
@@ -0,0 +1,24 @@
+repo system 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: c 27 1 x86_64
+repo available 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: d 28 1 x86_64
+#>=Obs: c
+#>=Pkg: e 28 1 x86_64
+#>=Obs: c
+#>=Pkg: f 28 1 x86_64
+#>=Obs: c < 30
+#>=Pkg: g 28 1 x86_64
+#>=Obs: c < 30
+
+system x86_64 rpm system
+solverflags yumobsoletes
+job update all packages
+result transaction,problems,alternatives <inline>
+#>alternative 6234e4e8  0 c-27-1.x86_64
+#>alternative 6234e4e8  1 + d-28-1.x86_64@available
+#>alternative 6234e4e8  2   f-28-1.x86_64@available
+#>erase c-27-1.x86_64@system d-28-1.x86_64@available
+#>install d-28-1.x86_64@available
+#>install e-28-1.x86_64@available
index 2b8f3c4..cf5d43b 100644 (file)
@@ -32,7 +32,8 @@ usage(int status)
 {
   fprintf(stderr, "\nUsage:\n"
           "conda2solv\n"
-          "  reads a 'synthesis' repository from <stdin> and writes a .solv file to <stdout>\n"
+          "  reads a conda repository from <stdin> and writes a .solv file to <stdout>\n"
+          "  -S : include signature data\n"
           "  -h : print help & exit\n"
          );
    exit(status);
@@ -44,14 +45,18 @@ main(int argc, char **argv)
   Pool *pool;
   Repo *repo;
   int c;
+  int flags = 0;
 
-  while ((c = getopt(argc, argv, "h")) >= 0)
+  while ((c = getopt(argc, argv, "hS")) >= 0)
     {
       switch(c)
        {
        case 'h':
          usage(0);
          break;
+       case 'S':
+         flags |= CONDA_ADD_WITH_SIGNATUREDATA;
+         break;
        default:
          usage(1);
          break;
@@ -59,7 +64,7 @@ main(int argc, char **argv)
     }
   pool = pool_create();
   repo = repo_create(pool, "<stdin>");
-  if (repo_add_conda(repo, stdin, 0))
+  if (repo_add_conda(repo, stdin, flags))
     {
       fprintf(stderr, "conda2solv: %s\n", pool_errstr(pool));
       exit(1);
index 712602d..2aec4b1 100644 (file)
@@ -1,4 +1,8 @@
-/* vim: sw=2 et
+/*
+ * Copyright (c) 2015, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
  */
 
 #include <stdio.h>
index 4eb66a0..f860a4d 100644 (file)
@@ -1,6 +1,3 @@
-/* vim: sw=2 et cino=>4,n-2,{1s
- */
-
 /*
  * Copyright (c) 2009-2015, SUSE LLC
  *
@@ -52,19 +49,11 @@ usage(char** argv)
 }
 
 #if defined(ENABLE_SUSEREPO) || defined(ENABLE_RPMMD) || defined(ENABLE_DEBIAN) || defined(ENABLE_ARCHREPO)
-static int
+static size_t
 strlen_comp(const char *str)
 {
-  size_t l = strlen(str);
-  if (l > 3 && !strcmp(str + l - 3, ".gz"))
-    return l - 3;
-  if (l > 3 && !strcmp(str + l - 3, ".xz"))
-    return l - 3;
-  if (l > 4 && !strcmp(str + l - 4, ".bz2"))
-    return l - 4;
-  if (l > 5 && !strcmp(str + l - 4, ".lzma"))
-    return l - 5;
-  return l;
+  const char *suf = strrchr(str, '.');
+  return strlen(str) - (suf && solv_xfopen_iscompressed(suf) ? strlen(suf) : 0);
 }
 #endif
 
@@ -103,7 +92,7 @@ main(int argc, char **argv)
       FILE *fp;
       int r;
 #if defined(ENABLE_SUSEREPO) || defined(ENABLE_RPMMD) || defined(ENABLE_DEBIAN) || defined(ENABLE_ARCHREPO)
-      int l;
+      size_t l;
 #endif
 
       if (!strcmp(argv[i], "--withsrc"))
@@ -354,7 +343,6 @@ main(int argc, char **argv)
       if (problemcount)
        {
          Id problem = 0;
-         Solvable *s2;
 
          status = 1;
          printf("can't install %s:\n", pool_solvable2str(pool, s));
@@ -371,40 +359,26 @@ main(int argc, char **argv)
                  solver_allruleinfos(solv, probr, &rinfo);
                  for (k = 0; k < rinfo.count; k += 4)
                    {
-                     Id dep, source, target;
+                     Id type, dep, source, target;
+                     type = rinfo.elements[k];
                      source = rinfo.elements[k + 1];
                      target = rinfo.elements[k + 2];
                      dep = rinfo.elements[k + 3];
-                     switch (rinfo.elements[k])
+
+                     /* special casing */
+                     switch (type)
                        {
                        case SOLVER_RULE_DISTUPGRADE:
-                         break;
-                       case SOLVER_RULE_INFARCH:
-                         s = pool_id2solvable(pool, source);
-                         printf("  %s has inferior architecture\n", pool_solvable2str(pool, s));
-                         break;
-                       case SOLVER_RULE_UPDATE:
-                         s = pool_id2solvable(pool, source);
-                         printf("  %s can not be updated\n", pool_solvable2str(pool, s));
-                         break;
                        case SOLVER_RULE_JOB:
                        case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
                        case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
                        case SOLVER_RULE_JOB_UNSUPPORTED:
                          break;
-                       case SOLVER_RULE_RPM:
-                         printf("  some dependency problem\n");
-                         break;
-                       case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
-                         printf("  nothing provides requested %s\n", pool_dep2str(pool, dep));
-                         break;
-                       case SOLVER_RULE_RPM_NOT_INSTALLABLE:
-                         s = pool_id2solvable(pool, source);
-                         printf("  package %s is not installable\n", pool_solvable2str(pool, s));
+                       case SOLVER_RULE_UPDATE:
+                         printf("  %s can not be updated\n", pool_solvid2str(pool, source));
                          break;
-                       case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
-                         s = pool_id2solvable(pool, source);
-                         printf("  nothing provides %s needed by %s\n", pool_dep2str(pool, dep), pool_solvable2str(pool, s));
+                       case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+                         printf("  %s\n", solver_problemruleinfo2str(solv, type, source, target, dep));
                          if (ISRELDEP(dep))
                            {
                              Reldep *rd = GETRELDEP(pool, dep);
@@ -416,28 +390,8 @@ main(int argc, char **argv)
                                }
                            }
                          break;
-                       case SOLVER_RULE_RPM_SAME_NAME:
-                         s = pool_id2solvable(pool, source);
-                         s2 = pool_id2solvable(pool, target);
-                         printf("  cannot install both %s and %s\n", pool_solvable2str(pool, s), pool_solvable2str(pool, s2));
-                         break;
-                       case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
-                         s = pool_id2solvable(pool, source);
-                         s2 = pool_id2solvable(pool, target);
-                         printf("  package %s conflicts with %s provided by %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep), pool_solvable2str(pool, s2));
-                         break;
-                       case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
-                         s = pool_id2solvable(pool, source);
-                         s2 = pool_id2solvable(pool, target);
-                         printf("  package %s obsoletes %s provided by %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep), pool_solvable2str(pool, s2));
-                         break;
-                       case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
-                         s = pool_id2solvable(pool, source);
-                         printf("  package %s requires %s, but none of the providers can be installed\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep));
-                         break;
-                       case SOLVER_RULE_RPM_SELF_CONFLICT:
-                         s = pool_id2solvable(pool, source);
-                         printf("  package %s conflicts with %s provided by itself\n", pool_solvable2str(pool, s), pool_dep2str(pool, dep));
+                       default:
+                         printf("  %s\n", solver_problemruleinfo2str(solv, type, source, target, dep));
                          break;
                        }
                    }
index 9d5d28b..e233cf2 100644 (file)
@@ -9,6 +9,7 @@
 #include "selection.h"
 #include "solverdebug.h"
 #include "testcase.h"
+#include "evr.h"
 
 static struct resultflags2str {
   Id flag;
@@ -76,6 +77,121 @@ free_considered(Pool *pool)
     }
 }
 
+void
+showwhy(Solver *solv, const char *showwhypkgstr)
+{
+  Pool *pool = solv->pool;
+  Queue dq, rq, iq;
+  int ii, i;
+
+  queue_init(&dq);
+  queue_init(&rq);
+  queue_init(&iq);
+
+  i = testcase_str2solvid(pool, showwhypkgstr);
+  if (i)
+    solver_get_decisionlist(solv, i, SOLVER_DECISIONLIST_SOLVABLE | SOLVER_DECISIONLIST_SORTED, &dq);
+  else
+    {
+      int selflags = SELECTION_NAME | SELECTION_CANON;
+      selection_make(pool, &dq, showwhypkgstr, selflags);
+      selection_solvables(pool, &dq, &iq);
+      if (!iq.count)
+       printf("No package matches %s\n", showwhypkgstr);
+      queue_empty(&dq);
+      solver_get_decisionlist_multiple(solv, &iq, SOLVER_DECISIONLIST_SOLVABLE | SOLVER_DECISIONLIST_SORTED, &dq);
+    }
+  for (ii = 0; ii < dq.count; ii += 3)
+    {
+      Id v = dq.elements[ii];
+      int reason = dq.elements[ii + 1];
+      int info = dq.elements[ii + 2];
+
+      printf("%s %s:\n", v < 0 ? "conflicted" : "installed", testcase_solvid2str(pool, v >= 0 ? v : -v));
+      /* special case some reasons where we want to show multiple rule infos or extra info */
+      if (reason == SOLVER_REASON_WEAKDEP || reason == SOLVER_REASON_UNIT_RULE || reason == SOLVER_REASON_RESOLVE)
+       {
+         queue_empty(&iq);
+         if (reason == SOLVER_REASON_WEAKDEP)
+           solver_allweakdepinfos(solv, v, &iq);
+         else if (info > 0)
+           solver_allruleinfos(solv, info, &iq);
+         if (iq.count)
+           {
+             for (i = 0; i < iq.count; i += 4)
+               {
+                 int bits;
+                 if (iq.elements[i] == SOLVER_RULE_LEARNT)
+                   {
+                     printf("  a learnt rule:\n");
+                     solver_ruleliterals(solv, info, &rq);
+                     for (i = 0; i < rq.count; i++)
+                       {
+                         Id p2 = rq.elements[i];
+                         printf("    %c %s\n", p2 > 0 ? '+' : '-', testcase_solvid2str(pool, p2 > 0 ? p2 : -p2));
+                       }
+                     continue;
+                   }
+                 bits = solver_calc_decisioninfo_bits(solv, v, iq.elements[i], iq.elements[i + 1], iq.elements[i + 2], iq.elements[i + 3]);
+                 printf("  %s\n", solver_decisioninfo2str(solv, bits, iq.elements[i], iq.elements[i + 1], iq.elements[i + 2], iq.elements[i + 3]));
+               }
+             continue;
+           }
+       }
+      printf("  %s\n", solver_decisionreason2str(solv, v, reason, info));
+    }
+  queue_free(&iq);
+  queue_free(&rq);
+  queue_free(&dq);
+}
+
+void
+doshowproof(Solver *solv, Id id, int flags, Queue *lq)
+{
+  Pool *pool = solv->pool;
+  Queue q, qp;
+  int i, j;
+
+  queue_init(&q);
+  queue_init(&qp);
+  solver_get_decisionlist(solv, id, flags | SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO | SOLVER_DECISIONLIST_MERGEDINFO, &q);
+  for (i = 0; i < q.count; i += 8)
+    {
+      Id v = q.elements[i];
+      int reason = q.elements[i + 1], bits = q.elements[i + 3], type = q.elements[i + 4];
+      Id from = q.elements[i + 5], to = q.elements[i + 6], dep = q.elements[i + 7];
+
+      if (reason != SOLVER_REASON_UNSOLVABLE && type == SOLVER_RULE_PKG_SAME_NAME)
+       continue;       /* do not show "obvious" decisions */
+
+      solver_decisionlist_solvables(solv, &q, i, &qp);
+      if (reason == SOLVER_REASON_UNSOLVABLE)
+        printf("unsolvable: ");
+      else
+        printf("%s %s: ", v < 0 ? "conflicted" : "installed", pool_solvidset2str(pool, &qp));
+      i += solver_decisionlist_merged(solv, &q, i) * 8;
+      if (type == 0)
+       {
+         printf("%s\n", solver_reason2str(solv, reason));
+         continue;
+       }
+      if (type == SOLVER_RULE_LEARNT && lq)
+       {
+         for (j = 0; j < lq->count; j++)
+           if (lq->elements[j] == q.elements[i + 2])
+             break;
+         if (j < lq->count)
+           {
+             printf("learnt rule #%d\n", j + 1);
+             continue;
+           }
+       }
+      printf("%s\n", solver_decisioninfo2str(solv, bits, type, from, to, dep));
+    }
+  queue_free(&qp);
+  queue_free(&q);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -84,12 +200,14 @@ main(int argc, char **argv)
   Queue solq;
   Solver *solv, *reusesolv = 0;
   char *result = 0;
+  char *showwhypkgstr = 0;
   int resultflags = 0;
   int debuglevel = 0;
   int writeresult = 0;
   char *writetestcase = 0;
   int multijob = 0;
   int rescallback = 0;
+  int showproof = 0;
   int c;
   int ex = 0;
   const char *list = 0;
@@ -98,7 +216,7 @@ main(int argc, char **argv)
   const char *p;
 
   queue_init(&solq);
-  while ((c = getopt(argc, argv, "vmrhL:l:s:T:")) >= 0)
+  while ((c = getopt(argc, argv, "vmrhL:l:s:T:W:P")) >= 0)
     {
       switch (c)
       {
@@ -122,6 +240,9 @@ main(int argc, char **argv)
          list = optarg;
          list_with_deps = 1;
           break;
+        case 'W':
+         showwhypkgstr = optarg;
+          break;
         case 's':
          if ((p = strchr(optarg, ':')))
            queue_push2(&solq, atoi(optarg), atoi(p + 1));
@@ -131,6 +252,9 @@ main(int argc, char **argv)
         case 'T':
          writetestcase = optarg;
           break;
+        case 'P':
+         showproof = 1;
+          break;
         default:
          usage(1);
           break;
@@ -228,6 +352,36 @@ main(int argc, char **argv)
                  queue_free(&q);
                }
            }
+         else if (showwhypkgstr)
+           {
+             solver_solve(solv, &job);
+             showwhy(solv, showwhypkgstr);
+           }
+         else if (showproof)
+           {
+             int pcnt = solver_solve(solv, &job);
+             int problem;
+             if (!pcnt)
+               printf("nothing to proof\n");
+             for (problem = 1; problem <= pcnt; problem++)
+               {
+                 Queue lq;
+                 int i;
+                 queue_init(&lq);
+                 solver_get_learnt(solv, problem, SOLVER_DECISIONLIST_PROBLEM, &lq);
+                 for (i = 0; i < lq.count; i++)
+                   {
+                     printf("Learnt rule #%d:\n", i + 1);
+                     doshowproof(solv, lq.elements[i], SOLVER_DECISIONLIST_LEARNTRULE, &lq);
+                     printf("\n");
+                   }
+                 printf("Proof #%d:\n", problem);
+                 doshowproof(solv, problem, SOLVER_DECISIONLIST_PROBLEM, &lq);
+                 queue_free(&lq);
+                 if (problem < pcnt)
+                   printf("\n");
+               }
+           }
          else if (result || writeresult)
            {
              char *myresult, *resultdiff;