From: Michael Tremer Date: Sat, 20 May 2017 21:01:55 +0000 (+0200) Subject: libpakfire: Allow accessing possible solutions for problems X-Git-Tag: 0.9.28~1285^2~1339 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9c8337dd957c9fc7361f091bb518e0eacf54a382;p=pakfire.git libpakfire: Allow accessing possible solutions for problems Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 4e48a6b1a..f67987d17 100644 --- a/Makefile.am +++ b/Makefile.am @@ -248,6 +248,7 @@ libpakfire_la_SOURCES = \ src/libpakfire/repocache.c \ src/libpakfire/request.c \ src/libpakfire/selector.c \ + src/libpakfire/solution.c \ src/libpakfire/solver.c \ src/libpakfire/step.c \ src/libpakfire/transaction.c \ @@ -272,6 +273,7 @@ pkginclude_HEADERS += \ src/libpakfire/include/pakfire/repocache.h \ src/libpakfire/include/pakfire/request.h \ src/libpakfire/include/pakfire/selector.h \ + src/libpakfire/include/pakfire/solution.h \ src/libpakfire/include/pakfire/solver.h \ src/libpakfire/include/pakfire/step.h \ src/libpakfire/include/pakfire/transaction.h \ diff --git a/src/_pakfire/_pakfiremodule.c b/src/_pakfire/_pakfiremodule.c index 8de3f47c7..43d8b6f99 100644 --- a/src/_pakfire/_pakfiremodule.c +++ b/src/_pakfire/_pakfiremodule.c @@ -99,10 +99,6 @@ static PyMethodDef Solvable_methods[] = { { NULL, NULL, 0, NULL } }; -static PyMethodDef Solution_methods[] = { - { NULL, NULL, 0, NULL } -}; - static struct PyModuleDef moduledef = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_pakfire", @@ -176,9 +172,9 @@ PyMODINIT_FUNC PyInit__pakfire(void) { PyModule_AddObject(module, "Request", (PyObject *)&RequestType); // Solution - SolutionType.tp_methods = Solution_methods; if (PyType_Ready(&SolutionType) < 0) return NULL; + Py_INCREF(&SolutionType); PyModule_AddObject(module, "Solution", (PyObject *)&SolutionType); diff --git a/src/_pakfire/problem.c b/src/_pakfire/problem.c index 0f00f1020..bd01ff9c0 100644 --- a/src/_pakfire/problem.c +++ b/src/_pakfire/problem.c @@ -26,6 +26,7 @@ #include "pool.h" #include "problem.h" +#include "solution.h" static ProblemObject* Problem_new_core(PyTypeObject* type, PakfireProblem problem) { ProblemObject* self = (ProblemObject *)type->tp_alloc(type, 0); @@ -77,11 +78,33 @@ static PyObject* Problem_string(ProblemObject* self) { return PyUnicode_FromString(string); } +static PyObject* Problem_get_solutions(ProblemObject* self) { + PyObject* list = PyList_New(0); + + PakfireSolution solution = pakfire_problem_get_solutions(self->problem); + while (solution) { + PyObject* s = new_solution(solution); + PyList_Append(list, s); + Py_DECREF(s); + + solution = pakfire_solution_next(solution); + } + + return list; +} + static struct PyMethodDef Problem_methods[] = { { NULL } }; static struct PyGetSetDef Problem_getsetters[] = { + { + "solutions", + (getter)Problem_get_solutions, + NULL, + NULL, + NULL, + }, { NULL }, }; diff --git a/src/_pakfire/solution.c b/src/_pakfire/solution.c index c1eac65ae..34887dbe0 100644 --- a/src/_pakfire/solution.c +++ b/src/_pakfire/solution.c @@ -1,7 +1,7 @@ /*############################################################################# # # # Pakfire - The IPFire package management system # -# Copyright (C) 2011 Pakfire development team # +# Copyright (C) 2017 Pakfire development team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # @@ -19,160 +19,60 @@ #############################################################################*/ #include +#include -#include -#include +#include +#include -#include "constants.h" -#include "problem.h" +#include "pool.h" #include "solution.h" -PyTypeObject SolutionType = { - PyVarObject_HEAD_INIT(NULL, 0) - 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) { -#if 0 - self->_solver = problem->_solver; - self->problem_id = problem->_id; -#endif - self->id = id; +static SolutionObject* Solution_new_core(PyTypeObject* type, PakfireSolution solution) { + SolutionObject* self = (SolutionObject *)type->tp_alloc(type, 0); + if (self) { + self->solution = solution; } - return (PyObject *)self; + return self; } -PyObject *Solution_dealloc(SolutionObject *self) { - Py_TYPE(self)->tp_free((PyObject *)self); +PyObject* new_solution(PakfireSolution solution) { + SolutionObject* s = Solution_new_core(&SolutionType, solution); - Py_RETURN_NONE; + return (PyObject*)s; } -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 && pool->installed && pool->solvables[what].repo == pool->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 && !(pool->installed && pool->solvables[what].repo == pool->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; +static PyObject* Solution_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + SolutionObject* self = Solution_new_core(type, NULL); - default: - snprintf(str, STRING_SIZE - 1, _("do something different")); - break; - } - - } else if (p == SOLVER_SOLUTION_INFARCH) { - s = pool->solvables + rp; - if (pool->installed && s->repo == pool->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 (pool->installed && s->repo == pool->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); + return (PyObject *)self; +} - // 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)); +static void Solution_dealloc(SolutionObject* self) { + if (self->solution) + pakfire_solution_free(self->solution); - 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)); + Py_TYPE(self)->tp_free((PyObject *)self); +} - 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)); - } +static int Solution_init(SolutionObject* self, PyObject* args, PyObject* kwds) { + return 0; +} - if (!illegal) - snprintf(str, STRING_SIZE - 1, _("allow replacement of %s with %s"), - pool_solvable2str(pool, s), pool_solvable2str(pool, sd)); - } - } - } +static PyObject* Solution_string(SolutionObject* self) { + const char* string = pakfire_solution_to_string(self->solution); - return Py_BuildValue("s", &str); + return PyUnicode_FromString(string); } + +PyTypeObject SolutionType = { + PyVarObject_HEAD_INIT(NULL, 0) + 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_init: (initproc)Solution_init, + tp_doc: "Solution object", + tp_str: (reprfunc)Solution_string, +}; diff --git a/src/_pakfire/solution.h b/src/_pakfire/solution.h index b49e1518d..44d6fcda9 100644 --- a/src/_pakfire/solution.h +++ b/src/_pakfire/solution.h @@ -1,7 +1,7 @@ /*############################################################################# # # # Pakfire - The IPFire package management system # -# Copyright (C) 2011 Pakfire development team # +# Copyright (C) 2017 Pakfire development team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # @@ -23,21 +23,15 @@ #include -#include "solver.h" +#include -// Sat Solution object typedef struct { - PyObject_HEAD - Solver *_solver; - Id problem_id; - Id id; + PyObject_HEAD + PakfireSolution solution; } 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; +PyObject* new_solution(PakfireSolution solution); + #endif /* PYTHON_PAKFIRE_SOLUTION_H */ diff --git a/src/libpakfire/include/pakfire/problem.h b/src/libpakfire/include/pakfire/problem.h index 07fb6785a..dd78729a5 100644 --- a/src/libpakfire/include/pakfire/problem.h +++ b/src/libpakfire/include/pakfire/problem.h @@ -35,6 +35,8 @@ void pakfire_problem_append(PakfireProblem problem, PakfireProblem new_problem); const char* pakfire_problem_to_string(PakfireProblem problem); +PakfireSolution pakfire_problem_get_solutions(PakfireProblem problem); + #ifdef PAKFIRE_PRIVATE struct _PakfireProblem { diff --git a/src/libpakfire/include/pakfire/solution.h b/src/libpakfire/include/pakfire/solution.h new file mode 100644 index 000000000..ce5ee0e96 --- /dev/null +++ b/src/libpakfire/include/pakfire/solution.h @@ -0,0 +1,48 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2017 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#ifndef PAKFIRE_SOLUTION_H +#define PAKFIRE_SOLUTION_H + +#include + +PakfireSolution pakfire_solution_create(PakfireProblem problem, Id id); +PakfireSolution pakfire_solution_ref(PakfireSolution solution); +void pakfire_solution_free(PakfireSolution solution); + +PakfireSolution pakfire_solution_next(PakfireSolution solution); +void pakfire_solution_append(PakfireSolution solution, PakfireSolution new_solution); + +char* pakfire_solution_to_string(PakfireSolution solution); + +#ifdef PAKFIRE_PRIVATE + +struct _PakfireSolution { + PakfireProblem problem; + Id id; + char** elements; + + PakfireSolution next; + int nrefs; +}; + +#endif + +#endif /* PAKFIRE_SOLUTION_H */ diff --git a/src/libpakfire/include/pakfire/types.h b/src/libpakfire/include/pakfire/types.h index 51d764212..26c01357f 100644 --- a/src/libpakfire/include/pakfire/types.h +++ b/src/libpakfire/include/pakfire/types.h @@ -36,6 +36,7 @@ typedef struct _PakfireRepo* PakfireRepo; typedef struct _PakfireRepoCache* PakfireRepoCache; typedef struct _PakfireRequest* PakfireRequest; typedef struct _PakfireSelector* PakfireSelector; +typedef struct _PakfireSolution* PakfireSolution; typedef struct _PakfireStep* PakfireStep; typedef struct _PakfireTransaction* PakfireTransaction; diff --git a/src/libpakfire/libpakfire.sym b/src/libpakfire/libpakfire.sym index 7e6e776c6..15f755cd4 100644 --- a/src/libpakfire/libpakfire.sym +++ b/src/libpakfire/libpakfire.sym @@ -163,6 +163,7 @@ global: pakfire_problem_append; pakfire_problem_create; pakfire_problem_free; + pakfire_problem_get_solutions; pakfire_problem_next; pakfire_problem_ref; pakfire_problem_to_string; @@ -243,6 +244,13 @@ global: pakfire_selector_providers; pakfire_selector_set; + # solution + pakfire_solution_create; + pakfire_solution_free; + pakfire_solution_next; + pakfire_solution_ref; + pakfire_solution_to_string; + # step pakfire_step_create; pakfire_step_free; diff --git a/src/libpakfire/problem.c b/src/libpakfire/problem.c index 7f2b1a76a..be1d0557b 100644 --- a/src/libpakfire/problem.c +++ b/src/libpakfire/problem.c @@ -22,6 +22,7 @@ #include #include #include +#include #include static char* to_string(PakfireProblem problem) { @@ -242,3 +243,19 @@ void pakfire_problem_append(PakfireProblem problem, PakfireProblem new_problem) const char* pakfire_problem_to_string(PakfireProblem problem) { return problem->string; } + +PakfireSolution pakfire_problem_get_solutions(PakfireProblem problem) { + PakfireSolution ret = NULL; + + Id solution = 0; + while ((solution = solver_next_solution(problem->request->solver, problem->id, solution)) != 0) { + PakfireSolution s = pakfire_solution_create(problem, solution); + + if (ret) + pakfire_solution_append(ret, s); + else + ret = s; + } + + return ret; +} diff --git a/src/libpakfire/solution.c b/src/libpakfire/solution.c new file mode 100644 index 000000000..fbbc41424 --- /dev/null +++ b/src/libpakfire/solution.c @@ -0,0 +1,197 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2017 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include + +#include + +#include +#include +#include +#include +#include + +static void import_elements(PakfireSolution solution) { + Solver* solver = solution->problem->request->solver; + Pool* pool = solver->pool; + + // Reserve memory + unsigned int num = solver_solutionelement_count(solver, solution->problem->id, solution->id); + char** elements = solution->elements = pakfire_calloc(num + 1, sizeof(*elements)); + + Id p; + Id rp; + Id element = 0; + while ((element = solver_next_solutionelement(solver, solution->problem->id, solution->id, element, &p, &rp)) != 0) { + char line[STRING_SIZE]; + + if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB) { + if (p == SOLVER_SOLUTION_JOB) + rp += solver->pooljobcnt; + + Id how = solver->job.elements[rp-1]; + Id what = solver->job.elements[rp]; + + // XXX pool_job2str must be localised + snprintf(line, STRING_SIZE - 1, _("do not ask to %s"), + pool_job2str(pool, how, what, 0)); + + } else if (p == SOLVER_SOLUTION_INFARCH) { + Solvable* s = pool->solvables + rp; + + if (pool->installed && s->repo == pool->installed) + snprintf(line, STRING_SIZE - 1, _("keep %s despite the inferior architecture"), + pool_solvable2str(pool, s)); + else + snprintf(line, STRING_SIZE - 1, _("install %s despite the inferior architecture"), + pool_solvable2str(pool, s)); + + } else if (p == SOLVER_SOLUTION_DISTUPGRADE) { + Solvable* s = pool->solvables + rp; + + if (pool->installed && s->repo == pool->installed) + snprintf(line, STRING_SIZE - 1, _("keep obsolete %s"), + pool_solvable2str(pool, s)); + else + snprintf(line, STRING_SIZE - 1, _("install %s"), + pool_solvable2str(pool, s)); + + } else if (p == SOLVER_SOLUTION_BEST) { + Solvable* s = pool->solvables + rp; + + if (pool->installed && s->repo == pool->installed) + snprintf(line, STRING_SIZE - 1, _("keep old %s"), + pool_solvable2str(pool, s)); + else + snprintf(line, STRING_SIZE - 1, _("install %s despite the old version"), + pool_solvable2str(pool, s)); + + } else if (p > 0 && rp == 0) + snprintf(line, STRING_SIZE - 1, _("allow deinstallation of %s"), + pool_solvid2str(pool, p)); + + else if (p > 0 && rp > 0) + snprintf(line, STRING_SIZE - 1, _("allow replacement of %s with %s"), + pool_solvid2str(pool, p), pool_solvid2str(pool, rp)); + + else + snprintf(line, STRING_SIZE - 1, _("bad solution element")); + + // Save line in elements array + *elements++ = pakfire_strdup(line); + } + + // Terminate array + *elements = NULL; +} + +PakfireSolution pakfire_solution_create(PakfireProblem problem, Id id) { + PakfireSolution solution = pakfire_calloc(1, sizeof(*solution)); + + solution->problem = pakfire_problem_ref(problem); + solution->id = id; + + // Initialise reference counter + solution->nrefs = 1; + solution->next = NULL; + + // Extract information from solver + import_elements(solution); + + return solution; +} + +PakfireSolution pakfire_solution_ref(PakfireSolution solution) { + solution->nrefs++; + + return solution; +} + +void pakfire_solution_free(PakfireSolution solution) { + if (--solution->nrefs > 0) + return; + + if (solution->next) + pakfire_solution_free(solution->next); + + pakfire_problem_free(solution->problem); + + if (solution->elements) + while (*solution->elements) + pakfire_free(*solution->elements++); + + pakfire_free(solution); +} + +PakfireSolution pakfire_solution_next(PakfireSolution solution) { + return solution->next; +} + +void pakfire_solution_append(PakfireSolution solution, PakfireSolution new_solution) { + PakfireSolution next; + + // Go to last problem in list + while ((next = pakfire_solution_next(solution)) != NULL) { + solution = next; + } + + solution->next = pakfire_solution_ref(new_solution); +} + +static size_t count_elements_length(PakfireSolution solution) { + size_t length = 0; + + char** elements = solution->elements; + while (*elements) { + length += strlen(*elements++) + 1; + } + + return length; +} + +char* pakfire_solution_to_string(PakfireSolution solution) { + // Determine length of output string + size_t length = count_elements_length(solution); + + // Allocate string + char s[length]; + char* p = s; + + char** elements = solution->elements; + while (*elements) { + // Copy line + char* e = *elements++; + while (*e) + *p++ = *e++; + + // Add newline + *p++ = '\n'; + } + + // Terminate string + if (p > s) + *(p-1) = '\0'; + + // Make sure that we wrote the string exactly to + // the last character + assert((s + length) == p); + + return pakfire_strdup(s); +}