#!/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.1 @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.1" 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()]) 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, os.getpid()) 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')): arg='='+os.sep.join(os.path.realpath(arg).split(os.sep)[-2:]) matches=portage.db[portage.root]['vartree'].dbapi.match(arg) if matches: pkgs+=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 if binsize: quickpkg.eout.einfo('Packages now in %s:' % portage.db[portage.root]['bintree'].pkgdir) for pkg in sorted(pkgs): quickpkg.eout.einfo(binsize[pkg]) if fail: quickpkg.eout.ewarn('The following packages could not be found:') for pkg in sorted(fail): quickpkg.eout.ewarn(' %s'%(pkg,))