#!/usr/bin/python # # buildroot.py -- Build a chrootable build root from an RHL install tree # Copyright (C) 2004 NC State University # Written by Jack Neely # # SDG # # Parts Copyright (C) Seth Vidal # Parts Copyright (C) Red Hat, Inc # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import rpm import sys import os import os.path import getopt sys.path.append("/usr/share/rkmaint") from archwork import * def progressBar(current, total, endbar, message="Progress"): if len(message) > 20: message = message[0:18] + "..." if len(message) < 20: message = " " * (20-len(message)) + message percent = int(current * 100L / total) bars = '#' * (percent / 2) spaces = ' ' * (50 - (percent / 2)) s = "\r%s: %s%s %s%%" % (message, bars, spaces, percent) sys.stdout.write(s) if endbar: print def formatRequire (name, version, flags): # so stolen from Seth Vidal string = name if flags: if flags & (rpm.RPMSENSE_LESS | rpm.RPMSENSE_GREATER | rpm.RPMSENSE_EQUAL): string = string + " " if flags & rpm.RPMSENSE_LESS: string = string + "<" if flags & rpm.RPMSENSE_GREATER: string = string + ">" if flags & rpm.RPMSENSE_EQUAL: string = string + "=" string = string + " %s" % version return string callbackfilehandles = {} def install_callback(what, bytes, total, key, user): # just mostly stolen from Seth Vidal if what == rpm.RPMCALLBACK_TRANS_PROGRESS: progressBar(bytes, total, bytes == total, "Transaction") elif what == rpm.RPMCALLBACK_TRANS_STOP: pass elif what == rpm.RPMCALLBACK_TRANS_START: pass elif what == rpm.RPMCALLBACK_INST_OPEN_FILE: pass if key != None: hrd, l = key fd = os.open(os.path.join(user, l), os.O_RDONLY) callbackfilehandles[key] = fd return fd else: print "No header - huh?" elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE: if key != None: os.close(callbackfilehandles[key]) del callbackfilehandles[key] else: print "Error: Tried to close a file with no RPM header." elif what == rpm.RPMCALLBACK_INST_PROGRESS: progressBar(bytes, total, bytes == total, key[0][rpm.RPMTAG_NAME]) elif what == rpm.RPMCALLBACK_UNPACK_ERROR: print "Error unpacking RPM." elif what == rpm.RPMCALLBACK_CPIO_ERROR: print "Error in CPIO archive." def formatErrors(errors): # more stealage from Seth Vidal if errors: for ((name, version, release), (reqname, reqversion), flags, suggest, sense) in errors: print "package %s needs %s" % ( name, formatRequire(reqname, reqversion, flags)) sys.exit(1) def checkInstall(hlist, excludes): # check the list mainly for the proper arches arch = os.uname()[4] hbyname = {} for h, l in hlist: name = h[rpm.RPMTAG_NAME] # Ignore list if name in excludes: continue if not hbyname.has_key(name): hbyname[name] = (h, l) else: # we should have a arch conflict here sort it current_arch = hbyname[name][0][rpm.RPMTAG_ARCH] new_arch = h[rpm.RPMTAG_ARCH] best = betterarch(current_arch, new_arch) mybest = betterarch(best, arch) if (best == new_arch) and (mybest == arch): hbyname[name] = (h, l) hlist = [] for key in hbyname.keys(): hlist.append(hbyname[key]) return hlist def getHeader(ts, filename): fd = os.open(filename, os.O_RDONLY) header = ts.hdrFromFdno(fd) isSource = header[rpm.RPMTAG_SOURCEPACKAGE] os.close(fd) return (header, isSource) def buildroot(base, a, version, instPath, excludes, product): rpmdir = os.path.join(base, version, a, product, "RPMS") rpms = os.listdir(rpmdir) for i in ( '/var', '/var/lib', '/var/lib/rpm', '/tmp', '/dev', '/etc', '/etc/sysconfig', '/etc/sysconfig/network-scripts', '/etc/X11', '/root', '/dist' ): try: os.mkdir(instPath + i) except OSError, (errno, msg): #print "Error making path %s%s. Continuing." % (instPath, i) pass ts = rpm.TransactionSet(instPath) # we are installing into a chroot so we will not have previous keys ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES) headerlist = [] rpmCount = len(rpms) - 1 for i in rpms: j = rpms.index(i) progressBar(j, rpmCount, (j == rpmCount), "Building Transaction") try: h, isSource = getHeader(ts, os.path.join(rpmdir, i)) headerlist.append((h, i)) except: print "Error handling: " + i raise headerlist = checkInstall(headerlist, excludes) for h, l in headerlist: ts.addInstall(h, (h, l), 'i') print "Transaction set made." errors = ts.check() formatErrors(errors) ts.order() print "Dependancies check completely succesfully." print "Starting RPM transaction..." #rpm.RPMTRANS_FLAG_TEST errors = ts.run(install_callback, rpmdir) if errors: for (err, (n1, filename, n2)) in errors: print err print " (%s, %s, %s)" % (n1, filename, n2) sys.exit(1) print "Completed run of transaction set." del ts def usage(): print "Usage: " + sys.argv[0] + " -v ver -r arch -e trees -i install_path" print " -h, --help\t\tThis text" print " -v, --version\tVersion of tree" print " -r, --arch\t\tTree arch" print " -e, --tree\t\tBase location of trees" print " -i, --installpath\tPath to build root in" print " -p, --product\tProduct Name, default is 'RedHat'" print " --exclude\tPackage name to exclude" print sys.exit(0) def main(): """This a a script that will take a Red Hat Linux tree and create a build root that is chrootable into so that you can have a build machine that build for multiple distributions. This seems to work with RHL 7.x, not tested with earlier versions. It will not work with 8.x. Will need much modification. This script also will find conflicts and dep problems in your package sets.""" print "buildroot.py Copyright 2003 NC State University" print "Written by Jack Neely " print "This program is licensed under the GNU General Public License." args = sys.argv[1:] try: optlist, rpmlist = getopt.getopt(args, 'hv:r:e:i:p:', ['help', 'ver=', 'arch=', 'tree=', 'installpath=', 'exclude=', 'product=']) except getopt.error: usage() verarg = 0 archarg = 0 treearg = 0 instarg = 0 excludes = [] product = "RedHat" for o, a in optlist: if o in ("--product", "-p"): product = a if o in ("--help", "-h"): usage() if o in ("--ver", "-v"): version = a verarg = 1 if o in ("--arch", "-r"): arch = a archarg = 1 if o in ("--tree", "-e"): trees = a treearg = 1 if o in ("--installpath", "-i"): instarg = 1 instPath = a if o == "--exclude": excludes.append(a) if archarg+verarg+treearg+instarg != 4: print "Not enough or bad arguments." usage() if instPath == "/": print "Your target install path is /." print "Refusing to destroy your working linux install." print "Yes, this means I'm bombing out NOW!" sys.exit(42) buildroot(trees, arch, version, instPath, excludes, product) if __name__ == "__main__": main()