# Copyright 1999-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Id$ from _emerge.EbuildPhase import EbuildPhase from _emerge.BinpkgFetcher import BinpkgFetcher from _emerge.BinpkgExtractorAsync import BinpkgExtractorAsync from _emerge.CompositeTask import CompositeTask from _emerge.BinpkgVerifier import BinpkgVerifier from _emerge.EbuildMerge import EbuildMerge from _emerge.EbuildBuildDir import EbuildBuildDir from portage.util import writemsg import portage from portage import os from portage import _encodings from portage import _unicode_encode import codecs import logging from portage.output import colorize class Binpkg(CompositeTask): __slots__ = ("find_blockers", "ldpath_mtimes", "logger", "opts", "pkg", "pkg_count", "prefetcher", "settings", "world_atom") + \ ("_bintree", "_build_dir", "_ebuild_path", "_fetched_pkg", "_image_dir", "_infloc", "_pkg_path", "_tree", "_verify") def _writemsg_level(self, msg, level=0, noiselevel=0): if not self.background: portage.util.writemsg_level(msg, level=level, noiselevel=noiselevel) log_path = self.settings.get("PORTAGE_LOG_FILE") if log_path is not None: f = codecs.open(_unicode_encode(log_path, encoding=_encodings['fs'], errors='strict'), mode='a', encoding=_encodings['content'], errors='replace') try: f.write(msg) finally: f.close() def _start(self): pkg = self.pkg settings = self.settings settings.setcpv(pkg) self._tree = "bintree" self._bintree = self.pkg.root_config.trees[self._tree] self._verify = not self.opts.pretend dir_path = os.path.join(settings["PORTAGE_TMPDIR"], "portage", pkg.category, pkg.pf) self._build_dir = EbuildBuildDir(dir_path=dir_path, pkg=pkg, settings=settings) self._image_dir = os.path.join(dir_path, "image") self._infloc = os.path.join(dir_path, "build-info") self._ebuild_path = os.path.join(self._infloc, pkg.pf + ".ebuild") settings["EBUILD"] = self._ebuild_path debug = settings.get("PORTAGE_DEBUG") == "1" portage.doebuild_environment(self._ebuild_path, "setup", settings["ROOT"], settings, debug, 1, self._bintree.dbapi) settings.configdict["pkg"]["EMERGE_FROM"] = pkg.type_name # The prefetcher has already completed or it # could be running now. If it's running now, # wait for it to complete since it holds # a lock on the file being fetched. The # portage.locks functions are only designed # to work between separate processes. Since # the lock is held by the current process, # use the scheduler and fetcher methods to # synchronize with the fetcher. prefetcher = self.prefetcher if prefetcher is None: pass elif not prefetcher.isAlive(): prefetcher.cancel() elif prefetcher.poll() is None: waiting_msg = ("Fetching '%s' " + \ "in the background. " + \ "To view fetch progress, run `tail -f " + \ "/var/log/emerge-fetch.log` in another " + \ "terminal.") % prefetcher.pkg_path msg_prefix = colorize("GOOD", " * ") from textwrap import wrap waiting_msg = "".join("%s%s\n" % (msg_prefix, line) \ for line in wrap(waiting_msg, 65)) if not self.background: writemsg(waiting_msg, noiselevel=-1) self._current_task = prefetcher prefetcher.addExitListener(self._prefetch_exit) return self._prefetch_exit(prefetcher) def _prefetch_exit(self, prefetcher): pkg = self.pkg pkg_count = self.pkg_count if not (self.opts.pretend or self.opts.fetchonly): self._build_dir.lock() # If necessary, discard old log so that we don't # append to it. self._build_dir.clean_log() # Initialze PORTAGE_LOG_FILE. portage.prepare_build_dirs(self.settings["ROOT"], self.settings, 1) fetcher = BinpkgFetcher(background=self.background, logfile=self.settings.get("PORTAGE_LOG_FILE"), pkg=self.pkg, pretend=self.opts.pretend, scheduler=self.scheduler) pkg_path = fetcher.pkg_path self._pkg_path = pkg_path if self.opts.getbinpkg and self._bintree.isremote(pkg.cpv): msg = " --- (%s of %s) Fetching Binary (%s::%s)" %\ (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg_path) short_msg = "emerge: (%s of %s) %s Fetch" % \ (pkg_count.curval, pkg_count.maxval, pkg.cpv) self.logger.log(msg, short_msg=short_msg) self._start_task(fetcher, self._fetcher_exit) return self._fetcher_exit(fetcher) def _fetcher_exit(self, fetcher): # The fetcher only has a returncode when # --getbinpkg is enabled. if fetcher.returncode is not None: self._fetched_pkg = True if self._default_exit(fetcher) != os.EX_OK: self._unlock_builddir() self.wait() return if self.opts.pretend: self._current_task = None self.returncode = os.EX_OK self.wait() return verifier = None if self._verify: logfile = None if self.background: logfile = self.settings.get("PORTAGE_LOG_FILE") verifier = BinpkgVerifier(background=self.background, logfile=logfile, pkg=self.pkg) self._start_task(verifier, self._verifier_exit) return self._verifier_exit(verifier) def _verifier_exit(self, verifier): if verifier is not None and \ self._default_exit(verifier) != os.EX_OK: self._unlock_builddir() self.wait() return logger = self.logger pkg = self.pkg pkg_count = self.pkg_count pkg_path = self._pkg_path if self._fetched_pkg: self._bintree.inject(pkg.cpv, filename=pkg_path) logfile = self.settings.get("PORTAGE_LOG_FILE") if logfile is not None and os.path.isfile(logfile): # Remove fetch log after successful fetch. try: os.unlink(logfile) except OSError: pass if self.opts.fetchonly: self._current_task = None self.returncode = os.EX_OK self.wait() return msg = " === (%s of %s) Merging Binary (%s::%s)" % \ (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg_path) short_msg = "emerge: (%s of %s) %s Merge Binary" % \ (pkg_count.curval, pkg_count.maxval, pkg.cpv) logger.log(msg, short_msg=short_msg) phase = "clean" settings = self.settings ebuild_phase = EbuildPhase(background=self.background, pkg=pkg, phase=phase, scheduler=self.scheduler, settings=settings, tree=self._tree) self._start_task(ebuild_phase, self._clean_exit) def _clean_exit(self, clean_phase): if self._default_exit(clean_phase) != os.EX_OK: self._unlock_builddir() self.wait() return dir_path = self._build_dir.dir_path infloc = self._infloc pkg = self.pkg pkg_path = self._pkg_path dir_mode = 0o755 for mydir in (dir_path, self._image_dir, infloc): portage.util.ensure_dirs(mydir, uid=portage.data.portage_uid, gid=portage.data.portage_gid, mode=dir_mode) # This initializes PORTAGE_LOG_FILE. portage.prepare_build_dirs(self.settings["ROOT"], self.settings, 1) self._writemsg_level(">>> Extracting info\n") pkg_xpak = portage.xpak.tbz2(self._pkg_path) check_missing_metadata = ("CATEGORY", "PF") missing_metadata = set() for k in check_missing_metadata: v = pkg_xpak.getfile(_unicode_encode(k, encoding=_encodings['repo.content'])) if not v: missing_metadata.add(k) pkg_xpak.unpackinfo(infloc) for k in missing_metadata: if k == "CATEGORY": v = pkg.category elif k == "PF": v = pkg.pf else: continue f = codecs.open(_unicode_encode(os.path.join(infloc, k), encoding=_encodings['fs'], errors='strict'), mode='w', encoding=_encodings['content'], errors='replace') try: f.write(v + "\n") finally: f.close() # Store the md5sum in the vdb. f = codecs.open(_unicode_encode(os.path.join(infloc, 'BINPKGMD5'), encoding=_encodings['fs'], errors='strict'), mode='w', encoding=_encodings['content'], errors='strict') try: f.write(str(portage.checksum.perform_md5(pkg_path)) + "\n") finally: f.close() # This gives bashrc users an opportunity to do various things # such as remove binary packages after they're installed. settings = self.settings settings.setcpv(self.pkg) settings["PORTAGE_BINPKG_FILE"] = pkg_path settings.backup_changes("PORTAGE_BINPKG_FILE") phase = "setup" setup_phase = EbuildPhase(background=self.background, pkg=self.pkg, phase=phase, scheduler=self.scheduler, settings=settings, tree=self._tree) setup_phase.addExitListener(self._setup_exit) self._current_task = setup_phase self.scheduler.scheduleSetup(setup_phase) def _setup_exit(self, setup_phase): if self._default_exit(setup_phase) != os.EX_OK: self._unlock_builddir() self.wait() return extractor = BinpkgExtractorAsync(background=self.background, image_dir=self._image_dir, pkg=self.pkg, pkg_path=self._pkg_path, logfile=self.settings.get("PORTAGE_LOG_FILE"), scheduler=self.scheduler) self._writemsg_level(">>> Extracting %s\n" % self.pkg.cpv) self._start_task(extractor, self._extractor_exit) def _extractor_exit(self, extractor): if self._final_exit(extractor) != os.EX_OK: self._unlock_builddir() self._writemsg_level("!!! Error Extracting '%s'\n" % \ self._pkg_path, noiselevel=-1, level=logging.ERROR) self.wait() def _unlock_builddir(self): if self.opts.pretend or self.opts.fetchonly: return portage.elog.elog_process(self.pkg.cpv, self.settings) self._build_dir.unlock() def install(self): # This gives bashrc users an opportunity to do various things # such as remove binary packages after they're installed. settings = self.settings settings["PORTAGE_BINPKG_FILE"] = self._pkg_path settings.backup_changes("PORTAGE_BINPKG_FILE") merge = EbuildMerge(find_blockers=self.find_blockers, ldpath_mtimes=self.ldpath_mtimes, logger=self.logger, pkg=self.pkg, pkg_count=self.pkg_count, pkg_path=self._pkg_path, scheduler=self.scheduler, settings=settings, tree=self._tree, world_atom=self.world_atom) try: retval = merge.execute() finally: settings.pop("PORTAGE_BINPKG_FILE", None) self._unlock_builddir() return retval