]> git.ipfire.org Git - people/stevee/pakfire.git/commitdiff
Add support for solutions.
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 29 Jul 2011 15:33:42 +0000 (17:33 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 29 Jul 2011 15:36:33 +0000 (17:36 +0200)
Makefile
pakfire/satsolver.py
po/POTFILES.in
po/pakfire.pot
src/_pakfiremodule.c
src/problem.c
src/problem.h
src/solution.c [new file with mode: 0644]
src/solution.h [new file with mode: 0644]

index c32cea939768aa92850160349edf29433e27ce63..953957623215734f8f216d152b77bc9aad96ff95 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ dist:
        python setup.py sdist
 
 .PHONY: install
-install:
+install: po
        python setup.py install  --root $(DESTDIR)
 
        -mkdir -pv $(DESTDIR)/etc/pakfire.repos.d
index d4a80a808d7cca29be1bd3279c4df1f900c8ea16..a795fe0a141cf4aa8e39d6cc7b7979eac3182803 100644 (file)
@@ -6,6 +6,7 @@ import _pakfire
 from _pakfire import *
 
 import transaction
+import util
 
 from i18n import _
 
@@ -76,42 +77,114 @@ class Solver(object):
                self.pakfire = pakfire
                self.pool = pool
 
-               self._solver = _pakfire.Solver(self.pool)
+       def solve(self, request, update=False, uninstall=False, allow_downgrade=False,
+                       interactive=False):
 
-       def solve(self, request, update=False, uninstall=False, allow_downgrade=False):
-               self._solver.set_allow_uninstall(uninstall)
-               self._solver.set_allow_downgrade(allow_downgrade)
+               # Create a new solver.
+               solver = _pakfire.Solver(self.pool)
+
+               solver.set_allow_uninstall(uninstall)
+               solver.set_allow_downgrade(allow_downgrade)
 
                # Configure the solver for an update.
                if update:
-                       self._solver.set_updatesystem(True)
-                       self._solver.set_do_split_provides(True)
+                       solver.set_updatesystem(True)
+                       solver.set_do_split_provides(True)
 
-               res = self._solver.solve(request)
+               # Actually solve the request.
+               res = solver.solve(request)
 
                logging.debug("Solver status: %s" % res)
 
                # If the solver succeeded, we return the transaction and return.
                if res:
                        # Return a resulting Transaction.
-                       t = Transaction(self._solver)
+                       t = Transaction(solver)
 
                        return transaction.Transaction.from_solver(self.pakfire, self, t)
 
                # Do the problem handling...
-               problems = self._solver.get_problems(request)
+               problems = solver.get_problems(request)
 
                logging.info("")
-               logging.info(_("The solver returned %s problems:") % len(problems))
-               logging.info("")
+               logging.info(_("The solver returned one problem:", "The solver returned %(num)s problems:",
+                       len(problems)) % { "num" : len(problems) })
 
                i = 0
-               for p in self._solver.get_problems(request):
+               for problem in problems:
                        i += 1
 
                        # Print information about the problem to the user.
-                       logging.info(_("  Problem #%d:") % i)
-                       logging.info("    %s" % p)
-                       logging.info("")
+                       logging.info("  #%d: %s" % (i, problem))
+
+               logging.info("")
 
-               return res
+               if not interactive:
+                       return False
+
+               # Ask the user if he or she want to modify the request. If not, just exit.
+               if not util.ask_user(_("Do you want to manually alter the request?")):
+                       return False
+
+               print _("You can now try to satisfy the solver by modifying your request.")
+
+               altered = False
+               while True:
+                       if len(problems) > 1:
+                               print _("Which problem to you want to resolve?")
+                               if altered:
+                                       print _("Press enter to try to re-solve the request.")
+                               print "[1-%s]:" % len(problems),
+
+                               answer = raw_input()
+
+                               # If the user did not enter anything, we abort immediately.
+                               if not answer:
+                                       break
+
+                               # If the user did type anything else than an integer, we ask
+                               # again.
+                               try:
+                                       answer = int(answer)
+                               except ValueError:
+                                       continue
+
+                               # If the user entered an integer outside of range, we ask
+                               # again.
+                               try:
+                                       problem = problems[answer - 1]
+                               except KeyError:
+                                       continue
+
+                       else:
+                               problem = problem[0]
+
+                       # Get all solutions.
+                       solutions = problem.get_solutions()
+
+                       if len(solutions) == 1:
+                               solution = solutions[0]
+                               print _("    Solution: %s") % solution
+                               print
+
+                               if util.ask_user("Do you accept the solution above?"):
+                                       altered = True
+                                       print "XXX do something"
+
+                               continue
+                       else:
+                               print _("    Solutions:")
+                               i = 0
+                               for solution in solutions:
+                                       i += 1
+                                       print "      #%d: %s" % (i, solution)
+
+                               print
+
+               if not altered:
+                       return False
+
+               # If the request was altered by the user, we try to solve it again
+               # and see what happens.
+               return self.solve(request, update=update, uninstall=uninstall,
+                       allow_downgrade=allow_downgrade, interactive=interactive)
index d99bf2768da9500af2e9cf9236cf225f2be8b196..f813265571fac705d2444e578c0c6c33f84b308a 100644 (file)
@@ -44,6 +44,7 @@ src/problem.c
 src/relation.c
 src/repo.c
 src/request.c
+src/solution.c
 src/solvable.c
 src/solver.c
 src/step.c
index be74ce9a800b21e00fec0c3582febdf4287095f7..389d63ad3a4208bc08290be25f86c93181e024ac 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-07-29 15:04+0200\n"
+"POT-Creation-Date: 2011-07-29 17:32+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -343,15 +343,40 @@ msgstr ""
 msgid "Loading installed packages"
 msgstr ""
 
-#: ../pakfire/satsolver.py:105
-#, python-format
-msgid "The solver returned %s problems:"
+#: ../pakfire/satsolver.py:110
+msgid "The solver returned one problem:"
 msgstr ""
 
 #. Print information about the problem to the user.
-#: ../pakfire/satsolver.py:113
+#: ../pakfire/satsolver.py:118
 #, python-format
-msgid "  Problem #%d:"
+msgid "  #%d: %s"
+msgstr ""
+
+#. Ask the user if he or she want to modify the request. If not, just exit.
+#: ../pakfire/satsolver.py:126
+msgid "Do you want to manually alter the request?"
+msgstr ""
+
+#: ../pakfire/satsolver.py:129
+msgid "You can now try to satisfy the solver by modifying your request."
+msgstr ""
+
+#: ../pakfire/satsolver.py:134
+msgid "Which problem to you want to resolve?"
+msgstr ""
+
+#: ../pakfire/satsolver.py:136
+msgid "Press enter to try to re-solve the request."
+msgstr ""
+
+#: ../pakfire/satsolver.py:167
+#, python-format
+msgid "    Solution: %s"
+msgstr ""
+
+#: ../pakfire/satsolver.py:176
+msgid "    Solutions:"
 msgstr ""
 
 #: ../pakfire/transaction.py:89
@@ -409,82 +434,172 @@ msgstr ""
 msgid "%s [y/N]"
 msgstr ""
 
-#: ../src/problem.c:126
+#: ../src/problem.c:140
 #, c-format
 msgid "%s does not belong to a distupgrade repository"
 msgstr ""
 
-#: ../src/problem.c:133
+#: ../src/problem.c:147
 #, c-format
 msgid "%s has inferior architecture"
 msgstr ""
 
-#: ../src/problem.c:140
+#: ../src/problem.c:154
 #, c-format
 msgid "problem with installed package %s"
 msgstr ""
 
-#: ../src/problem.c:146
+#: ../src/problem.c:160
 #, c-format
 msgid "conflicting requests"
 msgstr ""
 
-#: ../src/problem.c:151
+#: ../src/problem.c:165
 #, c-format
 msgid "nothing provides requested %s"
 msgstr ""
 
-#: ../src/problem.c:157
+#: ../src/problem.c:171
 #, c-format
 msgid "some dependency problem"
 msgstr ""
 
-#: ../src/problem.c:162
+#: ../src/problem.c:176
 #, c-format
 msgid "package %s is not installable"
 msgstr ""
 
-#: ../src/problem.c:169
+#: ../src/problem.c:183
 #, c-format
 msgid "nothing provides %s needed by %s"
 msgstr ""
 
-#: ../src/problem.c:176
+#: ../src/problem.c:190
 #, c-format
 msgid "cannot install both %s and %s"
 msgstr ""
 
-#: ../src/problem.c:183
+#: ../src/problem.c:197
 #, c-format
 msgid "package %s conflicts with %s provided by %s"
 msgstr ""
 
-#: ../src/problem.c:191
+#: ../src/problem.c:205
 #, c-format
 msgid "package %s obsoletes %s provided by %s"
 msgstr ""
 
-#: ../src/problem.c:199
+#: ../src/problem.c:213
 #, c-format
 msgid "installed package %s obsoletes %s provided by %s"
 msgstr ""
 
-#: ../src/problem.c:207
+#: ../src/problem.c:221
 #, c-format
 msgid "package %s implicitely obsoletes %s provided by %s"
 msgstr ""
 
-#: ../src/problem.c:215
+#: ../src/problem.c:229
 #, c-format
 msgid "package %s requires %s, but none of the providers can be installed"
 msgstr ""
 
-#: ../src/problem.c:222
+#: ../src/problem.c:236
 #, c-format
 msgid "package %s conflicts with %s provided by itself"
 msgstr ""
 
-#: ../src/problem.c:231
+#: ../src/problem.c:245
 #, c-format
 msgid "bad rule type"
 msgstr ""
+
+#: ../src/solution.c:67
+#, c-format
+msgid "do not keep %s installed"
+msgstr ""
+
+#: ../src/solution.c:70
+#, c-format
+msgid "do not install a solvable %s"
+msgstr ""
+
+#: ../src/solution.c:73
+#, c-format
+msgid "do not install %s"
+msgstr ""
+
+#: ../src/solution.c:79
+#, c-format
+msgid "do not forbid installation of %s"
+msgstr ""
+
+#: ../src/solution.c:82
+#, c-format
+msgid "do not deinstall all solvables %s"
+msgstr ""
+
+#: ../src/solution.c:85
+#, c-format
+msgid "do not deinstall %s"
+msgstr ""
+
+#: ../src/solution.c:90
+#, c-format
+msgid "do not install most recent version of %s"
+msgstr ""
+
+#: ../src/solution.c:95
+#, c-format
+msgid "do not lock %s"
+msgstr ""
+
+#: ../src/solution.c:100
+#, c-format
+msgid "do something different"
+msgstr ""
+
+#: ../src/solution.c:107
+#, c-format
+msgid "keep %s despite the inferior architecture"
+msgstr ""
+
+#: ../src/solution.c:110
+#, c-format
+msgid "install %s despite the inferior architecture"
+msgstr ""
+
+#: ../src/solution.c:116
+#, c-format
+msgid "keep obsolete %s"
+msgstr ""
+
+#: ../src/solution.c:119
+#, c-format
+msgid "install %s from excluded repository"
+msgstr ""
+
+#: ../src/solution.c:131
+#, c-format
+msgid "allow downgrade of %s to %s"
+msgstr ""
+
+#: ../src/solution.c:135
+#, c-format
+msgid "allow architecture change of %s to %s"
+msgstr ""
+
+#: ../src/solution.c:140
+#, c-format
+msgid "allow vendor change from '%s' (%s) to '%s' (%s)"
+msgstr ""
+
+#: ../src/solution.c:144
+#, c-format
+msgid "allow vendor change from '%s' (%s) to no vendor (%s)"
+msgstr ""
+
+#: ../src/solution.c:150
+#, c-format
+msgid "allow replacement of %s with %s"
+msgstr ""
index 0aaa2c8111f7c2314020347255b27764925dd17d..39e1b55e2fbab3409cc37861764a171748808ffe 100644 (file)
@@ -7,6 +7,7 @@
 #include "relation.h"
 #include "repo.h"
 #include "request.h"
+#include "solution.h"
 #include "solvable.h"
 #include "solver.h"
 #include "step.h"
@@ -30,6 +31,7 @@ static PyMethodDef Problem_methods[] = {
        {"get_source", (PyCFunction)Problem_get_source, METH_NOARGS, NULL},
        {"get_target", (PyCFunction)Problem_get_target, METH_NOARGS, NULL},
        {"get_dep", (PyCFunction)Problem_get_dep, METH_NOARGS, NULL},
+       {"get_solutions", (PyCFunction)Problem_get_solutions, METH_NOARGS, NULL},
        { NULL, NULL, 0, NULL }
 };
 
@@ -111,6 +113,10 @@ static PyMethodDef Solvable_methods[] = {
        { NULL, NULL, 0, NULL }
 };
 
+static PyMethodDef Solution_methods[] = {
+       { NULL, NULL, 0, NULL }
+};
+
 static PyMethodDef Solver_methods[] = {
        {"solve", (PyCFunction)Solver_solve, METH_VARARGS, NULL},
        {"get_allow_downgrade", (PyCFunction)Solver_get_allow_downgrade, METH_NOARGS, NULL},
@@ -193,6 +199,13 @@ void init_pakfire(void) {
        Py_INCREF(&RequestType);
        PyModule_AddObject(m, "Request", (PyObject *)&RequestType);
 
+       // Solution
+       SolutionType.tp_methods = Solution_methods;
+       if (PyType_Ready(&SolutionType) < 0)
+               return;
+       Py_INCREF(&SolutionType);
+       PyModule_AddObject(m, "Solution", (PyObject *)&SolutionType);
+
        // Solver
        SolverType.tp_methods = Solver_methods;
        if (PyType_Ready(&SolverType) < 0)
index d44ab234960b3ee5b7b9ef214e57489d554a7b98..77c916b20f320eb263336af36de4c23070a182ac 100644 (file)
@@ -5,6 +5,7 @@
 #include "problem.h"
 #include "relation.h"
 #include "request.h"
+#include "solution.h"
 #include "solvable.h"
 #include "solver.h"
 
@@ -111,8 +112,21 @@ PyObject *Problem_get_dep(ProblemObject *self) {
 }
 
 PyObject *Problem_get_solutions(ProblemObject *self) {
+       SolutionObject *solution;
+       Id s = 0;
+
        PyObject *list = PyList_New(0);
 
+       while ((s = solver_next_solution(self->_solver, self->_id, s)) != 0) {
+               solution = PyObject_New(SolutionObject, &SolutionType);
+
+               solution->_solver = self->_solver;
+               solution->problem_id = self->_id;
+               solution->id = s;
+
+               PyList_Append(list, (PyObject *)solution);
+       }
+
        return list;
 }
 
index 73074fddf61a7cfdbacb027d17131cea09c81dd7..15b3722cace04283f3aefc9abc6433fb386bbe16 100644 (file)
@@ -32,6 +32,8 @@ extern PyObject *Problem_get_source(ProblemObject *self);
 extern PyObject *Problem_get_target(ProblemObject *self);
 extern PyObject *Problem_get_dep(ProblemObject *self);
 
+extern PyObject *Problem_get_solutions(ProblemObject *self);
+
 extern PyTypeObject ProblemType;
 
 #endif
diff --git a/src/solution.c b/src/solution.c
new file mode 100644 (file)
index 0000000..469cb32
--- /dev/null
@@ -0,0 +1,157 @@
+
+#include <Python.h>
+
+#include <satsolver/policy.h>
+#include <satsolver/solverdebug.h>
+
+#include "config.h"
+#include "problem.h"
+#include "solution.h"
+
+PyTypeObject SolutionType = {
+       PyObject_HEAD_INIT(NULL)
+       tp_name: "_pakfire.Solution",
+       tp_basicsize: sizeof(SolutionObject),
+       tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+       tp_new: Solution_new,
+       tp_dealloc: (destructor)Solution_dealloc,
+       tp_doc: "Sat Solution objects",
+       tp_str: (reprfunc)Solution_string,
+};
+
+PyObject *Solution_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+       SolutionObject *self;
+       ProblemObject *problem;
+       Id id;
+
+       if (!PyArg_ParseTuple(args, "Oi", &problem, &id)) {
+               /* XXX raise exception */
+               return NULL;
+       }
+
+       self = (SolutionObject *)type->tp_alloc(type, 0);
+       if (self != NULL) {
+               self->_solver = problem->_solver;
+               self->problem_id = problem->_id;
+               self->id = id;
+       }
+
+       return (PyObject *)self;
+}
+
+PyObject *Solution_dealloc(SolutionObject *self) {
+       self->ob_type->tp_free((PyObject *)self);
+
+       Py_RETURN_NONE;
+}
+
+PyObject *Solution_string(SolutionObject *self) {
+       Pool *pool = self->_solver->pool;
+       Solver *solver = self->_solver;
+       char str[STRING_SIZE];
+
+       Solvable *s, *sd;
+       Id p, rp;
+       Id how, what, select;
+
+       Id element = 0;
+       while ((element = solver_next_solutionelement(solver, self->problem_id, self->id, element, &p, &rp)) != 0) {
+               if (p == SOLVER_SOLUTION_JOB) {
+                       how = solver->job.elements[rp - 1];
+                       what = solver->job.elements[rp];
+                       select = how & SOLVER_SELECTMASK;
+
+                       switch (how & SOLVER_JOBMASK) {
+                               case SOLVER_INSTALL:
+                                       if (select == SOLVER_SOLVABLE && solver->installed && pool->solvables[what].repo == solver->installed)
+                                               snprintf(str, STRING_SIZE - 1, _("do not keep %s installed"),
+                                                       pool_solvid2str(pool, what));
+                                       else if (select == SOLVER_SOLVABLE_PROVIDES)
+                                               snprintf(str, STRING_SIZE - 1, _("do not install a solvable %s"),
+                                                       solver_select2str(pool, select, what));
+                                       else
+                                               snprintf(str, STRING_SIZE - 1, _("do not install %s"),
+                                                       solver_select2str(pool, select, what));
+                                       break;
+
+                               case SOLVER_ERASE:
+                                       if (select == SOLVER_SOLVABLE && !(solver->installed && pool->solvables[what].repo == solver->installed))
+                                               snprintf(str, STRING_SIZE - 1, _("do not forbid installation of %s"),
+                                                       pool_solvid2str(pool, what));
+                                       else if (select == SOLVER_SOLVABLE_PROVIDES)
+                                               snprintf(str, STRING_SIZE - 1, _("do not deinstall all solvables %s"),
+                                                       solver_select2str(pool, select, what));
+                                       else
+                                               snprintf(str, STRING_SIZE - 1, _("do not deinstall %s"),
+                                                       solver_select2str(pool, select, what));
+                                       break;
+
+                               case SOLVER_UPDATE:
+                                       snprintf(str, STRING_SIZE - 1, _("do not install most recent version of %s"),
+                                               solver_select2str(pool, select, what));
+                                       break;
+
+                               case SOLVER_LOCK:
+                                       snprintf(str, STRING_SIZE - 1, _("do not lock %s"),
+                                               solver_select2str(pool, select, what));
+                                       break;
+
+                               default:
+                                       snprintf(str, STRING_SIZE - 1, _("do something different"));
+                                       break;
+                       }
+
+               } else if (p == SOLVER_SOLUTION_INFARCH) {
+                       s = pool->solvables + rp;
+                       if (solver->installed && s->repo == solver->installed)
+                               snprintf(str, STRING_SIZE - 1, _("keep %s despite the inferior architecture"),
+                                       pool_solvable2str(pool, s));
+                       else
+                               snprintf(str, STRING_SIZE - 1, _("install %s despite the inferior architecture"),
+                                       pool_solvable2str(pool, s));
+
+               } else if (p == SOLVER_SOLUTION_DISTUPGRADE) {
+                       s = pool->solvables + rp;
+                       if (solver->installed && s->repo == solver->installed)
+                               snprintf(str, STRING_SIZE - 1, _("keep obsolete %s"),
+                                       pool_solvable2str(pool, s));
+                       else
+                               snprintf(str, STRING_SIZE - 1, _("install %s from excluded repository"),
+                                       pool_solvable2str(pool, s));
+
+               } else {
+                       s = pool->solvables + p;
+                       sd = rp ? pool->solvables + rp : 0;
+
+                       if (sd) {
+                               int illegal = policy_is_illegal(solver, s, sd, 0);
+
+                               // XXX multiple if clauses can match here
+                               if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0)
+                                       snprintf(str, STRING_SIZE - 1, _("allow downgrade of %s to %s"),
+                                               pool_solvable2str(pool, s), pool_solvable2str(pool, sd));
+
+                               if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0)
+                                       snprintf(str, STRING_SIZE - 1, _("allow architecture change of %s to %s"),
+                                               pool_solvable2str(pool, s), pool_solvable2str(pool, sd));
+
+                               if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
+                                       if (sd->vendor)
+                                               snprintf(str, STRING_SIZE - 1, _("allow vendor change from '%s' (%s) to '%s' (%s)"),
+                                                       pool_id2str(pool, s->vendor), pool_solvable2str(pool, s),
+                                                       pool_id2str(pool, sd->vendor), pool_solvable2str(pool, sd));
+                                       else
+                                               snprintf(str, STRING_SIZE - 1, _("allow vendor change from '%s' (%s) to no vendor (%s)"),
+                                                       pool_id2str(pool, s->vendor), pool_solvable2str(pool, s),
+                                                       pool_solvable2str(pool, sd));
+                               }
+
+                               if (!illegal)
+                                       snprintf(str, STRING_SIZE - 1, _("allow replacement of %s with %s"),
+                                               pool_solvable2str(pool, s), pool_solvable2str(pool, sd));
+                       }
+               }
+       }
+
+       return Py_BuildValue("s", &str);
+}
diff --git a/src/solution.h b/src/solution.h
new file mode 100644 (file)
index 0000000..fdf05c9
--- /dev/null
@@ -0,0 +1,24 @@
+
+#ifndef PAKFIRE_SOLUTION_H
+#define PAKFIRE_SOLUTION_H
+
+#include <Python.h>
+
+#include "solver.h"
+
+// Sat Solution object
+typedef struct {
+    PyObject_HEAD
+    Solver *_solver;
+    Id problem_id;
+    Id id;
+} SolutionObject;
+
+extern PyObject *Solution_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
+extern PyObject *Solution_dealloc(SolutionObject *self);
+
+extern PyObject *Solution_string(SolutionObject *self);
+
+extern PyTypeObject SolutionType;
+
+#endif