2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
26 log
= logging
.getLogger("pakfire")
34 from constants
import *
37 # Put some variables into our own namespace, to make them easily accessible
38 # for code, that imports the satsolver module.
39 SEARCH_STRING
= _pakfire
.SEARCH_STRING
40 SEARCH_FILES
= _pakfire
.SEARCH_FILES
41 SEARCH_GLOB
= _pakfire
.SEARCH_GLOB
44 Solvable
= _pakfire
.Solvable
45 Relation
= _pakfire
.Relation
47 class Pool(_pakfire
.Pool
):
49 (">=", _pakfire
.REL_GE
,),
50 ("<=", _pakfire
.REL_LE
,),
51 ("=" , _pakfire
.REL_EQ
,),
52 ("<" , _pakfire
.REL_LT
,),
53 (">" , _pakfire
.REL_GT
,),
56 def create_relation(self
, s
):
59 if isinstance(s
, filelist
._File
):
60 return Relation(self
, s
.name
)
62 elif s
.startswith("/"):
63 return Relation(self
, s
)
65 for pattern
, type in self
.RELATIONS
:
69 name
, version
= s
.split(pattern
, 1)
70 return Relation(self
, name
.strip(), version
.strip(), type)
72 return Relation(self
, s
)
74 def create_request(self
, builder
=False, install
=None, remove
=None, update
=None, updateall
=False):
75 request
= Request(self
)
77 # Add multiinstall information.
78 for solv
in PAKFIRE_MULTIINSTALL
:
79 request
.noobsoletes(solv
)
82 for req
in self
.expand_requires(install
):
86 for req
in self
.expand_requires(remove
):
90 for req
in self
.expand_requires(update
):
93 # Configure the request to update all packages
101 def grouplist(self
, group
):
104 for solv
in self
.search(group
, _pakfire
.SEARCH_SUBSTRING
, "solvable:group"):
105 pkg
= packages
.SolvPackage(self
, solv
)
107 if group
in pkg
.groups
and not pkg
.name
in pkgs
:
108 pkgs
.append(pkg
.name
)
112 def expand_requires(self
, requires
):
118 if isinstance(req
, packages
.BinaryPackage
):
122 if isinstance(req
, packages
.SolvPackage
):
123 ret
.append(req
.solvable
)
126 assert type(req
) == type("a"), req
129 if req
.startswith("@"):
130 reqs
= self
.grouplist(req
[1:])
135 req
= self
.create_relation(req
)
140 def resolvdep(self
, pakfire
, pkg
, logger
=None):
141 assert os
.path
.exists(pkg
)
143 # Open the package file.
144 pkg
= packages
.open(pakfire
, None, pkg
)
146 # Create a new request.
147 request
= self
.create_request(install
=pkg
.requires
)
149 # Add build dependencies if needed.
150 if isinstance(pkg
, packages
.Makefile
) or isinstance(pkg
, packages
.SourcePackage
):
151 for req
in self
.expand_requires(BUILD_PACKAGES
):
155 solver
= self
.solve(request
, logger
=logger
)
160 raise DependencyError
, solver
.get_problem_string()
162 def solve(self
, request
, interactive
=False, logger
=None, force_best
=False, **kwargs
):
163 # XXX implement interactive
166 logger
= logging
.getLogger("pakfire")
169 solver
= Solver(self
, request
, logger
=logger
)
171 # Apply configuration to solver.
172 for key
, val
in kwargs
.items():
176 solver
.solve(force_best
=force_best
)
178 # Return the solver so one can do stuff with it...
181 def whatprovides(self
, pakfire
, what
):
183 for solv
in self
.providers(what
):
184 pkg
= packages
.SolvPackage(pakfire
, solv
)
190 class Request(_pakfire
.Request
):
191 def install(self
, what
):
192 if isinstance(what
, Solvable
):
193 self
.install_solvable(what
)
196 elif isinstance(what
, Relation
):
197 self
.install_relation(what
)
200 elif type(what
) == type("string"):
201 self
.install_name(what
)
204 raise Exception, "Unknown type"
206 def remove(self
, what
):
207 if isinstance(what
, Solvable
):
208 self
.remove_solvable(what
)
211 elif isinstance(what
, Relation
):
212 self
.remove_relation(what
)
215 elif type(what
) == type("string"):
216 self
.remove_name(what
)
219 raise Exception, "Unknown type"
221 def update(self
, what
):
222 if isinstance(what
, Solvable
):
223 self
.update_solvable(what
)
226 elif isinstance(what
, Relation
):
227 self
.update_relation(what
)
230 elif type(what
) == type("string"):
231 self
.update_name(what
)
234 raise Exception, "Unknown type"
236 def lock(self
, what
):
237 if isinstance(what
, Solvable
):
238 self
.lock_solvable(what
)
241 elif isinstance(what
, Relation
):
242 self
.lock_relation(what
)
245 elif type(what
) == type("string"):
249 raise Exception, "Unknown type"
251 def noobsoletes(self
, what
):
252 if isinstance(what
, Solvable
):
253 self
.noobsoletes_solvable(what
)
256 elif isinstance(what
, Relation
):
257 self
.noobsoletes_relation(what
)
260 elif type(what
) == type("string"):
261 self
.noobsoletes_name(what
)
264 raise Exception, "Unknown type"
267 class Solver(object):
269 "allow_archchange" : _pakfire
.SOLVER_FLAG_ALLOW_ARCHCHANGE
,
270 "allow_downgrade" : _pakfire
.SOLVER_FLAG_ALLOW_DOWNGRADE
,
271 "allow_uninstall" : _pakfire
.SOLVER_FLAG_ALLOW_UNINSTALL
,
272 "allow_vendorchange" : _pakfire
.SOLVER_FLAG_ALLOW_VENDORCHANGE
,
273 "ignore_recommended" : _pakfire
.SOLVER_FLAG_IGNORE_RECOMMENDED
,
276 def __init__(self
, pool
, request
, logger
=None):
278 logger
= logging
.getLogger("pakfire")
282 self
.request
= request
283 assert self
.request
, "Empty request?"
285 # Create a new solver.
286 self
.solver
= _pakfire
.Solver(self
.pool
)
288 # The status of the solver.
289 # None when the solving was not done, yet.
290 # True when the request could be solved.
291 # False when the request could not be solved.
294 # Time that was needed to solve the request.
297 # Cache the transaction and problems.
298 self
.__problems
= None
299 self
.__transaction
= None
301 # Create some sane settings for the most common use cases.
302 self
.set("allow_archchange", True)
304 def set(self
, option
, value
):
306 flag
= self
.option2flag
[option
]
308 raise Exception, "Unknown configuration setting: %s" % option
309 self
.solver
.set_flag(flag
, value
)
311 def get(self
, option
):
313 flag
= self
.option2flag
[option
]
315 raise Exception, "Unknown configuration setting: %s" % option
316 return self
.solver
.get_flag(flag
)
318 def solve(self
, force_best
=False):
319 assert self
.status
is None, "Solver did already solve something."
321 # Actually solve the request.
322 start_time
= time
.time()
323 self
.status
= self
.solver
.solve(self
.request
, force_best
)
325 # Save the amount of time that was needed to solve the request.
326 self
.time
= time
.time() - start_time
329 self
.logger
.info(_("Dependency solving finished in %.2f ms") % (self
.time
* 1000))
331 raise DependencyError
, self
.get_problem_string()
335 if self
.__problems
is None:
336 self
.__problems
= self
.solver
.get_problems(self
.request
)
338 return self
.__problems
340 def get_problem_string(self
):
341 assert self
.status
is False
344 _("The solver returned one problem:", "The solver returned %(num)s problems:",
345 len(self
.problems
)) % { "num" : len(self
.problems
) },
349 for problem
in self
.problems
:
352 # Print information about the problem.
353 lines
.append(" #%d: %s" % (i
, problem
))
355 return "\n".join(lines
)
359 # If the solver succeeded, we return the transaction and return.
361 # Return a resulting Transaction.
362 t
= Transaction(solver
)
364 return transaction
.Transaction
.from_solver(self
.pakfire
, self
, t
)
366 # Do the problem handling...
367 problems
= solver
.get_problems(request
)
370 logger
.info(_("The solver returned one problem:", "The solver returned %(num)s problems:",
371 len(problems
)) % { "num" : len(problems
) })
374 for problem
in problems
:
377 # Print information about the problem to the user.
378 logger
.info(" #%d: %s" % (i
, problem
))
385 # Ask the user if he or she want to modify the request. If not, just exit.
386 if not util
.ask_user(_("Do you want to manually alter the request?")):
389 print _("You can now try to satisfy the solver by modifying your request.")
393 if len(problems
) > 1:
394 print _("Which problem to you want to resolve?")
396 print _("Press enter to try to re-solve the request.")
397 print "[1-%s]:" % len(problems
),
401 # If the user did not enter anything, we abort immediately.
405 # If the user did type anything else than an integer, we ask
412 # If the user entered an integer outside of range, we ask
415 problem
= problems
[answer
- 1]
423 solutions
= problem
.get_solutions()
425 if len(solutions
) == 1:
426 solution
= solutions
[0]
427 print _(" Solution: %s") % solution
430 if util
.ask_user("Do you accept the solution above?"):
432 print "XXX do something"
436 print _(" Solutions:")
438 for solution
in solutions
:
440 print " #%d: %s" % (i
, solution
)
447 # If the request was altered by the user, we try to solve it again
448 # and see what happens.
449 return self
.solve(request
, update
=update
, uninstall
=uninstall
,
450 allow_downgrade
=allow_downgrade
, interactive
=interactive
)