]>
git.ipfire.org Git - people/stevee/pakfire.git/blob - python/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 ###############################################################################
40 log
= logging
.getLogger("pakfire")
42 from config
import Config
43 from constants
import *
46 class Pakfire(object):
49 def __init__(self
, path
="/", config
=None, configs
=None, arch
=None, **kwargs
):
50 # Indicates if this instance has already been initialized.
51 self
.initialized
= False
53 # Check if we are operating as the root user.
54 self
.check_root_user()
56 # The path where we are operating in.
59 # check if we are actually running on an ipfire system.
60 if not self
.mode
and self
.path
== "/":
61 self
.check_is_ipfire()
63 # Get the configuration.
67 self
.config
= self
._load
_config
(configs
)
69 # Update configuration with additional arguments.
70 for section
, settings
in kwargs
.items():
71 self
.config
.update(section
, settings
)
73 # Dump the configuration.
76 # Initialize the keyring.
77 self
.keyring
= keyring
.Keyring(self
)
79 # Get more information about the distribution we are running
81 self
.distro
= distro
.Distribution(self
.config
.get_distro_conf())
83 self
.distro
.arch
= arch
85 self
.pool
= satsolver
.Pool(self
.distro
.arch
)
86 self
.repos
= repository
.Repositories(self
)
90 Initialize pakfire instance.
95 # Initialize repositories.
96 self
.repos
.initialize()
98 self
.initialized
= True
100 def _load_config(self
, files
=None):
102 This method loads all needed configuration files.
104 return config
.Config(files
=files
)
108 logger
.setup_logging()
111 self
.repos
.shutdown()
113 self
.initialized
= False
116 def supported_arches(self
):
117 return system
.supported_arches
122 A shortcut that indicates if the system is running in offline mode.
124 return self
.config
.get("downloader", "offline", False)
126 def check_root_user(self
):
127 if not os
.getuid() == 0 or not os
.getgid() == 0:
128 raise Exception, "You must run pakfire as the root user."
130 def check_host_arch(self
, arch
):
132 Check if we can build for arch.
134 # If no arch was given on the command line we build for our
135 # own arch which should always work.
139 if not system
.host_supports_arch(arch
):
140 raise BuildError
, "Cannot build for the target architecture: %s" % arch
142 raise BuildError
, arch
144 def check_is_ipfire(self
):
145 ret
= os
.path
.exists("/etc/ipfire-release")
148 raise NotAnIPFireSystemError
, "You can run pakfire only on an IPFire system"
152 # XXX just backwards compatibility
153 return self
.mode
== "builder"
155 def install(self
, requires
, interactive
=True, logger
=None, signatures_mode
=None, **kwargs
):
156 # Initialize this pakfire instance.
160 logger
= logging
.getLogger("pakfire")
162 # Pointer to temporary repository.
165 # Sort out what we got...
166 download_packages
= []
171 if isinstance(req
, packages
.Package
):
172 relations
.append(req
)
175 # This looks like a file.
176 elif req
.endswith(".%s" % PACKAGE_EXTENSION
) and os
.path
.exists(req
) and os
.path
.isfile(req
):
177 local_packages
.append(req
)
181 elif req
.startswith("http://") or req
.startswith("https://") or req
.startswith("ftp://"):
182 download_packages
.append(req
)
185 # We treat the rest as relations. The solver will return any errors.
186 relations
.append(req
)
188 # Redefine requires, which will be the list that will be passed to the
193 # If we have got files to install, we need to create a temporary repository
194 # called 'localinstall'.
196 if local_packages
or download_packages
:
197 repo
= repository
.RepositoryDir(self
, "localinstall", _("Local install repository"),
198 os
.path
.join(LOCAL_TMP_PATH
, "repo_%s" % util
.random_string()))
200 # Register the repository.
201 self
.repos
.add_repo(repo
)
204 for download_package
in download_packages
:
205 repo
.download_package(download_package
)
207 # Add all packages to the repository index.
208 repo
.add_packages(local_packages
)
210 # Add all packages to the requires.
214 request
= self
.pool
.create_request(install
=requires
)
215 solver
= self
.pool
.solve(request
, logger
=logger
, interactive
=interactive
, **kwargs
)
217 # Create the transaction.
218 t
= transaction
.Transaction
.from_solver(self
, solver
)
219 t
.dump(logger
=logger
)
221 # Ask if the user acknowledges the transaction.
222 if interactive
and not t
.cli_yesno():
225 # Run the transaction.
226 t
.run(logger
=logger
, signatures_mode
=signatures_mode
)
230 # Remove the temporary repository we have created earlier.
232 self
.repos
.rem_repo(repo
)
234 def reinstall(self
, pkgs
, strict
=False, logger
=None):
236 Reinstall one or more packages.
238 If strict is True, only a package with excatly the same UUID
239 will replace the currently installed one.
241 # Initialize this pakfire instance.
245 logger
= logging
.getLogger("pakfire")
247 # XXX it is possible to install packages without fulfulling
253 for pkg
in self
.repos
.whatprovides(pattern
):
254 # Do not reinstall non-installed packages.
255 if not pkg
.is_installed():
261 logger
.warning(_("Could not find any installed package providing \"%s\".") \
263 elif len(_pkgs
) == 1:
264 reinstall_pkgs
.append(_pkgs
[0])
265 #t.add("reinstall", _pkgs[0])
267 logger
.warning(_("Multiple reinstall candidates for \"%(pattern)s\": %(pkgs)s") \
268 % { "pattern" : pattern
, "pkgs" : ", ".join(p
.friendly_name
for p
in sorted(_pkgs
)) })
270 if not reinstall_pkgs
:
271 logger
.info(_("Nothing to do"))
274 # Packages we want to replace.
275 # Contains a tuple with the old and the new package.
278 # Find the package that is installed in a remote repository to
279 # download it again and re-install it. We need that.
280 for pkg
in reinstall_pkgs
:
281 # Collect all candidates in here.
284 provides
= "%s=%s" % (pkg
.name
, pkg
.friendly_version
)
285 for _pkg
in self
.repos
.whatprovides(provides
):
286 if _pkg
.is_installed():
290 if pkg
.uuid
== _pkg
.uuid
:
296 logger
.warning(_("Could not find package %s in a remote repository.") % \
299 # Sort packages to reflect repository priorities, etc...
300 # and take the best (first) one.
303 # Re-install best package and cleanup the old one.
304 pkgs
.append((pkg
, _pkgs
[0]))
306 # Eventually, create a request.
310 for old
, new
in pkgs
:
311 if old
.uuid
== new
.uuid
:
312 _pkgs
.append((old
, new
))
315 # Create a new request.
316 request
= self
.pool
.create_request()
318 # Install the new package, the old will
319 # be cleaned up automatically.
320 request
.install(new
.solvable
)
323 solver
= self
.pool
.solve(request
)
324 t
= transaction
.Transaction
.from_solver(self
, solver
)
326 # Create new transaction.
327 t
= transaction
.Transaction(self
)
329 for old
, new
in _pkgs
:
330 # Install the new package and remove the old one.
331 t
.add(actions
.ActionReinstall
.type, new
)
332 t
.add(actions
.ActionCleanup
.type, old
)
337 logger
.info(_("Nothing to do"))
340 t
.dump(logger
=logger
)
342 if not t
.cli_yesno():
347 def update(self
, pkgs
=None, check
=False, excludes
=None, interactive
=True, logger
=None, sync
=False, **kwargs
):
349 check indicates, if the method should return after calculation
352 # Initialize this pakfire instance.
356 logger
= logging
.getLogger("pakfire")
358 # If there are given any packets on the command line, we will
359 # only update them. Otherwise, we update the whole system.
364 request
= self
.pool
.create_request(update
=pkgs
, updateall
=updateall
)
366 # Exclude packages that should not be updated.
367 for exclude
in excludes
or []:
368 logger
.info(_("Excluding %s.") % exclude
)
370 exclude
= self
.pool
.create_relation(exclude
)
371 request
.lock(exclude
)
373 # Update or downgrade to the latest version of all packages
374 # in the enabled repositories.
377 "allow_downgrade" : True,
378 "allow_uninstall" : True,
381 solver
= self
.pool
.solve(request
, logger
=logger
, **kwargs
)
383 if not solver
.status
:
384 logger
.info(_("Nothing to do"))
386 # If we are running in check mode, we return a non-zero value to
387 # indicate, that there are no updates.
393 # Create the transaction.
394 t
= transaction
.Transaction
.from_solver(self
, solver
)
395 t
.dump(logger
=logger
)
397 # Just exit here, because we won't do the transaction in this mode.
401 # Ask the user if the transaction is okay.
402 if interactive
and not t
.cli_yesno():
405 # Run the transaction.
408 def downgrade(self
, pkgs
, logger
=None, **kwargs
):
412 logger
= logging
.getLogger("pakfire")
414 # Initialize this pakfire instance.
417 # Create a new request.
418 request
= self
.pool
.create_request()
423 for pkg
in self
.repos
.whatprovides(pattern
):
424 # Only consider installed packages.
425 if not pkg
.is_installed():
428 if best
and pkg
> best
:
434 logger
.warning(_("\"%s\" package does not seem to be installed.") % pattern
)
436 rel
= self
.pool
.create_relation("%s < %s" % (best
.name
, best
.friendly_version
))
440 solver
= self
.pool
.solve(request
, allow_downgrade
=True, **kwargs
)
441 assert solver
.status
is True
443 # Create the transaction.
444 t
= transaction
.Transaction
.from_solver(self
, solver
)
445 t
.dump(logger
=logger
)
448 logger
.info(_("Nothing to do"))
451 if not t
.cli_yesno():
456 def remove(self
, pkgs
, logger
=None):
458 logger
= logging
.getLogger("pakfire")
460 # Initialize this pakfire instance.
463 # Create a new request.
464 request
= self
.pool
.create_request(remove
=pkgs
)
467 solver
= self
.pool
.solve(request
, allow_uninstall
=True)
468 assert solver
.status
is True
470 # Create the transaction.
471 t
= transaction
.Transaction
.from_solver(self
, solver
)
475 log
.info(_("Nothing to do"))
478 # Ask the user if okay.
479 if not t
.cli_yesno():
482 # Process the transaction.
485 def info(self
, patterns
):
486 # Initialize this pakfire instance.
491 # For all patterns we run a single search which returns us a bunch
492 # of solvables which are transformed into Package objects.
493 for pattern
in patterns
:
494 if os
.path
.exists(pattern
) and not os
.path
.isdir(pattern
):
495 pkg
= packages
.open(self
, self
.repos
.dummy
, pattern
)
500 solvs
= self
.pool
.search(pattern
, satsolver
.SEARCH_GLOB
, "solvable:name")
503 pkg
= packages
.SolvPackage(self
, solv
)
511 def search(self
, pattern
):
512 # Initialize this pakfire instance.
517 for solv
in self
.pool
.search(pattern
, satsolver
.SEARCH_STRING|satsolver
.SEARCH_FILES
):
518 pkg
= packages
.SolvPackage(self
, solv
)
520 # Check, if a package with the name is already in the resultset
521 # and always replace older ones by more recent ones.
522 if pkgs
.has_key(pkg
.name
):
523 if pkgs
[pkg
.name
] < pkg
:
528 # Return a list of the packages, alphabetically sorted.
529 return sorted(pkgs
.values())
531 def groupinstall(self
, group
, **kwargs
):
532 self
.install("@%s" % group
, **kwargs
)
534 def grouplist(self
, group
):
535 # Initialize this pakfire instance.
538 return self
.pool
.grouplist(group
)
540 def provides(self
, patterns
):
541 # Initialize this pakfire instance.
545 for pattern
in patterns
:
546 for pkg
in self
.pool
.whatprovides(self
, pattern
):
557 def resolvdep(self
, pkg
):
558 # Initialize this pakfire instance.
561 return self
.pool
.resolvdep(self
, pkg
)
564 # Initialize this pakfire instance.
567 return [r
for r
in self
.repos
]
570 # Initialize this pakfire instance.
573 log
.debug("Cleaning up everything...")
575 # Clean up repository caches.
578 def check(self
, allow_downgrade
=True, allow_uninstall
=True):
580 Try to fix any errors in the system.
582 # Initialize this pakfire instance.
585 # Detect any errors in the dependency tree.
586 # For that we create an empty request and solver and try to solve
588 request
= self
.pool
.create_request()
591 solver
= self
.pool
.solve(
593 allow_downgrade
=allow_downgrade
,
594 allow_uninstall
=allow_uninstall
,
597 if solver
.status
is False:
598 log
.info(_("Everything is fine."))
601 # Create the transaction.
602 t
= transaction
.Transaction
.from_solver(self
, solver
)
605 # Ask the user if okay.
606 if not t
.cli_yesno():
609 # Process the transaction.
612 def build(self
, makefile
, resultdir
, stages
=None, **kwargs
):
613 b
= builder
.Builder(self
, makefile
, resultdir
, **kwargs
)
616 b
.build(stages
=stages
)
619 raise BuildError
, _("Build command has failed.")
622 # If the build was successful, cleanup all temporary files.
625 def dist(self
, pkg
, resultdir
):
626 pkg
= packages
.Makefile(self
, pkg
)
628 return pkg
.dist(resultdir
=resultdir
)
631 class PakfireBuilder(Pakfire
):
634 def __init__(self
, distro_name
=None, *args
, **kwargs
):
635 self
.distro_name
= distro_name
638 "path" : os
.path
.join(BUILD_ROOT
, util
.random_string()),
641 Pakfire
.__init
__(self
, *args
, **kwargs
)
643 # Let's see what is our host distribution.
644 self
.host_distro
= distro
.Distribution()
646 def _load_config(self
, files
=None):
647 c
= config
.ConfigBuilder(files
=files
)
649 if self
.distro_name
is None:
650 self
.distro_name
= c
.get("builder", "distro", None)
653 c
.load_distro_config(self
.distro_name
)
655 if not c
.has_distro_conf():
656 log
.error(_("You have not set the distribution for which you want to build."))
657 log
.error(_("Please do so in builder.conf or on the CLI."))
658 raise ConfigError
, _("Distribution configuration is missing.")
662 def build(self
, pkg
, resultdirs
=None, shell
=False, install_test
=True, after_shell
=False, **kwargs
):
663 # As the BuildEnviron is only able to handle source packages, we must package makefiles.
664 if pkg
.endswith(".%s" % MAKEFILE_EXTENSION
):
665 pkg
= self
.dist(pkg
, resultdir
=LOCAL_TMP_PATH
)
667 b
= builder
.BuildEnviron(self
, pkg
, **kwargs
)
670 # Start to prepare the build environment by mounting
671 # the filesystems and extracting files.
676 b
.build(install_test
=install_test
)
679 # Raise the error, if the user does not want to
684 # Run a shell to debug the issue.
687 # Copy-out all resultfiles if the build was successful.
691 # Always include local repository.
692 resultdirs
.append(self
.repos
.local_build
.path
)
694 for resultdir
in resultdirs
:
698 b
.copy_result(resultdir
)
700 # If the user requests a shell after a successful build,
708 def shell(self
, pkg
, **kwargs
):
709 # As the BuildEnviron is only able to handle source packages, we must package makefiles.
710 if pkg
and pkg
.endswith(".%s" % MAKEFILE_EXTENSION
):
711 pkg
= self
.dist(pkg
, resultdir
=LOCAL_TMP_PATH
)
713 b
= builder
.BuildEnviron(self
, pkg
, **kwargs
)
719 b
.build(prepare
=True)
728 class PakfireClient(Pakfire
):
732 class PakfireServer(Pakfire
):
735 def repo_create(self
, path
, input_paths
, name
=None, key_id
=None, type="binary"):
736 assert type in ("binary", "source",)
739 name
= _("New repository")
741 # Create new repository.
742 repo
= repository
.RepositoryDir(self
, name
=name
, description
="New repository.",
743 path
=path
, key_id
=key_id
)
746 repo
.add_packages(input_paths
)
748 # Write metadata to disk.
751 # Return the new repository.
755 class PakfireKey(Pakfire
):