#!/usr/bin/python # # Copyright 2006 Martin Parm # Distributed under the terms of the GNU General Public License v2 # # $Header: /var/diku/cvs/pro/dipo-ng/bin/quickpkg.py,v 1.1 2007/03/09 14:58:47 parmus Exp $ # 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.7 @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 implementation does not write a logfile """ __version__ = "0.7" class Quickpkg: """An encapsulation of the core quickpkg functionality.""" def __init__(self): import os, pwd, grp, output 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()]) self.mypid = os.getpid() def __call__(self, cpv): """Make a binary package for Portage using files from the live filesystem. @param cpv: A complete package name with version and revision @type cpv: 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 portage, portage_exception, xpak, tarfile self.eout.ebegin('Building package for %s'%(cpv,)) cat, pkg = portage.catsplit(cpv) db=portage.dblink(cat, pkg, portage.root, mysettings=portage.settings) if not db.exists(): self.eout.eerror('Not found!') raise portage_exception.PackageNotFound(cpv) portage.db[portage.root]['bintree'].prevent_collision(cpv) binpkg=portage.db[portage.root]['bintree'].getname(cpv) 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, cat, os.path.basename(binpkg)) if not os.path.exists(os.path.dirname(symlink)): os.makedirs(os.path.dirname(symlink)) tmppkg='%s.%d'%(binpkg, self.mypid) contents=db.getcontents() tarball=tarfile.open(tmppkg, '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(tmppkg).recompose(db.getpath()) os.rename(tmppkg, 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 import os def usage(): print "QUICKPKG ver %s"%(__version__,) print "USAGE: quickpkg " print " a pkg can be of the form:" print " - /var/db/pkg///" 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 /var/db/pkg/net-www/apache-1.3.27-r1" print " package up apache, just version 1.3.27-r1" 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) def sizestr(size): format = '%d%sB' for letter in 'KMGT': size/=1024.0 if size<1024.0: break format = '%.1f%sB' return format%(size, letter) if len(sys.argv)<2: usage() quickpkg=Quickpkg() # Try to match up all packages import portage pkgs=[] fail=[] for arg in sys.argv[1:]: try: if os.path.isfile(os.path.join(arg,'CONTENTS')): real = os.sep for chunk in arg.split(os.sep): cur = os.path.join(real, chunk) if os.path.islink(cur): real = os.path.join(real, os.readlink(cur)) else: real = cur pkgs.append(os.sep.join(real.split(os.sep)[-2:])) else: matches=portage.db[portage.root]['vartree'].dbapi.match(arg) if len(matches): pkgs.extend(matches) else: quickpkg.eout.eerror("Could not find anything to match '%s'; skipping"%(arg,)) fail.append(arg) except KeyError, e: print e sys.exit(1) # Pack everything binsize={} for pkg in pkgs: binsize[pkg]='%s: %s'%(os.path.basename(pkg), sizestr(os.path.getsize(quickpkg(pkg)))); print quickpkg.eout.einfo('Packages now in %s:' % portage.db[portage.root]['bintree'].pkgdir) for pkg in sorted(pkgs): quickpkg.eout.einfo(binsize[pkg]) if len(fail): quickpkg.eout.ewarn('The following packages could not be found:') for pkg in sorted(fail): quickpkg.eout.ewarn(' %s'%(pkg,))