#!/usr/bin/python # # Copyright 2006 Martin Parm # Distributed under the terms of the GNU General Public License v2 # # $Header$ # Author: Martin Parm """A reimplementation of the quickpkg program. The main motivation for reimplenting quickpkg was to attend to U{bug #164655} (which results in e.g. U{bug #1642711}). By reimplementing quickpkg we also get the following new improvements: - Better integration with Portage by making direct use of the Portage API, thus trying to avoid any kind of hardcoding or implicite knowledge. - Faster execution as this implementation only loads (imports) Portage once, while the old implementation loads Portage once for each execution of C{portageq}. - By encapsulating the core quickpkg functionality it can easily be imported and reused in other programs. @author: Martin Parm @copyright: 2006 Martin Parm @license: GNU General Public License v2 @contact: parmus@diku.dk @version: 0.6 @note: This implementation of the quickpkg program differs from the original program in the following ways: - This implementation makes use of the C{Tarfile} Python module instead of the C{tar} program. - This only understands package atoms, not paths to the package database (e.g. C{/var/db/pkg/sys-apps/portage}). - This implementation does not show the size of the binary package (however it's easily implemented if asked for). - This implementation does not write a logfile """ __version__ = "0.6" class Quickpkg: """An encapsulation of the core quickpkg functionality.""" def __init__(self): import output import pwd import grp self.eout = output.EOutput() self.unames = dict([(entry[2],entry[0]) for entry in pwd.getpwall()]) self.gnames = dict([(entry[2],entry[0]) for entry in grp.getgrall()]) def __call__(self, pkg): """Make a binary package for Portage using files from the live filesystem. @param pkg: A complete package name with version and revision @type pkg: str @return: The path to the binary package @rtype: str @raise portage_exception.PackageNotFound: If the package isn't found in the Portage database. """ import sys import portage import portage_exception import xpak import tarfile import os self.eout.ebegin("Building package for %s" % pkg) sys.stdout.flush() s=pkg.split('/') db=portage.dblink(s[0], s[1], portage.root, mysettings=portage.settings) if not db.exists(): self.eout.eerror("Not found!") raise portage_exception.PackageNotFound(pkg) binpkg=portage.db[portage.root]['bintree'].getname(pkg) if not os.path.exists(os.path.dirname(binpkg)): os.makedirs(os.path.dirname(binpkg)) symlink=os.path.join(portage.db[portage.root]['bintree'].pkgdir, s[0], os.path.basename(binpkg)) if not os.path.exists(os.path.dirname(symlink)): os.makedirs(os.path.dirname(symlink)) contents=db.getcontents() tarball=tarfile.open(binpkg+'.$$', "w:bz2") for item in sorted(contents.keys()): if not os.path.exists(item): # Ignore ALL missing packages for now (due to bug #16162) # FIXME: Remove this when #16162 has been solved continue tarinfo=tarball.gettarinfo(item, './'+item) try: tarinfo.uname=self.unames[tarinfo.uid] except KeyError: tarinfo.uname=str(tarinfo.uid) self.unames[tarinfo.uid]=str(tarinfo.uid) try: tarinfo.gname=self.gnames[tarinfo.gid] except KeyError: tarinfo.gname=str(tarinfo.gid) self.gnames[tarinfo.gid]=str(tarinfo.gid) if contents[item][0]=='dir': tarinfo.linkname='' tarinfo.type=tarfile.DIRTYPE tarball.addfile(tarinfo) elif contents[item][0]=='obj': tarinfo.linkname='' tarinfo.type=tarfile.REGTYPE tarball.addfile(tarinfo, file(item)) else: tarball.addfile(tarinfo) tarball.close() xpak.tbz2(binpkg+'.$$').recompose(db.getpath()) os.rename(binpkg+'.$$', binpkg) if os.path.exists(symlink): # FIXME: This might not be a symlink at all os.remove(symlink) os.symlink(os.path.join('../All', os.path.basename(binpkg)), symlink) self.eout.eend(0) return binpkg if __name__ == '__main__': import sys def usage(): print "QUICKPKG ver %s" % __version__ print "USAGE: quickpkg " print " a pkg can be of the form:" print " - single depend-type atom ..." print " if portage can emerge it, quickpkg can make a package" print " for exact definitions of depend atoms, see ebuild(5)" print print "EXAMPLE:" print " quickpkg apache" print " package up apache, all versions of apache installed" print " quickpkg =apache-1.3.27-r1" print " package up apache, just version 1.3.27-r1" sys.exit(1) if len(sys.argv)<2: usage() quickpkg=Quickpkg() # Try to match up all packages import portage pkgs=[] for arg in sys.argv[1:]: try: matches=portage.db[portage.root]["vartree"].dbapi.match(arg) if not matches: print "Error: Could not find anything to match '%s'" % arg sys.exit(2) pkgs+=matches except KeyError, e: print e sys.exit(1) # Pack everything for pkg in pkgs: quickpkg(pkg)