]>
git.ipfire.org Git - pakfire.git/blob - pakfire/base.py
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 ###############################################################################
36 from constants
import *
39 class Pakfire(object):
41 (">=", satsolver
.REL_GE
,),
42 ("<=", satsolver
.REL_LE
,),
43 ("=" , satsolver
.REL_EQ
,),
44 ("<" , satsolver
.REL_LT
,),
45 (">" , satsolver
.REL_GT
,),
48 def __init__(self
, mode
=None, path
="/", configs
=[],
49 enable_repos
=None, disable_repos
=None,
50 distro_config
=None, **kwargs
):
53 assert mode
in ("normal", "builder", "server",)
56 # Check if we are operating as the root user.
57 self
.check_root_user()
59 # The path where we are operating in.
62 # Configure the instance of Pakfire we just started.
64 self
.path
= os
.path
.join(BUILD_ROOT
, util
.random_string())
66 elif mode
== "normal":
67 # check if we are actually running on an ipfire system.
69 self
.check_is_ipfire()
71 # Read configuration file(s)
72 self
.config
= config
.Config(type=mode
)
73 for filename
in configs
:
74 self
.config
.read(filename
)
75 # Assume, that all other keyword arguments are configuration
77 self
.config
.update(kwargs
)
80 logger
.setup_logging(self
.config
)
83 # Get more information about the distribution we are running
85 self
.distro
= distro
.Distribution(self
, distro_config
)
86 self
.pool
= satsolver
.Pool(self
.distro
.arch
)
87 self
.repos
= repository
.Repositories(self
,
88 enable_repos
=enable_repos
, disable_repos
=disable_repos
)
92 logger
.setup_logging()
94 def create_solver(self
):
95 return satsolver
.Solver(self
, self
.pool
)
97 def create_request(self
, builder
=False):
98 request
= satsolver
.Request(self
.pool
)
100 # Add multiinstall information.
101 for solv
in PAKFIRE_MULTIINSTALL
:
102 request
.noobsoletes(solv
)
106 def create_relation(self
, s
):
109 if s
.startswith("/"):
110 return satsolver
.Relation(self
.pool
, s
)
112 for pattern
, type in self
.RELATIONS
:
116 name
, version
= s
.split(pattern
, 1)
118 return satsolver
.Relation(self
.pool
, name
, version
, type)
120 return satsolver
.Relation(self
.pool
, s
)
123 if not self
.path
== "/":
130 # Get distribution information.
131 env
.update(self
.distro
.environ
)
136 def supported_arches(self
):
137 return self
.config
.supported_arches
142 A shortcut that indicates if the system is running in offline mode.
144 return self
.config
.get("offline", False)
146 def check_root_user(self
):
147 if not os
.getuid() == 0 or not os
.getgid() == 0:
148 raise Exception, "You must run pakfire as the root user."
150 def check_build_mode(self
):
152 Check if we are running in build mode.
153 Otherwise, raise an exception.
155 if not self
.mode
== "builder":
156 raise BuildError
, "Cannot build when not in build mode."
158 def check_host_arch(self
, arch
):
160 Check if we can build for arch.
162 # If no arch was given on the command line we build for our
163 # own arch which should always work.
167 if not self
.config
.host_supports_arch(arch
):
168 raise BuildError
, "Cannot build for the target architecture: %s" % arch
170 raise BuildError
, arch
172 def check_is_ipfire(self
):
173 return # XXX disabled for now
175 ret
= os
.path
.exists("/etc/ipfire-release")
178 raise NotAnIPFireSystemError
, "You can run pakfire only on an IPFire system"
182 # XXX just backwards compatibility
183 return self
.mode
== "builder"
185 def resolvdep(self
, requires
):
186 # Create a new request.
187 request
= self
.create_request()
189 req
= self
.create_relation(req
)
193 solver
= self
.create_solver()
194 t
= solver
.solve(request
)
199 logging
.info(_("Nothing to do"))
201 def install(self
, requires
, interactive
=True, logger
=None, **kwargs
):
203 logger
= logging
.getLogger()
205 # Create a new request.
206 request
= self
.create_request()
210 if req
.startswith("@"):
211 reqs
= self
.grouplist(req
[1:])
216 if not isinstance(req
, packages
.BinaryPackage
):
217 req
= self
.create_relation(req
)
222 solver
= self
.create_solver()
223 t
= solver
.solve(request
, **kwargs
)
227 raise DependencyError
229 logging
.info(_("Nothing to do"))
233 # Ask if the user acknowledges the transaction.
234 if not t
.cli_yesno():
238 t
.dump(logger
=logger
)
240 # Run the transaction.
243 def localinstall(self
, files
, yes
=None):
244 repo_name
= repo_desc
= "localinstall"
246 # Create a new repository that holds all packages we passed on
248 repo
= repository
.RepositoryDir(self
, repo_name
, repo_desc
,
249 os
.path
.join(LOCAL_TMP_PATH
, "repo_%s" % util
.random_string()))
251 # Register the repository.
252 self
.repos
.add_repo(repo
)
255 # Add all packages to the repository index.
257 repo
.collect_packages(file)
259 # Break if no packages were added at all.
261 logging
.critical(_("There are no packages to install."))
264 # Create a new request that installs all solvables from the
266 request
= self
.create_request()
267 for solv
in [p
.solvable
for p
in repo
]:
268 request
.install(solv
)
270 solver
= self
.create_solver()
271 t
= solver
.solve(request
)
273 # If solving was not possible, we exit here.
275 logging
.info(_("Nothing to do"))
279 # Ask the user if this is okay.
280 if not t
.cli_yesno():
287 # If okay, run the transcation.
291 # Remove the temporary copy of the repository we have created earlier.
293 self
.repos
.rem_repo(repo
)
295 def update(self
, pkgs
, check
=False):
297 check indicates, if the method should return after calculation
300 request
= self
.create_request()
302 # If there are given any packets on the command line, we will
303 # only update them. Otherwise, we update the whole system.
307 pkg
= self
.create_relation(pkg
)
312 solver
= self
.create_solver()
313 t
= solver
.solve(request
, update
=update
)
316 logging
.info(_("Nothing to do"))
318 # If we are running in check mode, we return a non-zero value to
319 # indicate, that there are no updates.
325 # Just exit here, because we won't do the transaction in this mode.
330 # Ask the user if the transaction is okay.
331 if not t
.cli_yesno():
334 # Run the transaction.
337 def remove(self
, pkgs
):
338 # Create a new request.
339 request
= self
.create_request()
341 pkg
= self
.create_relation(pkg
)
345 solver
= self
.create_solver()
346 t
= solver
.solve(request
, uninstall
=True)
349 logging
.info(_("Nothing to do"))
352 # Ask the user if okay.
353 if not t
.cli_yesno():
356 # Process the transaction.
359 def info(self
, patterns
):
362 # For all patterns we run a single search which returns us a bunch
363 # of solvables which are transformed into Package objects.
364 for pattern
in patterns
:
365 if os
.path
.exists(pattern
):
366 pkg
= packages
.open(self
, self
.repos
.dummy
, pattern
)
371 solvs
= self
.pool
.search(pattern
, satsolver
.SEARCH_GLOB
, "solvable:name")
374 pkg
= packages
.SolvPackage(self
, solv
)
382 def search(self
, pattern
):
385 for solv
in self
.pool
.search(pattern
, satsolver
.SEARCH_STRING|satsolver
.SEARCH_FILES
):
386 pkg
= packages
.SolvPackage(self
, solv
)
388 # Check, if a package with the name is already in the resultset
389 # and always replace older ones by more recent ones.
390 if pkgs
.has_key(pkg
.name
):
391 if pkgs
[pkg
.name
] < pkg
:
396 # Return a list of the packages, alphabetically sorted.
397 return sorted(pkgs
.values())
399 def groupinstall(self
, group
, **kwargs
):
400 self
.install("@%s" % group
, **kwargs
)
402 def grouplist(self
, group
):
405 for solv
in self
.pool
.search(group
, satsolver
.SEARCH_SUBSTRING
, "solvable:group"):
406 pkg
= packages
.SolvPackage(self
, solv
)
408 if group
in pkg
.groups
and not pkg
.name
in pkgs
:
409 pkgs
.append(pkg
.name
)
414 def build(pkg
, resultdirs
=None, shell
=False, install_test
=True, **kwargs
):
418 b
= builder
.BuildEnviron(pkg
, **kwargs
)
421 # Always include local repository.
422 resultdirs
.append(p
.repos
.local_build
.path
)
425 # Start to prepare the build environment by mounting
426 # the filesystems and extracting files.
430 b
.build(install_test
=install_test
)
432 # Copy-out all resultfiles
433 for resultdir
in resultdirs
:
437 b
.copy_result(resultdir
)
448 def _build(self
, pkg
, resultdir
, nodeps
=False, **kwargs
):
449 b
= builder
.Builder(self
, pkg
, resultdir
, **kwargs
)
454 raise BuildError
, _("Build command has failed.")
459 def shell(pkg
, **kwargs
):
460 b
= builder
.BuildEnviron(pkg
, **kwargs
)
468 def dist(self
, pkgs
, resultdirs
=None):
472 # Always include local repository
473 resultdirs
.append(self
.repos
.local_build
.path
)
476 pkg
= packages
.Makefile(self
, pkg
)
480 def provides(self
, patterns
):
482 for pattern
in patterns
:
483 for pkg
in self
.repos
.whatprovides(pattern
):
491 def repo_create(self
, path
, input_paths
, type="binary"):
492 assert type in ("binary", "source",)
494 repo
= repository
.RepositoryDir(
497 description
="New repository.",
502 for input_path
in input_paths
:
503 repo
.collect_packages(input_path
)
510 return [r
for r
in self
.repos
]
513 logging
.debug("Cleaning up everything...")
515 # Clean up repository caches.
518 def check(self
, downgrade
=True, uninstall
=True):
520 Try to fix any errors in the system.
522 # Detect any errors in the dependency tree.
523 # For that we create an empty request and solver and try to solve
525 request
= self
.create_request()
526 solver
= self
.create_solver()
528 # XXX the solver does crash if we call it with fix_system=1,
529 # allow_downgrade=1 and uninstall=1. Need to fix this.
530 allow_downgrade
= False
533 t
= solver
.solve(request
, fix_system
=True, allow_downgrade
=downgrade
,
537 logging
.info(_("Everything is fine."))
540 # Ask the user if okay.
541 if not t
.cli_yesno():
544 # Process the transaction.